diff options
author | Jeff Brown <jeffbrown@google.com> | 2011-10-10 14:50:10 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2011-10-11 11:03:19 -0700 |
commit | 0cde89f5f025b7826be009ebb9673b970e180e32 (patch) | |
tree | ee6a4c95c0b07890353a813afb15a52334384f0d | |
parent | 99f36683a4f2c218d52922ae7c2a0c0b3f2890ed (diff) | |
download | frameworks_base-0cde89f5f025b7826be009ebb9673b970e180e32.zip frameworks_base-0cde89f5f025b7826be009ebb9673b970e180e32.tar.gz frameworks_base-0cde89f5f025b7826be009ebb9673b970e180e32.tar.bz2 |
Use ashmem for CursorWindows.
Bug: 5332296
The memory dealer introduces additional delays for reclaiming
the memory owned by CursorWindows because the Binder object must
be finalized. Using ashmem instead gives CursorWindow more
direct control over the lifetime of the shared memory region.
The provider now allocates the CursorWindows and returns them
to clients with a read-only protection bit set on the ashmem
region.
Improved the encapsulation of CursorWindow. Callers shouldn't
need to care about details like how string fields are allocated.
Removed the compile-time configuration of string and numeric
storage modes to remove some dead weight.
Change-Id: I07c2bc2a9c573d7e435dcaecd269d25ea9807acd
-rw-r--r-- | core/java/android/content/ContentProviderNative.java | 110 | ||||
-rw-r--r-- | core/java/android/database/AbstractWindowedCursor.java | 6 | ||||
-rw-r--r-- | core/java/android/database/BulkCursorNative.java | 8 | ||||
-rw-r--r-- | core/java/android/database/BulkCursorToCursorAdaptor.java | 49 | ||||
-rw-r--r-- | core/java/android/database/CursorToBulkCursorAdaptor.java | 85 | ||||
-rw-r--r-- | core/java/android/database/CursorWindow.java | 43 | ||||
-rw-r--r-- | core/java/android/database/IBulkCursor.java | 2 | ||||
-rw-r--r-- | core/java/android/database/sqlite/SQLiteCursor.java | 2 | ||||
-rw-r--r-- | core/jni/android_database_CursorWindow.cpp | 312 | ||||
-rw-r--r-- | core/jni/android_database_SQLiteQuery.cpp | 78 | ||||
-rw-r--r-- | include/binder/CursorWindow.h | 291 | ||||
-rw-r--r-- | libs/binder/CursorWindow.cpp | 498 | ||||
-rw-r--r-- | libs/binder/Parcel.cpp | 4 |
13 files changed, 683 insertions, 805 deletions
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index 064755e..b089bf2 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -108,21 +108,22 @@ 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(), window); + cursor, observer, getProviderName()); 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); @@ -324,67 +325,58 @@ final class ContentProviderProxy implements IContentProvider 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 */); + BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); + 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); - - 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) { + 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 { adaptor.close(); - throw ex; - } finally { - data.recycle(); - reply.recycle(); + adaptor = null; } + return adaptor; + } catch (RemoteException ex) { + adaptor.close(); + throw ex; + } catch (RuntimeException ex) { + adaptor.close(); + throw ex; } 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(); } } diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java index 5836265..d0aedd2 100644 --- a/core/java/android/database/AbstractWindowedCursor.java +++ b/core/java/android/database/AbstractWindowedCursor.java @@ -189,12 +189,14 @@ 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() { + protected void clearOrCreateLocalWindow(String name) { 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 */); + mWindow = new CursorWindow(name, 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 4fada8c..20a9c67 100644 --- a/core/java/android/database/BulkCursorNative.java +++ b/core/java/android/database/BulkCursorNative.java @@ -109,9 +109,8 @@ public abstract class BulkCursorNative extends Binder implements IBulkCursor case REQUERY_TRANSACTION: { data.enforceInterface(IBulkCursor.descriptor); IContentObserver observer = - IContentObserver.Stub.asInterface(data.readStrongBinder()); - CursorWindow window = CursorWindow.CREATOR.createFromParcel(data); - int count = requery(observer, window); + IContentObserver.Stub.asInterface(data.readStrongBinder()); + int count = requery(observer); reply.writeNoException(); reply.writeInt(count); reply.writeBundle(getExtras()); @@ -294,13 +293,12 @@ final class BulkCursorProxy implements IBulkCursor { } } - public int requery(IContentObserver observer, CursorWindow window) throws RemoteException { + public int requery(IContentObserver observer) 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 cbdd07fb..885046b 100644 --- a/core/java/android/database/BulkCursorToCursorAdaptor.java +++ b/core/java/android/database/BulkCursorToCursorAdaptor.java @@ -38,11 +38,13 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { * Initializes the adaptor. * Must be called before first use. */ - public void initialize(IBulkCursor bulkCursor, int count, int idIndex) { + public void initialize(IBulkCursor bulkCursor, int count, int idIndex, + boolean wantsAllOnMoveCalls) { mBulkCursor = bulkCursor; mColumns = null; // lazily retrieved mCount = count; mRowIdColumnIndex = idIndex; + mWantsAllOnMoveCalls = wantsAllOnMoveCalls; } /** @@ -86,15 +88,12 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { try { // Make sure we have the proper window - if (mWindow != null) { - if (newPosition < mWindow.getStartPosition() || - newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) { - setWindow(mBulkCursor.getWindow(newPosition)); - } else if (mWantsAllOnMoveCalls) { - mBulkCursor.onMove(newPosition); - } - } else { + if (mWindow == null + || newPosition < mWindow.getStartPosition() + || newPosition >= mWindow.getStartPosition() + mWindow.getNumRows()) { setWindow(mBulkCursor.getWindow(newPosition)); + } else if (mWantsAllOnMoveCalls) { + mBulkCursor.onMove(newPosition); } } catch (RemoteException ex) { // We tried to get a window and failed @@ -145,25 +144,19 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { 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(); + 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; } } 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 a65b3b3..dd2c9b7 100644 --- a/core/java/android/database/CursorToBulkCursorAdaptor.java +++ b/core/java/android/database/CursorToBulkCursorAdaptor.java @@ -53,6 +53,7 @@ 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; @@ -87,26 +88,11 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative } } - public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName, - CursorWindow window) { + public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, + String providerName) { 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); } @@ -117,17 +103,22 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative } } - private void closeCursorAndWindowLocked() { + private void closeWindowForNonWindowedCursorLocked() { + if (mWindowForNonWindowedCursor != null) { + mWindowForNonWindowedCursor.close(); + mWindowForNonWindowedCursor = null; + mWindowForNonWindowedCursorWasFilled = false; + } + } + + private void disposeLocked() { if (mCursor != null) { unregisterObserverProxyLocked(); mCursor.close(); mCursor = null; } - if (mWindowForNonWindowedCursor != null) { - mWindowForNonWindowedCursor.close(); - mWindowForNonWindowedCursor = null; - } + closeWindowForNonWindowedCursorLocked(); } private void throwIfCursorIsClosed() { @@ -139,7 +130,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative @Override public void binderDied() { synchronized (mLock) { - closeCursorAndWindowLocked(); + disposeLocked(); } } @@ -148,17 +139,30 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative synchronized (mLock) { throwIfCursorIsClosed(); - mCursor.moveToPosition(startPos); - - final CursorWindow window; + CursorWindow window; if (mCursor instanceof AbstractWindowedCursor) { - window = ((AbstractWindowedCursor)mCursor).getWindow(); + AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor)mCursor; + window = windowedCursor.getWindow(); + if (window == null) { + window = new CursorWindow(mProviderName, false /*localOnly*/); + windowedCursor.setWindow(window); + } + + mCursor.moveToPosition(startPos); } else { window = mWindowForNonWindowedCursor; - if (window != null - && (startPos < window.getStartPosition() || - startPos >= (window.getStartPosition() + window.getNumRows()))) { + if (window == null) { + window = new CursorWindow(mProviderName, false /*localOnly*/); + mWindowForNonWindowedCursor = window; + } + + mCursor.moveToPosition(startPos); + + if (!mWindowForNonWindowedCursorWasFilled + || startPos < window.getStartPosition() + || startPos >= window.getStartPosition() + window.getNumRows()) { mCursor.fillWindow(startPos, window); + mWindowForNonWindowedCursorWasFilled = true; } } @@ -206,29 +210,24 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative unregisterObserverProxyLocked(); mCursor.deactivate(); } + + closeWindowForNonWindowedCursorLocked(); } } @Override public void close() { synchronized (mLock) { - closeCursorAndWindowLocked(); + disposeLocked(); } } @Override - public int requery(IContentObserver observer, CursorWindow window) { + public int requery(IContentObserver observer) { synchronized (mLock) { throwIfCursorIsClosed(); - if (mCursor instanceof AbstractWindowedCursor) { - ((AbstractWindowedCursor) mCursor).setWindow(window); - } else { - if (mWindowForNonWindowedCursor != null) { - mWindowForNonWindowedCursor.close(); - } - mWindowForNonWindowedCursor = window; - } + closeWindowForNonWindowedCursorLocked(); try { if (!mCursor.requery()) { @@ -241,12 +240,6 @@ 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 5a91b80..a18a721 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -22,7 +22,6 @@ 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; @@ -31,6 +30,13 @@ import android.util.SparseIntArray; /** * A buffer containing multiple cursor rows. + * <p> + * 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. + * </p> */ public class CursorWindow extends SQLiteClosable implements Parcelable { private static final String STATS_TAG = "CursorWindowStats"; @@ -52,10 +58,11 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { 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 int nativeCreate(String name, + int cursorWindowSize, boolean localOnly); + private static native int nativeCreateFromParcel(Parcel parcel); private static native void nativeDispose(int windowPtr); - private static native IBinder nativeGetBinder(int windowPtr); + private static native void nativeWriteToParcel(int windowPtr, Parcel parcel); private static native void nativeClear(int windowPtr); @@ -79,18 +86,21 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { private static native boolean nativePutNull(int windowPtr, int row, int column); /** - * Creates a new empty cursor window. + * Creates a new empty cursor window and gives it a name. * <p> * 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. * </p> * + * @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(boolean localWindow) { + public CursorWindow(String name, boolean localWindow) { mStartPos = 0; - mWindowPtr = nativeInitializeEmpty(sCursorWindowSize, localWindow); + mWindowPtr = nativeCreate(name, sCursorWindowSize, localWindow); if (mWindowPtr == 0) { throw new CursorWindowAllocationException("Cursor window allocation of " + (sCursorWindowSize / 1024) + " kb failed. " + printStats()); @@ -99,10 +109,23 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { recordNewWindow(Binder.getCallingPid(), mWindowPtr); } + /** + * Creates a new empty cursor window. + * <p> + * 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. + * </p> + * + * @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 = nativeInitializeFromBinder(binder); + mWindowPtr = nativeCreateFromParcel(source); if (mWindowPtr == 0) { throw new CursorWindowAllocationException("Cursor window could not be " + "created from binder."); @@ -687,8 +710,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 244c88f..7c96797 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, CursorWindow window) throws RemoteException; + public int requery(IContentObserver observer) 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 9d7e152..a1c36e2 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(); + clearOrCreateLocalWindow(getDatabase().getPath()); 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 fe1aca0..722aeea 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -57,14 +57,23 @@ static void throwUnknownTypeException(JNIEnv * env, jint type) { jniThrowException(env, "java/lang/IllegalStateException", msg.string()); } -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; +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("<unnamed>"); + } + + 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); return 0; } @@ -72,19 +81,13 @@ static jint nativeInitializeEmpty(JNIEnv* env, jclass clazz, return reinterpret_cast<jint>(window); } -static jint nativeInitializeFromBinder(JNIEnv* env, jclass clazz, jobject binderObj) { - sp<IMemory> memory = interface_cast<IMemory>(ibinderForJavaObject(env, binderObj)); - if (memory == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder"); - return 0; - } +static jint nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) { + Parcel* parcel = parcelForJavaObject(env, parcelObj); - CursorWindow* window = new CursorWindow(); - if (!window) { - return 0; - } - if (!window->setMemory(memory)) { - delete window; + CursorWindow* window; + status_t status = CursorWindow::createFromParcel(parcel, &window); + if (status || !window) { + LOGE("Could not create CursorWindow from Parcel due to error %d.", status); return 0; } @@ -101,22 +104,26 @@ static void nativeDispose(JNIEnv* env, jclass clazz, jint windowPtr) { } } -static jobject nativeGetBinder(JNIEnv * env, jclass clazz, jint windowPtr) { +static void nativeWriteToParcel(JNIEnv * env, jclass clazz, jint windowPtr, + jobject parcelObj) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); - if (window) { - sp<IMemory> memory = window->getMemory(); - if (memory != NULL) { - sp<IBinder> binder = memory->asBinder(); - return javaObjectForIBinder(env, binder); - } + 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()); } - return NULL; } static void nativeClear(JNIEnv * env, jclass clazz, jint windowPtr) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("Clearing window %p", window); - window->clear(); + status_t status = window->clear(); + if (status) { + LOG_WINDOW("Could not clear window. error=%d", status); + } } static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jint windowPtr) { @@ -127,12 +134,14 @@ static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jint windowPtr) { static jboolean nativeSetNumColumns(JNIEnv* env, jclass clazz, jint windowPtr, jint columnNum) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); - return window->setNumColumns(columnNum); + status_t status = window->setNumColumns(columnNum); + return status == OK; } static jboolean nativeAllocRow(JNIEnv* env, jclass clazz, jint windowPtr) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); - return window->allocRow() != NULL; + status_t status = window->allocRow(); + return status == OK; } static void nativeFreeLastRow(JNIEnv* env, jclass clazz, jint windowPtr) { @@ -145,14 +154,14 @@ static jint nativeGetType(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); + CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(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; + return CursorWindow::FIELD_TYPE_NULL; } - return fieldSlot->type; + return window->getFieldSlotType(fieldSlot); } static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jint windowPtr, @@ -160,29 +169,29 @@ static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); + CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return NULL; } - uint8_t type = fieldSlot->type; - if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) { - uint32_t size = fieldSlot->data.buffer.size; + 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); 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, - reinterpret_cast<jbyte*>(window->offsetToPtr(fieldSlot->data.buffer.offset))); + env->SetByteArrayRegion(byteArray, 0, size, static_cast<const jbyte*>(value)); return byteArray; - } else if (type == FIELD_TYPE_INTEGER) { + } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { throw_sqlite3_exception(env, "INTEGER data in nativeGetBlob "); - } else if (type == FIELD_TYPE_FLOAT) { + } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { throw_sqlite3_exception(env, "FLOAT data in nativeGetBlob "); - } else if (type == FIELD_TYPE_NULL) { + } else if (type == CursorWindow::FIELD_TYPE_NULL) { // do nothing } else { throwUnknownTypeException(env, type); @@ -195,43 +204,37 @@ static jstring nativeGetString(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("Getting string for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); + CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return NULL; } - uint8_t type = fieldSlot->type; - if (type == FIELD_TYPE_STRING) { - uint32_t size = fieldSlot->data.buffer.size; -#if WINDOW_STORAGE_UTF8 - if (size <= 1) { + 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) { 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); + String16 utf16(value, sizeIncludingNull - 1); return env->NewString(reinterpret_cast<const jchar*>(utf16.string()), utf16.size()); -#else - size_t chars = size / sizeof(char16_t); - return chars ? env->NewString(reinterpret_cast<jchar*>( - window->getFieldSlotValueString(fieldSlot)), chars) - : gEmptyString; -#endif - } else if (type == FIELD_TYPE_INTEGER) { + } else if (type == CursorWindow::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 == FIELD_TYPE_FLOAT) { + } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { double value = window->getFieldSlotValueDouble(fieldSlot); char buf[32]; snprintf(buf, sizeof(buf), "%g", value); return env->NewStringUTF(buf); - } else if (type == FIELD_TYPE_NULL) { + } else if (type == CursorWindow::FIELD_TYPE_NULL) { return NULL; - } else if (type == FIELD_TYPE_BLOB) { + } else if (type == CursorWindow::FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to string"); return NULL; } else { @@ -281,21 +284,6 @@ 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<jchar*>(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) { @@ -308,44 +296,34 @@ static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); + CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return; } - 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); + 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); } else { clearCharArrayBuffer(env, bufferObj); } -#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) { + } else if (type == CursorWindow::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 == FIELD_TYPE_FLOAT) { + } else if (type == CursorWindow::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 == FIELD_TYPE_NULL) { + } else if (type == CursorWindow::FIELD_TYPE_NULL) { clearCharArrayBuffer(env, bufferObj); - } else if (type == FIELD_TYPE_BLOB) { + } else if (type == CursorWindow::FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to string"); } else { throwUnknownTypeException(env, type); @@ -357,29 +335,24 @@ static jlong nativeGetLong(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("Getting long for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); + CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return 0; } - uint8_t type = fieldSlot->type; - if (type == FIELD_TYPE_INTEGER) { + int32_t type = window->getFieldSlotType(fieldSlot); + if (type == CursorWindow::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) { + } 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) { return jlong(window->getFieldSlotValueDouble(fieldSlot)); - } else if (type == FIELD_TYPE_NULL) { + } else if (type == CursorWindow::FIELD_TYPE_NULL) { return 0; - } else if (type == FIELD_TYPE_BLOB) { + } else if (type == CursorWindow::FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to long"); return 0; } else { @@ -393,29 +366,24 @@ static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jint windowPtr, CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); - field_slot_t* fieldSlot = window->getFieldSlotWithCheck(row, column); + CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); if (!fieldSlot) { throwExceptionWithRowCol(env, row, column); return 0.0; } - uint8_t type = fieldSlot->type; - if (type == FIELD_TYPE_FLOAT) { + int32_t type = window->getFieldSlotType(fieldSlot); + if (type == CursorWindow::FIELD_TYPE_FLOAT) { return window->getFieldSlotValueDouble(fieldSlot); - } 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) { + } 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) { return jdouble(window->getFieldSlotValueLong(fieldSlot)); - } else if (type == FIELD_TYPE_NULL) { + } else if (type == CursorWindow::FIELD_TYPE_NULL) { return 0.0; - } else if (type == FIELD_TYPE_BLOB) { + } else if (type == CursorWindow::FIELD_TYPE_BLOB) { throw_sqlite3_exception(env, "Unable to convert BLOB to double"); return 0.0; } else { @@ -427,82 +395,50 @@ 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<CursorWindow*>(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); - window->copyIn(offset, static_cast<const uint8_t*>(value), len); + status_t status = window->putBlob(row, column, value, len); env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT); - 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); + 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); return true; } static jboolean nativePutString(JNIEnv* env, jclass clazz, jint windowPtr, jstring valueObj, jint row, jint column) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); - field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, column); - if (fieldSlot == NULL) { - LOG_WINDOW(" getFieldSlotWithCheck error "); - return false; - } -#if WINDOW_STORAGE_UTF8 - size_t size = env->GetStringUTFLength(valueObj) + 1; + size_t sizeIncludingNull = 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 transfer to UTFChars"); + LOG_WINDOW("value can't be transferred to UTFChars"); return false; } + status_t status = window->putString(row, column, valueStr, sizeIncludingNull); + env->ReleaseStringUTFChars(valueObj, valueStr); - 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 + if (status) { + LOG_WINDOW("Failed to put string. error=%d", status); return false; } - window->copyIn(offset, reinterpret_cast<const uint8_t*>(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); + LOG_WINDOW("%d,%d is TEXT with %u bytes", row, column, sizeIncludingNull); return true; } static jboolean nativePutLong(JNIEnv* env, jclass clazz, jint windowPtr, jlong value, jint row, jint column) { CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); - if (!window->putLong(row, column, value)) { - LOG_WINDOW(" getFieldSlotWithCheck error "); + status_t status = window->putLong(row, column, value); + + if (status) { + LOG_WINDOW("Failed to put long. error=%d", status); return false; } @@ -513,8 +449,10 @@ 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<CursorWindow*>(windowPtr); - if (!window->putDouble(row, column, value)) { - LOG_WINDOW(" getFieldSlotWithCheck error "); + status_t status = window->putDouble(row, column, value); + + if (status) { + LOG_WINDOW("Failed to put double. error=%d", status); return false; } @@ -525,8 +463,10 @@ 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<CursorWindow*>(windowPtr); - if (!window->putNull(row, column)) { - LOG_WINDOW(" getFieldSlotWithCheck error "); + status_t status = window->putNull(row, column); + + if (status) { + LOG_WINDOW("Failed to put null. error=%d", status); return false; } @@ -537,14 +477,14 @@ static jboolean nativePutNull(JNIEnv* env, jclass clazz, jint windowPtr, static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ - { "nativeInitializeEmpty", "(IZ)I", - (void*)nativeInitializeEmpty }, - { "nativeInitializeFromBinder", "(Landroid/os/IBinder;)I", - (void*)nativeInitializeFromBinder }, + { "nativeCreate", "(Ljava/lang/String;IZ)I", + (void*)nativeCreate }, + { "nativeCreateFromParcel", "(Landroid/os/Parcel;)I", + (void*)nativeCreateFromParcel }, { "nativeDispose", "(I)V", (void*)nativeDispose }, - { "nativeGetBinder", "(I)Landroid/os/IBinder;", - (void*)nativeGetBinder }, + { "nativeWriteToParcel", "(ILandroid/os/Parcel;)V", + (void*)nativeWriteToParcel }, { "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 022a64c..8170f46 100644 --- a/core/jni/android_database_SQLiteQuery.cpp +++ b/core/jni/android_database_SQLiteQuery.cpp @@ -61,7 +61,8 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, window->getNumRows(), window->size(), window->freeSpace()); int numColumns = sqlite3_column_count(statement); - if (!window->setNumColumns(numColumns)) { + status_t status = window->setNumColumns(numColumns); + if (status) { LOGE("Failed to change column count from %d to %d", window->getNumColumns(), numColumns); jniThrowException(env, "java/lang/IllegalStateException", "numColumns mismatch"); return 0; @@ -88,10 +89,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. - field_slot_t* fieldDir = window->allocRow(); - if (!fieldDir) { - LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d", - startPos, addedRows); + status = window->allocRow(); + if (status) { + LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d", + startPos, addedRows, status); windowFull = true; continue; } @@ -101,37 +102,28 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, int type = sqlite3_column_type(statement, i); if (type == SQLITE_TEXT) { // TEXT data -#if WINDOW_STORAGE_UTF8 - const uint8_t* text = reinterpret_cast<const uint8_t*>( + const char* text = reinterpret_cast<const char*>( 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<const uint8_t*>( - 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); + 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); windowFull = true; break; } - 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); + LOG_WINDOW("%d,%d is TEXT with %u bytes", + startPos + addedRows, i, sizeIncludingNull); } else if (type == SQLITE_INTEGER) { // INTEGER data int64_t value = sqlite3_column_int64(statement, i); - if (!window->putLong(addedRows, i, value)) { - LOG_WINDOW("Failed allocating space for a long in column %d", i); + status = window->putLong(addedRows, i, value); + if (status) { + LOG_WINDOW("Failed allocating space for a long in column %d, error=%d", + i, status); windowFull = true; break; } @@ -139,35 +131,33 @@ static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr, } else if (type == SQLITE_FLOAT) { // FLOAT data double value = sqlite3_column_double(statement, i); - if (!window->putDouble(addedRows, i, value)) { - LOG_WINDOW("Failed allocating space for a double in column %d", i); + status = window->putDouble(addedRows, i, value); + if (status) { + LOG_WINDOW("Failed allocating space for a double in column %d, error=%d", + i, status); windowFull = true; break; } LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, 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) { - LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d", size, - startPos + addedRows, i); + 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); windowFull = true; break; } - 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); + LOG_WINDOW("%d,%d is Blob with %u bytes", + startPos + addedRows, i, size); } else if (type == SQLITE_NULL) { // NULL field - if (!window->putNull(addedRows, i)) { - LOG_WINDOW("Failed allocating space for a null in column %d", i); + status = window->putNull(addedRows, i); + if (status) { + LOG_WINDOW("Failed allocating space for a null in column %d, error=%d", + i, status); windowFull = true; break; } diff --git a/include/binder/CursorWindow.h b/include/binder/CursorWindow.h index d227244..5d490ed 100644 --- a/include/binder/CursorWindow.h +++ b/include/binder/CursorWindow.h @@ -21,18 +21,8 @@ #include <stddef.h> #include <stdint.h> -#include <binder/IMemory.h> -#include <utils/RefBase.h> - -#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)) - +#include <binder/Parcel.h> +#include <utils/String8.h> #if LOG_NDEBUG @@ -46,176 +36,157 @@ #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 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 + * 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 * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a - * field_slot_t per column, which has the size, offset, and type of the data for that field. + * FieldSlot 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 -{ +class CursorWindow { + CursorWindow(const String8& name, int ashmemFd, + void* data, size_t size, bool readOnly); + public: - CursorWindow(size_t maxSize); - CursorWindow(){} - bool setMemory(const sp<IMemory>&); - ~CursorWindow(); - - bool initBuffer(bool localOnly); - sp<IMemory> 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 + /* 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) { return fieldSlot->data.l; -#else - return copyOutLong(fieldSlot->data.buffer.offset); -#endif } - double getFieldSlotValueDouble(field_slot_t* fieldSlot) { -#if WINDOW_STORAGE_INLINE_NUMERICS + inline double getFieldSlotValueDouble(FieldSlot* fieldSlot) { 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<char*>(offsetToPtr(fieldSlot->data.buffer.offset)); + inline const char* getFieldSlotValueString(FieldSlot* fieldSlot, + size_t* outSizeIncludingNull) { + *outSizeIncludingNull = fieldSlot->data.buffer.size; + return static_cast<char*>(offsetToPtr(fieldSlot->data.buffer.offset)); } -#else - char16_t* getFieldSlotValueString(field_slot_t* fieldSlot) { - return reinterpret_cast<char16_t*>(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); } -#endif private: - uint8_t * mData; + 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; size_t mSize; - size_t mMaxSize; - window_header_t * mHeader; - sp<IMemory> mMemory; + bool mReadOnly; + Header* mHeader; + + inline void* offsetToPtr(uint32_t offset) { + return static_cast<uint8_t*>(mData) + offset; + } + + inline uint32_t offsetFromPtr(void* ptr) { + return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData); + } /** - * Offset of the lowest unused data byte in the array. + * 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 mFreeOffset; + 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); }; }; // namespace android diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp index b02374f..1b85a71 100644 --- a/libs/binder/CursorWindow.cpp +++ b/libs/binder/CursorWindow.cpp @@ -19,8 +19,9 @@ #include <utils/Log.h> #include <binder/CursorWindow.h> -#include <binder/MemoryHeapBase.h> -#include <binder/MemoryBase.h> + +#include <cutils/ashmem.h> +#include <sys/mman.h> #include <assert.h> #include <string.h> @@ -28,350 +29,325 @@ namespace android { -CursorWindow::CursorWindow(size_t maxSize) : - mMaxSize(maxSize) -{ +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<Header*>(mData); } -bool CursorWindow::setMemory(const sp<IMemory>& memory) -{ - mMemory = memory; - mData = (uint8_t *) memory->pointer(); - if (mData == NULL) { - return false; - } - 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; +CursorWindow::~CursorWindow() { + ::munmap(mData, mSize); + ::close(mAshmemFd); } -bool CursorWindow::initBuffer(bool localOnly) -{ - //TODO Use a non-memory dealer mmap region for localOnly - - sp<MemoryHeapBase> 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; +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; + } } - } - LOGE("CursorWindow heap allocation failed"); - return false; + ::munmap(data, size); + } + ::close(ashmemFd); + } + *outCursorWindow = NULL; + return result; +} + +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 { - LOGE("failed to create the CursorWindow heap"); - return false; + 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); + } + } } + *outCursorWindow = NULL; + return result; } -CursorWindow::~CursorWindow() -{ - // Everything that matters is a smart pointer +status_t CursorWindow::writeToParcel(Parcel* parcel) { + status_t status = parcel->writeString8(mName); + if (!status) { + status = parcel->writeDupFileDescriptor(mAshmemFd); + } + return status; } -void CursorWindow::clear() -{ +status_t CursorWindow::clear() { + if (mReadOnly) { + return INVALID_OPERATION; + } + + mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk); + mHeader->firstChunkOffset = sizeof(Header); mHeader->numRows = 0; mHeader->numColumns = 0; - 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; + + RowSlotChunk* firstChunk = static_cast<RowSlotChunk*>(offsetToPtr(mHeader->firstChunkOffset)); + firstChunk->nextChunkOffset = 0; + return OK; } -int32_t CursorWindow::freeSpace() -{ - int32_t freeSpace = mSize - mFreeOffset; - if (freeSpace < 0) { - freeSpace = 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; } - return freeSpace; + mHeader->numColumns = numColumns; + return OK; } -field_slot_t * CursorWindow::allocRow() -{ +status_t CursorWindow::allocRow() { + if (mReadOnly) { + return INVALID_OPERATION; + } + // Fill in the row slot - row_slot_t * rowSlot = allocRowSlot(); + RowSlot* rowSlot = allocRowSlot(); if (rowSlot == NULL) { - return NULL; + return NO_MEMORY; } // Allocate the slots for the field directory - size_t fieldDirSize = mHeader->numColumns * sizeof(field_slot_t); - uint32_t fieldDirOffset = alloc(fieldDirSize); + size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot); + uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/); if (!fieldDirOffset) { mHeader->numRows--; - LOG_WINDOW("The row failed, so back out the new row accounting from allocRowSlot %d", mHeader->numRows); - return NULL; + LOG_WINDOW("The row failed, so back out the new row accounting " + "from allocRowSlot %d", mHeader->numRows); + return NO_MEMORY; } - field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(fieldDirOffset); - memset(fieldDir, 0x0, fieldDirSize); + FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset)); + memset(fieldDir, 0, fieldDirSize); -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); + 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); rowSlot->offset = fieldDirOffset; + return OK; +} + +status_t CursorWindow::freeLastRow() { + if (mReadOnly) { + return INVALID_OPERATION; + } - return fieldDir; + if (mHeader->numRows > 0) { + mHeader->numRows--; + } + return OK; } -uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned) -{ - int32_t size; +uint32_t CursorWindow::alloc(size_t size, bool aligned) { uint32_t padding; if (aligned) { // 4 byte alignment - padding = 4 - (mFreeOffset & 0x3); + padding = (~mHeader->freeOffset + 1) & 3; } else { padding = 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; + 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; } - uint32_t offset = mFreeOffset + padding; - mFreeOffset += size; + mHeader->freeOffset = nextFreeOffset; return offset; } -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)); +CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) { + uint32_t chunkPos = row; + RowSlotChunk* chunk = static_cast<RowSlotChunk*>( + offsetToPtr(mHeader->firstChunkOffset)); + while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) { + chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset)); + chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS; } - return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t))); - LOG_WINDOW("exit getRowSlot current row num %d, this row %d", mHeader->numRows, row); + return &chunk->slots[chunkPos]; } -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) { +CursorWindow::RowSlot* CursorWindow::allocRowSlot() { + uint32_t chunkPos = mHeader->numRows; + RowSlotChunk* chunk = static_cast<RowSlotChunk*>( + offsetToPtr(mHeader->firstChunkOffset)); + while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) { + chunk = static_cast<RowSlotChunk*>(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) { 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<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset)); + chunk->nextChunkOffset = 0; + chunkPos = 0; } - mHeader->numRows++; - - return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t))); -} - -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; + mHeader->numRows += 1; + return &chunk->slots[chunkPos]; } -void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size) -{ - assert(offset + size <= mSize); - memcpy(mData + offset, data, size); -} - -void CursorWindow::copyIn(uint32_t offset, int64_t data) -{ - assert(offset + sizeof(int64_t) <= mSize); - memcpy(mData + offset, (uint8_t *)&data, sizeof(int64_t)); -} - -void CursorWindow::copyIn(uint32_t offset, double data) -{ - assert(offset + sizeof(double) <= mSize); - memcpy(mData + offset, (uint8_t *)&data, sizeof(double)); +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<FieldSlot*>(offsetToPtr(rowSlot->offset)); + return &fieldDir[column]; } -void CursorWindow::copyOut(uint32_t offset, uint8_t * data, size_t size) -{ - assert(offset + size <= mSize); - memcpy(data, mData + offset, size); +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); } -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; +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); } -double CursorWindow::copyOutDouble(uint32_t offset) -{ - double value; - assert(offset + sizeof(double) <= mSize); - memcpy(&value, mData + offset, sizeof(double)); - return value; -} +status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column, + const void* value, size_t size, int32_t type) { + if (mReadOnly) { + return INVALID_OPERATION; + } -bool CursorWindow::putLong(unsigned int row, unsigned int col, int64_t value) -{ - field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + FieldSlot* fieldSlot = getFieldSlot(row, column); if (!fieldSlot) { - return false; + return BAD_VALUE; } -#if WINDOW_STORAGE_INLINE_NUMERICS - fieldSlot->data.l = value; -#else - int offset = alloc(sizeof(int64_t)); + uint32_t offset = alloc(size); if (!offset) { - return false; + return NO_MEMORY; } - copyIn(offset, value); + memcpy(offsetToPtr(offset), value, size); + fieldSlot->type = type; fieldSlot->data.buffer.offset = offset; - fieldSlot->data.buffer.size = sizeof(int64_t); -#endif - fieldSlot->type = FIELD_TYPE_INTEGER; - return true; + fieldSlot->data.buffer.size = size; + return OK; } -bool CursorWindow::putDouble(unsigned int row, unsigned int col, double value) -{ - field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); - if (!fieldSlot) { - return false; +status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) { + if (mReadOnly) { + return INVALID_OPERATION; } -#if WINDOW_STORAGE_INLINE_NUMERICS - fieldSlot->data.d = value; -#else - int offset = alloc(sizeof(int64_t)); - if (!offset) { - return false; + FieldSlot* fieldSlot = getFieldSlot(row, column); + if (!fieldSlot) { + return BAD_VALUE; } - copyIn(offset, value); - - fieldSlot->data.buffer.offset = offset; - fieldSlot->data.buffer.size = sizeof(double); -#endif - fieldSlot->type = FIELD_TYPE_FLOAT; - return true; + fieldSlot->type = FIELD_TYPE_INTEGER; + fieldSlot->data.l = value; + return OK; } -bool CursorWindow::putNull(unsigned int row, unsigned int col) -{ - field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); - if (!fieldSlot) { - return false; +status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) { + if (mReadOnly) { + return INVALID_OPERATION; } - 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* fieldSlot = getFieldSlot(row, column); + if (!fieldSlot) { + return BAD_VALUE; } - *valueOut = getFieldSlotValueLong(fieldSlot); - return true; + fieldSlot->type = FIELD_TYPE_FLOAT; + fieldSlot->data.d = value; + return OK; } -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; +status_t CursorWindow::putNull(uint32_t row, uint32_t column) { + if (mReadOnly) { + return INVALID_OPERATION; } - *valueOut = getFieldSlotValueDouble(fieldSlot); - return true; -} - -bool CursorWindow::getNull(unsigned int row, unsigned int col, bool * valueOut) -{ - field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col); + FieldSlot* fieldSlot = getFieldSlot(row, column); if (!fieldSlot) { - return false; - } - - if (fieldSlot->type != FIELD_TYPE_NULL) { - *valueOut = false; - } else { - *valueOut = true; + return BAD_VALUE; } - return true; + + fieldSlot->type = FIELD_TYPE_NULL; + fieldSlot->data.buffer.offset = 0; + fieldSlot->data.buffer.size = 0; + return OK; } }; // namespace android diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 608877e..c7180ce 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) { |