diff options
| author | 2023-12-12 18:33:42 -0800 | |
|---|---|---|
| committer | 2023-12-15 12:33:38 -0800 | |
| commit | 62b54d65987ea65b2b6fdb8afa028f850bf52d87 (patch) | |
| tree | cfb95c01501dfe132ee7f04c8591d20dc6a43570 | |
| parent | 556f2734cce34e64a2b3a377284f3434aa12747d (diff) | |
Enable CursorWindow for Ravenwood
Bug: 314797745
Test: atest FrameworksCoreTestsRavenwood
Test: atest FrameworksCoreTests
Change-Id: I118b6ba36bdc85d5b712aed0f0e80bb9a9fc3125
10 files changed, 289 insertions, 28 deletions
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 52bba1484f1a..870546a6fd2b 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -22,14 +22,8 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; import android.database.sqlite.SQLiteClosable; import android.database.sqlite.SQLiteException; -import android.os.Binder; -import android.os.Build; import android.os.Parcel; import android.os.Parcelable; -import android.os.Process; -import android.util.Log; -import android.util.LongSparseArray; -import android.util.SparseIntArray; import dalvik.annotation.optimization.FastNative; import dalvik.system.CloseGuard; @@ -44,6 +38,9 @@ import dalvik.system.CloseGuard; * consumer for reading. * </p> */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass +@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( + "com.android.hoststubgen.nativesubstitution.CursorWindow_host") public class CursorWindow extends SQLiteClosable implements Parcelable { private static final String STATS_TAG = "CursorWindowStats"; @@ -61,7 +58,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { private int mStartPos; private final String mName; - private final CloseGuard mCloseGuard = CloseGuard.get(); + private final CloseGuard mCloseGuard; // May throw CursorWindowAllocationException private static native long nativeCreate(String name, int cursorWindowSize); @@ -147,7 +144,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { if (mWindowPtr == 0) { throw new AssertionError(); // Not possible, the native code won't return it. } - mCloseGuard.open("CursorWindow.close"); + mCloseGuard = createCloseGuard(); } /** @@ -175,7 +172,18 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { throw new AssertionError(); // Not possible, the native code won't return it. } mName = nativeGetName(mWindowPtr); - mCloseGuard.open("CursorWindow.close"); + mCloseGuard = createCloseGuard(); + } + + @android.ravenwood.annotation.RavenwoodReplace + private CloseGuard createCloseGuard() { + final CloseGuard closeGuard = CloseGuard.get(); + closeGuard.open("CursorWindow.close"); + return closeGuard; + } + + private CloseGuard createCloseGuard$ravenwood() { + return null; } @Override @@ -749,6 +757,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { dispose(); } + @android.ravenwood.annotation.RavenwoodReplace private static int getCursorWindowSize() { if (sCursorWindowSize < 0) { // The cursor window size. resource xml file specifies the value in kB. @@ -759,6 +768,10 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { return sCursorWindowSize; } + private static int getCursorWindowSize$ravenwood() { + return 1024; + } + @Override public String toString() { return getName() + " {" + Long.toHexString(mWindowPtr) + "}"; diff --git a/core/java/android/database/SQLException.java b/core/java/android/database/SQLException.java index 3402026a438a..4026f201ea81 100644 --- a/core/java/android/database/SQLException.java +++ b/core/java/android/database/SQLException.java @@ -19,6 +19,7 @@ package android.database; /** * An exception that indicates there was an error with SQL parsing or execution. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class SQLException extends RuntimeException { public SQLException() { } diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java index 2fca729f2551..8eb512a2cbc6 100644 --- a/core/java/android/database/sqlite/SQLiteClosable.java +++ b/core/java/android/database/sqlite/SQLiteClosable.java @@ -25,6 +25,7 @@ import java.io.Closeable; * * This class implements a primitive reference counting scheme for database objects. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public abstract class SQLiteClosable implements Closeable { @UnsupportedAppUsage private int mReferenceCount = 1; diff --git a/core/java/android/database/sqlite/SQLiteException.java b/core/java/android/database/sqlite/SQLiteException.java index a1d9c9f95a2f..531b40a7014f 100644 --- a/core/java/android/database/sqlite/SQLiteException.java +++ b/core/java/android/database/sqlite/SQLiteException.java @@ -21,6 +21,7 @@ import android.database.SQLException; /** * A SQLite exception that indicates there was an error with SQL parsing or execution. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class SQLiteException extends SQLException { public SQLiteException() { } diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 1a3ec27418a6..c0581746e6f6 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -202,12 +202,14 @@ android_ravenwood_test { "testng", ], srcs: [ + "src/android/database/CursorWindowTest.java", "src/android/os/**/*.java", - "src/com/android/internal/os/**/*.java", "src/android/util/**/*.java", + "src/com/android/internal/os/**/*.java", "src/com/android/internal/os/LongArrayMultiStateCounterTest.java", "src/com/android/internal/util/**/*.java", "src/com/android/internal/power/EnergyConsumerStatsTest.java", + ":FrameworksCoreTests{.aapt.srcjar}", ":FrameworksCoreTests-aidl", ":FrameworksCoreTests-helpers", diff --git a/core/tests/coretests/src/android/database/CursorWindowPerformanceTest.java b/core/tests/coretests/src/android/database/CursorWindowPerformanceTest.java new file mode 100644 index 000000000000..c6d3ebf0251e --- /dev/null +++ b/core/tests/coretests/src/android/database/CursorWindowPerformanceTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2023 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 android.test.PerformanceTestCase; + +import androidx.test.filters.SmallTest; + +import junit.framework.TestCase; + +/** + * <pre> + * m -j44 FrameworksCoreTests + * adb install -r -g \ + * ${ANDROID_PRODUCT_OUT}/testcases/FrameworksCoreTests/arm64/FrameworksCoreTests.apk + * adb shell am instrument -r -e perf true \ + * -e class 'android.database.CursorWindowPerformanceTest' + * -w 'com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner' + * </pre> + */ +public class CursorWindowPerformanceTest extends TestCase implements PerformanceTestCase { + + private final CursorWindowTest mTest = new CursorWindowTest(); + + @Override + public boolean isPerformanceOnly() { + return true; + } + + // These test can only be run once. + @Override + public int startPerformance(Intermediates intermediates) { + return 1; + } + + @SmallTest + public void testConstructor_WithName() { + mTest.testConstructor_WithName(); + } + + @SmallTest + public void testConstructorWithEmptyName() { + mTest.testConstructorWithEmptyName(); + } + + @SmallTest + public void testValues() { + mTest.testValues(); + } +} diff --git a/core/tests/coretests/src/android/database/CursorWindowTest.java b/core/tests/coretests/src/android/database/CursorWindowTest.java index 123da3e8703b..255020a84893 100644 --- a/core/tests/coretests/src/android/database/CursorWindowTest.java +++ b/core/tests/coretests/src/android/database/CursorWindowTest.java @@ -16,25 +16,29 @@ package android.database; -import android.test.PerformanceTestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import android.database.sqlite.SQLiteException; +import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; -import junit.framework.TestCase; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; import java.util.Arrays; -public class CursorWindowTest extends TestCase implements PerformanceTestCase { - public boolean isPerformanceOnly() { - return false; - } +@RunWith(AndroidJUnit4.class) +@SmallTest +public class CursorWindowTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); - // These test can only be run once. - public int startPerformance(Intermediates intermediates) { - return 1; - } - - @SmallTest + @Test public void testConstructor_WithName() { CursorWindow window = new CursorWindow("MyWindow"); assertEquals("MyWindow", window.getName()); @@ -42,7 +46,7 @@ public class CursorWindowTest extends TestCase implements PerformanceTestCase { window.close(); } - @SmallTest + @Test public void testConstructorWithEmptyName() { CursorWindow window = new CursorWindow(""); assertEquals("<unnamed>", window.getName()); @@ -50,7 +54,7 @@ public class CursorWindowTest extends TestCase implements PerformanceTestCase { window.close(); } - @SmallTest + @Test public void testConstructorWithNullName() { CursorWindow window = new CursorWindow(null); assertEquals("<unnamed>", window.getName()); @@ -58,7 +62,7 @@ public class CursorWindowTest extends TestCase implements PerformanceTestCase { window.close(); } - @SmallTest + @Test public void testDeprecatedConstructor() { @SuppressWarnings("deprecation") CursorWindow window = new CursorWindow(true /*this argument is ignored*/); @@ -67,7 +71,7 @@ public class CursorWindowTest extends TestCase implements PerformanceTestCase { window.close(); } - @SmallTest + @Test public void testValues() { CursorWindow window = new CursorWindow("MyWindow"); doTestValues(window); @@ -77,17 +81,25 @@ public class CursorWindowTest extends TestCase implements PerformanceTestCase { private void doTestValues(CursorWindow window) { assertTrue(window.setNumColumns(7)); assertTrue(window.allocRow()); + assertEquals(window.getType(0, 0), Cursor.FIELD_TYPE_NULL); + double db1 = 1.26; assertTrue(window.putDouble(db1, 0, 0)); + assertEquals(window.getType(0, 0), Cursor.FIELD_TYPE_FLOAT); double db2 = window.getDouble(0, 0); - assertEquals(db1, db2); + assertEquals(db1, db2, 0.01); + assertEquals(1, window.getInt(0, 0)); + assertEquals("1.26", window.getString(0, 0)); + assertThrows(SQLiteException.class, () -> window.getBlob(0, 0)); long int1 = Long.MAX_VALUE; assertTrue(window.putLong(int1, 0, 1)); + assertEquals(window.getType(0, 1), Cursor.FIELD_TYPE_INTEGER); long int2 = window.getLong(0, 1); assertEquals(int1, int2); assertTrue(window.putString("1198032740000", 0, 3)); + assertEquals(window.getType(0, 3), Cursor.FIELD_TYPE_STRING); assertEquals("1198032740000", window.getString(0, 3)); assertEquals(1198032740000L, window.getLong(0, 3)); @@ -97,13 +109,14 @@ public class CursorWindowTest extends TestCase implements PerformanceTestCase { assertTrue(window.putString(Double.toString(42.0), 0, 4)); assertEquals(Double.toString(42.0), window.getString(0, 4)); - assertEquals(42.0, window.getDouble(0, 4)); + assertEquals(42.0, window.getDouble(0, 4), 0.01); // put blob byte[] blob = new byte[1000]; byte value = 99; Arrays.fill(blob, value); assertTrue(window.putBlob(blob, 0, 6)); + assertEquals(window.getType(0, 6), Cursor.FIELD_TYPE_BLOB); assertTrue(Arrays.equals(blob, window.getBlob(0, 6))); } } diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt index 13908f1732e1..7744fcaf032a 100644 --- a/ravenwood/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/ravenwood-annotation-allowed-classes.txt @@ -90,6 +90,7 @@ android.database.ContentObserver android.database.Cursor android.database.CursorIndexOutOfBoundsException android.database.CursorJoiner +android.database.CursorWindow android.database.CursorWrapper android.database.DataSetObservable android.database.DataSetObserver @@ -97,6 +98,9 @@ android.database.MatrixCursor android.database.MatrixCursor$RowBuilder android.database.MergeCursor android.database.Observable +android.database.SQLException +android.database.sqlite.SQLiteClosable +android.database.sqlite.SQLiteException android.text.TextUtils android.text.TextUtils$SimpleStringSplitter diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp index 0e2e158c327e..4eac361d6e53 100644 --- a/tools/hoststubgen/hoststubgen/Android.bp +++ b/tools/hoststubgen/hoststubgen/Android.bp @@ -268,6 +268,9 @@ java_library_host { srcs: [ "helper-framework-runtime-src/**/*.java", ], + exclude_srcs: [ + "helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java", + ], libs: [ "hoststubgen-helper-runtime", "framework-all-hidden-api-host-impl", diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java new file mode 100644 index 000000000000..631fc0273c94 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2023 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 com.android.hoststubgen.nativesubstitution; + +import android.database.Cursor; +import android.database.sqlite.SQLiteException; +import android.util.Base64; + +import java.text.DecimalFormat; +import java.text.ParsePosition; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +public class CursorWindow_host { + + private static final HashMap<Long, CursorWindow_host> sInstances = new HashMap<>(); + private static long sNextId = 1; + + private int mColumnNum; + private static class Row { + String[] fields; + int[] types; + } + + private final List<Row> mRows = new ArrayList<>(); + + public static long nativeCreate(String name, int cursorWindowSize) { + CursorWindow_host instance = new CursorWindow_host(); + long instanceId = sNextId++; + sInstances.put(instanceId, instance); + return instanceId; + } + + public static void nativeDispose(long windowPtr) { + sInstances.remove(windowPtr); + } + + public static boolean nativeSetNumColumns(long windowPtr, int columnNum) { + sInstances.get(windowPtr).mColumnNum = columnNum; + return true; + } + + public static int nativeGetNumRows(long windowPtr) { + return sInstances.get(windowPtr).mRows.size(); + } + + public static boolean nativeAllocRow(long windowPtr) { + CursorWindow_host instance = sInstances.get(windowPtr); + Row row = new Row(); + row.fields = new String[instance.mColumnNum]; + row.types = new int[instance.mColumnNum]; + Arrays.fill(row.types, Cursor.FIELD_TYPE_NULL); + instance.mRows.add(row); + return true; + } + + private static boolean put(long windowPtr, String value, int type, int row, int column) { + CursorWindow_host instance = sInstances.get(windowPtr); + if (row >= instance.mRows.size() || column >= instance.mColumnNum) { + return false; + } + Row r = instance.mRows.get(row); + r.fields[column] = value; + r.types[column] = type; + return true; + } + + public static int nativeGetType(long windowPtr, int row, int column) { + CursorWindow_host instance = sInstances.get(windowPtr); + if (row >= instance.mRows.size() || column >= instance.mColumnNum) { + return Cursor.FIELD_TYPE_NULL; + } + + return instance.mRows.get(row).types[column]; + } + + public static boolean nativePutString(long windowPtr, String value, + int row, int column) { + return put(windowPtr, value, Cursor.FIELD_TYPE_STRING, row, column); + } + + public static String nativeGetString(long windowPtr, int row, int column) { + CursorWindow_host instance = sInstances.get(windowPtr); + if (row >= instance.mRows.size() || column >= instance.mColumnNum) { + return null; + } + + return instance.mRows.get(row).fields[column]; + } + + public static boolean nativePutLong(long windowPtr, long value, int row, int column) { + return put(windowPtr, Long.toString(value), Cursor.FIELD_TYPE_INTEGER, row, column); + } + + public static long nativeGetLong(long windowPtr, int row, int column) { + String value = nativeGetString(windowPtr, row, column); + if (value == null) { + return 0; + } + + Number number = new DecimalFormat().parse(value, new ParsePosition(0)); + return number == null ? 0 : number.longValue(); + } + + public static boolean nativePutDouble(long windowPtr, double value, int row, int column) { + return put(windowPtr, Double.toString(value), Cursor.FIELD_TYPE_FLOAT, row, column); + } + + public static double nativeGetDouble(long windowPtr, int row, int column) { + String value = nativeGetString(windowPtr, row, column); + if (value == null) { + return 0; + } + + Number number = new DecimalFormat().parse(value, new ParsePosition(0)); + return number == null ? 0 : number.doubleValue(); + } + + public static boolean nativePutBlob(long windowPtr, byte[] value, int row, int column) { + return put(windowPtr, value == null ? null : Base64.encodeToString(value, 0), + Cursor.FIELD_TYPE_BLOB, row, column); + } + + public static byte[] nativeGetBlob(long windowPtr, int row, int column) { + int type = nativeGetType(windowPtr, row, column); + switch (type) { + case Cursor.FIELD_TYPE_BLOB: { + String value = nativeGetString(windowPtr, row, column); + return value == null ? null : Base64.decode(value, 0); + } + case Cursor.FIELD_TYPE_STRING: { + String value = nativeGetString(windowPtr, row, column); + return value == null ? null : value.getBytes(); + } + case Cursor.FIELD_TYPE_FLOAT: + throw new SQLiteException(); + case Cursor.FIELD_TYPE_INTEGER: + throw new SQLiteException(); + case Cursor.FIELD_TYPE_NULL: + default: + return null; + } + } +} |