summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Dmitri Plotnikov <dplotnikov@google.com> 2023-12-12 18:33:42 -0800
committer Dmitri Plotnikov <dplotnikov@google.com> 2023-12-15 12:33:38 -0800
commit62b54d65987ea65b2b6fdb8afa028f850bf52d87 (patch)
treecfb95c01501dfe132ee7f04c8591d20dc6a43570
parent556f2734cce34e64a2b3a377284f3434aa12747d (diff)
Enable CursorWindow for Ravenwood
Bug: 314797745 Test: atest FrameworksCoreTestsRavenwood Test: atest FrameworksCoreTests Change-Id: I118b6ba36bdc85d5b712aed0f0e80bb9a9fc3125
-rw-r--r--core/java/android/database/CursorWindow.java31
-rw-r--r--core/java/android/database/SQLException.java1
-rw-r--r--core/java/android/database/sqlite/SQLiteClosable.java1
-rw-r--r--core/java/android/database/sqlite/SQLiteException.java1
-rw-r--r--core/tests/coretests/Android.bp4
-rw-r--r--core/tests/coretests/src/android/database/CursorWindowPerformanceTest.java64
-rw-r--r--core/tests/coretests/src/android/database/CursorWindowTest.java49
-rw-r--r--ravenwood/ravenwood-annotation-allowed-classes.txt4
-rw-r--r--tools/hoststubgen/hoststubgen/Android.bp3
-rw-r--r--tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java159
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;
+ }
+ }
+}