Loading apct-tests/perftests/core/AndroidManifest.xml +11 −1 Original line number Original line Diff line number Diff line Loading @@ -20,7 +20,17 @@ <action android:name="com.android.perftests.core.PERFTEST" /> <action android:name="com.android.perftests.core.PERFTEST" /> </intent-filter> </intent-filter> </activity> </activity> <service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" /> <service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" /> <provider android:name="android.os.SomeProvider" android:authorities="android.os.SomeProvider" android:exported="false" android:process=":some_provider" /> <service <service android:name="android.view.autofill.MyAutofillService" android:name="android.view.autofill.MyAutofillService" Loading apct-tests/perftests/core/src/android/database/CrossProcessCursorPerfTest.java 0 → 100644 +89 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package android.database; import static org.junit.Assert.assertEquals; import android.content.ContentProviderClient; import android.content.ContentValues; import android.content.Intent; import android.net.Uri; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @LargeTest public class CrossProcessCursorPerfTest { @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); /** * Measure transporting a small {@link Cursor}, roughly 1KB in size. */ @Test public void timeSmall() throws Exception { time(1); } /** * Measure transporting a small {@link Cursor}, roughly 54KB in size. */ @Test public void timeMedium() throws Exception { time(100); } /** * Measure transporting a small {@link Cursor}, roughly 5.4MB in size. */ @Test public void timeLarge() throws Exception { time(10_000); } private static final Uri TEST_URI = Uri.parse("content://android.os.SomeProvider/"); private void time(int count) throws Exception { try (ContentProviderClient client = InstrumentationRegistry.getTargetContext() .getContentResolver().acquireContentProviderClient(TEST_URI)) { // Configure remote side once with data size to return final ContentValues values = new ContentValues(); values.put(Intent.EXTRA_INDEX, count); client.update(TEST_URI, values, null); // Repeatedly query that data until we reach convergence final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { try (Cursor c = client.query(TEST_URI, null, null, null)) { // Actually walk the returned values to ensure we pull all // data from the remote side while (c.moveToNext()) { assertEquals(c.getPosition(), c.getInt(0)); } } } } } } apct-tests/perftests/core/src/android/os/SomeProvider.java 0 → 100644 +73 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package android.os; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Intent; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; import java.util.Arrays; public class SomeProvider extends ContentProvider { private Cursor mCursor; @Override public boolean onCreate() { return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return mCursor; } @Override public String getType(Uri uri) { throw new UnsupportedOperationException(); } @Override public Uri insert(Uri uri, ContentValues values) { throw new UnsupportedOperationException(); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { throw new UnsupportedOperationException(); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { final char[] valueRaw = new char[512]; Arrays.fill(valueRaw, '!'); final String value = new String(valueRaw); final int count = values.getAsInteger(Intent.EXTRA_INDEX); final MatrixCursor cursor = new MatrixCursor(new String[] { "_id", "value" }); for (int i = 0; i < count; i++) { MatrixCursor.RowBuilder row = cursor.newRow(); row.add(0, i); row.add(1, value); } mCursor = cursor; return 1; } } core/jni/android_database_CursorWindow.cpp +15 −7 Original line number Original line Diff line number Diff line Loading @@ -84,23 +84,31 @@ static int getFdCount() { } } static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, jint cursorWindowSize) { static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, jint cursorWindowSize) { status_t status; String8 name; String8 name; CursorWindow* window; const char* nameStr = env->GetStringUTFChars(nameObj, NULL); const char* nameStr = env->GetStringUTFChars(nameObj, NULL); name.setTo(nameStr); name.setTo(nameStr); env->ReleaseStringUTFChars(nameObj, nameStr); env->ReleaseStringUTFChars(nameObj, nameStr); CursorWindow* window; if (cursorWindowSize < 0) { status_t status = CursorWindow::create(name, cursorWindowSize, &window); status = INVALID_OPERATION; goto fail; } status = CursorWindow::create(name, cursorWindowSize, &window); if (status || !window) { if (status || !window) { jniThrowExceptionFmt(env, goto fail; "android/database/CursorWindowAllocationException", "Could not allocate CursorWindow '%s' of size %d due to error %d.", name.string(), cursorWindowSize, status); return 0; } } LOG_WINDOW("nativeInitializeEmpty: window = %p", window); LOG_WINDOW("nativeInitializeEmpty: window = %p", window); return reinterpret_cast<jlong>(window); return reinterpret_cast<jlong>(window); fail: jniThrowExceptionFmt(env, "android/database/CursorWindowAllocationException", "Could not allocate CursorWindow '%s' of size %d due to error %d.", name.string(), cursorWindowSize, status); return 0; } } static jlong nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) { static jlong nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) { Loading libs/androidfw/CursorWindow.cpp +155 −38 Original line number Original line Diff line number Diff line Loading @@ -30,23 +30,62 @@ namespace android { namespace android { CursorWindow::CursorWindow(const String8& name, int ashmemFd, /** void* data, size_t size, bool readOnly) : * By default windows are lightweight inline allocations of this size; mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) { * they're only inflated to ashmem regions when more space is needed. */ static constexpr const size_t kInlineSize = 16384; CursorWindow::CursorWindow(const String8& name, int ashmemFd, void* data, size_t size, size_t inflatedSize, bool readOnly) : mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mInflatedSize(inflatedSize), mReadOnly(readOnly) { mHeader = static_cast<Header*>(mData); mHeader = static_cast<Header*>(mData); } } CursorWindow::~CursorWindow() { CursorWindow::~CursorWindow() { if (mAshmemFd != -1) { ::munmap(mData, mSize); ::munmap(mData, mSize); ::close(mAshmemFd); ::close(mAshmemFd); } else { free(mData); } } status_t CursorWindow::create(const String8& name, size_t inflatedSize, CursorWindow** outCursorWindow) { *outCursorWindow = nullptr; size_t size = std::min(kInlineSize, inflatedSize); void* data = calloc(size, 1); if (!data) return NO_MEMORY; CursorWindow* window = new CursorWindow(name, -1, data, size, inflatedSize, false /*readOnly*/); status_t result = window->clear(); if (!result) { LOG_WINDOW("Created new CursorWindow: freeOffset=%d, " "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, window->mHeader->numRows, window->mHeader->numColumns, window->mSize, window->mData); *outCursorWindow = window; return OK; } delete window; return result; } } status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) { status_t CursorWindow::inflate() { // Shortcut when we can't expand any further if (mSize == mInflatedSize) return INVALID_OPERATION; String8 ashmemName("CursorWindow: "); String8 ashmemName("CursorWindow: "); ashmemName.append(name); ashmemName.append(mName); status_t result; status_t result; int ashmemFd = ashmem_create_region(ashmemName.string(), size); int ashmemFd = ashmem_create_region(ashmemName.string(), mInflatedSize); if (ashmemFd < 0) { if (ashmemFd < 0) { result = -errno; result = -errno; ALOGE("CursorWindow: ashmem_create_region() failed: errno=%d.", errno); ALOGE("CursorWindow: ashmem_create_region() failed: errno=%d.", errno); Loading @@ -55,7 +94,8 @@ status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** o if (result < 0) { if (result < 0) { ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d",errno); ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d",errno); } else { } else { void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0); void* data = ::mmap(NULL, mInflatedSize, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0); if (data == MAP_FAILED) { if (data == MAP_FAILED) { result = -errno; result = -errno; ALOGE("CursorWindow: mmap() failed: errno=%d.", errno); ALOGE("CursorWindow: mmap() failed: errno=%d.", errno); Loading @@ -64,33 +104,49 @@ status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** o if (result < 0) { if (result < 0) { ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d.", errno); ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d.", errno); } else { } else { CursorWindow* window = new CursorWindow(name, ashmemFd, // Move inline contents into new ashmem region data, size, false /*readOnly*/); memcpy(data, mData, mSize); result = window->clear(); free(mData); if (!result) { mAshmemFd = ashmemFd; LOG_WINDOW("Created new CursorWindow: freeOffset=%d, " mData = data; mHeader = static_cast<Header*>(mData); mSize = mInflatedSize; LOG_WINDOW("Inflated CursorWindow: freeOffset=%d, " "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, mHeader->freeOffset, window->mHeader->numRows, mHeader->numRows, window->mHeader->numColumns, mHeader->numColumns, window->mSize, window->mData); mSize, mData); *outCursorWindow = window; return OK; return OK; } } delete window; } } } ::munmap(data, size); ::munmap(data, mInflatedSize); } } ::close(ashmemFd); ::close(ashmemFd); } } *outCursorWindow = NULL; return result; return result; } } status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) { status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) { String8 name = parcel->readString8(); *outCursorWindow = nullptr; String8 name; status_t result = parcel->readString8(&name); if (result) return result; bool isAshmem; result = parcel->readBool(&isAshmem); if (result) return result; if (isAshmem) { return createFromParcelAshmem(parcel, name, outCursorWindow); } else { return createFromParcelInline(parcel, name, outCursorWindow); } } status_t CursorWindow::createFromParcelAshmem(Parcel* parcel, String8& name, CursorWindow** outCursorWindow) { status_t result; status_t result; int actualSize; int actualSize; int ashmemFd = parcel->readFileDescriptor(); int ashmemFd = parcel->readFileDescriptor(); Loading Loading @@ -122,8 +178,8 @@ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursor actualSize, (int) size, errno); actualSize, (int) size, errno); } else { } else { CursorWindow* window = new CursorWindow(name, dupAshmemFd, CursorWindow* window = new CursorWindow(name, dupAshmemFd, data, size, true /*readOnly*/); data, size, size, true /*readOnly*/); LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, " LOG_WINDOW("Created CursorWindow from ashmem parcel: freeOffset=%d, " "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, window->mHeader->freeOffset, window->mHeader->numRows, window->mHeader->numRows, Loading @@ -140,12 +196,62 @@ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursor return result; return result; } } status_t CursorWindow::createFromParcelInline(Parcel* parcel, String8& name, CursorWindow** outCursorWindow) { uint32_t sentSize; status_t result = parcel->readUint32(&sentSize); if (result) return result; if (sentSize > kInlineSize) return NO_MEMORY; void* data = calloc(sentSize, 1); if (!data) return NO_MEMORY; result = parcel->read(data, sentSize); if (result) return result; CursorWindow* window = new CursorWindow(name, -1, data, sentSize, sentSize, true /*readOnly*/); LOG_WINDOW("Created CursorWindow from inline parcel: freeOffset=%d, " "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, window->mHeader->numRows, window->mHeader->numColumns, window->mSize, window->mData); *outCursorWindow = window; return OK; } status_t CursorWindow::writeToParcel(Parcel* parcel) { status_t CursorWindow::writeToParcel(Parcel* parcel) { status_t status = parcel->writeString8(mName); LOG_WINDOW("Writing CursorWindow: freeOffset=%d, " if (!status) { "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", status = parcel->writeDupFileDescriptor(mAshmemFd); mHeader->freeOffset, mHeader->numRows, mHeader->numColumns, mSize, mData); status_t result = parcel->writeString8(mName); if (result) return result; if (mAshmemFd != -1) { result = parcel->writeBool(true); if (result) return result; return writeToParcelAshmem(parcel); } else { result = parcel->writeBool(false); if (result) return result; return writeToParcelInline(parcel); } } return status; } status_t CursorWindow::writeToParcelAshmem(Parcel* parcel) { return parcel->writeDupFileDescriptor(mAshmemFd); } status_t CursorWindow::writeToParcelInline(Parcel* parcel) { status_t result = parcel->writeUint32(mHeader->freeOffset); if (result) return result; return parcel->write(mData, mHeader->freeOffset); } } status_t CursorWindow::clear() { status_t CursorWindow::clear() { Loading Loading @@ -187,6 +293,7 @@ status_t CursorWindow::allocRow() { if (rowSlot == NULL) { if (rowSlot == NULL) { return NO_MEMORY; return NO_MEMORY; } } uint32_t rowSlotOffset = offsetFromPtr(rowSlot); // Allocate the slots for the field directory // Allocate the slots for the field directory size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot); size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot); Loading @@ -201,7 +308,8 @@ status_t CursorWindow::allocRow() { memset(fieldDir, 0, fieldDirSize); memset(fieldDir, 0, fieldDirSize); LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %zu bytes at offset %u\n", LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %zu bytes at offset %u\n", mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset); mHeader->numRows - 1, rowSlotOffset, fieldDirSize, fieldDirOffset); rowSlot = static_cast<RowSlot*>(offsetToPtr(rowSlotOffset)); rowSlot->offset = fieldDirOffset; rowSlot->offset = fieldDirOffset; return OK; return OK; } } Loading @@ -228,12 +336,16 @@ uint32_t CursorWindow::alloc(size_t size, bool aligned) { uint32_t offset = mHeader->freeOffset + padding; uint32_t offset = mHeader->freeOffset + padding; uint32_t nextFreeOffset = offset + size; uint32_t nextFreeOffset = offset + size; if (nextFreeOffset > mSize) { // Try inflating to ashmem before finally giving up inflate(); if (nextFreeOffset > mSize) { if (nextFreeOffset > mSize) { ALOGW("Window is full: requested allocation %zu bytes, " ALOGW("Window is full: requested allocation %zu bytes, " "free space %zu bytes, window size %zu bytes", "free space %zu bytes, window size %zu bytes", size, freeSpace(), mSize); size, freeSpace(), mSize); return 0; return 0; } } } mHeader->freeOffset = nextFreeOffset; mHeader->freeOffset = nextFreeOffset; return offset; return offset; Loading @@ -260,7 +372,10 @@ CursorWindow::RowSlot* CursorWindow::allocRowSlot() { } } if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) { if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) { if (!chunk->nextChunkOffset) { if (!chunk->nextChunkOffset) { chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/); uint32_t chunkOffset = offsetFromPtr(chunk); uint32_t newChunk = alloc(sizeof(RowSlotChunk), true /*aligned*/); chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunkOffset)); chunk->nextChunkOffset = newChunk; if (!chunk->nextChunkOffset) { if (!chunk->nextChunkOffset) { return NULL; return NULL; } } Loading Loading @@ -308,6 +423,7 @@ status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column, if (!fieldSlot) { if (!fieldSlot) { return BAD_VALUE; return BAD_VALUE; } } uint32_t fieldSlotOffset = offsetFromPtr(fieldSlot); uint32_t offset = alloc(size); uint32_t offset = alloc(size); if (!offset) { if (!offset) { Loading @@ -316,6 +432,7 @@ status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column, memcpy(offsetToPtr(offset), value, size); memcpy(offsetToPtr(offset), value, size); fieldSlot = static_cast<FieldSlot*>(offsetToPtr(fieldSlotOffset)); fieldSlot->type = type; fieldSlot->type = type; fieldSlot->data.buffer.offset = offset; fieldSlot->data.buffer.offset = offset; fieldSlot->data.buffer.size = size; fieldSlot->data.buffer.size = size; Loading Loading
apct-tests/perftests/core/AndroidManifest.xml +11 −1 Original line number Original line Diff line number Diff line Loading @@ -20,7 +20,17 @@ <action android:name="com.android.perftests.core.PERFTEST" /> <action android:name="com.android.perftests.core.PERFTEST" /> </intent-filter> </intent-filter> </activity> </activity> <service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" /> <service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" /> <provider android:name="android.os.SomeProvider" android:authorities="android.os.SomeProvider" android:exported="false" android:process=":some_provider" /> <service <service android:name="android.view.autofill.MyAutofillService" android:name="android.view.autofill.MyAutofillService" Loading
apct-tests/perftests/core/src/android/database/CrossProcessCursorPerfTest.java 0 → 100644 +89 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package android.database; import static org.junit.Assert.assertEquals; import android.content.ContentProviderClient; import android.content.ContentValues; import android.content.Intent; import android.net.Uri; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @LargeTest public class CrossProcessCursorPerfTest { @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); /** * Measure transporting a small {@link Cursor}, roughly 1KB in size. */ @Test public void timeSmall() throws Exception { time(1); } /** * Measure transporting a small {@link Cursor}, roughly 54KB in size. */ @Test public void timeMedium() throws Exception { time(100); } /** * Measure transporting a small {@link Cursor}, roughly 5.4MB in size. */ @Test public void timeLarge() throws Exception { time(10_000); } private static final Uri TEST_URI = Uri.parse("content://android.os.SomeProvider/"); private void time(int count) throws Exception { try (ContentProviderClient client = InstrumentationRegistry.getTargetContext() .getContentResolver().acquireContentProviderClient(TEST_URI)) { // Configure remote side once with data size to return final ContentValues values = new ContentValues(); values.put(Intent.EXTRA_INDEX, count); client.update(TEST_URI, values, null); // Repeatedly query that data until we reach convergence final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { try (Cursor c = client.query(TEST_URI, null, null, null)) { // Actually walk the returned values to ensure we pull all // data from the remote side while (c.moveToNext()) { assertEquals(c.getPosition(), c.getInt(0)); } } } } } }
apct-tests/perftests/core/src/android/os/SomeProvider.java 0 → 100644 +73 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package android.os; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Intent; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; import java.util.Arrays; public class SomeProvider extends ContentProvider { private Cursor mCursor; @Override public boolean onCreate() { return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return mCursor; } @Override public String getType(Uri uri) { throw new UnsupportedOperationException(); } @Override public Uri insert(Uri uri, ContentValues values) { throw new UnsupportedOperationException(); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { throw new UnsupportedOperationException(); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { final char[] valueRaw = new char[512]; Arrays.fill(valueRaw, '!'); final String value = new String(valueRaw); final int count = values.getAsInteger(Intent.EXTRA_INDEX); final MatrixCursor cursor = new MatrixCursor(new String[] { "_id", "value" }); for (int i = 0; i < count; i++) { MatrixCursor.RowBuilder row = cursor.newRow(); row.add(0, i); row.add(1, value); } mCursor = cursor; return 1; } }
core/jni/android_database_CursorWindow.cpp +15 −7 Original line number Original line Diff line number Diff line Loading @@ -84,23 +84,31 @@ static int getFdCount() { } } static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, jint cursorWindowSize) { static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, jint cursorWindowSize) { status_t status; String8 name; String8 name; CursorWindow* window; const char* nameStr = env->GetStringUTFChars(nameObj, NULL); const char* nameStr = env->GetStringUTFChars(nameObj, NULL); name.setTo(nameStr); name.setTo(nameStr); env->ReleaseStringUTFChars(nameObj, nameStr); env->ReleaseStringUTFChars(nameObj, nameStr); CursorWindow* window; if (cursorWindowSize < 0) { status_t status = CursorWindow::create(name, cursorWindowSize, &window); status = INVALID_OPERATION; goto fail; } status = CursorWindow::create(name, cursorWindowSize, &window); if (status || !window) { if (status || !window) { jniThrowExceptionFmt(env, goto fail; "android/database/CursorWindowAllocationException", "Could not allocate CursorWindow '%s' of size %d due to error %d.", name.string(), cursorWindowSize, status); return 0; } } LOG_WINDOW("nativeInitializeEmpty: window = %p", window); LOG_WINDOW("nativeInitializeEmpty: window = %p", window); return reinterpret_cast<jlong>(window); return reinterpret_cast<jlong>(window); fail: jniThrowExceptionFmt(env, "android/database/CursorWindowAllocationException", "Could not allocate CursorWindow '%s' of size %d due to error %d.", name.string(), cursorWindowSize, status); return 0; } } static jlong nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) { static jlong nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) { Loading
libs/androidfw/CursorWindow.cpp +155 −38 Original line number Original line Diff line number Diff line Loading @@ -30,23 +30,62 @@ namespace android { namespace android { CursorWindow::CursorWindow(const String8& name, int ashmemFd, /** void* data, size_t size, bool readOnly) : * By default windows are lightweight inline allocations of this size; mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) { * they're only inflated to ashmem regions when more space is needed. */ static constexpr const size_t kInlineSize = 16384; CursorWindow::CursorWindow(const String8& name, int ashmemFd, void* data, size_t size, size_t inflatedSize, bool readOnly) : mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mInflatedSize(inflatedSize), mReadOnly(readOnly) { mHeader = static_cast<Header*>(mData); mHeader = static_cast<Header*>(mData); } } CursorWindow::~CursorWindow() { CursorWindow::~CursorWindow() { if (mAshmemFd != -1) { ::munmap(mData, mSize); ::munmap(mData, mSize); ::close(mAshmemFd); ::close(mAshmemFd); } else { free(mData); } } status_t CursorWindow::create(const String8& name, size_t inflatedSize, CursorWindow** outCursorWindow) { *outCursorWindow = nullptr; size_t size = std::min(kInlineSize, inflatedSize); void* data = calloc(size, 1); if (!data) return NO_MEMORY; CursorWindow* window = new CursorWindow(name, -1, data, size, inflatedSize, false /*readOnly*/); status_t result = window->clear(); if (!result) { LOG_WINDOW("Created new CursorWindow: freeOffset=%d, " "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, window->mHeader->numRows, window->mHeader->numColumns, window->mSize, window->mData); *outCursorWindow = window; return OK; } delete window; return result; } } status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) { status_t CursorWindow::inflate() { // Shortcut when we can't expand any further if (mSize == mInflatedSize) return INVALID_OPERATION; String8 ashmemName("CursorWindow: "); String8 ashmemName("CursorWindow: "); ashmemName.append(name); ashmemName.append(mName); status_t result; status_t result; int ashmemFd = ashmem_create_region(ashmemName.string(), size); int ashmemFd = ashmem_create_region(ashmemName.string(), mInflatedSize); if (ashmemFd < 0) { if (ashmemFd < 0) { result = -errno; result = -errno; ALOGE("CursorWindow: ashmem_create_region() failed: errno=%d.", errno); ALOGE("CursorWindow: ashmem_create_region() failed: errno=%d.", errno); Loading @@ -55,7 +94,8 @@ status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** o if (result < 0) { if (result < 0) { ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d",errno); ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d",errno); } else { } else { void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0); void* data = ::mmap(NULL, mInflatedSize, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0); if (data == MAP_FAILED) { if (data == MAP_FAILED) { result = -errno; result = -errno; ALOGE("CursorWindow: mmap() failed: errno=%d.", errno); ALOGE("CursorWindow: mmap() failed: errno=%d.", errno); Loading @@ -64,33 +104,49 @@ status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** o if (result < 0) { if (result < 0) { ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d.", errno); ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d.", errno); } else { } else { CursorWindow* window = new CursorWindow(name, ashmemFd, // Move inline contents into new ashmem region data, size, false /*readOnly*/); memcpy(data, mData, mSize); result = window->clear(); free(mData); if (!result) { mAshmemFd = ashmemFd; LOG_WINDOW("Created new CursorWindow: freeOffset=%d, " mData = data; mHeader = static_cast<Header*>(mData); mSize = mInflatedSize; LOG_WINDOW("Inflated CursorWindow: freeOffset=%d, " "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, mHeader->freeOffset, window->mHeader->numRows, mHeader->numRows, window->mHeader->numColumns, mHeader->numColumns, window->mSize, window->mData); mSize, mData); *outCursorWindow = window; return OK; return OK; } } delete window; } } } ::munmap(data, size); ::munmap(data, mInflatedSize); } } ::close(ashmemFd); ::close(ashmemFd); } } *outCursorWindow = NULL; return result; return result; } } status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) { status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) { String8 name = parcel->readString8(); *outCursorWindow = nullptr; String8 name; status_t result = parcel->readString8(&name); if (result) return result; bool isAshmem; result = parcel->readBool(&isAshmem); if (result) return result; if (isAshmem) { return createFromParcelAshmem(parcel, name, outCursorWindow); } else { return createFromParcelInline(parcel, name, outCursorWindow); } } status_t CursorWindow::createFromParcelAshmem(Parcel* parcel, String8& name, CursorWindow** outCursorWindow) { status_t result; status_t result; int actualSize; int actualSize; int ashmemFd = parcel->readFileDescriptor(); int ashmemFd = parcel->readFileDescriptor(); Loading Loading @@ -122,8 +178,8 @@ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursor actualSize, (int) size, errno); actualSize, (int) size, errno); } else { } else { CursorWindow* window = new CursorWindow(name, dupAshmemFd, CursorWindow* window = new CursorWindow(name, dupAshmemFd, data, size, true /*readOnly*/); data, size, size, true /*readOnly*/); LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, " LOG_WINDOW("Created CursorWindow from ashmem parcel: freeOffset=%d, " "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, window->mHeader->freeOffset, window->mHeader->numRows, window->mHeader->numRows, Loading @@ -140,12 +196,62 @@ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursor return result; return result; } } status_t CursorWindow::createFromParcelInline(Parcel* parcel, String8& name, CursorWindow** outCursorWindow) { uint32_t sentSize; status_t result = parcel->readUint32(&sentSize); if (result) return result; if (sentSize > kInlineSize) return NO_MEMORY; void* data = calloc(sentSize, 1); if (!data) return NO_MEMORY; result = parcel->read(data, sentSize); if (result) return result; CursorWindow* window = new CursorWindow(name, -1, data, sentSize, sentSize, true /*readOnly*/); LOG_WINDOW("Created CursorWindow from inline parcel: freeOffset=%d, " "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, window->mHeader->numRows, window->mHeader->numColumns, window->mSize, window->mData); *outCursorWindow = window; return OK; } status_t CursorWindow::writeToParcel(Parcel* parcel) { status_t CursorWindow::writeToParcel(Parcel* parcel) { status_t status = parcel->writeString8(mName); LOG_WINDOW("Writing CursorWindow: freeOffset=%d, " if (!status) { "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", status = parcel->writeDupFileDescriptor(mAshmemFd); mHeader->freeOffset, mHeader->numRows, mHeader->numColumns, mSize, mData); status_t result = parcel->writeString8(mName); if (result) return result; if (mAshmemFd != -1) { result = parcel->writeBool(true); if (result) return result; return writeToParcelAshmem(parcel); } else { result = parcel->writeBool(false); if (result) return result; return writeToParcelInline(parcel); } } return status; } status_t CursorWindow::writeToParcelAshmem(Parcel* parcel) { return parcel->writeDupFileDescriptor(mAshmemFd); } status_t CursorWindow::writeToParcelInline(Parcel* parcel) { status_t result = parcel->writeUint32(mHeader->freeOffset); if (result) return result; return parcel->write(mData, mHeader->freeOffset); } } status_t CursorWindow::clear() { status_t CursorWindow::clear() { Loading Loading @@ -187,6 +293,7 @@ status_t CursorWindow::allocRow() { if (rowSlot == NULL) { if (rowSlot == NULL) { return NO_MEMORY; return NO_MEMORY; } } uint32_t rowSlotOffset = offsetFromPtr(rowSlot); // Allocate the slots for the field directory // Allocate the slots for the field directory size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot); size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot); Loading @@ -201,7 +308,8 @@ status_t CursorWindow::allocRow() { memset(fieldDir, 0, fieldDirSize); memset(fieldDir, 0, fieldDirSize); LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %zu bytes at offset %u\n", LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %zu bytes at offset %u\n", mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset); mHeader->numRows - 1, rowSlotOffset, fieldDirSize, fieldDirOffset); rowSlot = static_cast<RowSlot*>(offsetToPtr(rowSlotOffset)); rowSlot->offset = fieldDirOffset; rowSlot->offset = fieldDirOffset; return OK; return OK; } } Loading @@ -228,12 +336,16 @@ uint32_t CursorWindow::alloc(size_t size, bool aligned) { uint32_t offset = mHeader->freeOffset + padding; uint32_t offset = mHeader->freeOffset + padding; uint32_t nextFreeOffset = offset + size; uint32_t nextFreeOffset = offset + size; if (nextFreeOffset > mSize) { // Try inflating to ashmem before finally giving up inflate(); if (nextFreeOffset > mSize) { if (nextFreeOffset > mSize) { ALOGW("Window is full: requested allocation %zu bytes, " ALOGW("Window is full: requested allocation %zu bytes, " "free space %zu bytes, window size %zu bytes", "free space %zu bytes, window size %zu bytes", size, freeSpace(), mSize); size, freeSpace(), mSize); return 0; return 0; } } } mHeader->freeOffset = nextFreeOffset; mHeader->freeOffset = nextFreeOffset; return offset; return offset; Loading @@ -260,7 +372,10 @@ CursorWindow::RowSlot* CursorWindow::allocRowSlot() { } } if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) { if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) { if (!chunk->nextChunkOffset) { if (!chunk->nextChunkOffset) { chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/); uint32_t chunkOffset = offsetFromPtr(chunk); uint32_t newChunk = alloc(sizeof(RowSlotChunk), true /*aligned*/); chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunkOffset)); chunk->nextChunkOffset = newChunk; if (!chunk->nextChunkOffset) { if (!chunk->nextChunkOffset) { return NULL; return NULL; } } Loading Loading @@ -308,6 +423,7 @@ status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column, if (!fieldSlot) { if (!fieldSlot) { return BAD_VALUE; return BAD_VALUE; } } uint32_t fieldSlotOffset = offsetFromPtr(fieldSlot); uint32_t offset = alloc(size); uint32_t offset = alloc(size); if (!offset) { if (!offset) { Loading @@ -316,6 +432,7 @@ status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column, memcpy(offsetToPtr(offset), value, size); memcpy(offsetToPtr(offset), value, size); fieldSlot = static_cast<FieldSlot*>(offsetToPtr(fieldSlotOffset)); fieldSlot->type = type; fieldSlot->type = type; fieldSlot->data.buffer.offset = offset; fieldSlot->data.buffer.offset = offset; fieldSlot->data.buffer.size = size; fieldSlot->data.buffer.size = size; Loading