From 0060f8bf6362961097c1b580493aea33e5e961cb Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Wed, 12 Oct 2011 12:13:45 -0700 Subject: Fix bug 5449931 - Wrong theme for icon menus IconMenuPresenter should always use its own theme for the system inflater. Change-Id: Iacadce37d62a60cdf6220d82f1178098ed92d51e --- core/java/com/android/internal/view/menu/IconMenuPresenter.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/java/com/android/internal/view/menu/IconMenuPresenter.java b/core/java/com/android/internal/view/menu/IconMenuPresenter.java index 3b1decd9a555..2439b5df3acc 100644 --- a/core/java/com/android/internal/view/menu/IconMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/IconMenuPresenter.java @@ -22,7 +22,6 @@ import android.os.Bundle; import android.os.Parcelable; import android.util.SparseArray; import android.view.ContextThemeWrapper; -import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -44,15 +43,14 @@ public class IconMenuPresenter extends BaseMenuPresenter { private static final String OPEN_SUBMENU_KEY = "android:menu:icon:submenu"; public IconMenuPresenter(Context context) { - super(context, com.android.internal.R.layout.icon_menu_layout, + super(new ContextThemeWrapper(context, com.android.internal.R.style.Theme_IconMenu), + com.android.internal.R.layout.icon_menu_layout, com.android.internal.R.layout.icon_menu_item_layout); } @Override public void initForMenu(Context context, MenuBuilder menu) { - mContext = new ContextThemeWrapper(context, com.android.internal.R.style.Theme_IconMenu); - mInflater = LayoutInflater.from(mContext); - mMenu = menu; + super.initForMenu(context, menu); mMaxItems = -1; } -- cgit v1.2.3-59-g8ed1b From 46f335a494a2409cd15a6be44930ef5dbfdcc93f Mon Sep 17 00:00:00 2001 From: Brian Colonna Date: Thu, 13 Oct 2011 22:43:28 -0400 Subject: Fixed problem where Face Unlock area was showing I introduced a bug cl https://android-git.corp.google.com/g/#/c/141926 that caused black box to hide lock pattern even when not using Face Unlock. Fixed by adding the corresponding check to make sure Face Unlock is being used. Change-Id: I9c429c99d7db4d1ab831080f23a1e10123dbfe3e --- .../android/internal/policy/impl/LockPatternKeyguardView.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java index d1bb8d1176f2..fce209ef4bb7 100644 --- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java +++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java @@ -334,7 +334,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler stopAndUnbindFromFaceLock(); // Continue showing FaceLock area until dialer comes up - showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_EMERGENCY_DIALER_TIMEOUT); + if (mLockPatternUtils.usingBiometricWeak() && + mLockPatternUtils.isBiometricWeakInstalled()) { + showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_EMERGENCY_DIALER_TIMEOUT); + } pokeWakelock(EMERGENCY_CALL_TIMEOUT); if (TelephonyManager.getDefault().getCallState() @@ -535,7 +538,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler bindToFaceLock(); // Show FaceLock area, but only for a little bit so lockpattern will become visible if // FaceLock fails to start or crashes - showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_SERVICE_TIMEOUT); + if (mLockPatternUtils.usingBiometricWeak() && + mLockPatternUtils.isBiometricWeakInstalled()) { + showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_SERVICE_TIMEOUT); + } } else { hideFaceLockArea(); } -- cgit v1.2.3-59-g8ed1b From 73182fc8213ad4fdc548463a637c593d1f22dffe Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 21 Oct 2011 15:18:28 -0700 Subject: mDirtyRegion is single threaded, but could be accessed from a hwc thread We now have mInvalidateRegion which holds the region to invalidate, it can be set from any thread as long as mInvalidateLock is held. We use fine-grained locking here because mInvalidateRegion can be set from anywhere, in particular frmo HWC callbacks. Bug: 5466774 Change-Id: Iafca20aa3f5b25a87755e65bde7b769aa8f997bc --- services/surfaceflinger/SurfaceFlinger.cpp | 18 ++++++++++++++++-- services/surfaceflinger/SurfaceFlinger.h | 7 +++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 4b2866cb67fc..ba8f6308b1a0 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -788,6 +788,8 @@ void SurfaceFlinger::handlePageFlip() } unlockPageFlip(currentLayers); + + mDirtyRegion.orSelf(getAndClearInvalidateRegion()); mDirtyRegion.andSelf(screenRegion); } @@ -1798,12 +1800,24 @@ status_t SurfaceFlinger::onTransact( } void SurfaceFlinger::repaintEverything() { - Mutex::Autolock _l(mStateLock); const DisplayHardware& hw(graphicPlane(0).displayHardware()); - mDirtyRegion.set(hw.bounds()); + const Rect bounds(hw.getBounds()); + setInvalidateRegion(Region(bounds)); signalEvent(); } +void SurfaceFlinger::setInvalidateRegion(const Region& reg) { + Mutex::Autolock _l(mInvalidateLock); + mInvalidateRegion = reg; +} + +Region SurfaceFlinger::getAndClearInvalidateRegion() { + Mutex::Autolock _l(mInvalidateLock); + Region reg(mInvalidateRegion); + mInvalidateRegion.clear(); + return reg; +} + // --------------------------------------------------------------------------- status_t SurfaceFlinger::renderScreenToTexture(DisplayID dpy, diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 3c8f4e5ccf40..3284fdba999a 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -308,6 +308,9 @@ private: void composeSurfaces(const Region& dirty); + void setInvalidateRegion(const Region& reg); + Region getAndClearInvalidateRegion(); + ssize_t addClientLayer(const sp& client, const sp& lbc); status_t addLayer_l(const sp& layer); @@ -367,6 +370,10 @@ private: bool mLayersRemoved; DefaultKeyedVector< wp, wp > mLayerMap; + // access must be protected by mInvalidateLock + mutable Mutex mInvalidateLock; + Region mInvalidateRegion; + // constant members (no synchronization needed for access) sp mServerHeap; surface_flinger_cblk_t* mServerCblk; -- cgit v1.2.3-59-g8ed1b From 3014ea3d72f5c7b2a7e14f1c04395891b4f67407 Mon Sep 17 00:00:00 2001 From: The Android Automerger Date: Thu, 27 Oct 2011 17:39:53 -0700 Subject: Revert "Use ashmem for CursorWindows." This reverts commit 0cde89f5f025b7826be009ebb9673b970e180e32. --- .../android/content/ContentProviderNative.java | 110 ++--- .../android/database/AbstractWindowedCursor.java | 6 +- core/java/android/database/BulkCursorNative.java | 8 +- .../database/BulkCursorToCursorAdaptor.java | 49 +- .../database/CursorToBulkCursorAdaptor.java | 85 ++-- core/java/android/database/CursorWindow.java | 43 +- core/java/android/database/IBulkCursor.java | 2 +- .../java/android/database/sqlite/SQLiteCursor.java | 2 +- core/jni/android_database_CursorWindow.cpp | 312 +++++++------ core/jni/android_database_SQLiteQuery.cpp | 78 ++-- include/binder/CursorWindow.h | 291 ++++++------ libs/binder/CursorWindow.cpp | 498 +++++++++++---------- libs/binder/Parcel.cpp | 4 +- 13 files changed, 805 insertions(+), 683 deletions(-) diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index b089bf2b6584..064755e95fbd 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -108,22 +108,21 @@ abstract public class ContentProviderNative extends Binder implements IContentPr String sortOrder = data.readString(); 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()); + cursor, observer, getProviderName(), window); final IBinder binder = adaptor.asBinder(); final int count = adaptor.count(); final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex( adaptor.getColumnNames()); - final boolean wantsAllOnMoveCalls = adaptor.getWantsAllOnMoveCalls(); reply.writeNoException(); reply.writeStrongBinder(binder); reply.writeInt(count); reply.writeInt(index); - reply.writeInt(wantsAllOnMoveCalls ? 1 : 0); } else { reply.writeNoException(); reply.writeStrongBinder(null); @@ -325,58 +324,67 @@ final class ContentProviderProxy implements IContentProvider public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder) throws RemoteException { - BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); + CursorWindow window = new CursorWindow(false /* window will be used remotely */); 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()); - - mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); - - DatabaseUtils.readExceptionFromParcel(reply); - - IBulkCursor bulkCursor = BulkCursorNative.asInterface(reply.readStrongBinder()); - if (bulkCursor != null) { - int rowCount = reply.readInt(); - int idColumnPosition = reply.readInt(); - boolean wantsAllOnMoveCalls = reply.readInt() != 0; - adaptor.initialize(bulkCursor, rowCount, idColumnPosition, wantsAllOnMoveCalls); - } else { + 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); + + mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + + IBulkCursor bulkCursor = BulkCursorNative.asInterface(reply.readStrongBinder()); + if (bulkCursor != null) { + int rowCount = reply.readInt(); + int idColumnPosition = reply.readInt(); + adaptor.initialize(bulkCursor, rowCount, idColumnPosition); + } else { + adaptor.close(); + adaptor = null; + } + return adaptor; + } catch (RemoteException ex) { + adaptor.close(); + throw ex; + } catch (RuntimeException ex) { adaptor.close(); - adaptor = null; + throw ex; + } finally { + data.recycle(); + reply.recycle(); } - return adaptor; - } catch (RemoteException ex) { - adaptor.close(); - throw ex; - } catch (RuntimeException ex) { - adaptor.close(); - throw ex; } finally { - data.recycle(); - reply.recycle(); + // 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(); } } diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java index d0aedd2c9280..5836265c4945 100644 --- a/core/java/android/database/AbstractWindowedCursor.java +++ b/core/java/android/database/AbstractWindowedCursor.java @@ -189,14 +189,12 @@ public abstract class AbstractWindowedCursor extends AbstractCursor { /** * If there is a window, clear it. * Otherwise, creates a local window. - * - * @param name The window name. * @hide */ - protected void clearOrCreateLocalWindow(String name) { + protected void clearOrCreateLocalWindow() { if (mWindow == null) { // If there isn't a window set already it will only be accessed locally - mWindow = new CursorWindow(name, true /* the window is local only */); + mWindow = new CursorWindow(true /* the window is local only */); } else { mWindow.clear(); } diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java index 20a9c67198b2..4fada8c2fd29 100644 --- a/core/java/android/database/BulkCursorNative.java +++ b/core/java/android/database/BulkCursorNative.java @@ -109,8 +109,9 @@ public abstract class BulkCursorNative extends Binder implements IBulkCursor case REQUERY_TRANSACTION: { data.enforceInterface(IBulkCursor.descriptor); IContentObserver observer = - IContentObserver.Stub.asInterface(data.readStrongBinder()); - int count = requery(observer); + IContentObserver.Stub.asInterface(data.readStrongBinder()); + CursorWindow window = CursorWindow.CREATOR.createFromParcel(data); + int count = requery(observer, window); reply.writeNoException(); reply.writeInt(count); reply.writeBundle(getExtras()); @@ -293,12 +294,13 @@ final class BulkCursorProxy implements IBulkCursor { } } - public int requery(IContentObserver observer) throws RemoteException { + public int requery(IContentObserver observer, CursorWindow window) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IBulkCursor.descriptor); data.writeStrongInterface(observer); + window.writeToParcel(data, 0); boolean result = mRemote.transact(REQUERY_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java index 885046bf1995..cbdd07fb29d3 100644 --- a/core/java/android/database/BulkCursorToCursorAdaptor.java +++ b/core/java/android/database/BulkCursorToCursorAdaptor.java @@ -38,13 +38,11 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { * Initializes the adaptor. * Must be called before first use. */ - public void initialize(IBulkCursor bulkCursor, int count, int idIndex, - boolean wantsAllOnMoveCalls) { + public void initialize(IBulkCursor bulkCursor, int count, int idIndex) { mBulkCursor = bulkCursor; mColumns = null; // lazily retrieved mCount = count; mRowIdColumnIndex = idIndex; - mWantsAllOnMoveCalls = wantsAllOnMoveCalls; } /** @@ -88,12 +86,15 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { try { // Make sure we have the proper window - if (mWindow == null - || newPosition < mWindow.getStartPosition() - || newPosition >= mWindow.getStartPosition() + mWindow.getNumRows()) { + if (mWindow != null) { + if (newPosition < mWindow.getStartPosition() || + newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) { + setWindow(mBulkCursor.getWindow(newPosition)); + } else if (mWantsAllOnMoveCalls) { + mBulkCursor.onMove(newPosition); + } + } else { setWindow(mBulkCursor.getWindow(newPosition)); - } else if (mWantsAllOnMoveCalls) { - mBulkCursor.onMove(newPosition); } } catch (RemoteException ex) { // We tried to get a window and failed @@ -144,19 +145,25 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { throwIfCursorIsClosed(); try { - mCount = mBulkCursor.requery(getObserver()); - 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; + 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(); } } catch (Exception ex) { Log.e(TAG, "Unable to requery because the remote process exception " + ex.getMessage()); diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java index dd2c9b7c132c..a65b3b304558 100644 --- a/core/java/android/database/CursorToBulkCursorAdaptor.java +++ b/core/java/android/database/CursorToBulkCursorAdaptor.java @@ -53,7 +53,6 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative * for managing the lifetime of their window. */ private CursorWindow mWindowForNonWindowedCursor; - private boolean mWindowForNonWindowedCursorWasFilled; private static final class ContentObserverProxy extends ContentObserver { protected IContentObserver mRemote; @@ -88,11 +87,26 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative } } - public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, - String providerName) { + public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName, + CursorWindow window) { try { mCursor = (CrossProcessCursor) cursor; + if (mCursor instanceof AbstractWindowedCursor) { + AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor; + if (windowedCursor.hasWindow()) { + if (Log.isLoggable(TAG, Log.VERBOSE) || false) { + Log.v(TAG, "Cross process cursor has a local window before setWindow in " + + providerName, new RuntimeException()); + } + } + windowedCursor.setWindow(window); // cursor takes ownership of window + } else { + mWindowForNonWindowedCursor = window; // we own the 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); } @@ -103,22 +117,17 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative } } - private void closeWindowForNonWindowedCursorLocked() { - if (mWindowForNonWindowedCursor != null) { - mWindowForNonWindowedCursor.close(); - mWindowForNonWindowedCursor = null; - mWindowForNonWindowedCursorWasFilled = false; - } - } - - private void disposeLocked() { + private void closeCursorAndWindowLocked() { if (mCursor != null) { unregisterObserverProxyLocked(); mCursor.close(); mCursor = null; } - closeWindowForNonWindowedCursorLocked(); + if (mWindowForNonWindowedCursor != null) { + mWindowForNonWindowedCursor.close(); + mWindowForNonWindowedCursor = null; + } } private void throwIfCursorIsClosed() { @@ -130,7 +139,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative @Override public void binderDied() { synchronized (mLock) { - disposeLocked(); + closeCursorAndWindowLocked(); } } @@ -139,30 +148,17 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative synchronized (mLock) { throwIfCursorIsClosed(); - CursorWindow window; - if (mCursor instanceof AbstractWindowedCursor) { - AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor)mCursor; - window = windowedCursor.getWindow(); - if (window == null) { - window = new CursorWindow(mProviderName, false /*localOnly*/); - windowedCursor.setWindow(window); - } + mCursor.moveToPosition(startPos); - mCursor.moveToPosition(startPos); + final CursorWindow window; + if (mCursor instanceof AbstractWindowedCursor) { + window = ((AbstractWindowedCursor)mCursor).getWindow(); } else { window = mWindowForNonWindowedCursor; - if (window == null) { - window = new CursorWindow(mProviderName, false /*localOnly*/); - mWindowForNonWindowedCursor = window; - } - - mCursor.moveToPosition(startPos); - - if (!mWindowForNonWindowedCursorWasFilled - || startPos < window.getStartPosition() - || startPos >= window.getStartPosition() + window.getNumRows()) { + if (window != null + && (startPos < window.getStartPosition() || + startPos >= (window.getStartPosition() + window.getNumRows()))) { mCursor.fillWindow(startPos, window); - mWindowForNonWindowedCursorWasFilled = true; } } @@ -210,24 +206,29 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative unregisterObserverProxyLocked(); mCursor.deactivate(); } - - closeWindowForNonWindowedCursorLocked(); } } @Override public void close() { synchronized (mLock) { - disposeLocked(); + closeCursorAndWindowLocked(); } } @Override - public int requery(IContentObserver observer) { + public int requery(IContentObserver observer, CursorWindow window) { synchronized (mLock) { throwIfCursorIsClosed(); - closeWindowForNonWindowedCursorLocked(); + if (mCursor instanceof AbstractWindowedCursor) { + ((AbstractWindowedCursor) mCursor).setWindow(window); + } else { + if (mWindowForNonWindowedCursor != null) { + mWindowForNonWindowedCursor.close(); + } + mWindowForNonWindowedCursor = window; + } try { if (!mCursor.requery()) { @@ -240,6 +241,12 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative throw leakProgram; } + if (!(mCursor instanceof AbstractWindowedCursor)) { + if (window != null) { + mCursor.fillWindow(0, window); + } + } + unregisterObserverProxyLocked(); createAndRegisterObserverProxyLocked(observer); return mCursor.getCount(); diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index a18a72133455..5a91b80034e4 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -22,6 +22,7 @@ import android.content.res.Resources; import android.database.sqlite.SQLiteClosable; import android.database.sqlite.SQLiteException; import android.os.Binder; +import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; @@ -30,13 +31,6 @@ import android.util.SparseIntArray; /** * A buffer containing multiple cursor rows. - *

- * A {@link CursorWindow} is read-write when created and used locally. When sent - * to a remote process (by writing it to a {@link Parcel}), the remote process - * receives a read-only view of the cursor window. Typically the cursor window - * will be allocated by the producer, filled with data, and then sent to the - * consumer for reading. - *

*/ public class CursorWindow extends SQLiteClosable implements Parcelable { private static final String STATS_TAG = "CursorWindowStats"; @@ -58,11 +52,10 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { private final CloseGuard mCloseGuard = CloseGuard.get(); - private static native int nativeCreate(String name, - int cursorWindowSize, boolean localOnly); - private static native int nativeCreateFromParcel(Parcel parcel); + private static native int nativeInitializeEmpty(int cursorWindowSize, boolean localOnly); + private static native int nativeInitializeFromBinder(IBinder nativeBinder); private static native void nativeDispose(int windowPtr); - private static native void nativeWriteToParcel(int windowPtr, Parcel parcel); + private static native IBinder nativeGetBinder(int windowPtr); private static native void nativeClear(int windowPtr); @@ -86,21 +79,18 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { private static native boolean nativePutNull(int windowPtr, int row, int column); /** - * Creates a new empty cursor window and gives it a name. + * Creates a new empty cursor window. *

* The cursor initially has no rows or columns. Call {@link #setNumColumns(int)} to * set the number of columns before adding any rows to the cursor. *

* - * @param name The name of the cursor window, or null if none. * @param localWindow True if this window will be used in this process only, * false if it might be sent to another processes. - * - * @hide */ - public CursorWindow(String name, boolean localWindow) { + public CursorWindow(boolean localWindow) { mStartPos = 0; - mWindowPtr = nativeCreate(name, sCursorWindowSize, localWindow); + mWindowPtr = nativeInitializeEmpty(sCursorWindowSize, localWindow); if (mWindowPtr == 0) { throw new CursorWindowAllocationException("Cursor window allocation of " + (sCursorWindowSize / 1024) + " kb failed. " + printStats()); @@ -109,23 +99,10 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { recordNewWindow(Binder.getCallingPid(), mWindowPtr); } - /** - * Creates a new empty cursor window. - *

- * The cursor initially has no rows or columns. Call {@link #setNumColumns(int)} to - * set the number of columns before adding any rows to the cursor. - *

- * - * @param localWindow True if this window will be used in this process only, - * false if it might be sent to another processes. - */ - public CursorWindow(boolean localWindow) { - this(null, localWindow); - } - private CursorWindow(Parcel source) { + IBinder binder = source.readStrongBinder(); mStartPos = source.readInt(); - mWindowPtr = nativeCreateFromParcel(source); + mWindowPtr = nativeInitializeFromBinder(binder); if (mWindowPtr == 0) { throw new CursorWindowAllocationException("Cursor window could not be " + "created from binder."); @@ -710,8 +687,8 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { } public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(nativeGetBinder(mWindowPtr)); dest.writeInt(mStartPos); - nativeWriteToParcel(mWindowPtr, dest); if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) { releaseReference(); diff --git a/core/java/android/database/IBulkCursor.java b/core/java/android/database/IBulkCursor.java index 7c967970a34f..244c88fad38c 100644 --- a/core/java/android/database/IBulkCursor.java +++ b/core/java/android/database/IBulkCursor.java @@ -56,7 +56,7 @@ public interface IBulkCursor extends IInterface { public void close() throws RemoteException; - public int requery(IContentObserver observer) throws RemoteException; + public int requery(IContentObserver observer, CursorWindow window) throws RemoteException; boolean getWantsAllOnMoveCalls() throws RemoteException; diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java index a1c36e24c000..9d7e15201be5 100644 --- a/core/java/android/database/sqlite/SQLiteCursor.java +++ b/core/java/android/database/sqlite/SQLiteCursor.java @@ -155,7 +155,7 @@ public class SQLiteCursor extends AbstractWindowedCursor { } private void fillWindow(int startPos) { - clearOrCreateLocalWindow(getDatabase().getPath()); + clearOrCreateLocalWindow(); mWindow.setStartPosition(startPos); int count = getQuery().fillWindow(mWindow); if (startPos == 0) { // fillWindow returns count(*) only for startPos = 0 diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index 722aeea68296..fe1aca0b0600 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -57,23 +57,14 @@ static void throwUnknownTypeException(JNIEnv * env, jint type) { jniThrowException(env, "java/lang/IllegalStateException", msg.string()); } -static jint nativeCreate(JNIEnv* env, jclass clazz, - jstring nameObj, jint cursorWindowSize, jboolean localOnly) { - String8 name; - if (nameObj) { - const char* nameStr = env->GetStringUTFChars(nameObj, NULL); - name.setTo(nameStr); - env->ReleaseStringUTFChars(nameObj, nameStr); - } - if (name.size() == 0) { - name.setTo(""); - } - - CursorWindow* window; - status_t status = CursorWindow::create(name, cursorWindowSize, localOnly, &window); - if (status || !window) { - LOGE("Could not allocate CursorWindow '%s' of size %d due to error %d.", - name.string(), cursorWindowSize, status); +static jint nativeInitializeEmpty(JNIEnv* env, jclass clazz, + jint cursorWindowSize, jboolean localOnly) { + CursorWindow* window = new CursorWindow(cursorWindowSize); + if (!window) { + return 0; + } + if (!window->initBuffer(localOnly)) { + delete window; return 0; } @@ -81,13 +72,19 @@ static jint nativeCreate(JNIEnv* env, jclass clazz, return reinterpret_cast(window); } -static jint nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) { - Parcel* parcel = parcelForJavaObject(env, parcelObj); +static jint nativeInitializeFromBinder(JNIEnv* env, jclass clazz, jobject binderObj) { + sp memory = interface_cast(ibinderForJavaObject(env, binderObj)); + if (memory == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder"); + return 0; + } - CursorWindow* window; - status_t status = CursorWindow::createFromParcel(parcel, &window); - if (status || !window) { - LOGE("Could not create CursorWindow from Parcel due to error %d.", status); + CursorWindow* window = new CursorWindow(); + if (!window) { + return 0; + } + if (!window->setMemory(memory)) { + delete window; return 0; } @@ -104,26 +101,22 @@ static void nativeDispose(JNIEnv* env, jclass clazz, jint windowPtr) { } } -static void nativeWriteToParcel(JNIEnv * env, jclass clazz, jint windowPtr, - jobject parcelObj) { +static jobject nativeGetBinder(JNIEnv * env, jclass clazz, jint windowPtr) { CursorWindow* window = reinterpret_cast(windowPtr); - Parcel* parcel = parcelForJavaObject(env, parcelObj); - - status_t status = window->writeToParcel(parcel); - if (status) { - String8 msg; - msg.appendFormat("Could not write CursorWindow to Parcel due to error %d.", status); - jniThrowRuntimeException(env, msg.string()); + if (window) { + sp memory = window->getMemory(); + if (memory != NULL) { + sp binder = memory->asBinder(); + return javaObjectForIBinder(env, binder); + } } + return NULL; } static void nativeClear(JNIEnv * env, jclass clazz, jint windowPtr) { CursorWindow* window = reinterpret_cast(windowPtr); LOG_WINDOW("Clearing window %p", window); - status_t status = window->clear(); - if (status) { - LOG_WINDOW("Could not clear window. error=%d", status); - } + window->clear(); } static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jint windowPtr) { @@ -134,14 +127,12 @@ static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jint windowPtr) { static jboolean nativeSetNumColumns(JNIEnv* env, jclass clazz, jint windowPtr, jint columnNum) { CursorWindow* window = reinterpret_cast(windowPtr); - status_t status = window->setNumColumns(columnNum); - return status == OK; + return window->setNumColumns(columnNum); } static jboolean nativeAllocRow(JNIEnv* env, jclass clazz, jint windowPtr) { CursorWindow* window = reinterpret_cast(windowPtr); - status_t status = window->allocRow(); - return status == OK; + return window->allocRow() != NULL; } static void nativeFreeLastRow(JNIEnv* env, jclass clazz, jint windowPtr) { @@ -154,14 +145,14 @@ static jint nativeGetType(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast(windowPtr); LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window); - CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); + field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); if (!fieldSlot) { // FIXME: This is really broken but we have CTS tests that depend // on this legacy behavior. //throwExceptionWithRowCol(env, row, column); - return CursorWindow::FIELD_TYPE_NULL; + return FIELD_TYPE_NULL; } - return window->getFieldSlotType(fieldSlot); + return fieldSlot->type; } static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jint windowPtr, @@ -169,29 +160,29 @@ static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast(windowPtr); LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window); - CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); + field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return NULL; } - int32_t type = window->getFieldSlotType(fieldSlot); - if (type == CursorWindow::FIELD_TYPE_BLOB || type == CursorWindow::FIELD_TYPE_STRING) { - size_t size; - const void* value = window->getFieldSlotValueBlob(fieldSlot, &size); + uint8_t type = fieldSlot->type; + if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) { + uint32_t size = fieldSlot->data.buffer.size; jbyteArray byteArray = env->NewByteArray(size); if (!byteArray) { env->ExceptionClear(); throw_sqlite3_exception(env, "Native could not create new byte[]"); return NULL; } - env->SetByteArrayRegion(byteArray, 0, size, static_cast(value)); + env->SetByteArrayRegion(byteArray, 0, size, + reinterpret_cast(window->offsetToPtr(fieldSlot->data.buffer.offset))); return byteArray; - } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { + } else if (type == FIELD_TYPE_INTEGER) { throw_sqlite3_exception(env, "INTEGER data in nativeGetBlob "); - } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { + } else if (type == FIELD_TYPE_FLOAT) { throw_sqlite3_exception(env, "FLOAT data in nativeGetBlob "); - } else if (type == CursorWindow::FIELD_TYPE_NULL) { + } else if (type == FIELD_TYPE_NULL) { // do nothing } else { throwUnknownTypeException(env, type); @@ -204,37 +195,43 @@ static jstring nativeGetString(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast(windowPtr); LOG_WINDOW("Getting string for %d,%d from %p", row, column, window); - CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); + field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return NULL; } - int32_t type = window->getFieldSlotType(fieldSlot); - if (type == CursorWindow::FIELD_TYPE_STRING) { - size_t sizeIncludingNull; - const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); - if (sizeIncludingNull <= 1) { + uint8_t type = fieldSlot->type; + if (type == FIELD_TYPE_STRING) { + uint32_t size = fieldSlot->data.buffer.size; +#if WINDOW_STORAGE_UTF8 + if (size <= 1) { return gEmptyString; } // Convert to UTF-16 here instead of calling NewStringUTF. NewStringUTF // doesn't like UTF-8 strings with high codepoints. It actually expects // Modified UTF-8 with encoded surrogate pairs. - String16 utf16(value, sizeIncludingNull - 1); + String16 utf16(window->getFieldSlotValueString(fieldSlot), size - 1); return env->NewString(reinterpret_cast(utf16.string()), utf16.size()); - } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { +#else + size_t chars = size / sizeof(char16_t); + return chars ? env->NewString(reinterpret_cast( + window->getFieldSlotValueString(fieldSlot)), chars) + : gEmptyString; +#endif + } else if (type == FIELD_TYPE_INTEGER) { int64_t value = window->getFieldSlotValueLong(fieldSlot); char buf[32]; snprintf(buf, sizeof(buf), "%lld", value); return env->NewStringUTF(buf); - } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { + } else if (type == FIELD_TYPE_FLOAT) { double value = window->getFieldSlotValueDouble(fieldSlot); char buf[32]; snprintf(buf, sizeof(buf), "%g", value); return env->NewStringUTF(buf); - } else if (type == CursorWindow::FIELD_TYPE_NULL) { + } else if (type == FIELD_TYPE_NULL) { return NULL; - } else if (type == CursorWindow::FIELD_TYPE_BLOB) { + } else if (type == FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to string"); return NULL; } else { @@ -284,6 +281,21 @@ static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj, } } +#if !WINDOW_STORAGE_UTF8 +static void fillCharArrayBuffer(JNIEnv* env, jobject bufferObj, + const char16_t* str, size_t len) { + jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, len); + if (dataObj) { + if (len) { + jchar* data = static_cast(env->GetPrimitiveArrayCritical(dataObj, NULL)); + memcpy(data, str, len * sizeof(jchar)); + env->ReleasePrimitiveArrayCritical(dataObj, data, 0); + } + env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, len); + } +} +#endif + static void clearCharArrayBuffer(JNIEnv* env, jobject bufferObj) { jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, 0); if (dataObj) { @@ -296,34 +308,44 @@ static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast(windowPtr); LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); - CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); + field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return; } - int32_t type = window->getFieldSlotType(fieldSlot); - if (type == CursorWindow::FIELD_TYPE_STRING) { - size_t sizeIncludingNull; - const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); - if (sizeIncludingNull > 1) { - fillCharArrayBufferUTF(env, bufferObj, value, sizeIncludingNull - 1); + uint8_t type = fieldSlot->type; + if (type == FIELD_TYPE_STRING) { + uint32_t size = fieldSlot->data.buffer.size; +#if WINDOW_STORAGE_UTF8 + if (size > 1) { + fillCharArrayBufferUTF(env, bufferObj, + window->getFieldSlotValueString(fieldSlot), size - 1); } else { clearCharArrayBuffer(env, bufferObj); } - } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { +#else + size_t chars = size / sizeof(char16_t); + if (chars) { + fillCharArrayBuffer(env, bufferObj, + window->getFieldSlotValueString(fieldSlot), chars); + } else { + clearCharArrayBuffer(env, bufferObj); + } +#endif + } else if (type == FIELD_TYPE_INTEGER) { int64_t value = window->getFieldSlotValueLong(fieldSlot); char buf[32]; snprintf(buf, sizeof(buf), "%lld", value); fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); - } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { + } else if (type == FIELD_TYPE_FLOAT) { double value = window->getFieldSlotValueDouble(fieldSlot); char buf[32]; snprintf(buf, sizeof(buf), "%g", value); fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); - } else if (type == CursorWindow::FIELD_TYPE_NULL) { + } else if (type == FIELD_TYPE_NULL) { clearCharArrayBuffer(env, bufferObj); - } else if (type == CursorWindow::FIELD_TYPE_BLOB) { + } else if (type == FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to string"); } else { throwUnknownTypeException(env, type); @@ -335,24 +357,29 @@ static jlong nativeGetLong(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast(windowPtr); LOG_WINDOW("Getting long for %d,%d from %p", row, column, window); - CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); + field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return 0; } - int32_t type = window->getFieldSlotType(fieldSlot); - if (type == CursorWindow::FIELD_TYPE_INTEGER) { + uint8_t type = fieldSlot->type; + if (type == FIELD_TYPE_INTEGER) { return window->getFieldSlotValueLong(fieldSlot); - } else if (type == CursorWindow::FIELD_TYPE_STRING) { - size_t sizeIncludingNull; - const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); - return sizeIncludingNull > 1 ? strtoll(value, NULL, 0) : 0L; - } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { + } else if (type == FIELD_TYPE_STRING) { + uint32_t size = fieldSlot->data.buffer.size; +#if WINDOW_STORAGE_UTF8 + return size > 1 ? strtoll(window->getFieldSlotValueString(fieldSlot), NULL, 0) : 0L; +#else + size_t chars = size / sizeof(char16_t); + return chars ? strtoll(String8(window->getFieldSlotValueString(fieldSlot), chars) + .string(), NULL, 0) : 0L; +#endif + } else if (type == FIELD_TYPE_FLOAT) { return jlong(window->getFieldSlotValueDouble(fieldSlot)); - } else if (type == CursorWindow::FIELD_TYPE_NULL) { + } else if (type == FIELD_TYPE_NULL) { return 0; - } else if (type == CursorWindow::FIELD_TYPE_BLOB) { + } else if (type == FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to long"); return 0; } else { @@ -366,24 +393,29 @@ static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast(windowPtr); LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); - CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); + field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return 0.0; } - int32_t type = window->getFieldSlotType(fieldSlot); - if (type == CursorWindow::FIELD_TYPE_FLOAT) { + uint8_t type = fieldSlot->type; + if (type == FIELD_TYPE_FLOAT) { return window->getFieldSlotValueDouble(fieldSlot); - } else if (type == CursorWindow::FIELD_TYPE_STRING) { - size_t sizeIncludingNull; - const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); - return sizeIncludingNull > 1 ? strtod(value, NULL) : 0.0; - } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { + } else if (type == FIELD_TYPE_STRING) { + uint32_t size = fieldSlot->data.buffer.size; +#if WINDOW_STORAGE_UTF8 + return size > 1 ? strtod(window->getFieldSlotValueString(fieldSlot), NULL) : 0.0; +#else + size_t chars = size / sizeof(char16_t); + return chars ? strtod(String8(window->getFieldSlotValueString(fieldSlot), chars) + .string(), NULL) : 0.0; +#endif + } else if (type == FIELD_TYPE_INTEGER) { return jdouble(window->getFieldSlotValueLong(fieldSlot)); - } else if (type == CursorWindow::FIELD_TYPE_NULL) { + } else if (type == FIELD_TYPE_NULL) { return 0.0; - } else if (type == CursorWindow::FIELD_TYPE_BLOB) { + } else if (type == FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to double"); return 0.0; } else { @@ -395,50 +427,82 @@ static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jint windowPtr, static jboolean nativePutBlob(JNIEnv* env, jclass clazz, jint windowPtr, jbyteArray valueObj, jint row, jint column) { CursorWindow* window = reinterpret_cast(windowPtr); + field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, column); + if (fieldSlot == NULL) { + LOG_WINDOW(" getFieldSlotWithCheck error "); + return false; + } + jsize len = env->GetArrayLength(valueObj); + uint32_t offset = window->alloc(len); + if (!offset) { + LOG_WINDOW("Failed allocating %u bytes", len); + return false; + } void* value = env->GetPrimitiveArrayCritical(valueObj, NULL); - status_t status = window->putBlob(row, column, value, len); + window->copyIn(offset, static_cast(value), len); env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT); - if (status) { - LOG_WINDOW("Failed to put blob. error=%d", status); - return false; - } - - LOG_WINDOW("%d,%d is BLOB with %u bytes", row, column, len); + fieldSlot->type = FIELD_TYPE_BLOB; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = len; + LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, column, len, offset); return true; } static jboolean nativePutString(JNIEnv* env, jclass clazz, jint windowPtr, jstring valueObj, jint row, jint column) { CursorWindow* window = reinterpret_cast(windowPtr); + field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, column); + if (fieldSlot == NULL) { + LOG_WINDOW(" getFieldSlotWithCheck error "); + return false; + } - size_t sizeIncludingNull = env->GetStringUTFLength(valueObj) + 1; +#if WINDOW_STORAGE_UTF8 + size_t size = env->GetStringUTFLength(valueObj) + 1; const char* valueStr = env->GetStringUTFChars(valueObj, NULL); +#else + size_t size = env->GetStringLength(valueObj) * sizeof(jchar); + const jchar* valueStr = env->GetStringChars(valueObj, NULL); +#endif if (!valueStr) { - LOG_WINDOW("value can't be transferred to UTFChars"); + LOG_WINDOW("value can't be transfer to UTFChars"); return false; } - status_t status = window->putString(row, column, valueStr, sizeIncludingNull); - env->ReleaseStringUTFChars(valueObj, valueStr); - if (status) { - LOG_WINDOW("Failed to put string. error=%d", status); + uint32_t offset = window->alloc(size); + if (!offset) { + LOG_WINDOW("Failed allocating %u bytes", size); +#if WINDOW_STORAGE_UTF8 + env->ReleaseStringUTFChars(valueObj, valueStr); +#else + env->ReleaseStringChars(valueObj, valueStr); +#endif return false; } - LOG_WINDOW("%d,%d is TEXT with %u bytes", row, column, sizeIncludingNull); + window->copyIn(offset, reinterpret_cast(valueStr), size); + +#if WINDOW_STORAGE_UTF8 + env->ReleaseStringUTFChars(valueObj, valueStr); +#else + env->ReleaseStringChars(valueObj, valueStr); +#endif + + fieldSlot->type = FIELD_TYPE_STRING; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = size; + LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, column, size, offset); return true; } static jboolean nativePutLong(JNIEnv* env, jclass clazz, jint windowPtr, jlong value, jint row, jint column) { CursorWindow* window = reinterpret_cast(windowPtr); - status_t status = window->putLong(row, column, value); - - if (status) { - LOG_WINDOW("Failed to put long. error=%d", status); + if (!window->putLong(row, column, value)) { + LOG_WINDOW(" getFieldSlotWithCheck error "); return false; } @@ -449,10 +513,8 @@ static jboolean nativePutLong(JNIEnv* env, jclass clazz, jint windowPtr, static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jint windowPtr, jdouble value, jint row, jint column) { CursorWindow* window = reinterpret_cast(windowPtr); - status_t status = window->putDouble(row, column, value); - - if (status) { - LOG_WINDOW("Failed to put double. error=%d", status); + if (!window->putDouble(row, column, value)) { + LOG_WINDOW(" getFieldSlotWithCheck error "); return false; } @@ -463,10 +525,8 @@ static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jint windowPtr, static jboolean nativePutNull(JNIEnv* env, jclass clazz, jint windowPtr, jint row, jint column) { CursorWindow* window = reinterpret_cast(windowPtr); - status_t status = window->putNull(row, column); - - if (status) { - LOG_WINDOW("Failed to put null. error=%d", status); + if (!window->putNull(row, column)) { + LOG_WINDOW(" getFieldSlotWithCheck error "); return false; } @@ -477,14 +537,14 @@ static jboolean nativePutNull(JNIEnv* env, jclass clazz, jint windowPtr, static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ - { "nativeCreate", "(Ljava/lang/String;IZ)I", - (void*)nativeCreate }, - { "nativeCreateFromParcel", "(Landroid/os/Parcel;)I", - (void*)nativeCreateFromParcel }, + { "nativeInitializeEmpty", "(IZ)I", + (void*)nativeInitializeEmpty }, + { "nativeInitializeFromBinder", "(Landroid/os/IBinder;)I", + (void*)nativeInitializeFromBinder }, { "nativeDispose", "(I)V", (void*)nativeDispose }, - { "nativeWriteToParcel", "(ILandroid/os/Parcel;)V", - (void*)nativeWriteToParcel }, + { "nativeGetBinder", "(I)Landroid/os/IBinder;", + (void*)nativeGetBinder }, { "nativeClear", "(I)V", (void*)nativeClear }, { "nativeGetNumRows", "(I)I", diff --git a/core/jni/android_database_SQLiteQuery.cpp b/core/jni/android_database_SQLiteQuery.cpp index 8170f46ecb29..022a64c5b65d 100644 --- a/core/jni/android_database_SQLiteQuery.cpp +++ b/core/jni/android_database_SQLiteQuery.cpp @@ -61,8 +61,7 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, window->getNumRows(), window->size(), window->freeSpace()); int numColumns = sqlite3_column_count(statement); - status_t status = window->setNumColumns(numColumns); - if (status) { + if (!window->setNumColumns(numColumns)) { LOGE("Failed to change column count from %d to %d", window->getNumColumns(), numColumns); jniThrowException(env, "java/lang/IllegalStateException", "numColumns mismatch"); return 0; @@ -89,10 +88,10 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, // Allocate a new field directory for the row. This pointer is not reused // since it may be possible for it to be relocated on a call to alloc() when // the field data is being allocated. - status = window->allocRow(); - if (status) { - LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d", - startPos, addedRows, status); + field_slot_t* fieldDir = window->allocRow(); + if (!fieldDir) { + LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d", + startPos, addedRows); windowFull = true; continue; } @@ -102,28 +101,37 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, int type = sqlite3_column_type(statement, i); if (type == SQLITE_TEXT) { // TEXT data - const char* text = reinterpret_cast( +#if WINDOW_STORAGE_UTF8 + const uint8_t* text = reinterpret_cast( sqlite3_column_text(statement, i)); // SQLite does not include the NULL terminator in size, but does // ensure all strings are NULL terminated, so increase size by // one to make sure we store the terminator. - size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1; - status = window->putString(addedRows, i, text, sizeIncludingNull); - if (status) { - LOG_WINDOW("Failed allocating %u bytes for text at %d,%d, error=%d", - sizeIncludingNull, startPos + addedRows, i, status); + size_t size = sqlite3_column_bytes(statement, i) + 1; +#else + const uint8_t* text = reinterpret_cast( + sqlite3_column_text16(statement, i)); + size_t size = sqlite3_column_bytes16(statement, i); +#endif + int offset = window->alloc(size); + if (!offset) { + LOG_WINDOW("Failed allocating %u bytes for text/blob at %d,%d", size, + startPos + addedRows, i); windowFull = true; break; } - LOG_WINDOW("%d,%d is TEXT with %u bytes", - startPos + addedRows, i, sizeIncludingNull); + window->copyIn(offset, text, size); + + field_slot_t* fieldSlot = window->getFieldSlot(addedRows, i); + fieldSlot->type = FIELD_TYPE_STRING; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = size; + LOG_WINDOW("%d,%d is TEXT with %u bytes", startPos + addedRows, i, size); } else if (type == SQLITE_INTEGER) { // INTEGER data int64_t value = sqlite3_column_int64(statement, i); - status = window->putLong(addedRows, i, value); - if (status) { - LOG_WINDOW("Failed allocating space for a long in column %d, error=%d", - i, status); + if (!window->putLong(addedRows, i, value)) { + LOG_WINDOW("Failed allocating space for a long in column %d", i); windowFull = true; break; } @@ -131,33 +139,35 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, } else if (type == SQLITE_FLOAT) { // FLOAT data double value = sqlite3_column_double(statement, i); - status = window->putDouble(addedRows, i, value); - if (status) { - LOG_WINDOW("Failed allocating space for a double in column %d, error=%d", - i, status); + if (!window->putDouble(addedRows, i, value)) { + LOG_WINDOW("Failed allocating space for a double in column %d", i); windowFull = true; break; } LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value); } else if (type == SQLITE_BLOB) { // BLOB data - const void* blob = sqlite3_column_blob(statement, i); - size_t size = sqlite3_column_bytes(statement, i); - status = window->putBlob(addedRows, i, blob, size); - if (status) { - LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d, error=%d", - size, startPos + addedRows, i, status); + uint8_t const * blob = (uint8_t const *)sqlite3_column_blob(statement, i); + size_t size = sqlite3_column_bytes16(statement, i); + int offset = window->alloc(size); + if (!offset) { + LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d", size, + startPos + addedRows, i); windowFull = true; break; } - LOG_WINDOW("%d,%d is Blob with %u bytes", - startPos + addedRows, i, size); + window->copyIn(offset, blob, size); + + field_slot_t* fieldSlot = window->getFieldSlot(addedRows, i); + fieldSlot->type = FIELD_TYPE_BLOB; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = size; + LOG_WINDOW("%d,%d is Blob with %u bytes @ %d", + startPos + addedRows, i, size, offset); } else if (type == SQLITE_NULL) { // NULL field - status = window->putNull(addedRows, i); - if (status) { - LOG_WINDOW("Failed allocating space for a null in column %d, error=%d", - i, status); + if (!window->putNull(addedRows, i)) { + LOG_WINDOW("Failed allocating space for a null in column %d", i); windowFull = true; break; } diff --git a/include/binder/CursorWindow.h b/include/binder/CursorWindow.h index 5d490ed988af..d227244df92c 100644 --- a/include/binder/CursorWindow.h +++ b/include/binder/CursorWindow.h @@ -21,8 +21,18 @@ #include #include -#include -#include +#include +#include + +#define DEFAULT_WINDOW_SIZE 4096 +#define WINDOW_ALLOCATION_SIZE 4096 + +#define ROW_SLOT_CHUNK_NUM_ROWS 16 + +// Row slots are allocated in chunks of ROW_SLOT_CHUNK_NUM_ROWS, +// with an offset after the rows that points to the next chunk +#define ROW_SLOT_CHUNK_SIZE ((ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)) + sizeof(uint32_t)) + #if LOG_NDEBUG @@ -36,157 +46,176 @@ #endif + +// When defined to true strings are stored as UTF8, otherwise they're UTF16 +#define WINDOW_STORAGE_UTF8 1 + +// When defined to true numberic values are stored inline in the field_slot_t, otherwise they're allocated in the window +#define WINDOW_STORAGE_INLINE_NUMERICS 1 + namespace android { +typedef struct +{ + uint32_t numRows; + uint32_t numColumns; +} window_header_t; + +typedef struct +{ + uint32_t offset; +} row_slot_t; + +typedef struct +{ + uint8_t type; + union { + double d; + int64_t l; + struct { + uint32_t offset; + uint32_t size; + } buffer; + } data; +} __attribute__((packed)) field_slot_t; + +#define FIELD_TYPE_NULL 0 +#define FIELD_TYPE_INTEGER 1 +#define FIELD_TYPE_FLOAT 2 +#define FIELD_TYPE_STRING 3 +#define FIELD_TYPE_BLOB 4 + /** * This class stores a set of rows from a database in a buffer. The begining of the - * window has first chunk of RowSlots, which are offsets to the row directory, followed by - * an offset to the next chunk in a linked-list of additional chunk of RowSlots in case + * window has first chunk of row_slot_ts, which are offsets to the row directory, followed by + * an offset to the next chunk in a linked-list of additional chunk of row_slot_ts in case * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a - * FieldSlot per column, which has the size, offset, and type of the data for that field. + * field_slot_t per column, which has the size, offset, and type of the data for that field. * Note that the data types come from sqlite3.h. - * - * Strings are stored in UTF-8. */ -class CursorWindow { - CursorWindow(const String8& name, int ashmemFd, - void* data, size_t size, bool readOnly); - +class CursorWindow +{ public: - /* Field types. */ - enum { - FIELD_TYPE_NULL = 0, - FIELD_TYPE_INTEGER = 1, - FIELD_TYPE_FLOAT = 2, - FIELD_TYPE_STRING = 3, - FIELD_TYPE_BLOB = 4, - }; - - /* Opaque type that describes a field slot. */ - struct FieldSlot { - private: - int32_t type; - union { - double d; - int64_t l; - struct { - uint32_t offset; - uint32_t size; - } buffer; - } data; - - friend class CursorWindow; - } __attribute((packed)); - - ~CursorWindow(); - - static status_t create(const String8& name, size_t size, bool localOnly, - CursorWindow** outCursorWindow); - static status_t createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow); - - status_t writeToParcel(Parcel* parcel); - - inline String8 name() { return mName; } - inline size_t size() { return mSize; } - inline size_t freeSpace() { return mSize - mHeader->freeOffset; } - inline uint32_t getNumRows() { return mHeader->numRows; } - inline uint32_t getNumColumns() { return mHeader->numColumns; } - - status_t clear(); - status_t setNumColumns(uint32_t numColumns); - - /** - * Allocate a row slot and its directory. - * The row is initialized will null entries for each field. - */ - status_t allocRow(); - status_t freeLastRow(); - - status_t putBlob(uint32_t row, uint32_t column, const void* value, size_t size); - status_t putString(uint32_t row, uint32_t column, const char* value, size_t sizeIncludingNull); - status_t putLong(uint32_t row, uint32_t column, int64_t value); - status_t putDouble(uint32_t row, uint32_t column, double value); - status_t putNull(uint32_t row, uint32_t column); - - /** - * Gets the field slot at the specified row and column. - * Returns null if the requested row or column is not in the window. - */ - FieldSlot* getFieldSlot(uint32_t row, uint32_t column); - - inline int32_t getFieldSlotType(FieldSlot* fieldSlot) { - return fieldSlot->type; - } - - inline int64_t getFieldSlotValueLong(FieldSlot* fieldSlot) { + CursorWindow(size_t maxSize); + CursorWindow(){} + bool setMemory(const sp&); + ~CursorWindow(); + + bool initBuffer(bool localOnly); + sp getMemory() {return mMemory;} + + size_t size() {return mSize;} + uint8_t * data() {return mData;} + uint32_t getNumRows() {return mHeader->numRows;} + uint32_t getNumColumns() {return mHeader->numColumns;} + void freeLastRow() { + if (mHeader->numRows > 0) { + mHeader->numRows--; + } + } + bool setNumColumns(uint32_t numColumns) + { + uint32_t cur = mHeader->numColumns; + if (cur > 0 && cur != numColumns) { + LOGE("Trying to go from %d columns to %d", cur, numColumns); + return false; + } + mHeader->numColumns = numColumns; + return true; + } + + int32_t freeSpace(); + + void clear(); + + /** + * Allocate a row slot and its directory. The returned + * pointer points to the begining of the row's directory + * or NULL if there wasn't room. The directory is + * initialied with NULL entries for each field. + */ + field_slot_t * allocRow(); + + /** + * Allocate a portion of the window. Returns the offset + * of the allocation, or 0 if there isn't enough space. + * If aligned is true, the allocation gets 4 byte alignment. + */ + uint32_t alloc(size_t size, bool aligned = false); + + /** + * Copy data into the window at the given offset. + */ + void copyIn(uint32_t offset, uint8_t const * data, size_t size); + void copyIn(uint32_t offset, int64_t data); + void copyIn(uint32_t offset, double data); + + void copyOut(uint32_t offset, uint8_t * data, size_t size); + int64_t copyOutLong(uint32_t offset); + double copyOutDouble(uint32_t offset); + + bool putLong(unsigned int row, unsigned int col, int64_t value); + bool putDouble(unsigned int row, unsigned int col, double value); + bool putNull(unsigned int row, unsigned int col); + + bool getLong(unsigned int row, unsigned int col, int64_t * valueOut); + bool getDouble(unsigned int row, unsigned int col, double * valueOut); + bool getNull(unsigned int row, unsigned int col, bool * valueOut); + + uint8_t * offsetToPtr(uint32_t offset) {return mData + offset;} + + row_slot_t * allocRowSlot(); + + row_slot_t * getRowSlot(int row); + + /** + * return NULL if Failed to find rowSlot or + * Invalid rowSlot + */ + field_slot_t * getFieldSlotWithCheck(int row, int column); + field_slot_t * getFieldSlot(int row, int column) + { + int fieldDirOffset = getRowSlot(row)->offset; + return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column; + } + + int64_t getFieldSlotValueLong(field_slot_t* fieldSlot) { +#if WINDOW_STORAGE_INLINE_NUMERICS return fieldSlot->data.l; +#else + return copyOutLong(fieldSlot->data.buffer.offset); +#endif } - inline double getFieldSlotValueDouble(FieldSlot* fieldSlot) { + double getFieldSlotValueDouble(field_slot_t* fieldSlot) { +#if WINDOW_STORAGE_INLINE_NUMERICS return fieldSlot->data.d; +#else + return copyOutDouble(fieldSlot->data.buffer.offset); +#endif } - inline const char* getFieldSlotValueString(FieldSlot* fieldSlot, - size_t* outSizeIncludingNull) { - *outSizeIncludingNull = fieldSlot->data.buffer.size; - return static_cast(offsetToPtr(fieldSlot->data.buffer.offset)); +#if WINDOW_STORAGE_UTF8 + char* getFieldSlotValueString(field_slot_t* fieldSlot) { + return reinterpret_cast(offsetToPtr(fieldSlot->data.buffer.offset)); } - - inline const void* getFieldSlotValueBlob(FieldSlot* fieldSlot, size_t* outSize) { - *outSize = fieldSlot->data.buffer.size; - return offsetToPtr(fieldSlot->data.buffer.offset); +#else + char16_t* getFieldSlotValueString(field_slot_t* fieldSlot) { + return reinterpret_cast(offsetToPtr(fieldSlot->data.buffer.offset)); } +#endif private: - static const size_t ROW_SLOT_CHUNK_NUM_ROWS = 100; - - struct Header { - // Offset of the lowest unused byte in the window. - uint32_t freeOffset; - - // Offset of the first row slot chunk. - uint32_t firstChunkOffset; - - uint32_t numRows; - uint32_t numColumns; - }; - - struct RowSlot { - uint32_t offset; - }; - - struct RowSlotChunk { - RowSlot slots[ROW_SLOT_CHUNK_NUM_ROWS]; - uint32_t nextChunkOffset; - }; - - String8 mName; - int mAshmemFd; - void* mData; + uint8_t * mData; size_t mSize; - bool mReadOnly; - Header* mHeader; - - inline void* offsetToPtr(uint32_t offset) { - return static_cast(mData) + offset; - } - - inline uint32_t offsetFromPtr(void* ptr) { - return static_cast(ptr) - static_cast(mData); - } + size_t mMaxSize; + window_header_t * mHeader; + sp mMemory; /** - * Allocate a portion of the window. Returns the offset - * of the allocation, or 0 if there isn't enough space. - * If aligned is true, the allocation gets 4 byte alignment. + * Offset of the lowest unused data byte in the array. */ - uint32_t alloc(size_t size, bool aligned = false); - - RowSlot* getRowSlot(uint32_t row); - RowSlot* allocRowSlot(); - - status_t putBlobOrString(uint32_t row, uint32_t column, - const void* value, size_t size, int32_t type); + uint32_t mFreeOffset; }; }; // namespace android diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp index 1b85a71ca8ff..b02374f1ed78 100644 --- a/libs/binder/CursorWindow.cpp +++ b/libs/binder/CursorWindow.cpp @@ -19,9 +19,8 @@ #include #include - -#include -#include +#include +#include #include #include @@ -29,325 +28,350 @@ namespace android { -CursorWindow::CursorWindow(const String8& name, int ashmemFd, - void* data, size_t size, bool readOnly) : - mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) { - mHeader = static_cast(mData); -} - -CursorWindow::~CursorWindow() { - ::munmap(mData, mSize); - ::close(mAshmemFd); +CursorWindow::CursorWindow(size_t maxSize) : + mMaxSize(maxSize) +{ } -status_t CursorWindow::create(const String8& name, size_t size, bool localOnly, - CursorWindow** outCursorWindow) { - String8 ashmemName("CursorWindow: "); - ashmemName.append(name); - ashmemName.append(localOnly ? " (local)" : " (remote)"); - - status_t result; - int ashmemFd = ashmem_create_region(ashmemName.string(), size); - if (ashmemFd < 0) { - result = -errno; - } else { - result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE); - if (result >= 0) { - void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0); - if (data == MAP_FAILED) { - result = -errno; - } else { - result = ashmem_set_prot_region(ashmemFd, PROT_READ); - if (result >= 0) { - CursorWindow* window = new CursorWindow(name, ashmemFd, - data, size, false /*readOnly*/); - result = window->clear(); - if (!result) { - LOG_WINDOW("Created new CursorWindow: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%d, mData=%p", - window->mHeader->freeOffset, - window->mHeader->numRows, - window->mHeader->numColumns, - window->mSize, window->mData); - *outCursorWindow = window; - return OK; - } - delete window; - } - } - ::munmap(data, size); - } - ::close(ashmemFd); +bool CursorWindow::setMemory(const sp& memory) +{ + mMemory = memory; + mData = (uint8_t *) memory->pointer(); + if (mData == NULL) { + return false; } - *outCursorWindow = NULL; - return result; + mHeader = (window_header_t *) mData; + + // Make the window read-only + ssize_t size = memory->size(); + mSize = size; + mMaxSize = size; + mFreeOffset = size; +LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRows = %d, numColumns = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mHeader->numRows, mHeader->numColumns, mSize, mMaxSize, mData); + return true; } -status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) { - String8 name = parcel->readString8(); - - status_t result; - int ashmemFd = parcel->readFileDescriptor(); - if (ashmemFd == int(BAD_TYPE)) { - result = BAD_TYPE; - } else { - ssize_t size = ashmem_get_size_region(ashmemFd); - if (size < 0) { - result = UNKNOWN_ERROR; - } else { - int dupAshmemFd = ::dup(ashmemFd); - if (dupAshmemFd < 0) { - result = -errno; - } else { - void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0); - if (data == MAP_FAILED) { - result = -errno; - } else { - CursorWindow* window = new CursorWindow(name, dupAshmemFd, - data, size, true /*readOnly*/); - LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%d, mData=%p", - window->mHeader->freeOffset, - window->mHeader->numRows, - window->mHeader->numColumns, - window->mSize, window->mData); - *outCursorWindow = window; - return OK; - } - ::close(dupAshmemFd); +bool CursorWindow::initBuffer(bool localOnly) +{ + //TODO Use a non-memory dealer mmap region for localOnly + + sp heap; + heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow"); + if (heap != NULL) { + mMemory = new MemoryBase(heap, 0, mMaxSize); + if (mMemory != NULL) { + mData = (uint8_t *) mMemory->pointer(); + if (mData) { + mHeader = (window_header_t *) mData; + mSize = mMaxSize; + + // Put the window into a clean state + clear(); + LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mSize, mMaxSize, mData); + return true; } - } + } + LOGE("CursorWindow heap allocation failed"); + return false; + } else { + LOGE("failed to create the CursorWindow heap"); + return false; } - *outCursorWindow = NULL; - return result; } -status_t CursorWindow::writeToParcel(Parcel* parcel) { - status_t status = parcel->writeString8(mName); - if (!status) { - status = parcel->writeDupFileDescriptor(mAshmemFd); - } - return status; +CursorWindow::~CursorWindow() +{ + // Everything that matters is a smart pointer } -status_t CursorWindow::clear() { - if (mReadOnly) { - return INVALID_OPERATION; - } - - mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk); - mHeader->firstChunkOffset = sizeof(Header); +void CursorWindow::clear() +{ mHeader->numRows = 0; mHeader->numColumns = 0; - - RowSlotChunk* firstChunk = static_cast(offsetToPtr(mHeader->firstChunkOffset)); - firstChunk->nextChunkOffset = 0; - return OK; + mFreeOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE; + // Mark the first chunk's next 'pointer' as null + *((uint32_t *)(mData + mFreeOffset - sizeof(uint32_t))) = 0; } -status_t CursorWindow::setNumColumns(uint32_t numColumns) { - if (mReadOnly) { - return INVALID_OPERATION; - } - - uint32_t cur = mHeader->numColumns; - if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) { - LOGE("Trying to go from %d columns to %d", cur, numColumns); - return INVALID_OPERATION; +int32_t CursorWindow::freeSpace() +{ + int32_t freeSpace = mSize - mFreeOffset; + if (freeSpace < 0) { + freeSpace = 0; } - mHeader->numColumns = numColumns; - return OK; + return freeSpace; } -status_t CursorWindow::allocRow() { - if (mReadOnly) { - return INVALID_OPERATION; - } - +field_slot_t * CursorWindow::allocRow() +{ // Fill in the row slot - RowSlot* rowSlot = allocRowSlot(); + row_slot_t * rowSlot = allocRowSlot(); if (rowSlot == NULL) { - return NO_MEMORY; + return NULL; } // Allocate the slots for the field directory - size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot); - uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/); + size_t fieldDirSize = mHeader->numColumns * sizeof(field_slot_t); + uint32_t fieldDirOffset = alloc(fieldDirSize); if (!fieldDirOffset) { mHeader->numRows--; - LOG_WINDOW("The row failed, so back out the new row accounting " - "from allocRowSlot %d", mHeader->numRows); - return NO_MEMORY; + LOG_WINDOW("The row failed, so back out the new row accounting from allocRowSlot %d", mHeader->numRows); + return NULL; } - FieldSlot* fieldDir = static_cast(offsetToPtr(fieldDirOffset)); - memset(fieldDir, 0, fieldDirSize); + field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(fieldDirOffset); + memset(fieldDir, 0x0, fieldDirSize); - LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", - mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset); +LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", (mHeader->numRows - 1), ((uint8_t *)rowSlot) - mData, fieldDirSize, fieldDirOffset); rowSlot->offset = fieldDirOffset; - return OK; -} - -status_t CursorWindow::freeLastRow() { - if (mReadOnly) { - return INVALID_OPERATION; - } - if (mHeader->numRows > 0) { - mHeader->numRows--; - } - return OK; + return fieldDir; } -uint32_t CursorWindow::alloc(size_t size, bool aligned) { +uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned) +{ + int32_t size; uint32_t padding; if (aligned) { // 4 byte alignment - padding = (~mHeader->freeOffset + 1) & 3; + padding = 4 - (mFreeOffset & 0x3); } else { padding = 0; } - uint32_t offset = mHeader->freeOffset + padding; - uint32_t nextFreeOffset = offset + size; - if (nextFreeOffset > mSize) { - LOGE("Window is full: requested allocation %d bytes, " - "free space %d bytes, window size %d bytes", - size, freeSpace(), mSize); - return 0; + size = requestedSize + padding; + + if (size > freeSpace()) { + LOGV("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d", mSize, size, + freeSpace(), mHeader->numRows); + // Only grow the window if the first row doesn't fit + if (mHeader->numRows > 1) { + LOGV("not growing since there are already %d row(s), max size %d", mHeader->numRows, + mMaxSize); + return 0; + } + + // Find a new size that will fit the allocation + int allocated = mSize - freeSpace(); + int newSize = mSize + WINDOW_ALLOCATION_SIZE; + while (size > (newSize - allocated)) { + newSize += WINDOW_ALLOCATION_SIZE; + if (newSize > mMaxSize) { + LOGE("Attempting to grow window beyond max size (%d)", mMaxSize); + return 0; + } + } +LOG_WINDOW("found size %d", newSize); + mSize = newSize; } - mHeader->freeOffset = nextFreeOffset; + uint32_t offset = mFreeOffset + padding; + mFreeOffset += size; return offset; } -CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) { - uint32_t chunkPos = row; - RowSlotChunk* chunk = static_cast( - offsetToPtr(mHeader->firstChunkOffset)); - while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) { - chunk = static_cast(offsetToPtr(chunk->nextChunkOffset)); - chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS; +row_slot_t * CursorWindow::getRowSlot(int row) +{ + LOG_WINDOW("enter getRowSlot current row num %d, this row %d", mHeader->numRows, row); + int chunkNum = row / ROW_SLOT_CHUNK_NUM_ROWS; + int chunkPos = row % ROW_SLOT_CHUNK_NUM_ROWS; + int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t); + uint8_t * rowChunk = mData + sizeof(window_header_t); + for (int i = 0; i < chunkNum; i++) { + rowChunk = offsetToPtr(*((uint32_t *)(mData + chunkPtrOffset))); + chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)); } - return &chunk->slots[chunkPos]; + return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t))); + LOG_WINDOW("exit getRowSlot current row num %d, this row %d", mHeader->numRows, row); } -CursorWindow::RowSlot* CursorWindow::allocRowSlot() { - uint32_t chunkPos = mHeader->numRows; - RowSlotChunk* chunk = static_cast( - offsetToPtr(mHeader->firstChunkOffset)); - while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) { - chunk = static_cast(offsetToPtr(chunk->nextChunkOffset)); - chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS; - } - if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) { - if (!chunk->nextChunkOffset) { - chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/); - if (!chunk->nextChunkOffset) { +row_slot_t * CursorWindow::allocRowSlot() +{ + int chunkNum = mHeader->numRows / ROW_SLOT_CHUNK_NUM_ROWS; + int chunkPos = mHeader->numRows % ROW_SLOT_CHUNK_NUM_ROWS; + int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t); + uint8_t * rowChunk = mData + sizeof(window_header_t); +LOG_WINDOW("Allocating row slot, mHeader->numRows is %d, chunkNum is %d, chunkPos is %d", mHeader->numRows, chunkNum, chunkPos); + for (int i = 0; i < chunkNum; i++) { + uint32_t nextChunkOffset = *((uint32_t *)(mData + chunkPtrOffset)); +LOG_WINDOW("nextChunkOffset is %d", nextChunkOffset); + if (nextChunkOffset == 0) { + // Allocate a new row chunk + nextChunkOffset = alloc(ROW_SLOT_CHUNK_SIZE, true); + if (nextChunkOffset == 0) { return NULL; } + rowChunk = offsetToPtr(nextChunkOffset); +LOG_WINDOW("allocated new chunk at %d, rowChunk = %p", nextChunkOffset, rowChunk); + *((uint32_t *)(mData + chunkPtrOffset)) = rowChunk - mData; + // Mark the new chunk's next 'pointer' as null + *((uint32_t *)(rowChunk + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t))) = 0; + } else { +LOG_WINDOW("follwing 'pointer' to next chunk, offset of next pointer is %d", chunkPtrOffset); + rowChunk = offsetToPtr(nextChunkOffset); + chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)); } - chunk = static_cast(offsetToPtr(chunk->nextChunkOffset)); - chunk->nextChunkOffset = 0; - chunkPos = 0; } - mHeader->numRows += 1; - return &chunk->slots[chunkPos]; + mHeader->numRows++; + + return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t))); } -CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) { - if (row >= mHeader->numRows || column >= mHeader->numColumns) { - LOGE("Failed to read row %d, column %d from a CursorWindow which " - "has %d rows, %d columns.", - row, column, mHeader->numRows, mHeader->numColumns); - return NULL; - } - RowSlot* rowSlot = getRowSlot(row); - if (!rowSlot) { - LOGE("Failed to find rowSlot for row %d.", row); - return NULL; - } - FieldSlot* fieldDir = static_cast(offsetToPtr(rowSlot->offset)); - return &fieldDir[column]; +field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column) +{ + if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) { + LOGE("Failed to read row# %d, column# from a CursorWindow which has %d rows, %d columns.", + row, column, mHeader->numRows, mHeader->numColumns); + return NULL; + } + row_slot_t * rowSlot = getRowSlot(row); + if (!rowSlot) { + LOGE("Failed to find rowSlot for row %d", row); + return NULL; + } + if (rowSlot->offset == 0 || rowSlot->offset >= mSize) { + LOGE("Invalid rowSlot, offset = %d", rowSlot->offset); + return NULL; + } + int fieldDirOffset = rowSlot->offset; + return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column; } -status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) { - return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB); +void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size) +{ + assert(offset + size <= mSize); + memcpy(mData + offset, data, size); } -status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value, - size_t sizeIncludingNull) { - return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING); +void CursorWindow::copyIn(uint32_t offset, int64_t data) +{ + assert(offset + sizeof(int64_t) <= mSize); + memcpy(mData + offset, (uint8_t *)&data, sizeof(int64_t)); } -status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column, - const void* value, size_t size, int32_t type) { - if (mReadOnly) { - return INVALID_OPERATION; - } +void CursorWindow::copyIn(uint32_t offset, double data) +{ + assert(offset + sizeof(double) <= mSize); + memcpy(mData + offset, (uint8_t *)&data, sizeof(double)); +} + +void CursorWindow::copyOut(uint32_t offset, uint8_t * data, size_t size) +{ + assert(offset + size <= mSize); + memcpy(data, mData + offset, size); +} - FieldSlot* fieldSlot = getFieldSlot(row, column); +int64_t CursorWindow::copyOutLong(uint32_t offset) +{ + int64_t value; + assert(offset + sizeof(int64_t) <= mSize); + memcpy(&value, mData + offset, sizeof(int64_t)); + return value; +} + +double CursorWindow::copyOutDouble(uint32_t offset) +{ + double value; + assert(offset + sizeof(double) <= mSize); + memcpy(&value, mData + offset, sizeof(double)); + return value; +} + +bool CursorWindow::putLong(unsigned int row, unsigned int col, int64_t value) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); if (!fieldSlot) { - return BAD_VALUE; + return false; } - uint32_t offset = alloc(size); +#if WINDOW_STORAGE_INLINE_NUMERICS + fieldSlot->data.l = value; +#else + int offset = alloc(sizeof(int64_t)); if (!offset) { - return NO_MEMORY; + return false; } - memcpy(offsetToPtr(offset), value, size); + copyIn(offset, value); - fieldSlot->type = type; fieldSlot->data.buffer.offset = offset; - fieldSlot->data.buffer.size = size; - return OK; + fieldSlot->data.buffer.size = sizeof(int64_t); +#endif + fieldSlot->type = FIELD_TYPE_INTEGER; + return true; } -status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) { - if (mReadOnly) { - return INVALID_OPERATION; +bool CursorWindow::putDouble(unsigned int row, unsigned int col, double value) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot) { + return false; } - FieldSlot* fieldSlot = getFieldSlot(row, column); - if (!fieldSlot) { - return BAD_VALUE; +#if WINDOW_STORAGE_INLINE_NUMERICS + fieldSlot->data.d = value; +#else + int offset = alloc(sizeof(int64_t)); + if (!offset) { + return false; } - fieldSlot->type = FIELD_TYPE_INTEGER; - fieldSlot->data.l = value; - return OK; + copyIn(offset, value); + + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = sizeof(double); +#endif + fieldSlot->type = FIELD_TYPE_FLOAT; + return true; } -status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) { - if (mReadOnly) { - return INVALID_OPERATION; +bool CursorWindow::putNull(unsigned int row, unsigned int col) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot) { + return false; } - FieldSlot* fieldSlot = getFieldSlot(row, column); - if (!fieldSlot) { - return BAD_VALUE; + fieldSlot->type = FIELD_TYPE_NULL; + fieldSlot->data.buffer.offset = 0; + fieldSlot->data.buffer.size = 0; + return true; +} + +bool CursorWindow::getLong(unsigned int row, unsigned int col, int64_t * valueOut) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot || fieldSlot->type != FIELD_TYPE_INTEGER) { + return false; } - fieldSlot->type = FIELD_TYPE_FLOAT; - fieldSlot->data.d = value; - return OK; + *valueOut = getFieldSlotValueLong(fieldSlot); + return true; } -status_t CursorWindow::putNull(uint32_t row, uint32_t column) { - if (mReadOnly) { - return INVALID_OPERATION; +bool CursorWindow::getDouble(unsigned int row, unsigned int col, double * valueOut) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + if (!fieldSlot || fieldSlot->type != FIELD_TYPE_FLOAT) { + return false; } - FieldSlot* fieldSlot = getFieldSlot(row, column); + *valueOut = getFieldSlotValueDouble(fieldSlot); + return true; +} + +bool CursorWindow::getNull(unsigned int row, unsigned int col, bool * valueOut) +{ + field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); if (!fieldSlot) { - return BAD_VALUE; + return false; } - - fieldSlot->type = FIELD_TYPE_NULL; - fieldSlot->data.buffer.offset = 0; - fieldSlot->data.buffer.size = 0; - return OK; + + if (fieldSlot->type != FIELD_TYPE_NULL) { + *valueOut = false; + } else { + *valueOut = true; + } + return true; } }; // namespace android diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index c7180cee03cd..608877eb7dd7 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -752,7 +752,7 @@ status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob) int result = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); if (result < 0) { - status = result; + status = -result; } else { void* ptr = ::mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { @@ -760,7 +760,7 @@ status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob) } else { result = ashmem_set_prot_region(fd, PROT_READ); if (result < 0) { - status = result; + status = -result; } else { status = writeInt32(1); if (!status) { -- cgit v1.2.3-59-g8ed1b From eb3127eb423f04ecb8af920b8fa363686e3fa18b Mon Sep 17 00:00:00 2001 From: The Android Automerger Date: Thu, 27 Oct 2011 17:40:24 -0700 Subject: Revert "Fix ownership of CursorWindows across processes." This reverts commit d2183654e03d589b120467f4e98da1b178ceeadb. --- core/java/android/content/ContentProvider.java | 23 ++- .../android/content/ContentProviderNative.java | 178 +++++++++-------- core/java/android/content/IContentProvider.java | 10 + core/java/android/database/AbstractCursor.java | 10 +- .../android/database/AbstractWindowedCursor.java | 26 --- core/java/android/database/BulkCursorNative.java | 8 +- .../database/BulkCursorToCursorAdaptor.java | 112 +++++------ core/java/android/database/CrossProcessCursor.java | 2 +- .../database/CursorToBulkCursorAdaptor.java | 213 ++++++--------------- core/java/android/database/CursorWindow.java | 16 -- .../java/android/database/sqlite/SQLiteCursor.java | 17 +- .../src/android/test/mock/MockContentProvider.java | 39 ++-- .../android/test/mock/MockIContentProvider.java | 9 + .../bridge/android/BridgeContentProvider.java | 23 +-- 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. *

- * 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. - *

* 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. - *

- * 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. - *

* * {@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 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 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 -- cgit v1.2.3-59-g8ed1b From 8c01cb5818a969b8b4d7361f5a679c1960de8679 Mon Sep 17 00:00:00 2001 From: The Android Automerger Date: Thu, 27 Oct 2011 17:40:45 -0700 Subject: Revert "Clean up handrolled Binder proxies." This reverts commit df6611d8c0cd69c3dcb93462eb138e0bbf137b88. --- .../android/content/ContentProviderNative.java | 392 ++++++++++----------- core/java/android/database/BulkCursorNative.java | 231 ++++++------ 2 files changed, 311 insertions(+), 312 deletions(-) diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index 9a20951b8a99..abeeb7406957 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -332,60 +332,60 @@ final class ContentProviderProxy implements IContentProvider BulkCursorToCursorAdaptor adaptor) throws RemoteException { 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(observer.asBinder()); - window.writeToParcel(data, 0); - - // Flag for whether or not we want the number of rows in the - // cursor and the position of the "_id" column index (or -1 if - // non-existent). Only to be returned if binder != null. - final boolean wantsCursorMetadata = (adaptor != null); - data.writeInt(wantsCursorMetadata ? 1 : 0); - - mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); - DatabaseUtils.readExceptionFromParcel(reply); + data.writeInterfaceToken(IContentProvider.descriptor); - IBulkCursor bulkCursor = null; - IBinder bulkCursorBinder = reply.readStrongBinder(); - if (bulkCursorBinder != null) { - bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder); - - if (wantsCursorMetadata) { - int rowCount = reply.readInt(); - int idColumnPosition = reply.readInt(); - if (bulkCursor != null) { - adaptor.set(bulkCursor, rowCount, idColumnPosition); - } + url.writeToParcel(data, 0); + int length = 0; + if (projection != null) { + length = projection.length; + } + data.writeInt(length); + for (int i = 0; i < length; i++) { + data.writeString(projection[i]); + } + data.writeString(selection); + if (selectionArgs != null) { + length = selectionArgs.length; + } else { + length = 0; + } + data.writeInt(length); + for (int i = 0; i < length; i++) { + data.writeString(selectionArgs[i]); + } + data.writeString(sortOrder); + data.writeStrongBinder(observer.asBinder()); + window.writeToParcel(data, 0); + + // Flag for whether or not we want the number of rows in the + // cursor and the position of the "_id" column index (or -1 if + // non-existent). Only to be returned if binder != null. + final boolean wantsCursorMetadata = (adaptor != null); + data.writeInt(wantsCursorMetadata ? 1 : 0); + + mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + + IBulkCursor bulkCursor = null; + IBinder bulkCursorBinder = reply.readStrongBinder(); + if (bulkCursorBinder != null) { + bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder); + + if (wantsCursorMetadata) { + int rowCount = reply.readInt(); + int idColumnPosition = reply.readInt(); + if (bulkCursor != null) { + adaptor.set(bulkCursor, rowCount, idColumnPosition); } } - return bulkCursor; - } finally { - data.recycle(); - reply.recycle(); } + + data.recycle(); + reply.recycle(); + + return bulkCursor; } public IBulkCursor bulkQuery(Uri url, String[] projection, @@ -416,240 +416,240 @@ final class ContentProviderProxy implements IContentProvider { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IContentProvider.descriptor); - url.writeToParcel(data, 0); + data.writeInterfaceToken(IContentProvider.descriptor); - mRemote.transact(IContentProvider.GET_TYPE_TRANSACTION, data, reply, 0); + url.writeToParcel(data, 0); - DatabaseUtils.readExceptionFromParcel(reply); - String out = reply.readString(); - return out; - } finally { - data.recycle(); - reply.recycle(); - } + mRemote.transact(IContentProvider.GET_TYPE_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + String out = reply.readString(); + + data.recycle(); + reply.recycle(); + + return out; } public Uri insert(Uri url, ContentValues values) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IContentProvider.descriptor); - url.writeToParcel(data, 0); - values.writeToParcel(data, 0); + data.writeInterfaceToken(IContentProvider.descriptor); - mRemote.transact(IContentProvider.INSERT_TRANSACTION, data, reply, 0); + url.writeToParcel(data, 0); + values.writeToParcel(data, 0); - DatabaseUtils.readExceptionFromParcel(reply); - Uri out = Uri.CREATOR.createFromParcel(reply); - return out; - } finally { - data.recycle(); - reply.recycle(); - } + mRemote.transact(IContentProvider.INSERT_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + Uri out = Uri.CREATOR.createFromParcel(reply); + + data.recycle(); + reply.recycle(); + + return out; } public int bulkInsert(Uri url, ContentValues[] values) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IContentProvider.descriptor); - url.writeToParcel(data, 0); - data.writeTypedArray(values, 0); + data.writeInterfaceToken(IContentProvider.descriptor); - mRemote.transact(IContentProvider.BULK_INSERT_TRANSACTION, data, reply, 0); + url.writeToParcel(data, 0); + data.writeTypedArray(values, 0); - DatabaseUtils.readExceptionFromParcel(reply); - int count = reply.readInt(); - return count; - } finally { - data.recycle(); - reply.recycle(); - } + mRemote.transact(IContentProvider.BULK_INSERT_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + int count = reply.readInt(); + + data.recycle(); + reply.recycle(); + + return count; } public ContentProviderResult[] applyBatch(ArrayList operations) throws RemoteException, OperationApplicationException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IContentProvider.descriptor); - data.writeInt(operations.size()); - for (ContentProviderOperation operation : operations) { - operation.writeToParcel(data, 0); - } - mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0); - - DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply); - final ContentProviderResult[] results = - reply.createTypedArray(ContentProviderResult.CREATOR); - return results; - } finally { - data.recycle(); - reply.recycle(); + + data.writeInterfaceToken(IContentProvider.descriptor); + data.writeInt(operations.size()); + for (ContentProviderOperation operation : operations) { + operation.writeToParcel(data, 0); } + mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply); + final ContentProviderResult[] results = + reply.createTypedArray(ContentProviderResult.CREATOR); + + data.recycle(); + reply.recycle(); + + return results; } public int delete(Uri url, String selection, String[] selectionArgs) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IContentProvider.descriptor); - url.writeToParcel(data, 0); - data.writeString(selection); - data.writeStringArray(selectionArgs); + data.writeInterfaceToken(IContentProvider.descriptor); - mRemote.transact(IContentProvider.DELETE_TRANSACTION, data, reply, 0); + url.writeToParcel(data, 0); + data.writeString(selection); + data.writeStringArray(selectionArgs); - DatabaseUtils.readExceptionFromParcel(reply); - int count = reply.readInt(); - return count; - } finally { - data.recycle(); - reply.recycle(); - } + mRemote.transact(IContentProvider.DELETE_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + int count = reply.readInt(); + + data.recycle(); + reply.recycle(); + + return count; } public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IContentProvider.descriptor); - url.writeToParcel(data, 0); - values.writeToParcel(data, 0); - data.writeString(selection); - data.writeStringArray(selectionArgs); + data.writeInterfaceToken(IContentProvider.descriptor); - mRemote.transact(IContentProvider.UPDATE_TRANSACTION, data, reply, 0); + url.writeToParcel(data, 0); + values.writeToParcel(data, 0); + data.writeString(selection); + data.writeStringArray(selectionArgs); - DatabaseUtils.readExceptionFromParcel(reply); - int count = reply.readInt(); - return count; - } finally { - data.recycle(); - reply.recycle(); - } + mRemote.transact(IContentProvider.UPDATE_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + int count = reply.readInt(); + + data.recycle(); + reply.recycle(); + + return count; } public ParcelFileDescriptor openFile(Uri url, String mode) throws RemoteException, FileNotFoundException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IContentProvider.descriptor); - url.writeToParcel(data, 0); - data.writeString(mode); + data.writeInterfaceToken(IContentProvider.descriptor); - mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0); + url.writeToParcel(data, 0); + data.writeString(mode); - DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); - int has = reply.readInt(); - ParcelFileDescriptor fd = has != 0 ? reply.readFileDescriptor() : null; - return fd; - } finally { - data.recycle(); - reply.recycle(); - } + mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); + int has = reply.readInt(); + ParcelFileDescriptor fd = has != 0 ? reply.readFileDescriptor() : null; + + data.recycle(); + reply.recycle(); + + return fd; } public AssetFileDescriptor openAssetFile(Uri url, String mode) throws RemoteException, FileNotFoundException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IContentProvider.descriptor); - url.writeToParcel(data, 0); - data.writeString(mode); + data.writeInterfaceToken(IContentProvider.descriptor); - mRemote.transact(IContentProvider.OPEN_ASSET_FILE_TRANSACTION, data, reply, 0); + url.writeToParcel(data, 0); + data.writeString(mode); - DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); - int has = reply.readInt(); - AssetFileDescriptor fd = has != 0 - ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null; - return fd; - } finally { - data.recycle(); - reply.recycle(); - } + mRemote.transact(IContentProvider.OPEN_ASSET_FILE_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); + int has = reply.readInt(); + AssetFileDescriptor fd = has != 0 + ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null; + + data.recycle(); + reply.recycle(); + + return fd; } public Bundle call(String method, String request, Bundle args) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IContentProvider.descriptor); - data.writeString(method); - data.writeString(request); - data.writeBundle(args); + data.writeInterfaceToken(IContentProvider.descriptor); - mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0); + data.writeString(method); + data.writeString(request); + data.writeBundle(args); - DatabaseUtils.readExceptionFromParcel(reply); - Bundle bundle = reply.readBundle(); - return bundle; - } finally { - data.recycle(); - reply.recycle(); - } + mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + Bundle bundle = reply.readBundle(); + + data.recycle(); + reply.recycle(); + + return bundle; } public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IContentProvider.descriptor); - url.writeToParcel(data, 0); - data.writeString(mimeTypeFilter); + data.writeInterfaceToken(IContentProvider.descriptor); - mRemote.transact(IContentProvider.GET_STREAM_TYPES_TRANSACTION, data, reply, 0); + url.writeToParcel(data, 0); + data.writeString(mimeTypeFilter); - DatabaseUtils.readExceptionFromParcel(reply); - String[] out = reply.createStringArray(); - return out; - } finally { - data.recycle(); - reply.recycle(); - } + mRemote.transact(IContentProvider.GET_STREAM_TYPES_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + String[] out = reply.createStringArray(); + + data.recycle(); + reply.recycle(); + + return out; } public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts) throws RemoteException, FileNotFoundException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IContentProvider.descriptor); - - url.writeToParcel(data, 0); - data.writeString(mimeType); - data.writeBundle(opts); - - mRemote.transact(IContentProvider.OPEN_TYPED_ASSET_FILE_TRANSACTION, data, reply, 0); - - DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); - int has = reply.readInt(); - AssetFileDescriptor fd = has != 0 - ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null; - return fd; - } finally { - data.recycle(); - reply.recycle(); - } + + data.writeInterfaceToken(IContentProvider.descriptor); + + url.writeToParcel(data, 0); + data.writeString(mimeType); + data.writeBundle(opts); + + mRemote.transact(IContentProvider.OPEN_TYPED_ASSET_FILE_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); + int has = reply.readInt(); + AssetFileDescriptor fd = has != 0 + ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null; + + data.recycle(); + reply.recycle(); + + return fd; } private IBinder mRemote; diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java index 9925a9ab7c0d..fa62d6921778 100644 --- a/core/java/android/database/BulkCursorNative.java +++ b/core/java/android/database/BulkCursorNative.java @@ -20,13 +20,12 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; -import android.os.Parcelable; import android.os.RemoteException; /** * Native implementation of the bulk cursor. This is only for use in implementing * IPC, application code should use the Cursor interface. - * + * * {@hide} */ public abstract class BulkCursorNative extends Binder implements IBulkCursor @@ -68,7 +67,7 @@ public abstract class BulkCursorNative extends Binder implements IBulkCursor } reply.writeNoException(); reply.writeInt(1); - window.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + window.writeToParcel(reply, 0); return true; } @@ -185,172 +184,172 @@ final class BulkCursorProxy implements IBulkCursor { { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IBulkCursor.descriptor); - data.writeInt(startPos); - mRemote.transact(GET_CURSOR_WINDOW_TRANSACTION, data, reply, 0); - DatabaseUtils.readExceptionFromParcel(reply); + data.writeInterfaceToken(IBulkCursor.descriptor); - CursorWindow window = null; - if (reply.readInt() == 1) { - window = CursorWindow.newFromParcel(reply); - } - return window; - } finally { - data.recycle(); - reply.recycle(); + data.writeInt(startPos); + + mRemote.transact(GET_CURSOR_WINDOW_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + + CursorWindow window = null; + if (reply.readInt() == 1) { + window = CursorWindow.newFromParcel(reply); } + + data.recycle(); + reply.recycle(); + + return window; } public void onMove(int position) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IBulkCursor.descriptor); - data.writeInt(position); - mRemote.transact(ON_MOVE_TRANSACTION, data, reply, 0); - DatabaseUtils.readExceptionFromParcel(reply); - } finally { - data.recycle(); - reply.recycle(); - } + data.writeInterfaceToken(IBulkCursor.descriptor); + + data.writeInt(position); + + mRemote.transact(ON_MOVE_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + + data.recycle(); + reply.recycle(); } public int count() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IBulkCursor.descriptor); - boolean result = mRemote.transact(COUNT_TRANSACTION, data, reply, 0); - DatabaseUtils.readExceptionFromParcel(reply); + data.writeInterfaceToken(IBulkCursor.descriptor); - int count; - if (result == false) { - count = -1; - } else { - count = reply.readInt(); - } - return count; - } finally { - data.recycle(); - reply.recycle(); + boolean result = mRemote.transact(COUNT_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + + int count; + if (result == false) { + count = -1; + } else { + count = reply.readInt(); } + data.recycle(); + reply.recycle(); + return count; } public String[] getColumnNames() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IBulkCursor.descriptor); - mRemote.transact(GET_COLUMN_NAMES_TRANSACTION, data, reply, 0); - DatabaseUtils.readExceptionFromParcel(reply); + data.writeInterfaceToken(IBulkCursor.descriptor); - String[] columnNames = null; - int numColumns = reply.readInt(); - columnNames = new String[numColumns]; - for (int i = 0; i < numColumns; i++) { - columnNames[i] = reply.readString(); - } - return columnNames; - } finally { - data.recycle(); - reply.recycle(); + mRemote.transact(GET_COLUMN_NAMES_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + + String[] columnNames = null; + int numColumns = reply.readInt(); + columnNames = new String[numColumns]; + for (int i = 0; i < numColumns; i++) { + columnNames[i] = reply.readString(); } + + data.recycle(); + reply.recycle(); + return columnNames; } public void deactivate() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IBulkCursor.descriptor); - mRemote.transact(DEACTIVATE_TRANSACTION, data, reply, 0); - DatabaseUtils.readExceptionFromParcel(reply); - } finally { - data.recycle(); - reply.recycle(); - } + data.writeInterfaceToken(IBulkCursor.descriptor); + + mRemote.transact(DEACTIVATE_TRANSACTION, data, reply, 0); + DatabaseUtils.readExceptionFromParcel(reply); + + data.recycle(); + reply.recycle(); } public void close() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IBulkCursor.descriptor); - mRemote.transact(CLOSE_TRANSACTION, data, reply, 0); - DatabaseUtils.readExceptionFromParcel(reply); - } finally { - data.recycle(); - reply.recycle(); - } + data.writeInterfaceToken(IBulkCursor.descriptor); + + mRemote.transact(CLOSE_TRANSACTION, data, reply, 0); + DatabaseUtils.readExceptionFromParcel(reply); + + data.recycle(); + reply.recycle(); } public int requery(IContentObserver observer, CursorWindow window) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IBulkCursor.descriptor); - data.writeStrongInterface(observer); - window.writeToParcel(data, 0); - boolean result = mRemote.transact(REQUERY_TRANSACTION, data, reply, 0); - DatabaseUtils.readExceptionFromParcel(reply); + data.writeInterfaceToken(IBulkCursor.descriptor); - int count; - if (!result) { - count = -1; - } else { - count = reply.readInt(); - mExtras = reply.readBundle(); - } - return count; - } finally { - data.recycle(); - reply.recycle(); + data.writeStrongInterface(observer); + window.writeToParcel(data, 0); + + boolean result = mRemote.transact(REQUERY_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + + int count; + if (!result) { + count = -1; + } else { + count = reply.readInt(); + mExtras = reply.readBundle(); } + + data.recycle(); + reply.recycle(); + + return count; } public boolean getWantsAllOnMoveCalls() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IBulkCursor.descriptor); - mRemote.transact(WANTS_ON_MOVE_TRANSACTION, data, reply, 0); - DatabaseUtils.readExceptionFromParcel(reply); + data.writeInterfaceToken(IBulkCursor.descriptor); - int result = reply.readInt(); - return result != 0; - } finally { - data.recycle(); - reply.recycle(); - } + mRemote.transact(WANTS_ON_MOVE_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + + int result = reply.readInt(); + data.recycle(); + reply.recycle(); + return result != 0; } public Bundle getExtras() throws RemoteException { if (mExtras == null) { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IBulkCursor.descriptor); - mRemote.transact(GET_EXTRAS_TRANSACTION, data, reply, 0); - DatabaseUtils.readExceptionFromParcel(reply); + data.writeInterfaceToken(IBulkCursor.descriptor); - mExtras = reply.readBundle(); - } finally { - data.recycle(); - reply.recycle(); - } + mRemote.transact(GET_EXTRAS_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + + mExtras = reply.readBundle(); + data.recycle(); + reply.recycle(); } return mExtras; } @@ -358,19 +357,19 @@ final class BulkCursorProxy implements IBulkCursor { public Bundle respond(Bundle extras) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(IBulkCursor.descriptor); - data.writeBundle(extras); - mRemote.transact(RESPOND_TRANSACTION, data, reply, 0); - DatabaseUtils.readExceptionFromParcel(reply); + data.writeInterfaceToken(IBulkCursor.descriptor); - Bundle returnExtras = reply.readBundle(); - return returnExtras; - } finally { - data.recycle(); - reply.recycle(); - } + data.writeBundle(extras); + + mRemote.transact(RESPOND_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + + Bundle returnExtras = reply.readBundle(); + data.recycle(); + reply.recycle(); + return returnExtras; } } -- cgit v1.2.3-59-g8ed1b From 095b0981d4912723d028a3ec93a2e677d063f8ca Mon Sep 17 00:00:00 2001 From: The Android Automerger Date: Thu, 27 Oct 2011 17:41:04 -0700 Subject: Revert "Fix regression in CursorWindow.getString()" This reverts commit 715311fa5aeb39fd0904209e1428a3656c721c3d. --- core/jni/android_database_CursorWindow.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index fe1aca0b0600..7f5c0d4f26ef 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -205,14 +205,8 @@ static jstring nativeGetString(JNIEnv* env, jclass clazz, jint windowPtr, if (type == FIELD_TYPE_STRING) { uint32_t size = fieldSlot->data.buffer.size; #if WINDOW_STORAGE_UTF8 - if (size <= 1) { - return gEmptyString; - } - // Convert to UTF-16 here instead of calling NewStringUTF. NewStringUTF - // doesn't like UTF-8 strings with high codepoints. It actually expects - // Modified UTF-8 with encoded surrogate pairs. - String16 utf16(window->getFieldSlotValueString(fieldSlot), size - 1); - return env->NewString(reinterpret_cast(utf16.string()), utf16.size()); + return size > 1 ? env->NewStringUTF(window->getFieldSlotValueString(fieldSlot)) + : gEmptyString; #else size_t chars = size / sizeof(char16_t); return chars ? env->NewString(reinterpret_cast( -- cgit v1.2.3-59-g8ed1b From 8f2eb43b4f4ed99cdcb83698ab7af938c911a55c Mon Sep 17 00:00:00 2001 From: The Android Automerger Date: Thu, 27 Oct 2011 17:41:26 -0700 Subject: Revert "Fix regression in CursorWindow.copyStingToBuffer." This reverts commit d0ff68da6a606602235fb8749473999e3d1bde53. --- core/jni/android_database_CursorWindow.cpp | 2 +- include/utils/Unicode.h | 7 ------- libs/utils/Unicode.cpp | 13 ++++++------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index 7f5c0d4f26ef..9ff2cb279101 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -267,7 +267,7 @@ static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj, if (dataObj) { if (size) { jchar* data = static_cast(env->GetPrimitiveArrayCritical(dataObj, NULL)); - utf8_to_utf16_no_null_terminator(reinterpret_cast(str), len, + utf8_to_utf16(reinterpret_cast(str), len, reinterpret_cast(data)); env->ReleasePrimitiveArrayCritical(dataObj, data, 0); } diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h index 92735337705d..6afb291f4a52 100644 --- a/include/utils/Unicode.h +++ b/include/utils/Unicode.h @@ -149,13 +149,6 @@ void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst); */ ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen); -/** - * Convert UTF-8 to UTF-16 including surrogate pairs. - * Returns a pointer to the end of the string (where a null terminator might go - * if you wanted to add one). - */ -char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* src, size_t srcLen, char16_t* dst); - /** * Convert UTF-8 to UTF-16 including surrogate pairs. The destination buffer * must be large enough to hold the result as measured by utf8_to_utf16_length diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp index 41cbf035e5ae..78c61b4fc632 100644 --- a/libs/utils/Unicode.cpp +++ b/libs/utils/Unicode.cpp @@ -542,7 +542,11 @@ ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len) return u16measuredLen; } -char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* u8str, size_t u8len, char16_t* u16str) +/** + * Convert a UTF-8 string to UTF-16. The destination UTF-16 buffer must have + * space for NULL at the end. + */ +void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) { const uint8_t* const u8end = u8str + u8len; const uint8_t* u8cur = u8str; @@ -565,12 +569,7 @@ char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* u8str, size_t u8len, c u8cur += u8len; } - return u16cur; -} - -void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) { - char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str); - *end = 0; + *u16cur = 0; } } -- cgit v1.2.3-59-g8ed1b From f78da99419b68ad0d0b05ad2bcbbb328f6d06446 Mon Sep 17 00:00:00 2001 From: The Android Automerger Date: Thu, 27 Oct 2011 17:41:45 -0700 Subject: Revert "Clean up CursorWindow lifetime." This reverts commit 7ce745248d4de0e6543a559c93423df899832100. --- core/java/android/database/AbstractCursor.java | 5 +- .../android/database/AbstractWindowedCursor.java | 53 +--- .../database/BulkCursorToCursorAdaptor.java | 5 +- core/java/android/database/CursorWindow.java | 20 +- core/java/android/database/CursorWrapper.java | 4 +- .../java/android/database/sqlite/SQLiteCursor.java | 194 +++++++++++- .../android/database/sqlite/SQLiteDatabase.java | 26 ++ core/java/android/database/sqlite/SQLiteQuery.java | 30 +- core/jni/android_database_SQLiteQuery.cpp | 328 ++++++++++++++------- .../src/android/database/DatabaseCursorTest.java | 103 +++++++ 10 files changed, 582 insertions(+), 186 deletions(-) diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java index 3f23b8900c5f..5fe42dbc3282 100644 --- a/core/java/android/database/AbstractCursor.java +++ b/core/java/android/database/AbstractCursor.java @@ -64,10 +64,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { /* Methods that may optionally be implemented by subclasses */ /** - * If the cursor is backed by a {@link CursorWindow}, returns a pre-filled - * window with the contents of the cursor, otherwise null. - * - * @return The pre-filled window that backs this cursor, or null if none. + * returns a pre-filled window, return NULL if no such window */ public CursorWindow getWindow() { return null; diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java index bfc8123e07fd..3d957693f652 100644 --- a/core/java/android/database/AbstractWindowedCursor.java +++ b/core/java/android/database/AbstractWindowedCursor.java @@ -18,22 +18,8 @@ package android.database; /** * A base class for Cursors that store their data in {@link CursorWindow}s. - *

- * 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 - * filled with new data. - *

- * If the contents of the cursor change or become invalid, the old window must be closed - * (because it is owned by the cursor) and set to null. - *

*/ public abstract class AbstractWindowedCursor extends AbstractCursor { - /** - * The cursor window owned by this cursor. - */ - protected CursorWindow mWindow; - @Override public byte[] getBlob(int columnIndex) { checkPosition(); @@ -140,44 +126,25 @@ public abstract class AbstractWindowedCursor extends AbstractCursor { public CursorWindow getWindow() { return mWindow; } - + /** - * Sets a new cursor window for the cursor to use. - *

- * The cursor takes ownership of the provided cursor window; the cursor window - * will be closed when the cursor is closed or when the cursor adopts a new - * cursor window. - *

- * If the cursor previously had a cursor window, then it is closed when the - * new cursor window is assigned. - *

- * - * @param window The new cursor window, typically a remote cursor window. + * Set a new cursor window to cursor, usually set a remote cursor window + * @param window cursor window */ public void setWindow(CursorWindow window) { - if (window != mWindow) { - closeWindow(); - mWindow = window; + if (mWindow != null) { + mWindow.close(); } + mWindow = window; } - - /** - * Returns true if the cursor has an associated cursor window. - * - * @return True if the cursor has an associated cursor window. - */ + public boolean hasWindow() { return mWindow != null; } /** - * Closes the cursor window and sets {@link #mWindow} to null. - * @hide + * This needs be updated in {@link #onMove} by subclasses, and + * needs to be set to NULL when the contents of the cursor change. */ - protected void closeWindow() { - if (mWindow != null) { - mWindow.close(); - mWindow = null; - } - } + protected CursorWindow mWindow; } diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java index 9c1b26d0a648..16becf57ba18 100644 --- a/core/java/android/database/BulkCursorToCursorAdaptor.java +++ b/core/java/android/database/BulkCursorToCursorAdaptor.java @@ -154,7 +154,10 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { false /* the window will be accessed across processes */)); if (mCount != -1) { mPos = -1; - closeWindow(); + if (mWindow != null) { + mWindow.close(); + mWindow = null; + } // 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 diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 2e3ef286fe80..183662f64ce0 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -105,12 +105,8 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { } @Override - protected void finalize() throws Throwable { - try { - dispose(); - } finally { - super.finalize(); - } + protected void finalize() { + dispose(); } private void dispose() { @@ -149,12 +145,10 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { /** * Gets the start position of this cursor window. - *

- * The start position is the zero-based index of the first row that this window contains + * The start position is the index of the first row that this window contains * relative to the entire result set of the {@link Cursor}. - *

* - * @return The zero-based start position. + * @return The start position. */ public int getStartPosition() { return mStartPos; @@ -162,12 +156,10 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { /** * Sets the start position of this cursor window. - *

- * The start position is the zero-based index of the first row that this window contains + * The start position is the index of the first row that this window contains * relative to the entire result set of the {@link Cursor}. - *

* - * @param pos The new zero-based start position. + * @param pos The new start position. */ public void setStartPosition(int pos) { mStartPos = pos; diff --git a/core/java/android/database/CursorWrapper.java b/core/java/android/database/CursorWrapper.java index 320733e4538e..3c3bd430a150 100644 --- a/core/java/android/database/CursorWrapper.java +++ b/core/java/android/database/CursorWrapper.java @@ -33,9 +33,7 @@ public class CursorWrapper implements Cursor { } /** - * Gets the underlying cursor that is wrapped by this instance. - * - * @return The wrapped cursor. + * @return the wrapped cursor */ public Cursor getWrappedCursor() { return mCursor; diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java index 81fe8241106a..ea9346d93fbb 100644 --- a/core/java/android/database/sqlite/SQLiteCursor.java +++ b/core/java/android/database/sqlite/SQLiteCursor.java @@ -18,11 +18,16 @@ package android.database.sqlite; import android.database.AbstractWindowedCursor; import android.database.CursorWindow; +import android.database.DataSetObserver; +import android.os.Handler; +import android.os.Message; +import android.os.Process; import android.os.StrictMode; import android.util.Log; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; /** * A Cursor implementation that exposes results from a query on a @@ -55,7 +60,139 @@ public class SQLiteCursor extends AbstractWindowedCursor { /** Used to find out where a cursor was allocated in case it never got released. */ private final Throwable mStackTrace; + + /** + * mMaxRead is the max items that each cursor window reads + * default to a very high value + */ + private int mMaxRead = Integer.MAX_VALUE; + private int mInitialRead = Integer.MAX_VALUE; + private int mCursorState = 0; + private ReentrantLock mLock = null; + private boolean mPendingData = false; + /** + * support for a cursor variant that doesn't always read all results + * initialRead is the initial number of items that cursor window reads + * if query contains more than this number of items, a thread will be + * created and handle the left over items so that caller can show + * results as soon as possible + * @param initialRead initial number of items that cursor read + * @param maxRead leftover items read at maxRead items per time + * @hide + */ + public void setLoadStyle(int initialRead, int maxRead) { + mMaxRead = maxRead; + mInitialRead = initialRead; + mLock = new ReentrantLock(true); + } + + private void queryThreadLock() { + if (mLock != null) { + mLock.lock(); + } + } + + private void queryThreadUnlock() { + if (mLock != null) { + mLock.unlock(); + } + } + + + /** + * @hide + */ + final private class QueryThread implements Runnable { + private final int mThreadState; + QueryThread(int version) { + mThreadState = version; + } + private void sendMessage() { + if (mNotificationHandler != null) { + mNotificationHandler.sendEmptyMessage(1); + mPendingData = false; + } else { + mPendingData = true; + } + + } + public void run() { + // use cached mWindow, to avoid get null mWindow + CursorWindow cw = mWindow; + Process.setThreadPriority(Process.myTid(), Process.THREAD_PRIORITY_BACKGROUND); + // the cursor's state doesn't change + while (true) { + mLock.lock(); + try { + if (mCursorState != mThreadState) { + break; + } + + int count = getQuery().fillWindow(cw, mMaxRead, mCount); + // return -1 means there is still more data to be retrieved from the resultset + if (count != 0) { + if (count == NO_COUNT){ + mCount += mMaxRead; + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "received -1 from native_fill_window. read " + + mCount + " rows so far"); + } + sendMessage(); + } else { + mCount += count; + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "received all data from native_fill_window. read " + + mCount + " rows."); + } + sendMessage(); + break; + } + } else { + break; + } + } catch (Exception e) { + // end the tread when the cursor is close + break; + } finally { + mLock.unlock(); + } + } + } + } + + /** + * @hide + */ + protected class MainThreadNotificationHandler extends Handler { + public void handleMessage(Message msg) { + notifyDataSetChange(); + } + } + + /** + * @hide + */ + protected MainThreadNotificationHandler mNotificationHandler; + + public void registerDataSetObserver(DataSetObserver observer) { + super.registerDataSetObserver(observer); + if ((Integer.MAX_VALUE != mMaxRead || Integer.MAX_VALUE != mInitialRead) && + mNotificationHandler == null) { + queryThreadLock(); + try { + mNotificationHandler = new MainThreadNotificationHandler(); + if (mPendingData) { + notifyDataSetChange(); + mPendingData = false; + } + } finally { + queryThreadUnlock(); + } + } + + } + /** * Execute a query and provide access to its result set through a Cursor * interface. For a query such as: {@code SELECT name, birth, phone FROM @@ -156,23 +293,36 @@ public class SQLiteCursor extends AbstractWindowedCursor { return mCount; } - private void fillWindow(int startPos) { + private void fillWindow (int startPos) { 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(); + mCursorState++; + queryThreadLock(); + try { + mWindow.clear(); + } finally { + queryThreadUnlock(); + } } mWindow.setStartPosition(startPos); - int count = getQuery().fillWindow(mWindow); - if (startPos == 0) { // fillWindow returns count(*) only for startPos = 0 + int count = getQuery().fillWindow(mWindow, mInitialRead, 0); + // return -1 means there is still more data to be retrieved from the resultset + if (count == NO_COUNT){ + mCount = startPos + mInitialRead; + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "received -1 from native_fill_window. read " + mCount + " rows so far"); + } + Thread t = new Thread(new QueryThread(mCursorState), "query thread"); + t.start(); + } else if (startPos == 0) { // native_fill_window returns count(*) only for startPos = 0 if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "received count(*) from native_fill_window: " + count); } mCount = count; } else if (mCount <= 0) { - throw new IllegalStateException("Row count should never be zero or negative " - + "when the start position is non-zero"); + throw new IllegalStateException("count should never be non-zero negative number"); } } @@ -216,7 +366,11 @@ public class SQLiteCursor extends AbstractWindowedCursor { private void deactivateCommon() { if (false) Log.v(TAG, "<<< Releasing cursor " + this); - closeWindow(); + mCursorState = 0; + if (mWindow != null) { + mWindow.close(); + mWindow = null; + } if (false) Log.v("DatabaseWindow", "closing window in release()"); } @@ -285,12 +439,16 @@ public class SQLiteCursor extends AbstractWindowedCursor { // This one will recreate the temp table, and get its count mDriver.cursorRequeried(this); mCount = NO_COUNT; + mCursorState++; + queryThreadLock(); try { mQuery.requery(); } catch (IllegalStateException e) { // for backwards compatibility, just return false Log.w(TAG, "requery() failed " + e.getMessage(), e); return false; + } finally { + queryThreadUnlock(); } } @@ -314,9 +472,18 @@ public class SQLiteCursor extends AbstractWindowedCursor { } @Override - public void setWindow(CursorWindow window) { - super.setWindow(window); - mCount = NO_COUNT; + public void setWindow(CursorWindow window) { + if (mWindow != null) { + mCursorState++; + queryThreadLock(); + try { + mWindow.close(); + } finally { + queryThreadUnlock(); + } + mCount = NO_COUNT; + } + mWindow = window; } /** @@ -354,4 +521,11 @@ public class SQLiteCursor extends AbstractWindowedCursor { super.finalize(); } } + + /** + * this is only for testing purposes. + */ + /* package */ int getMCount() { + return mCount; + } } diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 00d7ce80a6fa..d23873d7dc97 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -1597,6 +1597,32 @@ public class SQLiteDatabase extends SQLiteClosable { return cursor; } + /** + * Runs the provided SQL and returns a cursor over the result set. + * The cursor will read an initial set of rows and the return to the caller. + * It will continue to read in batches and send data changed notifications + * when the later batches are ready. + * @param sql the SQL query. The SQL string must not be ; terminated + * @param selectionArgs You may include ?s in where clause in the query, + * which will be replaced by the values from selectionArgs. The + * values will be bound as Strings. + * @param initialRead set the initial count of items to read from the cursor + * @param maxRead set the count of items to read on each iteration after the first + * @return A {@link Cursor} object, which is positioned before the first entry. Note that + * {@link Cursor}s are not synchronized, see the documentation for more details. + * + * This work is incomplete and not fully tested or reviewed, so currently + * hidden. + * @hide + */ + public Cursor rawQuery(String sql, String[] selectionArgs, + int initialRead, int maxRead) { + SQLiteCursor c = (SQLiteCursor)rawQueryWithFactory( + null, sql, selectionArgs, null); + c.setLoadStyle(initialRead, maxRead); + return c; + } + /** * Convenience method for inserting a row into the database. * diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java index 7db0914140b2..06a41b28504c 100644 --- a/core/java/android/database/sqlite/SQLiteQuery.java +++ b/core/java/android/database/sqlite/SQLiteQuery.java @@ -30,11 +30,6 @@ import android.util.Log; public class SQLiteQuery extends SQLiteProgram { private static final String TAG = "SQLiteQuery"; - private static native int nativeFillWindow(int databasePtr, int statementPtr, int windowPtr, - int startPos, int offsetParam); - private static native int nativeColumnCount(int statementPtr); - private static native String nativeColumnName(int statementPtr, int columnIndex); - /** The index of the unbound OFFSET parameter */ private int mOffsetIndex = 0; @@ -73,15 +68,19 @@ public class SQLiteQuery extends SQLiteProgram { * @param window The window to fill into * @return number of total rows in the query */ - /* package */ int fillWindow(CursorWindow window) { + /* package */ int fillWindow(CursorWindow window, + int maxRead, int lastPos) { mDatabase.lock(mSql); long timeStart = SystemClock.uptimeMillis(); try { acquireReference(); try { window.acquireReference(); - int numRows = nativeFillWindow(nHandle, nStatement, window.mWindowPtr, - window.getStartPosition(), mOffsetIndex); + // if the start pos is not equal to 0, then most likely window is + // too small for the data set, loading by another thread + // is not safe in this situation. the native code will ignore maxRead + int numRows = native_fill_window(window.mWindowPtr, window.getStartPosition(), + mOffsetIndex, maxRead, lastPos); mDatabase.logTimeStat(mSql, timeStart); return numRows; } catch (IllegalStateException e){ @@ -112,7 +111,7 @@ public class SQLiteQuery extends SQLiteProgram { /* package */ int columnCountLocked() { acquireReference(); try { - return nativeColumnCount(nStatement); + return native_column_count(); } finally { releaseReference(); } @@ -128,17 +127,17 @@ public class SQLiteQuery extends SQLiteProgram { /* package */ String columnNameLocked(int columnIndex) { acquireReference(); try { - return nativeColumnName(nStatement, columnIndex); + return native_column_name(columnIndex); } finally { releaseReference(); } } - + @Override public String toString() { return "SQLiteQuery: " + mSql; } - + @Override public void close() { super.close(); @@ -154,4 +153,11 @@ public class SQLiteQuery extends SQLiteProgram { } compileAndbindAllArgs(); } + + private final native int native_fill_window(int windowPtr, + int startPos, int offsetParam, int maxRead, int lastPos); + + private final native int native_column_count(); + + private final native String native_column_name(int columnIndex); } diff --git a/core/jni/android_database_SQLiteQuery.cpp b/core/jni/android_database_SQLiteQuery.cpp index 022a64c5b65d..fe62256c1766 100644 --- a/core/jni/android_database_SQLiteQuery.cpp +++ b/core/jni/android_database_SQLiteQuery.cpp @@ -35,20 +35,99 @@ namespace android { -static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, - jint statementPtr, jint windowPtr, jint startPos, jint offsetParam) { - sqlite3* database = reinterpret_cast(databasePtr); - sqlite3_stmt* statement = reinterpret_cast(statementPtr); - CursorWindow* window = reinterpret_cast(windowPtr); +static jfieldID gHandleField; +static jfieldID gStatementField; + + +#define GET_STATEMENT(env, object) \ + (sqlite3_stmt *)env->GetIntField(object, gStatementField) +#define GET_HANDLE(env, object) \ + (sqlite3 *)env->GetIntField(object, gHandleField) + +static int skip_rows(sqlite3_stmt *statement, int maxRows) { + int retryCount = 0; + for (int i = 0; i < maxRows; i++) { + int err = sqlite3_step(statement); + if (err == SQLITE_ROW){ + // do nothing + } else if (err == SQLITE_DONE) { + return i; + } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) { + // The table is locked, retry + LOG_WINDOW("Database locked, retrying"); + if (retryCount > 50) { + LOGE("Bailing on database busy rety"); + break; + } + // Sleep to give the thread holding the lock a chance to finish + usleep(1000); + retryCount++; + continue; + } else { + return -1; + } + } + LOG_WINDOW("skip_rows row %d", maxRows); + return maxRows; +} + +static int finish_program_and_get_row_count(sqlite3_stmt *statement) { + int numRows = 0; + int retryCount = 0; + while (true) { + int err = sqlite3_step(statement); + if (err == SQLITE_ROW){ + numRows++; + } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) { + // The table is locked, retry + LOG_WINDOW("Database locked, retrying"); + if (retryCount > 50) { + LOGE("Bailing on database busy rety"); + break; + } + // Sleep to give the thread holding the lock a chance to finish + usleep(1000); + retryCount++; + continue; + } else { + // no need to throw exception + break; + } + } + sqlite3_reset(statement); + LOG_WINDOW("finish_program_and_get_row_count row %d", numRows); + return numRows; +} + +static jint native_fill_window(JNIEnv* env, jobject object, jint windowPtr, + jint startPos, jint offsetParam, jint maxRead, jint lastPos) +{ + int err; + sqlite3_stmt * statement = GET_STATEMENT(env, object); + int numRows = lastPos; + maxRead += lastPos; + int numColumns; + int retryCount; + int boundParams; + CursorWindow * window; + bool gotAllRows = true; + bool gotException = false; + + if (statement == NULL) { + LOGE("Invalid statement in fillWindow()"); + jniThrowException(env, "java/lang/IllegalStateException", + "Attempting to access a deactivated, closed, or empty cursor"); + return 0; + } // Only do the binding if there is a valid offsetParam. If no binding needs to be done - // offsetParam will be set to 0, an invalid value. - if (offsetParam > 0) { + // offsetParam will be set to 0, an invliad value. + if(offsetParam > 0) { // Bind the offset parameter, telling the program which row to start with - int err = sqlite3_bind_int(statement, offsetParam, startPos); + err = sqlite3_bind_int(statement, offsetParam, startPos); if (err != SQLITE_OK) { LOGE("Unable to bind offset position, offsetParam = %d", offsetParam); - throw_sqlite3_exception(env, database); + throw_sqlite3_exception(env, GET_HANDLE(env, object)); return 0; } LOG_WINDOW("Bound to startPos %d", startPos); @@ -56,123 +135,136 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, LOG_WINDOW("Not binding to startPos %d", startPos); } - // We assume numRows is initially 0. - LOG_WINDOW("Window: numRows = %d, size = %d, freeSpace = %d", - window->getNumRows(), window->size(), window->freeSpace()); + // Get the native window + window = reinterpret_cast(windowPtr); + if (!window) { + LOGE("Invalid CursorWindow"); + jniThrowException(env, "java/lang/IllegalArgumentException", + "Bad CursorWindow"); + return 0; + } + LOG_WINDOW("Window: numRows = %d, size = %d, freeSpace = %d", window->getNumRows(), window->size(), window->freeSpace()); - int numColumns = sqlite3_column_count(statement); + numColumns = sqlite3_column_count(statement); if (!window->setNumColumns(numColumns)) { LOGE("Failed to change column count from %d to %d", window->getNumColumns(), numColumns); jniThrowException(env, "java/lang/IllegalStateException", "numColumns mismatch"); return 0; } - int retryCount = 0; - int totalRows = 0; - int addedRows = 0; - bool windowFull = false; - bool gotException = false; - const bool countAllRows = (startPos == 0); // when startPos is 0, we count all rows - while (!gotException && (!windowFull || countAllRows)) { - int err = sqlite3_step(statement); + retryCount = 0; + if (startPos > 0) { + int num = skip_rows(statement, startPos); + if (num < 0) { + throw_sqlite3_exception(env, GET_HANDLE(env, object)); + return 0; + } else if (num < startPos) { + LOGE("startPos %d > actual rows %d", startPos, num); + return num; + } + } + + while(startPos != 0 || numRows < maxRead) { + err = sqlite3_step(statement); if (err == SQLITE_ROW) { - LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows); + LOG_WINDOW("\nStepped statement %p to row %d", statement, startPos + numRows); retryCount = 0; - totalRows += 1; - - // Skip the row if the window is full or we haven't reached the start position yet. - if (startPos >= totalRows || windowFull) { - continue; - } // Allocate a new field directory for the row. This pointer is not reused - // since it may be possible for it to be relocated on a call to alloc() when + // since it mey be possible for it to be relocated on a call to alloc() when // the field data is being allocated. - field_slot_t* fieldDir = window->allocRow(); - if (!fieldDir) { - LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d", - startPos, addedRows); - windowFull = true; - continue; + { + field_slot_t * fieldDir = window->allocRow(); + if (!fieldDir) { + LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d", startPos, numRows); + gotAllRows = false; + goto return_count; + } } - // Pack the row into the window. - for (int i = 0; i < numColumns; i++) { + // Pack the row into the window + int i; + for (i = 0; i < numColumns; i++) { int type = sqlite3_column_type(statement, i); if (type == SQLITE_TEXT) { // TEXT data #if WINDOW_STORAGE_UTF8 - const uint8_t* text = reinterpret_cast( - sqlite3_column_text(statement, i)); + uint8_t const * text = (uint8_t const *)sqlite3_column_text(statement, i); // SQLite does not include the NULL terminator in size, but does // ensure all strings are NULL terminated, so increase size by // one to make sure we store the terminator. size_t size = sqlite3_column_bytes(statement, i) + 1; #else - const uint8_t* text = reinterpret_cast( - sqlite3_column_text16(statement, i)); + uint8_t const * text = (uint8_t const *)sqlite3_column_text16(statement, i); size_t size = sqlite3_column_bytes16(statement, i); #endif int offset = window->alloc(size); if (!offset) { + window->freeLastRow(); LOG_WINDOW("Failed allocating %u bytes for text/blob at %d,%d", size, - startPos + addedRows, i); - windowFull = true; - break; + startPos + numRows, i); + gotAllRows = false; + goto return_count; } + window->copyIn(offset, text, size); - field_slot_t* fieldSlot = window->getFieldSlot(addedRows, i); + // This must be updated after the call to alloc(), since that + // may move the field around in the window + field_slot_t * fieldSlot = window->getFieldSlot(numRows, i); fieldSlot->type = FIELD_TYPE_STRING; fieldSlot->data.buffer.offset = offset; fieldSlot->data.buffer.size = size; - LOG_WINDOW("%d,%d is TEXT with %u bytes", startPos + addedRows, i, size); + + LOG_WINDOW("%d,%d is TEXT with %u bytes", startPos + numRows, i, size); } else if (type == SQLITE_INTEGER) { // INTEGER data int64_t value = sqlite3_column_int64(statement, i); - if (!window->putLong(addedRows, i, value)) { + if (!window->putLong(numRows, i, value)) { + window->freeLastRow(); LOG_WINDOW("Failed allocating space for a long in column %d", i); - windowFull = true; - break; + gotAllRows = false; + goto return_count; } - LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + addedRows, i, value); + LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + numRows, i, value); } else if (type == SQLITE_FLOAT) { // FLOAT data double value = sqlite3_column_double(statement, i); - if (!window->putDouble(addedRows, i, value)) { + if (!window->putDouble(numRows, i, value)) { + window->freeLastRow(); LOG_WINDOW("Failed allocating space for a double in column %d", i); - windowFull = true; - break; + gotAllRows = false; + goto return_count; } - LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value); + LOG_WINDOW("%d,%d is FLOAT %lf", startPos + numRows, i, value); } else if (type == SQLITE_BLOB) { // BLOB data uint8_t const * blob = (uint8_t const *)sqlite3_column_blob(statement, i); size_t size = sqlite3_column_bytes16(statement, i); int offset = window->alloc(size); if (!offset) { + window->freeLastRow(); LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d", size, - startPos + addedRows, i); - windowFull = true; - break; + startPos + numRows, i); + gotAllRows = false; + goto return_count; } + window->copyIn(offset, blob, size); - field_slot_t* fieldSlot = window->getFieldSlot(addedRows, i); + // This must be updated after the call to alloc(), since that + // may move the field around in the window + field_slot_t * fieldSlot = window->getFieldSlot(numRows, i); fieldSlot->type = FIELD_TYPE_BLOB; fieldSlot->data.buffer.offset = offset; fieldSlot->data.buffer.size = size; - LOG_WINDOW("%d,%d is Blob with %u bytes @ %d", - startPos + addedRows, i, size, offset); + + LOG_WINDOW("%d,%d is Blob with %u bytes @ %d", startPos + numRows, i, size, offset); } else if (type == SQLITE_NULL) { // NULL field - if (!window->putNull(addedRows, i)) { - LOG_WINDOW("Failed allocating space for a null in column %d", i); - windowFull = true; - break; - } + window->putNull(numRows, i); - LOG_WINDOW("%d,%d is NULL", startPos + addedRows, i); + LOG_WINDOW("%d,%d is NULL", startPos + numRows, i); } else { // Unknown data LOGE("Unknown column type when filling database window"); @@ -182,12 +274,14 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, } } - // Update the final row tally. - if (windowFull || gotException) { - window->freeLastRow(); - } else { - addedRows += 1; + if (i < numColumns) { + // Not all the fields fit in the window + // Unknown data error happened + break; } + + // Mark the row as complete in the window + numRows++; } else if (err == SQLITE_DONE) { // All rows processed, bail LOG_WINDOW("Processed all rows"); @@ -196,41 +290,63 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, // The table is locked, retry LOG_WINDOW("Database locked, retrying"); if (retryCount > 50) { - LOGE("Bailing on database busy retry"); - throw_sqlite3_exception(env, database, "retrycount exceeded"); + LOGE("Bailing on database busy rety"); + throw_sqlite3_exception(env, GET_HANDLE(env, object), "retrycount exceeded"); gotException = true; - } else { - // Sleep to give the thread holding the lock a chance to finish - usleep(1000); - retryCount++; + break; } + + // Sleep to give the thread holding the lock a chance to finish + usleep(1000); + + retryCount++; + continue; } else { - throw_sqlite3_exception(env, database); + throw_sqlite3_exception(env, GET_HANDLE(env, object)); gotException = true; + break; } } - LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows" - "to the window in %d bytes", - statement, totalRows, addedRows, window->size() - window->freeSpace()); - sqlite3_reset(statement); - - // Report the total number of rows on request. - if (startPos > totalRows) { - LOGE("startPos %d > actual rows %d", startPos, totalRows); + LOG_WINDOW("Resetting statement %p after fetching %d rows in %d bytes\n\n\n\n", statement, + numRows, window->size() - window->freeSpace()); + LOG_WINDOW("Filled window with %d rows in %d bytes", numRows, + window->size() - window->freeSpace()); + if (err == SQLITE_ROW) { + // there is more data to be returned. let the caller know by returning -1 + return -1; + } + return_count: + if (startPos) { + sqlite3_reset(statement); + LOG_WINDOW("Not doing count(*) because startPos %d is non-zero", startPos); + return 0; + } else if (gotAllRows) { + sqlite3_reset(statement); + LOG_WINDOW("Not doing count(*) because we already know the count(*)"); + return numRows; + } else if (gotException) { + return 0; + } else { + // since startPos == 0, we need to get the count(*) of the result set + return numRows + 1 + finish_program_and_get_row_count(statement); } - return countAllRows ? totalRows : 0; } -static jint nativeColumnCount(JNIEnv* env, jclass clazz, jint statementPtr) { - sqlite3_stmt* statement = reinterpret_cast(statementPtr); +static jint native_column_count(JNIEnv* env, jobject object) +{ + sqlite3_stmt * statement = GET_STATEMENT(env, object); + return sqlite3_column_count(statement); } -static jstring nativeColumnName(JNIEnv* env, jclass clazz, jint statementPtr, - jint columnIndex) { - sqlite3_stmt* statement = reinterpret_cast(statementPtr); - const char* name = sqlite3_column_name(statement, columnIndex); +static jstring native_column_name(JNIEnv* env, jobject object, jint columnIndex) +{ + sqlite3_stmt * statement = GET_STATEMENT(env, object); + char const * name; + + name = sqlite3_column_name(statement, columnIndex); + return env->NewStringUTF(name); } @@ -238,16 +354,30 @@ static jstring nativeColumnName(JNIEnv* env, jclass clazz, jint statementPtr, static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ - { "nativeFillWindow", "(IIIII)I", - (void*)nativeFillWindow }, - { "nativeColumnCount", "(I)I", - (void*)nativeColumnCount}, - { "nativeColumnName", "(II)Ljava/lang/String;", - (void*)nativeColumnName}, + {"native_fill_window", "(IIIII)I", + (void *)native_fill_window}, + {"native_column_count", "()I", (void*)native_column_count}, + {"native_column_name", "(I)Ljava/lang/String;", (void *)native_column_name}, }; int register_android_database_SQLiteQuery(JNIEnv * env) { + jclass clazz; + + clazz = env->FindClass("android/database/sqlite/SQLiteQuery"); + if (clazz == NULL) { + LOGE("Can't find android/database/sqlite/SQLiteQuery"); + return -1; + } + + gHandleField = env->GetFieldID(clazz, "nHandle", "I"); + gStatementField = env->GetFieldID(clazz, "nStatement", "I"); + + if (gHandleField == NULL || gStatementField == NULL) { + LOGE("Error locating fields"); + return -1; + } + return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteQuery", sMethods, NELEM(sMethods)); } diff --git a/core/tests/coretests/src/android/database/DatabaseCursorTest.java b/core/tests/coretests/src/android/database/DatabaseCursorTest.java index 179338dde3bd..d5b9ee6d44e4 100644 --- a/core/tests/coretests/src/android/database/DatabaseCursorTest.java +++ b/core/tests/coretests/src/android/database/DatabaseCursorTest.java @@ -290,7 +290,110 @@ public class DatabaseCursorTest extends AndroidTestCase implements PerformanceTe public void onInvalidated() { } } + + //@Large + @Suppress + public void testLoadingThreadDelayRegisterData() throws Exception { + mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); + + final int count = 505; + String sql = "INSERT INTO test (data) VALUES (?);"; + SQLiteStatement s = mDatabase.compileStatement(sql); + for (int i = 0; i < count; i++) { + s.bindLong(1, i); + s.execute(); + } + + int maxRead = 500; + int initialRead = 5; + SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;", + null, initialRead, maxRead); + + TestObserver observer = new TestObserver(count, c); + c.getCount(); + c.registerDataSetObserver(observer); + if (!observer.quit) { + Looper.loop(); + } + c.close(); + } + + //@LargeTest + @BrokenTest("Consistently times out") + @Suppress + public void testLoadingThread() throws Exception { + mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); + + final int count = 50000; + String sql = "INSERT INTO test (data) VALUES (?);"; + SQLiteStatement s = mDatabase.compileStatement(sql); + for (int i = 0; i < count; i++) { + s.bindLong(1, i); + s.execute(); + } + int maxRead = 1000; + int initialRead = 5; + SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;", + null, initialRead, maxRead); + + TestObserver observer = new TestObserver(count, c); + c.registerDataSetObserver(observer); + c.getCount(); + + Looper.loop(); + c.close(); + } + + //@LargeTest + @BrokenTest("Consistently times out") + @Suppress + public void testLoadingThreadClose() throws Exception { + mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); + + final int count = 1000; + String sql = "INSERT INTO test (data) VALUES (?);"; + SQLiteStatement s = mDatabase.compileStatement(sql); + for (int i = 0; i < count; i++) { + s.bindLong(1, i); + s.execute(); + } + + int maxRead = 11; + int initialRead = 5; + SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;", + null, initialRead, maxRead); + + TestObserver observer = new TestObserver(count, c); + c.registerDataSetObserver(observer); + c.getCount(); + c.close(); + } + + @LargeTest + public void testLoadingThreadDeactivate() throws Exception { + mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); + + final int count = 1000; + String sql = "INSERT INTO test (data) VALUES (?);"; + SQLiteStatement s = mDatabase.compileStatement(sql); + for (int i = 0; i < count; i++) { + s.bindLong(1, i); + s.execute(); + } + + int maxRead = 11; + int initialRead = 5; + SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;", + null, initialRead, maxRead); + + TestObserver observer = new TestObserver(count, c); + c.registerDataSetObserver(observer); + c.getCount(); + c.deactivate(); + c.close(); + } + @LargeTest public void testManyRowsLong() throws Exception { mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);"); -- cgit v1.2.3-59-g8ed1b From dd8f899a01b42efa87cd7749a919ed84a478c9bb Mon Sep 17 00:00:00 2001 From: The Android Automerger Date: Thu, 27 Oct 2011 17:42:01 -0700 Subject: Revert "Restore broken CursorWindow.getType behavior." This reverts commit aa32c30b81134fc7ebd9408f4757d1dc4410f338. --- core/jni/android_database_CursorWindow.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index 9ff2cb279101..14c6397db8c4 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -147,10 +147,8 @@ static jint nativeGetType(JNIEnv* env, jclass clazz, jint windowPtr, field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); if (!fieldSlot) { - // FIXME: This is really broken but we have CTS tests that depend - // on this legacy behavior. - //throwExceptionWithRowCol(env, row, column); - return FIELD_TYPE_NULL; + throwExceptionWithRowCol(env, row, column); + return NULL; } return fieldSlot->type; } -- cgit v1.2.3-59-g8ed1b From 7ec683b5c7f562960f3c7c701ccb77ebe8f23e4d Mon Sep 17 00:00:00 2001 From: The Android Automerger Date: Thu, 27 Oct 2011 17:42:17 -0700 Subject: Revert "Clean up CursorWindow code." This reverts commit 3bc6bbc92cd2095f42039b5aadd0a14d0e5d9230. --- core/java/android/database/CursorWindow.java | 792 ++++++++++----------- .../database/CursorWindowAllocationException.java | 15 +- core/java/android/database/sqlite/SQLiteQuery.java | 4 +- core/jni/android_database_CursorWindow.cpp | 777 +++++++++++--------- core/jni/android_database_SQLiteQuery.cpp | 12 +- include/android_runtime/AndroidRuntime.h | 4 + include/binder/CursorWindow.h | 28 +- libs/binder/CursorWindow.cpp | 41 +- 8 files changed, 858 insertions(+), 815 deletions(-) diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 183662f64ce0..f7cbf7a4e4c6 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -18,7 +18,6 @@ package android.database; import android.content.res.Resources; import android.database.sqlite.SQLiteClosable; -import android.database.sqlite.SQLiteException; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; @@ -40,613 +39,538 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { Resources.getSystem().getInteger( com.android.internal.R.integer.config_cursorWindowSize) * 1024; - /** - * The native CursorWindow object pointer. (FOR INTERNAL USE ONLY) - * @hide + /** The pointer to the native window class. set by the native methods in + * android_database_CursorWindow.cpp */ - public int mWindowPtr; + private int nWindow; private int mStartPos; - private static native int nativeInitializeEmpty(int cursorWindowSize, boolean localOnly); - private static native int nativeInitializeFromBinder(IBinder nativeBinder); - private static native void nativeDispose(int windowPtr); - private static native IBinder nativeGetBinder(int windowPtr); - - private static native void nativeClear(int windowPtr); - - private static native int nativeGetNumRows(int windowPtr); - private static native boolean nativeSetNumColumns(int windowPtr, int columnNum); - private static native boolean nativeAllocRow(int windowPtr); - private static native void nativeFreeLastRow(int windowPtr); - - private static native int nativeGetType(int windowPtr, int row, int column); - private static native byte[] nativeGetBlob(int windowPtr, int row, int column); - private static native String nativeGetString(int windowPtr, int row, int column); - private static native long nativeGetLong(int windowPtr, int row, int column); - private static native double nativeGetDouble(int windowPtr, int row, int column); - private static native void nativeCopyStringToBuffer(int windowPtr, int row, int column, - CharArrayBuffer buffer); - - private static native boolean nativePutBlob(int windowPtr, byte[] value, int row, int column); - private static native boolean nativePutString(int windowPtr, String value, int row, int column); - private static native boolean nativePutLong(int windowPtr, long value, int row, int column); - private static native boolean nativePutDouble(int windowPtr, double value, int row, int column); - private static native boolean nativePutNull(int windowPtr, int row, int column); - - /** - * Creates a new empty cursor window. - *

- * The cursor initially has no rows or columns. Call {@link #setNumColumns(int)} to - * set the number of columns before adding any rows to the cursor. - *

+ /** + * Creates a new empty window. * - * @param localWindow True if this window will be used in this process only, - * false if it might be sent to another processes. + * @param localWindow true if this window will be used in this process only */ public CursorWindow(boolean localWindow) { mStartPos = 0; - mWindowPtr = nativeInitializeEmpty(sCursorWindowSize, localWindow); - if (mWindowPtr == 0) { - throw new CursorWindowAllocationException("Cursor window allocation of " + - (sCursorWindowSize / 1024) + " kb failed. " + printStats()); - } - recordNewWindow(Binder.getCallingPid(), mWindowPtr); + int rslt = native_init(sCursorWindowSize, localWindow); + printDebugMsgIfError(rslt); + recordNewWindow(Binder.getCallingPid(), nWindow); } - private CursorWindow(Parcel source) { - IBinder binder = source.readStrongBinder(); - mStartPos = source.readInt(); - mWindowPtr = nativeInitializeFromBinder(binder); - if (mWindowPtr == 0) { - throw new CursorWindowAllocationException("Cursor window could not be " - + "created from binder."); + private void printDebugMsgIfError(int rslt) { + if (rslt > 0) { + // cursor window allocation failed. either low memory or too many cursors being open. + // print info to help in debugging this. + throw new CursorWindowAllocationException("Cursor Window allocation of " + + sCursorWindowSize/1024 + " kb failed. " + printStats()); } } - @Override - protected void finalize() { - dispose(); + /** + * Returns the starting position of this window within the entire + * Cursor's result set. + * + * @return the starting position of this window within the entire + * Cursor's result set. + */ + public int getStartPosition() { + return mStartPos; } - private void dispose() { - if (mWindowPtr != 0) { - recordClosingOfWindow(mWindowPtr); - nativeDispose(mWindowPtr); - mWindowPtr = 0; + /** + * Set the start position of cursor window + * @param pos + */ + public void setStartPosition(int pos) { + mStartPos = pos; + } + + /** + * Returns the number of rows in this window. + * + * @return the number of rows in this window. + */ + public int getNumRows() { + acquireReference(); + try { + return getNumRows_native(); + } finally { + releaseReference(); } } - + + private native int getNumRows_native(); /** - * Closes the cursor window and frees its underlying resources when all other - * remaining references have been released. + * Set number of Columns + * @param columnNum + * @return true if success */ - public void close() { - releaseReference(); + public boolean setNumColumns(int columnNum) { + acquireReference(); + try { + return setNumColumns_native(columnNum); + } finally { + releaseReference(); + } } - + + private native boolean setNumColumns_native(int columnNum); + /** - * Clears out the existing contents of the window, making it safe to reuse - * for new data. - *

- * The start position ({@link #getStartPosition()}), number of rows ({@link #getNumRows()}), - * and number of columns in the cursor are all reset to zero. - *

+ * Allocate a row in cursor window + * @return false if cursor window is out of memory */ - public void clear() { + public boolean allocRow(){ acquireReference(); try { - mStartPos = 0; - nativeClear(mWindowPtr); + return allocRow_native(); } finally { releaseReference(); } } - + + private native boolean allocRow_native(); + /** - * Gets the start position of this cursor window. - * The start position is the index of the first row that this window contains - * relative to the entire result set of the {@link Cursor}. - * - * @return The start position. + * Free the last row */ - public int getStartPosition() { - return mStartPos; + public void freeLastRow(){ + acquireReference(); + try { + freeLastRow_native(); + } finally { + releaseReference(); + } } + + private native void freeLastRow_native(); /** - * Sets the start position of this cursor window. - * The start position is the index of the first row that this window contains - * relative to the entire result set of the {@link Cursor}. - * - * @param pos The new start position. + * copy byte array to cursor window + * @param value + * @param row + * @param col + * @return false if fail to copy */ - public void setStartPosition(int pos) { - mStartPos = pos; + public boolean putBlob(byte[] value, int row, int col) { + acquireReference(); + try { + return putBlob_native(value, row - mStartPos, col); + } finally { + releaseReference(); + } } + + private native boolean putBlob_native(byte[] value, int row, int col); /** - * Gets the number of rows in this window. - * - * @return The number of rows in this cursor window. + * Copy String to cursor window + * @param value + * @param row + * @param col + * @return false if fail to copy */ - public int getNumRows() { + public boolean putString(String value, int row, int col) { acquireReference(); try { - return nativeGetNumRows(mWindowPtr); + return putString_native(value, row - mStartPos, col); } finally { releaseReference(); } } - + + private native boolean putString_native(String value, int row, int col); + /** - * Sets the number of columns in this window. - *

- * This method must be called before any rows are added to the window, otherwise - * it will fail to set the number of columns if it differs from the current number - * of columns. - *

- * - * @param columnNum The new number of columns. - * @return True if successful. + * Copy integer to cursor window + * @param value + * @param row + * @param col + * @return false if fail to copy */ - public boolean setNumColumns(int columnNum) { + public boolean putLong(long value, int row, int col) { acquireReference(); try { - return nativeSetNumColumns(mWindowPtr, columnNum); + return putLong_native(value, row - mStartPos, col); } finally { releaseReference(); } } + + private native boolean putLong_native(long value, int row, int col); + /** - * Allocates a new row at the end of this cursor window. - * - * @return True if successful, false if the cursor window is out of memory. + * Copy double to cursor window + * @param value + * @param row + * @param col + * @return false if fail to copy */ - public boolean allocRow(){ + public boolean putDouble(double value, int row, int col) { acquireReference(); try { - return nativeAllocRow(mWindowPtr); + return putDouble_native(value, row - mStartPos, col); } finally { releaseReference(); } } + + private native boolean putDouble_native(double value, int row, int col); /** - * Frees the last row in this cursor window. + * Set the [row, col] value to NULL + * @param row + * @param col + * @return false if fail to copy */ - public void freeLastRow(){ + public boolean putNull(int row, int col) { acquireReference(); try { - nativeFreeLastRow(mWindowPtr); + return putNull_native(row - mStartPos, col); } finally { releaseReference(); } } + + private native boolean putNull_native(int row, int col); + /** - * Returns true if the field at the specified row and column index - * has type {@link Cursor#FIELD_TYPE_NULL}. - * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return True if the field has type {@link Cursor#FIELD_TYPE_NULL}. - * @deprecated Use {@link #getType(int, int)} instead. + * Returns {@code true} if given field is {@code NULL}. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return {@code true} if given field is {@code NULL} + * @deprecated use {@link #getType(int, int)} instead */ @Deprecated - public boolean isNull(int row, int column) { - return getType(row, column) == Cursor.FIELD_TYPE_NULL; + public boolean isNull(int row, int col) { + return getType(row, col) == Cursor.FIELD_TYPE_NULL; } - + /** - * Returns true if the field at the specified row and column index - * has type {@link Cursor#FIELD_TYPE_BLOB} or {@link Cursor#FIELD_TYPE_NULL}. + * Returns a byte array for the given field. * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return True if the field has type {@link Cursor#FIELD_TYPE_BLOB} or - * {@link Cursor#FIELD_TYPE_NULL}. - * @deprecated Use {@link #getType(int, int)} instead. + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return a String value for the given field */ - @Deprecated - public boolean isBlob(int row, int column) { - int type = getType(row, column); - return type == Cursor.FIELD_TYPE_BLOB || type == Cursor.FIELD_TYPE_NULL; + public byte[] getBlob(int row, int col) { + acquireReference(); + try { + return getBlob_native(row - mStartPos, col); + } finally { + releaseReference(); + } } /** - * Returns true if the field at the specified row and column index - * has type {@link Cursor#FIELD_TYPE_INTEGER}. + * Returns the value at (row, col) as a byte array. * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return True if the field has type {@link Cursor#FIELD_TYPE_INTEGER}. - * @deprecated Use {@link #getType(int, int)} instead. + *

If the value is null, then null is returned. If the + * type of column col is a string type, then the result + * is the array of bytes that make up the internal representation of the + * string value. If the type of column col is integral or floating-point, + * then an {@link SQLiteException} is thrown. */ - @Deprecated - public boolean isLong(int row, int column) { - return getType(row, column) == Cursor.FIELD_TYPE_INTEGER; + private native byte[] getBlob_native(int row, int col); + + /** + * Returns data type of the given column's value. + *

+ * Returned column types are + *

    + *
  • {@link Cursor#FIELD_TYPE_NULL}
  • + *
  • {@link Cursor#FIELD_TYPE_INTEGER}
  • + *
  • {@link Cursor#FIELD_TYPE_FLOAT}
  • + *
  • {@link Cursor#FIELD_TYPE_STRING}
  • + *
  • {@link Cursor#FIELD_TYPE_BLOB}
  • + *
+ *

+ * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return the value type + */ + public int getType(int row, int col) { + acquireReference(); + try { + return getType_native(row - mStartPos, col); + } finally { + releaseReference(); + } } /** - * Returns true if the field at the specified row and column index - * has type {@link Cursor#FIELD_TYPE_FLOAT}. + * Checks if a field contains either a blob or is null. * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return True if the field has type {@link Cursor#FIELD_TYPE_FLOAT}. - * @deprecated Use {@link #getType(int, int)} instead. + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return {@code true} if given field is {@code NULL} or a blob + * @deprecated use {@link #getType(int, int)} instead */ @Deprecated - public boolean isFloat(int row, int column) { - return getType(row, column) == Cursor.FIELD_TYPE_FLOAT; + public boolean isBlob(int row, int col) { + int type = getType(row, col); + return type == Cursor.FIELD_TYPE_BLOB || type == Cursor.FIELD_TYPE_NULL; } /** - * Returns true if the field at the specified row and column index - * has type {@link Cursor#FIELD_TYPE_STRING} or {@link Cursor#FIELD_TYPE_NULL}. + * Checks if a field contains a long * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return True if the field has type {@link Cursor#FIELD_TYPE_STRING} - * or {@link Cursor#FIELD_TYPE_NULL}. - * @deprecated Use {@link #getType(int, int)} instead. + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return {@code true} if given field is a long + * @deprecated use {@link #getType(int, int)} instead */ @Deprecated - public boolean isString(int row, int column) { - int type = getType(row, column); - return type == Cursor.FIELD_TYPE_STRING || type == Cursor.FIELD_TYPE_NULL; + public boolean isLong(int row, int col) { + return getType(row, col) == Cursor.FIELD_TYPE_INTEGER; } /** - * Returns the type of the field at the specified row and column index. - *

- * The returned field types are: - *

    - *
  • {@link Cursor#FIELD_TYPE_NULL}
  • - *
  • {@link Cursor#FIELD_TYPE_INTEGER}
  • - *
  • {@link Cursor#FIELD_TYPE_FLOAT}
  • - *
  • {@link Cursor#FIELD_TYPE_STRING}
  • - *
  • {@link Cursor#FIELD_TYPE_BLOB}
  • - *
- *

+ * Checks if a field contains a float. * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return The field type. + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return {@code true} if given field is a float + * @deprecated use {@link #getType(int, int)} instead */ - public int getType(int row, int column) { - acquireReference(); - try { - return nativeGetType(mWindowPtr, row - mStartPos, column); - } finally { - releaseReference(); - } + @Deprecated + public boolean isFloat(int row, int col) { + return getType(row, col) == Cursor.FIELD_TYPE_FLOAT; } /** - * Gets the value of the field at the specified row and column index as a byte array. - *

- * The result is determined as follows: - *

    - *
  • If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result - * is null.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then the result - * is the blob value.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result - * is the array of bytes that make up the internal representation of the - * string value.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_INTEGER} or - * {@link Cursor#FIELD_TYPE_FLOAT}, then a {@link SQLiteException} is thrown.
  • - *
- *

+ * Checks if a field contains either a String or is null. * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return The value of the field as a byte array. + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return {@code true} if given field is {@code NULL} or a String + * @deprecated use {@link #getType(int, int)} instead */ - public byte[] getBlob(int row, int column) { - acquireReference(); - try { - return nativeGetBlob(mWindowPtr, row - mStartPos, column); - } finally { - releaseReference(); - } + @Deprecated + public boolean isString(int row, int col) { + int type = getType(row, col); + return type == Cursor.FIELD_TYPE_STRING || type == Cursor.FIELD_TYPE_NULL; } + private native int getType_native(int row, int col); + /** - * Gets the value of the field at the specified row and column index as a string. - *

- * The result is determined as follows: - *

    - *
  • If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result - * is null.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result - * is the string value.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result - * is a string representation of the integer in decimal, obtained by formatting the - * value with the printf family of functions using - * format specifier %lld.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result - * is a string representation of the floating-point value in decimal, obtained by - * formatting the value with the printf family of functions using - * format specifier %g.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a - * {@link SQLiteException} is thrown.
  • - *
- *

- * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return The value of the field as a string. + * Returns a String for the given field. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return a String value for the given field */ - public String getString(int row, int column) { + public String getString(int row, int col) { acquireReference(); try { - return nativeGetString(mWindowPtr, row - mStartPos, column); + return getString_native(row - mStartPos, col); } finally { releaseReference(); } } - + /** - * Copies the text of the field at the specified row and column index into - * a {@link CharArrayBuffer}. - *

- * The buffer is populated as follows: - *

    - *
  • If the buffer is too small for the value to be copied, then it is - * automatically resized.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the buffer - * is set to an empty string.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the buffer - * is set to the contents of the string.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the buffer - * is set to a string representation of the integer in decimal, obtained by formatting the - * value with the printf family of functions using - * format specifier %lld.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the buffer is - * set to a string representation of the floating-point value in decimal, obtained by - * formatting the value with the printf family of functions using - * format specifier %g.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a - * {@link SQLiteException} is thrown.
  • - *
- *

+ * Returns the value at (row, col) as a String. * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @param buffer The {@link CharArrayBuffer} to hold the string. It is automatically - * resized if the requested string is larger than the buffer's current capacity. + *

If the value is null, then null is returned. If the + * type of column col is integral, then the result is the string + * that is obtained by formatting the integer value with the printf + * family of functions using format specifier %lld. If the + * type of column col is floating-point, then the result is the string + * that is obtained by formatting the floating-point value with the + * printf family of functions using format specifier %g. + * If the type of column col is a blob type, then an + * {@link SQLiteException} is thrown. + */ + private native String getString_native(int row, int col); + + /** + * copy the text for the given field in the provided char array. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @param buffer the CharArrayBuffer to copy the text into, + * If the requested string is larger than the buffer + * a new char buffer will be created to hold the string. and assigne to + * CharArrayBuffer.data */ - public void copyStringToBuffer(int row, int column, CharArrayBuffer buffer) { + public void copyStringToBuffer(int row, int col, CharArrayBuffer buffer) { if (buffer == null) { throw new IllegalArgumentException("CharArrayBuffer should not be null"); } + if (buffer.data == null) { + buffer.data = new char[64]; + } acquireReference(); try { - nativeCopyStringToBuffer(mWindowPtr, row, column, buffer); + char[] newbuf = copyStringToBuffer_native( + row - mStartPos, col, buffer.data.length, buffer); + if (newbuf != null) { + buffer.data = newbuf; + } } finally { releaseReference(); } } - + + private native char[] copyStringToBuffer_native( + int row, int col, int bufferSize, CharArrayBuffer buffer); + /** - * Gets the value of the field at the specified row and column index as a long. - *

- * The result is determined as follows: - *

    - *
  • If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result - * is 0L.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result - * is the value obtained by parsing the string value with strtoll. - *
  • If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result - * is the long value.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result - * is the floating-point value converted to a long.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a - * {@link SQLiteException} is thrown.
  • - *
- *

- * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return The value of the field as a long. + * Returns a long for the given field. + * row is 0 based + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return a long value for the given field */ - public long getLong(int row, int column) { + public long getLong(int row, int col) { acquireReference(); try { - return nativeGetLong(mWindowPtr, row - mStartPos, column); + return getLong_native(row - mStartPos, col); } finally { releaseReference(); } } - + /** - * Gets the value of the field at the specified row and column index as a - * double. - *

- * The result is determined as follows: - *

    - *
  • If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result - * is 0.0.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result - * is the value obtained by parsing the string value with strtod. - *
  • If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result - * is the integer value converted to a double.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result - * is the double value.
  • - *
  • If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a - * {@link SQLiteException} is thrown.
  • - *
- *

+ * Returns the value at (row, col) as a long. * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return The value of the field as a double. + *

If the value is null, then 0L is returned. If the + * type of column col is a string type, then the result + * is the long that is obtained by parsing the string value with + * strtoll. If the type of column col is + * floating-point, then the result is the floating-point value casted to a long. + * If the type of column col is a blob type, then an + * {@link SQLiteException} is thrown. */ - public double getDouble(int row, int column) { + private native long getLong_native(int row, int col); + + /** + * Returns a double for the given field. + * row is 0 based + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return a double value for the given field + */ + public double getDouble(int row, int col) { acquireReference(); try { - return nativeGetDouble(mWindowPtr, row - mStartPos, column); + return getDouble_native(row - mStartPos, col); } finally { releaseReference(); } } - - /** - * Gets the value of the field at the specified row and column index as a - * short. - *

- * The result is determined by invoking {@link #getLong} and converting the - * result to short. - *

- * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return The value of the field as a short. - */ - public short getShort(int row, int column) { - return (short) getLong(row, column); - } - - /** - * Gets the value of the field at the specified row and column index as an - * int. - *

- * The result is determined by invoking {@link #getLong} and converting the - * result to int. - *

- * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return The value of the field as an int. - */ - public int getInt(int row, int column) { - return (int) getLong(row, column); - } - + /** - * Gets the value of the field at the specified row and column index as a - * float. - *

- * The result is determined by invoking {@link #getDouble} and converting the - * result to float. - *

+ * Returns the value at (row, col) as a double. * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return The value of the field as an float. + *

If the value is null, then 0.0 is returned. If the + * type of column col is a string type, then the result + * is the double that is obtained by parsing the string value with + * strtod. If the type of column col is + * integral, then the result is the integer value casted to a double. + * If the type of column col is a blob type, then an + * {@link SQLiteException} is thrown. */ - public float getFloat(int row, int column) { - return (float) getDouble(row, column); - } + private native double getDouble_native(int row, int col); /** - * Copies a byte array into the field at the specified row and column index. - * - * @param value The value to store. - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return True if successful. + * Returns a short for the given field. + * row is 0 based + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return a short value for the given field */ - public boolean putBlob(byte[] value, int row, int column) { + public short getShort(int row, int col) { acquireReference(); try { - return nativePutBlob(mWindowPtr, value, row - mStartPos, column); + return (short) getLong_native(row - mStartPos, col); } finally { releaseReference(); } } /** - * Copies a string into the field at the specified row and column index. - * - * @param value The value to store. - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return True if successful. + * Returns an int for the given field. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return an int value for the given field */ - public boolean putString(String value, int row, int column) { + public int getInt(int row, int col) { acquireReference(); try { - return nativePutString(mWindowPtr, value, row - mStartPos, column); + return (int) getLong_native(row - mStartPos, col); } finally { releaseReference(); } } - + /** - * Puts a long integer into the field at the specified row and column index. - * - * @param value The value to store. - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return True if successful. + * Returns a float for the given field. + * row is 0 based + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return a float value for the given field */ - public boolean putLong(long value, int row, int column) { + public float getFloat(int row, int col) { acquireReference(); try { - return nativePutLong(mWindowPtr, value, row - mStartPos, column); + return (float) getDouble_native(row - mStartPos, col); } finally { releaseReference(); } - } - + } + /** - * Puts a double-precision floating point value into the field at the - * specified row and column index. - * - * @param value The value to store. - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return True if successful. + * Clears out the existing contents of the window, making it safe to reuse + * for new data. Note that the number of columns in the window may NOT + * change across a call to clear(). */ - public boolean putDouble(double value, int row, int column) { + public void clear() { acquireReference(); try { - return nativePutDouble(mWindowPtr, value, row - mStartPos, column); + mStartPos = 0; + native_clear(); } finally { releaseReference(); } } + /** Clears out the native side of things */ + private native void native_clear(); + /** - * Puts a null value into the field at the specified row and column index. - * - * @param row The zero-based row index, relative to the cursor window's - * start position ({@link #getStartPosition()}). - * @param column The zero-based column index. - * @return True if successful. + * Cleans up the native resources associated with the window. */ - public boolean putNull(int row, int column) { - acquireReference(); - try { - return nativePutNull(mWindowPtr, row - mStartPos, column); - } finally { - releaseReference(); - } + public void close() { + releaseReference(); } + + private native void close_native(); + @Override + protected void finalize() { + if (nWindow == 0) { + return; + } + // due to bugs 3329504, 3502276, cursorwindow sometimes is closed in fialize() + // don't print any warning saying "don't release cursor in finzlize" + // because it is a bug in framework code - NOT an app bug. + recordClosingOfWindow(nWindow); + close_native(); + } + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public CursorWindow createFromParcel(Parcel source) { @@ -667,13 +591,30 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { } public void writeToParcel(Parcel dest, int flags) { - dest.writeStrongBinder(nativeGetBinder(mWindowPtr)); + dest.writeStrongBinder(native_getBinder()); dest.writeInt(mStartPos); } + private CursorWindow(Parcel source) { + IBinder nativeBinder = source.readStrongBinder(); + mStartPos = source.readInt(); + int rslt = native_init(nativeBinder); + printDebugMsgIfError(rslt); + } + + /** Get the binder for the native side of the window */ + private native IBinder native_getBinder(); + + /** Does the native side initialization for an empty window */ + private native int native_init(int cursorWindowSize, boolean localOnly); + + /** Does the native side initialization with an existing binder from another process */ + private native int native_init(IBinder nativeBinder); + @Override protected void onAllReferencesReleased() { - dispose(); + recordClosingOfWindow(nWindow); + close_native(); } private static final SparseIntArray sWindowToPidMap = new SparseIntArray(); @@ -696,7 +637,6 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { sWindowToPidMap.delete(window); } } - private String printStats() { StringBuilder buff = new StringBuilder(); int myPid = Process.myPid(); diff --git a/core/java/android/database/CursorWindowAllocationException.java b/core/java/android/database/CursorWindowAllocationException.java index 2e3227dc6788..ba7df68592ef 100644 --- a/core/java/android/database/CursorWindowAllocationException.java +++ b/core/java/android/database/CursorWindowAllocationException.java @@ -18,12 +18,17 @@ package android.database; /** * This exception is thrown when a CursorWindow couldn't be allocated, - * most probably due to memory not being available. - * - * @hide + * most probably due to memory not being available */ -public class CursorWindowAllocationException extends RuntimeException { - public CursorWindowAllocationException(String description) { +class CursorWindowAllocationException extends java.lang.RuntimeException +{ + public CursorWindowAllocationException() + { + super(); + } + + public CursorWindowAllocationException(String description) + { super(description); } } diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java index 06a41b28504c..dc882d921472 100644 --- a/core/java/android/database/sqlite/SQLiteQuery.java +++ b/core/java/android/database/sqlite/SQLiteQuery.java @@ -79,7 +79,7 @@ public class SQLiteQuery extends SQLiteProgram { // if the start pos is not equal to 0, then most likely window is // too small for the data set, loading by another thread // is not safe in this situation. the native code will ignore maxRead - int numRows = native_fill_window(window.mWindowPtr, window.getStartPosition(), + int numRows = native_fill_window(window, window.getStartPosition(), mOffsetIndex, maxRead, lastPos); mDatabase.logTimeStat(mSql, timeStart); return numRows; @@ -154,7 +154,7 @@ public class SQLiteQuery extends SQLiteProgram { compileAndbindAllArgs(); } - private final native int native_fill_window(int windowPtr, + private final native int native_fill_window(CursorWindow window, int startPos, int offsetParam, int maxRead, int lastPos); private final native int native_column_count(); diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index 14c6397db8c4..419e464f8fdd 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -34,75 +33,69 @@ #include "sqlite3_exception.h" #include "android_util_Binder.h" + namespace android { -static struct { - jfieldID data; - jfieldID sizeCopied; -} gCharArrayBufferClassInfo; +static jfieldID gWindowField; +static jfieldID gBufferField; +static jfieldID gSizeCopiedField; -static jstring gEmptyString; +#define GET_WINDOW(env, object) ((CursorWindow *)env->GetIntField(object, gWindowField)) +#define SET_WINDOW(env, object, window) (env->SetIntField(object, gWindowField, (int)window)) +#define SET_BUFFER(env, object, buf) (env->SetObjectField(object, gBufferField, buf)) +#define SET_SIZE_COPIED(env, object, size) (env->SetIntField(object, gSizeCopiedField, size)) -static void throwExceptionWithRowCol(JNIEnv* env, jint row, jint column) { - String8 msg; - msg.appendFormat("Couldn't read row %d, col %d from CursorWindow. " - "Make sure the Cursor is initialized correctly before accessing data from it.", - row, column); - jniThrowException(env, "java/lang/IllegalStateException", msg.string()); +CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow) +{ + return GET_WINDOW(env, javaWindow); } -static void throwUnknownTypeException(JNIEnv * env, jint type) { - String8 msg; - msg.appendFormat("UNKNOWN type %d", type); - jniThrowException(env, "java/lang/IllegalStateException", msg.string()); -} +static jint native_init_empty(JNIEnv * env, jobject object, jint cursorWindowSize, + jboolean localOnly) +{ + uint8_t * data; + size_t size; + CursorWindow * window; -static jint nativeInitializeEmpty(JNIEnv* env, jclass clazz, - jint cursorWindowSize, jboolean localOnly) { - CursorWindow* window = new CursorWindow(cursorWindowSize); + window = new CursorWindow(cursorWindowSize); if (!window) { - return 0; + return 1; } if (!window->initBuffer(localOnly)) { delete window; - return 0; + return 1; } - LOG_WINDOW("nativeInitializeEmpty: window = %p", window); - return reinterpret_cast(window); + LOG_WINDOW("native_init_empty: window = %p", window); + SET_WINDOW(env, object, window); + return 0; } -static jint nativeInitializeFromBinder(JNIEnv* env, jclass clazz, jobject binderObj) { - sp memory = interface_cast(ibinderForJavaObject(env, binderObj)); +static jint native_init_memory(JNIEnv * env, jobject object, jobject memObj) +{ + sp memory = interface_cast(ibinderForJavaObject(env, memObj)); if (memory == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder"); - return 0; + return 1; } - CursorWindow* window = new CursorWindow(); + CursorWindow * window = new CursorWindow(); if (!window) { - return 0; + return 1; } if (!window->setMemory(memory)) { delete window; - return 0; + return 1; } - LOG_WINDOW("nativeInitializeFromBinder: numRows = %d, numColumns = %d, window = %p", - window->getNumRows(), window->getNumColumns(), window); - return reinterpret_cast(window); -} - -static void nativeDispose(JNIEnv* env, jclass clazz, jint windowPtr) { - CursorWindow* window = reinterpret_cast(windowPtr); - if (window) { - LOG_WINDOW("Closing window %p", window); - delete window; - } + LOG_WINDOW("native_init_memory: numRows = %d, numColumns = %d, window = %p", window->getNumRows(), window->getNumColumns(), window); + SET_WINDOW(env, object, window); + return 0; } -static jobject nativeGetBinder(JNIEnv * env, jclass clazz, jint windowPtr) { - CursorWindow* window = reinterpret_cast(windowPtr); +static jobject native_getBinder(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); if (window) { sp memory = window->getMemory(); if (memory != NULL) { @@ -113,484 +106,568 @@ static jobject nativeGetBinder(JNIEnv * env, jclass clazz, jint windowPtr) { return NULL; } -static void nativeClear(JNIEnv * env, jclass clazz, jint windowPtr) { - CursorWindow* window = reinterpret_cast(windowPtr); - LOG_WINDOW("Clearing window %p", window); +static void native_clear(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Clearing window %p", window); + if (window == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", "clear() called after close()"); + return; + } window->clear(); } -static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jint windowPtr) { - CursorWindow* window = reinterpret_cast(windowPtr); - return window->getNumRows(); -} - -static jboolean nativeSetNumColumns(JNIEnv* env, jclass clazz, jint windowPtr, - jint columnNum) { - CursorWindow* window = reinterpret_cast(windowPtr); - return window->setNumColumns(columnNum); +static void native_close(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (window) { +LOG_WINDOW("Closing window %p", window); + delete window; + SET_WINDOW(env, object, 0); + } } -static jboolean nativeAllocRow(JNIEnv* env, jclass clazz, jint windowPtr) { - CursorWindow* window = reinterpret_cast(windowPtr); - return window->allocRow() != NULL; +static void throwExceptionWithRowCol(JNIEnv * env, jint row, jint column) +{ + char buf[200]; + snprintf(buf, sizeof(buf), "Couldn't read row %d, col %d from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it", + row, column); + jniThrowException(env, "java/lang/IllegalStateException", buf); } -static void nativeFreeLastRow(JNIEnv* env, jclass clazz, jint windowPtr) { - CursorWindow* window = reinterpret_cast(windowPtr); - window->freeLastRow(); +static void throwUnknowTypeException(JNIEnv * env, jint type) +{ + char buf[80]; + snprintf(buf, sizeof(buf), "UNKNOWN type %d", type); + jniThrowException(env, "java/lang/IllegalStateException", buf); } -static jint nativeGetType(JNIEnv* env, jclass clazz, jint windowPtr, - jint row, jint column) { - CursorWindow* window = reinterpret_cast(windowPtr); - LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window); +static jlong getLong_native(JNIEnv * env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Getting long for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); - if (!fieldSlot) { + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { throwExceptionWithRowCol(env, row, column); - return NULL; + return 0; + } + + uint8_t type = field.type; + if (type == FIELD_TYPE_INTEGER) { + int64_t value; + if (window->getLong(row, column, &value)) { + return value; + } + return 0; + } else if (type == FIELD_TYPE_STRING) { + uint32_t size = field.data.buffer.size; + if (size > 0) { +#if WINDOW_STORAGE_UTF8 + return strtoll((char const *)window->offsetToPtr(field.data.buffer.offset), NULL, 0); +#else + String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2); + char const * str = ascii.string(); + return strtoll(str, NULL, 0); +#endif + } else { + return 0; + } + } else if (type == FIELD_TYPE_FLOAT) { + double value; + if (window->getDouble(row, column, &value)) { + return value; + } + return 0; + } else if (type == FIELD_TYPE_NULL) { + return 0; + } else if (type == FIELD_TYPE_BLOB) { + throw_sqlite3_exception(env, "Unable to convert BLOB to long"); + return 0; + } else { + throwUnknowTypeException(env, type); + return 0; } - return fieldSlot->type; } -static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jint windowPtr, - jint row, jint column) { - CursorWindow* window = reinterpret_cast(windowPtr); - LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window); +static jbyteArray getBlob_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); - if (!fieldSlot) { + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { throwExceptionWithRowCol(env, row, column); return NULL; } - uint8_t type = fieldSlot->type; + uint8_t type = field.type; if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) { - uint32_t size = fieldSlot->data.buffer.size; - jbyteArray byteArray = env->NewByteArray(size); + jbyteArray byteArray = env->NewByteArray(field.data.buffer.size); if (!byteArray) { - env->ExceptionClear(); throw_sqlite3_exception(env, "Native could not create new byte[]"); return NULL; } - env->SetByteArrayRegion(byteArray, 0, size, - reinterpret_cast(window->offsetToPtr(fieldSlot->data.buffer.offset))); + env->SetByteArrayRegion(byteArray, 0, field.data.buffer.size, + (const jbyte*)window->offsetToPtr(field.data.buffer.offset)); return byteArray; } else if (type == FIELD_TYPE_INTEGER) { - throw_sqlite3_exception(env, "INTEGER data in nativeGetBlob "); + throw_sqlite3_exception(env, "INTEGER data in getBlob_native "); } else if (type == FIELD_TYPE_FLOAT) { - throw_sqlite3_exception(env, "FLOAT data in nativeGetBlob "); + throw_sqlite3_exception(env, "FLOAT data in getBlob_native "); } else if (type == FIELD_TYPE_NULL) { // do nothing } else { - throwUnknownTypeException(env, type); + throwUnknowTypeException(env, type); } return NULL; } -static jstring nativeGetString(JNIEnv* env, jclass clazz, jint windowPtr, - jint row, jint column) { - CursorWindow* window = reinterpret_cast(windowPtr); - LOG_WINDOW("Getting string for %d,%d from %p", row, column, window); +static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Getting string for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); - if (!fieldSlot) { + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { throwExceptionWithRowCol(env, row, column); return NULL; } - uint8_t type = fieldSlot->type; + uint8_t type = field.type; if (type == FIELD_TYPE_STRING) { - uint32_t size = fieldSlot->data.buffer.size; + uint32_t size = field.data.buffer.size; + if (size > 0) { #if WINDOW_STORAGE_UTF8 - return size > 1 ? env->NewStringUTF(window->getFieldSlotValueString(fieldSlot)) - : gEmptyString; + // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string + String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); + return env->NewString((jchar const *)utf16.string(), utf16.size()); #else - size_t chars = size / sizeof(char16_t); - return chars ? env->NewString(reinterpret_cast( - window->getFieldSlotValueString(fieldSlot)), chars) - : gEmptyString; + return env->NewString((jchar const *)window->offsetToPtr(field.data.buffer.offset), size / 2); #endif + } else { + return env->NewStringUTF(""); + } } else if (type == FIELD_TYPE_INTEGER) { - int64_t value = window->getFieldSlotValueLong(fieldSlot); - char buf[32]; - snprintf(buf, sizeof(buf), "%lld", value); - return env->NewStringUTF(buf); + int64_t value; + if (window->getLong(row, column, &value)) { + char buf[32]; + snprintf(buf, sizeof(buf), "%lld", value); + return env->NewStringUTF(buf); + } + return NULL; } else if (type == FIELD_TYPE_FLOAT) { - double value = window->getFieldSlotValueDouble(fieldSlot); - char buf[32]; - snprintf(buf, sizeof(buf), "%g", value); - return env->NewStringUTF(buf); + double value; + if (window->getDouble(row, column, &value)) { + char buf[32]; + snprintf(buf, sizeof(buf), "%g", value); + return env->NewStringUTF(buf); + } + return NULL; } else if (type == FIELD_TYPE_NULL) { return NULL; } else if (type == FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to string"); return NULL; } else { - throwUnknownTypeException(env, type); + throwUnknowTypeException(env, type); return NULL; } } -static jcharArray allocCharArrayBuffer(JNIEnv* env, jobject bufferObj, size_t size) { - jcharArray dataObj = jcharArray(env->GetObjectField(bufferObj, - gCharArrayBufferClassInfo.data)); - if (dataObj && size) { - jsize capacity = env->GetArrayLength(dataObj); - if (size_t(capacity) < size) { - env->DeleteLocalRef(dataObj); - dataObj = NULL; - } - } - if (!dataObj) { - jsize capacity = size; - if (capacity < 64) { - capacity = 64; - } - dataObj = env->NewCharArray(capacity); // might throw OOM - if (dataObj) { - env->SetObjectField(bufferObj, gCharArrayBufferClassInfo.data, dataObj); - } - } - return dataObj; -} +/** + * Use this only to convert characters that are known to be within the + * 0-127 range for direct conversion to UTF-16 + */ +static jint charToJchar(const char* src, jchar* dst, jint bufferSize) +{ + int32_t len = strlen(src); -static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj, - const char* str, size_t len) { - ssize_t size = utf8_to_utf16_length(reinterpret_cast(str), len); - if (size < 0) { - size = 0; // invalid UTF8 string - } - jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, size); - if (dataObj) { - if (size) { - jchar* data = static_cast(env->GetPrimitiveArrayCritical(dataObj, NULL)); - utf8_to_utf16(reinterpret_cast(str), len, - reinterpret_cast(data)); - env->ReleasePrimitiveArrayCritical(dataObj, data, 0); - } - env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, size); + if (bufferSize < len) { + len = bufferSize; } -} -#if !WINDOW_STORAGE_UTF8 -static void fillCharArrayBuffer(JNIEnv* env, jobject bufferObj, - const char16_t* str, size_t len) { - jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, len); - if (dataObj) { - if (len) { - jchar* data = static_cast(env->GetPrimitiveArrayCritical(dataObj, NULL)); - memcpy(data, str, len * sizeof(jchar)); - env->ReleasePrimitiveArrayCritical(dataObj, data, 0); - } - env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, len); + for (int i = 0; i < len; i++) { + *dst++ = (*src++ & 0x7F); } + return len; } -#endif -static void clearCharArrayBuffer(JNIEnv* env, jobject bufferObj) { - jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, 0); - if (dataObj) { - env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, 0); +static jcharArray copyStringToBuffer_native(JNIEnv* env, jobject object, jint row, + jint column, jint bufferSize, jobject buf) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot"); + return NULL; } -} - -static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jint windowPtr, - jint row, jint column, jobject bufferObj) { - CursorWindow* window = reinterpret_cast(windowPtr); - LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); - if (!fieldSlot) { - throwExceptionWithRowCol(env, row, column); - return; + jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField); + if (buffer == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null"); + return NULL; } - - uint8_t type = fieldSlot->type; + jchar* dst = env->GetCharArrayElements(buffer, NULL); + uint8_t type = field.type; + uint32_t sizeCopied = 0; + jcharArray newArray = NULL; if (type == FIELD_TYPE_STRING) { - uint32_t size = fieldSlot->data.buffer.size; + uint32_t size = field.data.buffer.size; + if (size > 0) { #if WINDOW_STORAGE_UTF8 - if (size > 1) { - fillCharArrayBufferUTF(env, bufferObj, - window->getFieldSlotValueString(fieldSlot), size - 1); - } else { - clearCharArrayBuffer(env, bufferObj); - } + // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string + String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); + int32_t strSize = utf16.size(); + if (strSize > bufferSize || dst == NULL) { + newArray = env->NewCharArray(strSize); + env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string()); + } else { + memcpy(dst, (jchar const *)utf16.string(), strSize * 2); + } + sizeCopied = strSize; #else - size_t chars = size / sizeof(char16_t); - if (chars) { - fillCharArrayBuffer(env, bufferObj, - window->getFieldSlotValueString(fieldSlot), chars); - } else { - clearCharArrayBuffer(env, bufferObj); - } + sizeCopied = size/2 + size % 2; + if (size > bufferSize * 2 || dst == NULL) { + newArray = env->NewCharArray(sizeCopied); + memcpy(newArray, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size); + } else { + memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size); + } #endif + } } else if (type == FIELD_TYPE_INTEGER) { - int64_t value = window->getFieldSlotValueLong(fieldSlot); - char buf[32]; - snprintf(buf, sizeof(buf), "%lld", value); - fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); + int64_t value; + if (window->getLong(row, column, &value)) { + char buf[32]; + int len; + snprintf(buf, sizeof(buf), "%lld", value); + sizeCopied = charToJchar(buf, dst, bufferSize); + } } else if (type == FIELD_TYPE_FLOAT) { - double value = window->getFieldSlotValueDouble(fieldSlot); - char buf[32]; - snprintf(buf, sizeof(buf), "%g", value); - fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); + double value; + if (window->getDouble(row, column, &value)) { + char tempbuf[32]; + snprintf(tempbuf, sizeof(tempbuf), "%g", value); + sizeCopied = charToJchar(tempbuf, dst, bufferSize); + } } else if (type == FIELD_TYPE_NULL) { - clearCharArrayBuffer(env, bufferObj); } else if (type == FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to string"); } else { - throwUnknownTypeException(env, type); - } -} - -static jlong nativeGetLong(JNIEnv* env, jclass clazz, jint windowPtr, - jint row, jint column) { - CursorWindow* window = reinterpret_cast(windowPtr); - LOG_WINDOW("Getting long for %d,%d from %p", row, column, window); - - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); - if (!fieldSlot) { - throwExceptionWithRowCol(env, row, column); - return 0; - } - - uint8_t type = fieldSlot->type; - if (type == FIELD_TYPE_INTEGER) { - return window->getFieldSlotValueLong(fieldSlot); - } else if (type == FIELD_TYPE_STRING) { - uint32_t size = fieldSlot->data.buffer.size; -#if WINDOW_STORAGE_UTF8 - return size > 1 ? strtoll(window->getFieldSlotValueString(fieldSlot), NULL, 0) : 0L; -#else - size_t chars = size / sizeof(char16_t); - return chars ? strtoll(String8(window->getFieldSlotValueString(fieldSlot), chars) - .string(), NULL, 0) : 0L; -#endif - } else if (type == FIELD_TYPE_FLOAT) { - return jlong(window->getFieldSlotValueDouble(fieldSlot)); - } else if (type == FIELD_TYPE_NULL) { - return 0; - } else if (type == FIELD_TYPE_BLOB) { - throw_sqlite3_exception(env, "Unable to convert BLOB to long"); - return 0; - } else { - throwUnknownTypeException(env, type); - return 0; + LOGE("Unknown field type %d", type); + throw_sqlite3_exception(env, "UNKNOWN type in copyStringToBuffer_native()"); } + SET_SIZE_COPIED(env, buf, sizeCopied); + env->ReleaseCharArrayElements(buffer, dst, JNI_OK); + return newArray; } -static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jint windowPtr, - jint row, jint column) { - CursorWindow* window = reinterpret_cast(windowPtr); - LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); +static jdouble getDouble_native(JNIEnv* env, jobject object, jint row, jint column) +{ + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); +LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); - if (!fieldSlot) { + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { throwExceptionWithRowCol(env, row, column); return 0.0; } - uint8_t type = fieldSlot->type; + uint8_t type = field.type; if (type == FIELD_TYPE_FLOAT) { - return window->getFieldSlotValueDouble(fieldSlot); + double value; + if (window->getDouble(row, column, &value)) { + return value; + } + return 0.0; } else if (type == FIELD_TYPE_STRING) { - uint32_t size = fieldSlot->data.buffer.size; + uint32_t size = field.data.buffer.size; + if (size > 0) { #if WINDOW_STORAGE_UTF8 - return size > 1 ? strtod(window->getFieldSlotValueString(fieldSlot), NULL) : 0.0; + return strtod((char const *)window->offsetToPtr(field.data.buffer.offset), NULL); #else - size_t chars = size / sizeof(char16_t); - return chars ? strtod(String8(window->getFieldSlotValueString(fieldSlot), chars) - .string(), NULL) : 0.0; + String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2); + char const * str = ascii.string(); + return strtod(str, NULL); #endif + } else { + return 0.0; + } } else if (type == FIELD_TYPE_INTEGER) { - return jdouble(window->getFieldSlotValueLong(fieldSlot)); + int64_t value; + if (window->getLong(row, column, &value)) { + return (double) value; + } + return 0.0; } else if (type == FIELD_TYPE_NULL) { return 0.0; } else if (type == FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to double"); return 0.0; } else { - throwUnknownTypeException(env, type); + throwUnknowTypeException(env, type); return 0.0; } } -static jboolean nativePutBlob(JNIEnv* env, jclass clazz, jint windowPtr, - jbyteArray valueObj, jint row, jint column) { - CursorWindow* window = reinterpret_cast(windowPtr); - field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, column); +bool isNull_native(CursorWindow *window, jint row, jint column) +{ + LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window); + + bool isNull; + if (window->getNull(row, column, &isNull)) { + return isNull; + } + + //TODO throw execption? + return true; +} + +static jint getNumRows(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); + return window->getNumRows(); +} + +static jboolean setNumColumns(JNIEnv * env, jobject object, jint columnNum) +{ + CursorWindow * window = GET_WINDOW(env, object); + return window->setNumColumns(columnNum); +} + +static jboolean allocRow(JNIEnv * env, jobject object) +{ + CursorWindow * window = GET_WINDOW(env, object); + return window->allocRow() != NULL; +} + +static jboolean putBlob_native(JNIEnv * env, jobject object, jbyteArray value, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!value) { + LOG_WINDOW("How did a null value send to here"); + return false; + } + field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col); if (fieldSlot == NULL) { LOG_WINDOW(" getFieldSlotWithCheck error "); return false; } - jsize len = env->GetArrayLength(valueObj); - uint32_t offset = window->alloc(len); + jint len = env->GetArrayLength(value); + int offset = window->alloc(len); if (!offset) { LOG_WINDOW("Failed allocating %u bytes", len); return false; } + jbyte * bytes = env->GetByteArrayElements(value, NULL); + window->copyIn(offset, (uint8_t const *)bytes, len); - void* value = env->GetPrimitiveArrayCritical(valueObj, NULL); - window->copyIn(offset, static_cast(value), len); - env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT); - + // This must be updated after the call to alloc(), since that + // may move the field around in the window fieldSlot->type = FIELD_TYPE_BLOB; fieldSlot->data.buffer.offset = offset; fieldSlot->data.buffer.size = len; - LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, column, len, offset); + env->ReleaseByteArrayElements(value, bytes, JNI_ABORT); + LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, col, len, offset); return true; } -static jboolean nativePutString(JNIEnv* env, jclass clazz, jint windowPtr, - jstring valueObj, jint row, jint column) { - CursorWindow* window = reinterpret_cast(windowPtr); - field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, column); +static jboolean putString_native(JNIEnv * env, jobject object, jstring value, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!value) { + LOG_WINDOW("How did a null value send to here"); + return false; + } + field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col); if (fieldSlot == NULL) { LOG_WINDOW(" getFieldSlotWithCheck error "); return false; } #if WINDOW_STORAGE_UTF8 - size_t size = env->GetStringUTFLength(valueObj) + 1; - const char* valueStr = env->GetStringUTFChars(valueObj, NULL); + int len = env->GetStringUTFLength(value) + 1; + char const * valStr = env->GetStringUTFChars(value, NULL); #else - size_t size = env->GetStringLength(valueObj) * sizeof(jchar); - const jchar* valueStr = env->GetStringChars(valueObj, NULL); + int len = env->GetStringLength(value); + // GetStringLength return number of chars and one char takes 2 bytes + len *= 2; + const jchar* valStr = env->GetStringChars(value, NULL); #endif - if (!valueStr) { + if (!valStr) { LOG_WINDOW("value can't be transfer to UTFChars"); return false; } - uint32_t offset = window->alloc(size); + int offset = window->alloc(len); if (!offset) { - LOG_WINDOW("Failed allocating %u bytes", size); + LOG_WINDOW("Failed allocating %u bytes", len); #if WINDOW_STORAGE_UTF8 - env->ReleaseStringUTFChars(valueObj, valueStr); + env->ReleaseStringUTFChars(value, valStr); #else - env->ReleaseStringChars(valueObj, valueStr); + env->ReleaseStringChars(value, valStr); #endif return false; } - window->copyIn(offset, reinterpret_cast(valueStr), size); + window->copyIn(offset, (uint8_t const *)valStr, len); + + // This must be updated after the call to alloc(), since that + // may move the field around in the window + fieldSlot->type = FIELD_TYPE_STRING; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = len; + LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, col, len, offset); #if WINDOW_STORAGE_UTF8 - env->ReleaseStringUTFChars(valueObj, valueStr); + env->ReleaseStringUTFChars(value, valStr); #else - env->ReleaseStringChars(valueObj, valueStr); + env->ReleaseStringChars(value, valStr); #endif - fieldSlot->type = FIELD_TYPE_STRING; - fieldSlot->data.buffer.offset = offset; - fieldSlot->data.buffer.size = size; - LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, column, size, offset); return true; } -static jboolean nativePutLong(JNIEnv* env, jclass clazz, jint windowPtr, - jlong value, jint row, jint column) { - CursorWindow* window = reinterpret_cast(windowPtr); - if (!window->putLong(row, column, value)) { +static jboolean putLong_native(JNIEnv * env, jobject object, jlong value, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!window->putLong(row, col, value)) { LOG_WINDOW(" getFieldSlotWithCheck error "); return false; } - LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, column, value); + LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, col, value); + return true; } -static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jint windowPtr, - jdouble value, jint row, jint column) { - CursorWindow* window = reinterpret_cast(windowPtr); - if (!window->putDouble(row, column, value)) { +static jboolean putDouble_native(JNIEnv * env, jobject object, jdouble value, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!window->putDouble(row, col, value)) { LOG_WINDOW(" getFieldSlotWithCheck error "); return false; } - LOG_WINDOW("%d,%d is FLOAT %lf", row, column, value); + LOG_WINDOW("%d,%d is FLOAT %lf", row, col, value); + return true; } -static jboolean nativePutNull(JNIEnv* env, jclass clazz, jint windowPtr, - jint row, jint column) { - CursorWindow* window = reinterpret_cast(windowPtr); - if (!window->putNull(row, column)) { +static jboolean putNull_native(JNIEnv * env, jobject object, jint row, jint col) +{ + CursorWindow * window = GET_WINDOW(env, object); + if (!window->putNull(row, col)) { LOG_WINDOW(" getFieldSlotWithCheck error "); return false; } - LOG_WINDOW("%d,%d is NULL", row, column); + LOG_WINDOW("%d,%d is NULL", row, col); + return true; } -static JNINativeMethod sMethods[] = +// free the last row +static void freeLastRow(JNIEnv * env, jobject object) { + CursorWindow * window = GET_WINDOW(env, object); + window->freeLastRow(); +} + +static jint getType_native(JNIEnv* env, jobject object, jint row, jint column) { - /* name, signature, funcPtr */ - { "nativeInitializeEmpty", "(IZ)I", - (void*)nativeInitializeEmpty }, - { "nativeInitializeFromBinder", "(Landroid/os/IBinder;)I", - (void*)nativeInitializeFromBinder }, - { "nativeDispose", "(I)V", - (void*)nativeDispose }, - { "nativeGetBinder", "(I)Landroid/os/IBinder;", - (void*)nativeGetBinder }, - { "nativeClear", "(I)V", - (void*)nativeClear }, - { "nativeGetNumRows", "(I)I", - (void*)nativeGetNumRows }, - { "nativeSetNumColumns", "(II)Z", - (void*)nativeSetNumColumns }, - { "nativeAllocRow", "(I)Z", - (void*)nativeAllocRow }, - { "nativeFreeLastRow", "(I)V", - (void*)nativeFreeLastRow }, - { "nativeGetType", "(III)I", - (void*)nativeGetType }, - { "nativeGetBlob", "(III)[B", - (void*)nativeGetBlob }, - { "nativeGetString", "(III)Ljava/lang/String;", - (void*)nativeGetString }, - { "nativeGetLong", "(III)J", - (void*)nativeGetLong }, - { "nativeGetDouble", "(III)D", - (void*)nativeGetDouble }, - { "nativeCopyStringToBuffer", "(IIILandroid/database/CharArrayBuffer;)V", - (void*)nativeCopyStringToBuffer }, - { "nativePutBlob", "(I[BII)Z", - (void*)nativePutBlob }, - { "nativePutString", "(ILjava/lang/String;II)Z", - (void*)nativePutString }, - { "nativePutLong", "(IJII)Z", - (void*)nativePutLong }, - { "nativePutDouble", "(IDII)Z", - (void*)nativePutDouble }, - { "nativePutNull", "(III)Z", - (void*)nativePutNull }, -}; + int32_t err; + CursorWindow * window = GET_WINDOW(env, object); + LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window); + + if (isNull_native(window, row, column)) { + return FIELD_TYPE_NULL; + } + + field_slot_t field; + err = window->read_field_slot(row, column, &field); + if (err != 0) { + throwExceptionWithRowCol(env, row, column); + return NULL; + } -#define FIND_CLASS(var, className) \ - var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); + return field.type; +} -#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ - var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ - LOG_FATAL_IF(! var, "Unable to find field " fieldName); +static JNINativeMethod sMethods[] = +{ + /* name, signature, funcPtr */ + {"native_init", "(IZ)I", (void *)native_init_empty}, + {"native_init", "(Landroid/os/IBinder;)I", (void *)native_init_memory}, + {"native_getBinder", "()Landroid/os/IBinder;", (void *)native_getBinder}, + {"native_clear", "()V", (void *)native_clear}, + {"close_native", "()V", (void *)native_close}, + {"getLong_native", "(II)J", (void *)getLong_native}, + {"getBlob_native", "(II)[B", (void *)getBlob_native}, + {"getString_native", "(II)Ljava/lang/String;", (void *)getString_native}, + {"copyStringToBuffer_native", "(IIILandroid/database/CharArrayBuffer;)[C", (void *)copyStringToBuffer_native}, + {"getDouble_native", "(II)D", (void *)getDouble_native}, + {"getNumRows_native", "()I", (void *)getNumRows}, + {"setNumColumns_native", "(I)Z", (void *)setNumColumns}, + {"allocRow_native", "()Z", (void *)allocRow}, + {"putBlob_native", "([BII)Z", (void *)putBlob_native}, + {"putString_native", "(Ljava/lang/String;II)Z", (void *)putString_native}, + {"putLong_native", "(JII)Z", (void *)putLong_native}, + {"putDouble_native", "(DII)Z", (void *)putDouble_native}, + {"freeLastRow_native", "()V", (void *)freeLastRow}, + {"putNull_native", "(II)Z", (void *)putNull_native}, + {"getType_native", "(II)I", (void *)getType_native}, +}; int register_android_database_CursorWindow(JNIEnv * env) { jclass clazz; - FIND_CLASS(clazz, "android/database/CharArrayBuffer"); - GET_FIELD_ID(gCharArrayBufferClassInfo.data, clazz, - "data", "[C"); - GET_FIELD_ID(gCharArrayBufferClassInfo.sizeCopied, clazz, - "sizeCopied", "I"); + clazz = env->FindClass("android/database/CursorWindow"); + if (clazz == NULL) { + LOGE("Can't find android/database/CursorWindow"); + return -1; + } + + gWindowField = env->GetFieldID(clazz, "nWindow", "I"); + + if (gWindowField == NULL) { + LOGE("Error locating fields"); + return -1; + } + + clazz = env->FindClass("android/database/CharArrayBuffer"); + if (clazz == NULL) { + LOGE("Can't find android/database/CharArrayBuffer"); + return -1; + } - gEmptyString = jstring(env->NewGlobalRef(env->NewStringUTF(""))); - LOG_FATAL_IF(!gEmptyString, "Unable to create empty string"); + gBufferField = env->GetFieldID(clazz, "data", "[C"); + + if (gBufferField == NULL) { + LOGE("Error locating fields data in CharArrayBuffer"); + return -1; + } + + gSizeCopiedField = env->GetFieldID(clazz, "sizeCopied", "I"); + + if (gSizeCopiedField == NULL) { + LOGE("Error locating fields sizeCopied in CharArrayBuffer"); + return -1; + } return AndroidRuntime::registerNativeMethods(env, "android/database/CursorWindow", sMethods, NELEM(sMethods)); diff --git a/core/jni/android_database_SQLiteQuery.cpp b/core/jni/android_database_SQLiteQuery.cpp index fe62256c1766..9e421891ae6f 100644 --- a/core/jni/android_database_SQLiteQuery.cpp +++ b/core/jni/android_database_SQLiteQuery.cpp @@ -35,6 +35,12 @@ namespace android { +sqlite3_stmt * compile(JNIEnv* env, jobject object, + sqlite3 * handle, jstring sqlString); + +// From android_database_CursorWindow.cpp +CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow); + static jfieldID gHandleField; static jfieldID gStatementField; @@ -99,7 +105,7 @@ static int finish_program_and_get_row_count(sqlite3_stmt *statement) { return numRows; } -static jint native_fill_window(JNIEnv* env, jobject object, jint windowPtr, +static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, jint startPos, jint offsetParam, jint maxRead, jint lastPos) { int err; @@ -136,7 +142,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jint windowPtr, } // Get the native window - window = reinterpret_cast(windowPtr); + window = get_window_from_object(env, javaWindow); if (!window) { LOGE("Invalid CursorWindow"); jniThrowException(env, "java/lang/IllegalArgumentException", @@ -354,7 +360,7 @@ static jstring native_column_name(JNIEnv* env, jobject object, jint columnIndex) static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ - {"native_fill_window", "(IIIII)I", + {"native_fill_window", "(Landroid/database/CursorWindow;IIII)I", (void *)native_fill_window}, {"native_column_count", "()I", (void*)native_column_count}, {"native_column_name", "(I)Ljava/lang/String;", (void *)native_column_name}, diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h index fd33d59845d7..32cd4f5f9c09 100644 --- a/include/android_runtime/AndroidRuntime.h +++ b/include/android_runtime/AndroidRuntime.h @@ -31,6 +31,8 @@ namespace android { +class CursorWindow; + class AndroidRuntime { public: @@ -131,6 +133,8 @@ private: static int javaThreadShell(void* args); }; +extern CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow); + } #endif diff --git a/include/binder/CursorWindow.h b/include/binder/CursorWindow.h index d227244df92c..f0b2909dce93 100644 --- a/include/binder/CursorWindow.h +++ b/include/binder/CursorWindow.h @@ -143,6 +143,8 @@ public: */ uint32_t alloc(size_t size, bool aligned = false); + uint32_t read_field_slot(int row, int column, field_slot_t * slot); + /** * Copy data into the window at the given offset. */ @@ -179,32 +181,6 @@ public: return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column; } - int64_t getFieldSlotValueLong(field_slot_t* fieldSlot) { -#if WINDOW_STORAGE_INLINE_NUMERICS - return fieldSlot->data.l; -#else - return copyOutLong(fieldSlot->data.buffer.offset); -#endif - } - - double getFieldSlotValueDouble(field_slot_t* fieldSlot) { -#if WINDOW_STORAGE_INLINE_NUMERICS - return fieldSlot->data.d; -#else - return copyOutDouble(fieldSlot->data.buffer.offset); -#endif - } - -#if WINDOW_STORAGE_UTF8 - char* getFieldSlotValueString(field_slot_t* fieldSlot) { - return reinterpret_cast(offsetToPtr(fieldSlot->data.buffer.offset)); - } -#else - char16_t* getFieldSlotValueString(field_slot_t* fieldSlot) { - return reinterpret_cast(offsetToPtr(fieldSlot->data.buffer.offset)); - } -#endif - private: uint8_t * mData; size_t mSize; diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp index b02374f1ed78..47bbd04e1f91 100644 --- a/libs/binder/CursorWindow.cpp +++ b/libs/binder/CursorWindow.cpp @@ -236,6 +236,33 @@ field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column) return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column; } +uint32_t CursorWindow::read_field_slot(int row, int column, field_slot_t * slotOut) +{ + if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) { + LOGE("Can't read row# %d, col# %d from CursorWindow. Make sure your Cursor is initialized correctly.", + row, column); + return -1; + } + row_slot_t * rowSlot = getRowSlot(row); + if (!rowSlot) { + LOGE("Failed to find rowSlot for row %d", row); + return -1; + } + if (rowSlot->offset == 0 || rowSlot->offset >= mSize) { + LOGE("Invalid rowSlot, offset = %d", rowSlot->offset); + return -1; + } +LOG_WINDOW("Found field directory for %d,%d at rowSlot %d, offset %d", row, column, (uint8_t *)rowSlot - mData, rowSlot->offset); + field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(rowSlot->offset); +LOG_WINDOW("Read field_slot_t %d,%d: offset = %d, size = %d, type = %d", row, column, fieldDir[column].data.buffer.offset, fieldDir[column].data.buffer.size, fieldDir[column].type); + + // Copy the data to the out param + slotOut->data.buffer.offset = fieldDir[column].data.buffer.offset; + slotOut->data.buffer.size = fieldDir[column].data.buffer.size; + slotOut->type = fieldDir[column].type; + return 0; +} + void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size) { assert(offset + size <= mSize); @@ -343,8 +370,12 @@ bool CursorWindow::getLong(unsigned int row, unsigned int col, int64_t * valueOu if (!fieldSlot || fieldSlot->type != FIELD_TYPE_INTEGER) { return false; } - - *valueOut = getFieldSlotValueLong(fieldSlot); + +#if WINDOW_STORAGE_INLINE_NUMERICS + *valueOut = fieldSlot->data.l; +#else + *valueOut = copyOutLong(fieldSlot->data.buffer.offset); +#endif return true; } @@ -355,7 +386,11 @@ bool CursorWindow::getDouble(unsigned int row, unsigned int col, double * valueO return false; } - *valueOut = getFieldSlotValueDouble(fieldSlot); +#if WINDOW_STORAGE_INLINE_NUMERICS + *valueOut = fieldSlot->data.d; +#else + *valueOut = copyOutDouble(fieldSlot->data.buffer.offset); +#endif return true; } -- cgit v1.2.3-59-g8ed1b