Merge "Implements a basic version of AndroidRuntime for host" into main
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index 30c926c..7e2a5ac 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -17,6 +17,8 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android/graphics/jni_runtime.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <jni_wrappers.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/jni_macros.h>
 #include <unicode/putil.h>
@@ -27,9 +29,6 @@
 #include <unordered_map>
 #include <vector>
 
-#include "android_view_InputDevice.h"
-#include "core_jni_helpers.h"
-#include "jni.h"
 #ifdef _WIN32
 #include <windows.h>
 #else
@@ -38,8 +37,6 @@
 #include <sys/stat.h>
 #endif
 
-#include <iostream>
-
 using namespace std;
 
 /*
@@ -49,12 +46,6 @@
  * (see AndroidRuntime.cpp).
  */
 
-static JavaVM* javaVM;
-static jclass bridge;
-static jclass layoutLog;
-static jmethodID getLogId;
-static jmethodID logMethodId;
-
 extern int register_android_os_Binder(JNIEnv* env);
 extern int register_libcore_util_NativeAllocationRegistry_Delegate(JNIEnv* env);
 
@@ -168,28 +159,9 @@
         }
     }
 
-    if (register_android_graphics_classes(env) < 0) {
-        return -1;
-    }
-
     return 0;
 }
 
-int AndroidRuntime::registerNativeMethods(JNIEnv* env, const char* className,
-                                          const JNINativeMethod* gMethods, int numMethods) {
-    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
-}
-
-JNIEnv* AndroidRuntime::getJNIEnv() {
-    JNIEnv* env;
-    if (javaVM->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) return nullptr;
-    return env;
-}
-
-JavaVM* AndroidRuntime::getJavaVM() {
-    return javaVM;
-}
-
 static vector<string> parseCsv(const string& csvString) {
     vector<string> result;
     istringstream stream(csvString);
@@ -200,29 +172,6 @@
     return result;
 }
 
-void LayoutlibLogger(base::LogId, base::LogSeverity severity, const char* tag, const char* file,
-                     unsigned int line, const char* message) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
-    jint logPrio = severity;
-    jstring tagString = env->NewStringUTF(tag);
-    jstring messageString = env->NewStringUTF(message);
-
-    jobject bridgeLog = env->CallStaticObjectMethod(bridge, getLogId);
-
-    env->CallVoidMethod(bridgeLog, logMethodId, logPrio, tagString, messageString);
-
-    env->DeleteLocalRef(tagString);
-    env->DeleteLocalRef(messageString);
-    env->DeleteLocalRef(bridgeLog);
-}
-
-void LayoutlibAborter(const char* abort_message) {
-    // Layoutlib should not call abort() as it would terminate Studio.
-    // Throw an exception back to Java instead.
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
-    jniThrowRuntimeException(env, "The Android framework has encountered a fatal error");
-}
-
 // This method has been copied/adapted from system/core/init/property_service.cpp
 // If the ro.product.cpu.abilist* properties have not been explicitly
 // set, derive them from ro.system.product.cpu.abilist* properties.
@@ -311,62 +260,49 @@
 #endif
 }
 
-static bool init_icu(const char* dataPath) {
-    void* addr = mmapFile(dataPath);
-    UErrorCode err = U_ZERO_ERROR;
-    udata_setCommonData(addr, &err);
-    if (err != U_ZERO_ERROR) {
-        return false;
-    }
-    return true;
-}
-
-// Creates an array of InputDevice from key character map files
-static void init_keyboard(JNIEnv* env, const vector<string>& keyboardPaths) {
-    jclass inputDevice = FindClassOrDie(env, "android/view/InputDevice");
-    jobjectArray inputDevicesArray =
-            env->NewObjectArray(keyboardPaths.size(), inputDevice, nullptr);
-    int keyboardId = 1;
-
-    for (const string& path : keyboardPaths) {
-        base::Result<std::shared_ptr<KeyCharacterMap>> charMap =
-                KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
-
-        InputDeviceInfo info = InputDeviceInfo();
-        info.initialize(keyboardId, 0, 0, InputDeviceIdentifier(),
-                        "keyboard " + std::to_string(keyboardId), true, false,
-                        ui::LogicalDisplayId::DEFAULT);
-        info.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-        info.setKeyCharacterMap(*charMap);
-
-        jobject inputDeviceObj = android_view_InputDevice_create(env, info);
-        if (inputDeviceObj) {
-            env->SetObjectArrayElement(inputDevicesArray, keyboardId - 1, inputDeviceObj);
-            env->DeleteLocalRef(inputDeviceObj);
+// Loads the ICU data file from the location specified in the system property ro.icu.data.path
+static void loadIcuData() {
+    string icuPath = base::GetProperty("ro.icu.data.path", "");
+    if (!icuPath.empty()) {
+        // Set the location of ICU data
+        void* addr = mmapFile(icuPath.c_str());
+        UErrorCode err = U_ZERO_ERROR;
+        udata_setCommonData(addr, &err);
+        if (err != U_ZERO_ERROR) {
+            ALOGE("Unable to load ICU data\n");
         }
-        keyboardId++;
     }
-
-    if (bridge == nullptr) {
-        bridge = FindClassOrDie(env, "com/android/layoutlib/bridge/Bridge");
-        bridge = MakeGlobalRefOrDie(env, bridge);
-    }
-    jmethodID setInputManager = GetStaticMethodIDOrDie(env, bridge, "setInputManager",
-                                                       "([Landroid/view/InputDevice;)V");
-    env->CallStaticVoidMethod(bridge, setInputManager, inputDevicesArray);
-    env->DeleteLocalRef(inputDevicesArray);
 }
 
-} // namespace android
+static int register_android_core_classes(JNIEnv* env) {
+    jclass system = FindClassOrDie(env, "java/lang/System");
+    jmethodID getPropertyMethod =
+            GetStaticMethodIDOrDie(env, system, "getProperty",
+                                   "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
 
-using namespace android;
+    // Get the names of classes that need to register their native methods
+    auto nativesClassesJString =
+            (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+                                                 env->NewStringUTF("core_native_classes"),
+                                                 env->NewStringUTF(""));
+    const char* nativesClassesArray = env->GetStringUTFChars(nativesClassesJString, nullptr);
+    string nativesClassesString(nativesClassesArray);
+    vector<string> classesToRegister = parseCsv(nativesClassesString);
+    env->ReleaseStringUTFChars(nativesClassesJString, nativesClassesArray);
+
+    if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) {
+        return JNI_ERR;
+    }
+
+    return 0;
+}
 
 // Called right before aborting by LOG_ALWAYS_FATAL. Print the pending exception.
 void abort_handler(const char* abort_message) {
     ALOGE("About to abort the process...");
 
-    JNIEnv* env = NULL;
-    if (javaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    if (env == nullptr) {
         ALOGE("vm->GetEnv() failed");
         return;
     }
@@ -377,107 +313,98 @@
     ALOGE("Aborting because: %s", abort_message);
 }
 
+// ------------------ Host implementation of AndroidRuntime ------------------
+
+/*static*/ JavaVM* AndroidRuntime::mJavaVM;
+
+/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env, const char* className,
+                                                     const JNINativeMethod* gMethods,
+                                                     int numMethods) {
+    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
+}
+
+/*static*/ JNIEnv* AndroidRuntime::getJNIEnv() {
+    JNIEnv* env;
+    JavaVM* vm = AndroidRuntime::getJavaVM();
+    if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
+        return nullptr;
+    }
+    return env;
+}
+
+/*static*/ JavaVM* AndroidRuntime::getJavaVM() {
+    return mJavaVM;
+}
+
+/*static*/ int AndroidRuntime::startReg(JNIEnv* env) {
+    if (register_android_core_classes(env) < 0) {
+        return JNI_ERR;
+    }
+    if (register_android_graphics_classes(env) < 0) {
+        return JNI_ERR;
+    }
+    return 0;
+}
+
+void AndroidRuntime::onVmCreated(JNIEnv* env) {
+    env->GetJavaVM(&mJavaVM);
+}
+
+void AndroidRuntime::onStarted() {
+    property_initialize_ro_cpu_abilist();
+    loadIcuData();
+
+    // Use English locale for number format to ensure correct parsing of floats when using strtof
+    setlocale(LC_NUMERIC, "en_US.UTF-8");
+}
+
+void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    // Register native functions.
+    if (startReg(env) < 0) {
+        ALOGE("Unable to register all android native methods\n");
+    }
+    onStarted();
+}
+
+AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength)
+      : mExitWithoutCleanup(false), mArgBlockStart(argBlockStart), mArgBlockLength(argBlockLength) {
+    init_android_graphics();
+}
+
+AndroidRuntime::~AndroidRuntime() {}
+
+// Version of AndroidRuntime to run on host
+class HostRuntime : public AndroidRuntime {
+public:
+    HostRuntime() : AndroidRuntime(nullptr, 0) {}
+
+    void onVmCreated(JNIEnv* env) override {
+        AndroidRuntime::onVmCreated(env);
+        // initialize logging, so ANDROD_LOG_TAGS env variable is respected
+        android::base::InitLogging(nullptr, android::base::StderrLogger, abort_handler);
+    }
+
+    void onStarted() override {
+        AndroidRuntime::onStarted();
+    }
+};
+
+} // namespace android
+
+using namespace android;
+
 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
-    javaVM = vm;
     JNIEnv* env = nullptr;
     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
         return JNI_ERR;
     }
 
-    __android_log_set_aborter(abort_handler);
+    Vector<String8> args;
+    HostRuntime runtime;
 
-    init_android_graphics();
-
-    // Configuration is stored as java System properties.
-    // Get a reference to System.getProperty
-    jclass system = FindClassOrDie(env, "java/lang/System");
-    jmethodID getPropertyMethod =
-            GetStaticMethodIDOrDie(env, system, "getProperty",
-                                   "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
-
-    // Java system properties that contain LayoutLib config. The initial values in the map
-    // are the default values if the property is not specified.
-    std::unordered_map<std::string, std::string> systemProperties =
-            {{"core_native_classes", ""},
-             {"register_properties_during_load", ""},
-             {"icu.data.path", ""},
-             {"use_bridge_for_logging", ""},
-             {"keyboard_paths", ""}};
-
-    for (auto& [name, defaultValue] : systemProperties) {
-        jstring propertyString =
-                (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
-                                                     env->NewStringUTF(name.c_str()),
-                                                     env->NewStringUTF(defaultValue.c_str()));
-        const char* propertyChars = env->GetStringUTFChars(propertyString, 0);
-        systemProperties[name] = string(propertyChars);
-        env->ReleaseStringUTFChars(propertyString, propertyChars);
-    }
-    // Get the names of classes that need to register their native methods
-    vector<string> classesToRegister = parseCsv(systemProperties["core_native_classes"]);
-
-    if (systemProperties["register_properties_during_load"] == "true") {
-        // Set the system properties first as they could be used in the static initialization of
-        // other classes
-        if (register_android_os_SystemProperties(env) < 0) {
-            return JNI_ERR;
-        }
-        classesToRegister.erase(find(classesToRegister.begin(), classesToRegister.end(),
-                                     "android.os.SystemProperties"));
-        bridge = FindClassOrDie(env, "com/android/layoutlib/bridge/Bridge");
-        bridge = MakeGlobalRefOrDie(env, bridge);
-        jmethodID setSystemPropertiesMethod =
-                GetStaticMethodIDOrDie(env, bridge, "setSystemProperties", "()V");
-        env->CallStaticVoidMethod(bridge, setSystemPropertiesMethod);
-        property_initialize_ro_cpu_abilist();
-    }
-
-    if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) {
-        return JNI_ERR;
-    }
-
-    if (!systemProperties["icu.data.path"].empty()) {
-        // Set the location of ICU data
-        bool icuInitialized = init_icu(systemProperties["icu.data.path"].c_str());
-        if (!icuInitialized) {
-            return JNI_ERR;
-        }
-    }
-
-    if (systemProperties["use_bridge_for_logging"] == "true") {
-        layoutLog = FindClassOrDie(env, "com/android/ide/common/rendering/api/ILayoutLog");
-        layoutLog = MakeGlobalRefOrDie(env, layoutLog);
-        logMethodId = GetMethodIDOrDie(env, layoutLog, "logAndroidFramework",
-                                       "(ILjava/lang/String;Ljava/lang/String;)V");
-        if (bridge == nullptr) {
-            bridge = FindClassOrDie(env, "com/android/layoutlib/bridge/Bridge");
-            bridge = MakeGlobalRefOrDie(env, bridge);
-        }
-        getLogId = GetStaticMethodIDOrDie(env, bridge, "getLog",
-                                          "()Lcom/android/ide/common/rendering/api/ILayoutLog;");
-        android::base::SetLogger(LayoutlibLogger);
-        android::base::SetAborter(LayoutlibAborter);
-    } else {
-        // initialize logging, so ANDROD_LOG_TAGS env variable is respected
-        android::base::InitLogging(nullptr, android::base::StderrLogger);
-    }
-
-    // Use English locale for number format to ensure correct parsing of floats when using strtof
-    setlocale(LC_NUMERIC, "en_US.UTF-8");
-
-    if (!systemProperties["keyboard_paths"].empty()) {
-        vector<string> keyboardPaths = parseCsv(systemProperties["keyboard_paths"]);
-        init_keyboard(env, keyboardPaths);
-    } else {
-        fprintf(stderr, "Skip initializing keyboard\n");
-    }
+    runtime.onVmCreated(env);
+    runtime.start("HostRuntime", args, false);
 
     return JNI_VERSION_1_6;
 }
-
-JNIEXPORT void JNI_OnUnload(JavaVM* vm, void*) {
-    JNIEnv* env = nullptr;
-    vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
-    env->DeleteGlobalRef(bridge);
-    env->DeleteGlobalRef(layoutLog);
-}