[Ravenwood] Start using HWUI native methods
Enable Matrix, Path and Interpolator for starters
Bug: 337110712
Bug: 337329128
Test: ./ravenwood/scripts/run-ravenwood-tests.sh
Test: atest CtsGraphicsTestCasesRavenwood
Test: atest CtsGraphicsTestCases
Change-Id: Iefa17f91837c5382067bf17fc148b158133a4de5
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 319f115..6d31578 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -465,6 +465,11 @@
* how pixels are stored. This affects the quality (color depth) as
* well as the ability to display transparent/translucent colors.
*/
+ // It's touched by Graphics.cpp, so we need to make this enum usable on Ravenwood.
+ // Otherwise, all the ctors would throw, which would make the class unloadable
+ // because the static initializer needs the enum members because of `sConfigs`.
+ // TODO: Remove it once we expose the outer class.
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public enum Config {
// these native values must match up with the enum in SkBitmap.h
diff --git a/graphics/java/android/graphics/Interpolator.java b/graphics/java/android/graphics/Interpolator.java
index 1045464..994fb2d 100644
--- a/graphics/java/android/graphics/Interpolator.java
+++ b/graphics/java/android/graphics/Interpolator.java
@@ -18,6 +18,9 @@
import android.os.SystemClock;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodClassLoadHook(
+ android.ravenwood.annotation.RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
public class Interpolator {
public Interpolator(int valueCount) {
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index bc58feb..fbb690c 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -28,6 +28,9 @@
/**
* The Matrix class holds a 3x3 matrix for transforming coordinates.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodClassLoadHook(
+ android.ravenwood.annotation.RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
public class Matrix {
public static final int MSCALE_X = 0; //!< use with getValues/setValues
@@ -229,7 +232,7 @@
private static class NoImagePreloadHolder {
public static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(
- Matrix.class.getClassLoader(), nGetNativeFinalizer());
+ Matrix.class.getClassLoader(), nGetNativeFinalizerWrapper());
}
private final long native_instance;
@@ -238,7 +241,7 @@
* Create an identity matrix
*/
public Matrix() {
- native_instance = nCreate(0);
+ native_instance = nCreateWrapper(0);
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
}
@@ -248,7 +251,7 @@
* @param src The matrix to copy into this matrix
*/
public Matrix(Matrix src) {
- native_instance = nCreate(src != null ? src.native_instance : 0);
+ native_instance = nCreateWrapper(src != null ? src.native_instance : 0);
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
}
@@ -846,6 +849,34 @@
return native_instance;
}
+ /**
+ * Wrapper method we use to switch to ExtraNatives.nCreate(src) only on Ravenwood.
+ *
+ * @see ExtraNatives
+ */
+ @android.ravenwood.annotation.RavenwoodReplace
+ private static long nCreateWrapper(long src) {
+ return nCreate(src);
+ }
+
+ private static long nCreateWrapper$ravenwood(long src) {
+ return ExtraNatives.nCreate(src);
+ }
+
+ /**
+ * Wrapper method we use to switch to ExtraNatives.nGetNativeFinalizer(src) only on Ravenwood.
+ *
+ * @see ExtraNatives
+ */
+ @android.ravenwood.annotation.RavenwoodReplace
+ private static long nGetNativeFinalizerWrapper() {
+ return nGetNativeFinalizer();
+ }
+
+ private static long nGetNativeFinalizerWrapper$ravenwood() {
+ return ExtraNatives.nGetNativeFinalizer();
+ }
+
// ------------------ Regular JNI ------------------------
private static native long nCreate(long nSrc_or_zero);
@@ -943,4 +974,25 @@
private static native float nMapRadius(long nObject, float radius);
@CriticalNative
private static native boolean nEquals(long nA, long nB);
+
+ /**
+ * Due to b/337329128, native methods that are called by the static initializers cannot be
+ * in the same class when running on a host side JVM (such as on Ravenwood and Android Studio).
+ *
+ * There are two methods that are called by the static initializers (either directly or
+ * indirectly) in this class, namely nCreate() and nGetNativeFinalizer(). On Ravenwood
+ * these methods can't be on the Matrix class itself, so we use a nested class to host them.
+ *
+ * We still keep the original nCreate() method and call it on non-ravenwood environment,
+ * in order to avoid problems in downstream (such as Android Studio).
+ *
+ * @see #nCreateWrapper(long)
+ * @see #nGetNativeFinalizerWrapper()
+ *
+ * TODO(b/337110712) Clean it up somehow. (remove the original nCreate() and unify the code?)
+ */
+ private static class ExtraNatives {
+ static native long nCreate(long nSrc_or_zero);
+ static native long nGetNativeFinalizer();
+ }
}
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index deb89fa..073307c 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -36,11 +36,16 @@
* (based on the paint's Style), or it can be used for clipping or to draw
* text on a path.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodClassLoadHook(
+ android.ravenwood.annotation.RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
public class Path {
-
- private static final NativeAllocationRegistry sRegistry =
- NativeAllocationRegistry.createMalloced(
- Path.class.getClassLoader(), nGetFinalizer());
+ // See b/337329128 for why we need an inner class here.
+ private static class NoImagePreloadHolder {
+ static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ Path.class.getClassLoader(), nGetFinalizer());
+ }
/**
* @hide
@@ -52,7 +57,7 @@
*/
public Path() {
mNativePath = nInit();
- sRegistry.registerNativeAllocation(this, mNativePath);
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePath);
}
/**
@@ -62,7 +67,7 @@
*/
public Path(@Nullable Path src) {
mNativePath = nInit(src != null ? src.mNativePath : 0);
- sRegistry.registerNativeAllocation(this, mNativePath);
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePath);
}
/**
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index 8315c4c..07e97f8 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -211,11 +211,7 @@
static jfieldID gRegion_nativeInstanceID;
static jmethodID gRegion_constructorMethodID;
-static jclass gByte_class;
-static jobject gVMRuntime;
-static jclass gVMRuntime_class;
-static jmethodID gVMRuntime_newNonMovableArray;
-static jmethodID gVMRuntime_addressOf;
+static jclass gByte_class;
static jclass gColorSpace_class;
static jmethodID gColorSpace_getMethodID;
@@ -789,13 +785,6 @@
gByte_class = (jclass) env->NewGlobalRef(
env->GetStaticObjectField(c, env->GetStaticFieldID(c, "TYPE", "Ljava/lang/Class;")));
- gVMRuntime_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "dalvik/system/VMRuntime"));
- m = env->GetStaticMethodID(gVMRuntime_class, "getRuntime", "()Ldalvik/system/VMRuntime;");
- gVMRuntime = env->NewGlobalRef(env->CallStaticObjectMethod(gVMRuntime_class, m));
- gVMRuntime_newNonMovableArray = GetMethodIDOrDie(env, gVMRuntime_class, "newNonMovableArray",
- "(Ljava/lang/Class;I)Ljava/lang/Object;");
- gVMRuntime_addressOf = GetMethodIDOrDie(env, gVMRuntime_class, "addressOf", "(Ljava/lang/Object;)J");
-
gColorSpace_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ColorSpace"));
gColorSpace_getMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class,
"get", "(Landroid/graphics/ColorSpace$Named;)Landroid/graphics/ColorSpace;");
diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp
index ca667b0..c0d791a 100644
--- a/libs/hwui/jni/android_graphics_Matrix.cpp
+++ b/libs/hwui/jni/android_graphics_Matrix.cpp
@@ -376,11 +376,24 @@
{"nEquals", "(JJ)Z", (void*) SkMatrixGlue::equals}
};
+static const JNINativeMethod extra_methods[] = {
+ {"nGetNativeFinalizer", "()J", (void*)SkMatrixGlue::getNativeFinalizer},
+ {"nCreate", "(J)J", (void*)SkMatrixGlue::create},
+};
+
static jclass sClazz;
static jfieldID sNativeInstanceField;
static jmethodID sCtor;
int register_android_graphics_Matrix(JNIEnv* env) {
+ // Methods only used on Ravenwood (for now). See the javadoc on Matrix$ExtraNativesx
+ // for why we need it.
+ //
+ // We don't need it on non-ravenwood, but we don't (yet) have a way to detect ravenwood
+ // environment, so we just always run it.
+ RegisterMethodsOrDie(env, "android/graphics/Matrix$ExtraNatives", extra_methods,
+ NELEM(extra_methods));
+
int result = RegisterMethodsOrDie(env, "android/graphics/Matrix", methods, NELEM(methods));
jclass clazz = FindClassOrDie(env, "android/graphics/Matrix");
diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
index 7dc197e..7a3142b 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
@@ -28,8 +28,10 @@
* Add this with a fully-specified method name (e.g. {@code "com.package.Class.methodName"})
* of a callback to get a callback at the class load time.
*
- * The method must be {@code public static} with a single argument that takes
- * {@link Class}.
+ * The method must be {@code public static} with a single argument that takes {@link Class}.
+ *
+ * Typically, this is used with {@link #LIBANDROID_LOADING_HOOK}, which will load the necessary
+ * native libraries.
*
* @hide
*/
@@ -37,4 +39,10 @@
@Retention(RetentionPolicy.CLASS)
public @interface RavenwoodClassLoadHook {
String value();
+
+ /**
+ * Class load hook that loads <code>libandroid_runtime</code>.
+ */
+ public static String LIBANDROID_LOADING_HOOK
+ = "com.android.platform.test.ravenwood.runtimehelper.ClassLoadHook.onClassLoaded";
}
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
index 9057d16..96b7057 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
@@ -44,6 +44,14 @@
public static final String LIBANDROID_RUNTIME_NAME = "android_runtime";
+ /**
+ * Extra strings needed to pass to register_android_graphics_classes().
+ *
+ * `android.graphics.Graphics` is not actually a class, so we can't use the same initialization
+ * strategy than the "normal" classes. So we just hardcode it here.
+ */
+ public static final String GRAPHICS_EXTRA_INIT_PARAMS = ",android.graphics.Graphics";
+
private static String sInitialDir = new File("").getAbsolutePath();
static {
@@ -98,7 +106,6 @@
private static void loadFrameworkNativeCode() {
// This is called from class-initializers, so no synchronization is needed.
if (sLoadFrameworkNativeCodeCalled) {
- // This method has already been called before.s
return;
}
sLoadFrameworkNativeCodeCalled = true;
@@ -112,7 +119,8 @@
}
if (SKIP_LOADING_LIBANDROID) {
- log("Skip loading " + LIBANDROID_RUNTIME_NAME);
+ log("Skip loading native runtime.");
+ return;
}
// Make sure these properties are not set.
@@ -121,27 +129,39 @@
ensurePropertyNotSet(KEYBOARD_PATHS);
ensurePropertyNotSet(GRAPHICS_NATIVE_CLASSES);
- // Tell libandroid what JNI to use.
- final var jniClasses = getCoreNativeClassesToUse();
- if (jniClasses.isEmpty()) {
- log("No classes require JNI methods, skip loading " + LIBANDROID_RUNTIME_NAME);
+ // Load the libraries, if needed.
+ final var libanrdoidClasses = getClassesWithNativeMethods(sLibandroidClasses);
+ final var libhwuiClasses = getClassesWithNativeMethods(sLibhwuiClasses);
+ if (libanrdoidClasses.isEmpty() && libhwuiClasses.isEmpty()) {
+ log("No classes require JNI methods, skip loading native runtime.");
return;
}
- setProperty(CORE_NATIVE_CLASSES, jniClasses);
- setProperty(GRAPHICS_NATIVE_CLASSES, "");
+ setProperty(CORE_NATIVE_CLASSES, libanrdoidClasses);
+ setProperty(GRAPHICS_NATIVE_CLASSES, libhwuiClasses + GRAPHICS_EXTRA_INIT_PARAMS);
+ log("Loading " + LIBANDROID_RUNTIME_NAME + " for '" + libanrdoidClasses + "' and '"
+ + libhwuiClasses + "'");
RavenwoodUtils.loadJniLibrary(LIBANDROID_RUNTIME_NAME);
}
/**
- * Classes with native methods that are backed by `libandroid_runtime`.
+ * Classes with native methods that are backed by libandroid_runtime.
*
- * At runtime, we check if these classes have any methods, and if so, we'll have
- * `libandroid_runtime` register the native functions.
+ * See frameworks/base/core/jni/platform/host/HostRuntime.cpp
*/
- private static final Class<?>[] sClassesWithLibandroidNativeMethods = {
+ private static final Class<?>[] sLibandroidClasses = {
android.util.Log.class,
- android.os.Parcel.class,
+ };
+
+ /**
+ * Classes with native methods that are backed by libhwui.
+ *
+ * See frameworks/base/libs/hwui/apex/LayoutlibLoader.cpp
+ */
+ private static final Class<?>[] sLibhwuiClasses = {
+ android.graphics.Interpolator.class,
+ android.graphics.Matrix.class,
+ android.graphics.Path.class,
};
/**
@@ -157,17 +177,15 @@
}
/**
- * Create a list of classes as comma-separated that require JNI methods to be set up.
- *
- * <p>This list is used by frameworks/base/core/jni/LayoutlibLoader.cpp to decide
- * what JNI methods to set up.
+ * Create a list of classes as comma-separated that require JNI methods to be set up from
+ * a given class list, ignoring classes with no native methods.
*/
- private static String getCoreNativeClassesToUse() {
+ private static String getClassesWithNativeMethods(Class<?>[] classes) {
final var coreNativeClassesToLoad = new ArrayList<String>();
- for (var clazz : sClassesWithLibandroidNativeMethods) {
+ for (var clazz : classes) {
if (hasNativeMethod(clazz)) {
- log("Class %s has native methods", clazz);
+ log("Class %s has native methods", clazz.getCanonicalName());
coreNativeClassesToLoad.add(clazz.getName());
}
}
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index 243e224..e452299 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -236,7 +236,11 @@
android.accounts.Account
+android.graphics.Bitmap$Config
android.graphics.Insets
+android.graphics.Interpolator
+android.graphics.Matrix
+android.graphics.Path
android.graphics.Point
android.graphics.PointF
android.graphics.Rect