xref: /aosp_15_r20/frameworks/layoutlib/jni/LayoutlibLoader.cpp (revision fc3927be90a325f95c74a9043993a80ef388dc46)
1*fc3927beSAndroid Build Coastguard Worker /*
2*fc3927beSAndroid Build Coastguard Worker  * Copyright 2024 The Android Open Source Project
3*fc3927beSAndroid Build Coastguard Worker  *
4*fc3927beSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*fc3927beSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*fc3927beSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*fc3927beSAndroid Build Coastguard Worker  *
8*fc3927beSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*fc3927beSAndroid Build Coastguard Worker  *
10*fc3927beSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*fc3927beSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*fc3927beSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*fc3927beSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*fc3927beSAndroid Build Coastguard Worker  * limitations under the License.
15*fc3927beSAndroid Build Coastguard Worker  */
16*fc3927beSAndroid Build Coastguard Worker 
17*fc3927beSAndroid Build Coastguard Worker #include <android-base/logging.h>
18*fc3927beSAndroid Build Coastguard Worker #include <android-base/properties.h>
19*fc3927beSAndroid Build Coastguard Worker #include <android_runtime/AndroidRuntime.h>
20*fc3927beSAndroid Build Coastguard Worker #include <android_view_InputDevice.h>
21*fc3927beSAndroid Build Coastguard Worker #include <jni_wrappers.h>
22*fc3927beSAndroid Build Coastguard Worker 
23*fc3927beSAndroid Build Coastguard Worker #include <clocale>
24*fc3927beSAndroid Build Coastguard Worker #include <sstream>
25*fc3927beSAndroid Build Coastguard Worker #include <unordered_map>
26*fc3927beSAndroid Build Coastguard Worker #include <vector>
27*fc3927beSAndroid Build Coastguard Worker 
28*fc3927beSAndroid Build Coastguard Worker using namespace std;
29*fc3927beSAndroid Build Coastguard Worker 
30*fc3927beSAndroid Build Coastguard Worker static jclass bridge;
31*fc3927beSAndroid Build Coastguard Worker static jclass layoutLog;
32*fc3927beSAndroid Build Coastguard Worker static jmethodID getLogId;
33*fc3927beSAndroid Build Coastguard Worker static jmethodID logMethodId;
34*fc3927beSAndroid Build Coastguard Worker 
35*fc3927beSAndroid Build Coastguard Worker namespace android {
36*fc3927beSAndroid Build Coastguard Worker 
37*fc3927beSAndroid Build Coastguard Worker extern int register_android_view_LayoutlibRenderer(JNIEnv* env);
38*fc3927beSAndroid Build Coastguard Worker 
39*fc3927beSAndroid Build Coastguard Worker #define REG_JNI(name) \
40*fc3927beSAndroid Build Coastguard Worker     { name }
41*fc3927beSAndroid Build Coastguard Worker struct RegJNIRec {
42*fc3927beSAndroid Build Coastguard Worker     int (*mProc)(JNIEnv*);
43*fc3927beSAndroid Build Coastguard Worker };
44*fc3927beSAndroid Build Coastguard Worker 
45*fc3927beSAndroid Build Coastguard Worker static const RegJNIRec gRegJNI[] = {
46*fc3927beSAndroid Build Coastguard Worker         REG_JNI(register_android_view_LayoutlibRenderer),
47*fc3927beSAndroid Build Coastguard Worker };
48*fc3927beSAndroid Build Coastguard Worker 
register_jni_procs(JNIEnv * env)49*fc3927beSAndroid Build Coastguard Worker int register_jni_procs(JNIEnv* env) {
50*fc3927beSAndroid Build Coastguard Worker     for (size_t i = 0; i < NELEM(android::gRegJNI); i++) {
51*fc3927beSAndroid Build Coastguard Worker         if (android::gRegJNI[i].mProc(env) < 0) {
52*fc3927beSAndroid Build Coastguard Worker             return -1;
53*fc3927beSAndroid Build Coastguard Worker         }
54*fc3927beSAndroid Build Coastguard Worker     }
55*fc3927beSAndroid Build Coastguard Worker 
56*fc3927beSAndroid Build Coastguard Worker     return 0;
57*fc3927beSAndroid Build Coastguard Worker }
58*fc3927beSAndroid Build Coastguard Worker 
parseCsv(const string & csvString)59*fc3927beSAndroid Build Coastguard Worker static vector<string> parseCsv(const string& csvString) {
60*fc3927beSAndroid Build Coastguard Worker     vector<string> result;
61*fc3927beSAndroid Build Coastguard Worker     istringstream stream(csvString);
62*fc3927beSAndroid Build Coastguard Worker     string segment;
63*fc3927beSAndroid Build Coastguard Worker     while (getline(stream, segment, ',')) {
64*fc3927beSAndroid Build Coastguard Worker         result.push_back(segment);
65*fc3927beSAndroid Build Coastguard Worker     }
66*fc3927beSAndroid Build Coastguard Worker     return result;
67*fc3927beSAndroid Build Coastguard Worker }
68*fc3927beSAndroid Build Coastguard Worker 
69*fc3927beSAndroid Build Coastguard Worker // Creates an array of InputDevice from key character map files
init_keyboard(const vector<string> & keyboardPaths)70*fc3927beSAndroid Build Coastguard Worker static void init_keyboard(const vector<string>& keyboardPaths) {
71*fc3927beSAndroid Build Coastguard Worker     JNIEnv* env = AndroidRuntime::getJNIEnv();
72*fc3927beSAndroid Build Coastguard Worker     jclass inputDevice = FindClassOrDie(env, "android/view/InputDevice");
73*fc3927beSAndroid Build Coastguard Worker     jobjectArray inputDevicesArray =
74*fc3927beSAndroid Build Coastguard Worker             env->NewObjectArray(keyboardPaths.size(), inputDevice, nullptr);
75*fc3927beSAndroid Build Coastguard Worker     int keyboardId = 1;
76*fc3927beSAndroid Build Coastguard Worker 
77*fc3927beSAndroid Build Coastguard Worker     for (const string& path : keyboardPaths) {
78*fc3927beSAndroid Build Coastguard Worker         base::Result<std::unique_ptr<KeyCharacterMap>> charMap =
79*fc3927beSAndroid Build Coastguard Worker                 KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
80*fc3927beSAndroid Build Coastguard Worker 
81*fc3927beSAndroid Build Coastguard Worker         InputDeviceInfo info = InputDeviceInfo();
82*fc3927beSAndroid Build Coastguard Worker         info.initialize(keyboardId, 0, 0, InputDeviceIdentifier(),
83*fc3927beSAndroid Build Coastguard Worker                         "keyboard " + std::to_string(keyboardId), true, false,
84*fc3927beSAndroid Build Coastguard Worker                         ui::LogicalDisplayId::DEFAULT);
85*fc3927beSAndroid Build Coastguard Worker         info.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
86*fc3927beSAndroid Build Coastguard Worker         info.setKeyCharacterMap(std::move(*charMap));
87*fc3927beSAndroid Build Coastguard Worker 
88*fc3927beSAndroid Build Coastguard Worker         jobject inputDeviceObj = android_view_InputDevice_create(env, info);
89*fc3927beSAndroid Build Coastguard Worker         if (inputDeviceObj) {
90*fc3927beSAndroid Build Coastguard Worker             env->SetObjectArrayElement(inputDevicesArray, keyboardId - 1, inputDeviceObj);
91*fc3927beSAndroid Build Coastguard Worker             env->DeleteLocalRef(inputDeviceObj);
92*fc3927beSAndroid Build Coastguard Worker         }
93*fc3927beSAndroid Build Coastguard Worker         keyboardId++;
94*fc3927beSAndroid Build Coastguard Worker     }
95*fc3927beSAndroid Build Coastguard Worker 
96*fc3927beSAndroid Build Coastguard Worker     if (bridge == nullptr) {
97*fc3927beSAndroid Build Coastguard Worker         bridge = FindClassOrDie(env, "com/android/layoutlib/bridge/Bridge");
98*fc3927beSAndroid Build Coastguard Worker         bridge = MakeGlobalRefOrDie(env, bridge);
99*fc3927beSAndroid Build Coastguard Worker     }
100*fc3927beSAndroid Build Coastguard Worker     jmethodID setInputManager = GetStaticMethodIDOrDie(env, bridge, "setInputManager",
101*fc3927beSAndroid Build Coastguard Worker                                                        "([Landroid/view/InputDevice;)V");
102*fc3927beSAndroid Build Coastguard Worker     env->CallStaticVoidMethod(bridge, setInputManager, inputDevicesArray);
103*fc3927beSAndroid Build Coastguard Worker     env->DeleteLocalRef(inputDevicesArray);
104*fc3927beSAndroid Build Coastguard Worker }
105*fc3927beSAndroid Build Coastguard Worker 
LayoutlibLogger(base::LogId,base::LogSeverity severity,const char * tag,const char * file,unsigned int line,const char * message)106*fc3927beSAndroid Build Coastguard Worker void LayoutlibLogger(base::LogId, base::LogSeverity severity, const char* tag, const char* file,
107*fc3927beSAndroid Build Coastguard Worker                      unsigned int line, const char* message) {
108*fc3927beSAndroid Build Coastguard Worker     JNIEnv* env = AndroidRuntime::getJNIEnv();
109*fc3927beSAndroid Build Coastguard Worker     jint logPrio = severity;
110*fc3927beSAndroid Build Coastguard Worker     jstring tagString = env->NewStringUTF(tag);
111*fc3927beSAndroid Build Coastguard Worker     jstring messageString = env->NewStringUTF(message);
112*fc3927beSAndroid Build Coastguard Worker 
113*fc3927beSAndroid Build Coastguard Worker     jobject bridgeLog = env->CallStaticObjectMethod(bridge, getLogId);
114*fc3927beSAndroid Build Coastguard Worker 
115*fc3927beSAndroid Build Coastguard Worker     env->CallVoidMethod(bridgeLog, logMethodId, logPrio, tagString, messageString);
116*fc3927beSAndroid Build Coastguard Worker 
117*fc3927beSAndroid Build Coastguard Worker     env->DeleteLocalRef(tagString);
118*fc3927beSAndroid Build Coastguard Worker     env->DeleteLocalRef(messageString);
119*fc3927beSAndroid Build Coastguard Worker     env->DeleteLocalRef(bridgeLog);
120*fc3927beSAndroid Build Coastguard Worker }
121*fc3927beSAndroid Build Coastguard Worker 
LayoutlibAborter(const char * abort_message)122*fc3927beSAndroid Build Coastguard Worker void LayoutlibAborter(const char* abort_message) {
123*fc3927beSAndroid Build Coastguard Worker     // Layoutlib should not call abort() as it would terminate Studio.
124*fc3927beSAndroid Build Coastguard Worker     // Throw an exception back to Java instead.
125*fc3927beSAndroid Build Coastguard Worker     JNIEnv* env = AndroidRuntime::getJNIEnv();
126*fc3927beSAndroid Build Coastguard Worker     jniThrowRuntimeException(env, "The Android framework has encountered a fatal error");
127*fc3927beSAndroid Build Coastguard Worker }
128*fc3927beSAndroid Build Coastguard Worker 
129*fc3927beSAndroid Build Coastguard Worker class LayoutlibRuntime : public AndroidRuntime {
130*fc3927beSAndroid Build Coastguard Worker public:
LayoutlibRuntime()131*fc3927beSAndroid Build Coastguard Worker     LayoutlibRuntime() : AndroidRuntime(nullptr, 0) {}
132*fc3927beSAndroid Build Coastguard Worker 
onVmCreated(JNIEnv * env)133*fc3927beSAndroid Build Coastguard Worker     void onVmCreated(JNIEnv* env) override {
134*fc3927beSAndroid Build Coastguard Worker         AndroidRuntime::onVmCreated(env);
135*fc3927beSAndroid Build Coastguard Worker         android::base::SetLogger(LayoutlibLogger);
136*fc3927beSAndroid Build Coastguard Worker         android::base::SetAborter(LayoutlibAborter);
137*fc3927beSAndroid Build Coastguard Worker     }
138*fc3927beSAndroid Build Coastguard Worker 
onStarted()139*fc3927beSAndroid Build Coastguard Worker     void onStarted() override {
140*fc3927beSAndroid Build Coastguard Worker         JNIEnv* env = AndroidRuntime::getJNIEnv();
141*fc3927beSAndroid Build Coastguard Worker         register_jni_procs(env);
142*fc3927beSAndroid Build Coastguard Worker 
143*fc3927beSAndroid Build Coastguard Worker         jmethodID setSystemPropertiesMethod =
144*fc3927beSAndroid Build Coastguard Worker                 GetStaticMethodIDOrDie(env, bridge, "setSystemProperties", "()V");
145*fc3927beSAndroid Build Coastguard Worker         env->CallStaticVoidMethod(bridge, setSystemPropertiesMethod);
146*fc3927beSAndroid Build Coastguard Worker 
147*fc3927beSAndroid Build Coastguard Worker         string keyboard_paths = base::GetProperty("ro.keyboard.paths", "");
148*fc3927beSAndroid Build Coastguard Worker         vector<string> keyboardPaths = parseCsv(keyboard_paths);
149*fc3927beSAndroid Build Coastguard Worker         init_keyboard(keyboardPaths);
150*fc3927beSAndroid Build Coastguard Worker 
151*fc3927beSAndroid Build Coastguard Worker         AndroidRuntime::onStarted();
152*fc3927beSAndroid Build Coastguard Worker     }
153*fc3927beSAndroid Build Coastguard Worker };
154*fc3927beSAndroid Build Coastguard Worker 
155*fc3927beSAndroid Build Coastguard Worker } // namespace android
156*fc3927beSAndroid Build Coastguard Worker 
157*fc3927beSAndroid Build Coastguard Worker using namespace android;
158*fc3927beSAndroid Build Coastguard Worker 
JNI_OnLoad(JavaVM * vm,void *)159*fc3927beSAndroid Build Coastguard Worker JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
160*fc3927beSAndroid Build Coastguard Worker     JNIEnv* env = nullptr;
161*fc3927beSAndroid Build Coastguard Worker     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
162*fc3927beSAndroid Build Coastguard Worker         return JNI_ERR;
163*fc3927beSAndroid Build Coastguard Worker     }
164*fc3927beSAndroid Build Coastguard Worker 
165*fc3927beSAndroid Build Coastguard Worker     layoutLog = FindClassOrDie(env, "com/android/ide/common/rendering/api/ILayoutLog");
166*fc3927beSAndroid Build Coastguard Worker     layoutLog = MakeGlobalRefOrDie(env, layoutLog);
167*fc3927beSAndroid Build Coastguard Worker     logMethodId = GetMethodIDOrDie(env, layoutLog, "logAndroidFramework",
168*fc3927beSAndroid Build Coastguard Worker                                    "(ILjava/lang/String;Ljava/lang/String;)V");
169*fc3927beSAndroid Build Coastguard Worker     bridge = FindClassOrDie(env, "com/android/layoutlib/bridge/Bridge");
170*fc3927beSAndroid Build Coastguard Worker     bridge = MakeGlobalRefOrDie(env, bridge);
171*fc3927beSAndroid Build Coastguard Worker     getLogId = GetStaticMethodIDOrDie(env, bridge, "getLog",
172*fc3927beSAndroid Build Coastguard Worker                                       "()Lcom/android/ide/common/rendering/api/ILayoutLog;");
173*fc3927beSAndroid Build Coastguard Worker 
174*fc3927beSAndroid Build Coastguard Worker     Vector<String8> args;
175*fc3927beSAndroid Build Coastguard Worker     LayoutlibRuntime runtime;
176*fc3927beSAndroid Build Coastguard Worker 
177*fc3927beSAndroid Build Coastguard Worker     runtime.onVmCreated(env);
178*fc3927beSAndroid Build Coastguard Worker     runtime.start("LayoutlibRuntime", args, false);
179*fc3927beSAndroid Build Coastguard Worker 
180*fc3927beSAndroid Build Coastguard Worker     return JNI_VERSION_1_6;
181*fc3927beSAndroid Build Coastguard Worker }
182*fc3927beSAndroid Build Coastguard Worker 
JNI_OnUnload(JavaVM * vm,void *)183*fc3927beSAndroid Build Coastguard Worker JNIEXPORT void JNI_OnUnload(JavaVM* vm, void*) {
184*fc3927beSAndroid Build Coastguard Worker     JNIEnv* env = nullptr;
185*fc3927beSAndroid Build Coastguard Worker     vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
186*fc3927beSAndroid Build Coastguard Worker     env->DeleteGlobalRef(bridge);
187*fc3927beSAndroid Build Coastguard Worker     env->DeleteGlobalRef(layoutLog);
188*fc3927beSAndroid Build Coastguard Worker }
189