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