[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