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