diff options
-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) { |