1 /*
2 * Copyright 2006, 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_runtime/AndroidRuntime.h>
18
19 #include <binder/Parcel.h>
20 #include <input/Input.h>
21 #include <input/InputDevice.h>
22 #include <input/KeyCharacterMap.h>
23
24 #include <jni.h>
25 #include <nativehelper/JNIHelp.h>
26
27 #include "android_os_Parcel.h"
28 #include "android_view_KeyEvent.h"
29
30 #include "core_jni_helpers.h"
31
32 namespace android {
33
34 static struct {
35 jclass clazz;
36 jmethodID ctor;
37 } gKeyCharacterMapClassInfo;
38
39 static struct {
40 jclass clazz;
41 } gKeyEventClassInfo;
42
43 static struct {
44 jfieldID keyCode;
45 jfieldID metaState;
46 } gFallbackActionClassInfo;
47
48
49 class NativeKeyCharacterMap {
50 public:
NativeKeyCharacterMap(int32_t deviceId,std::unique_ptr<KeyCharacterMap> map)51 NativeKeyCharacterMap(int32_t deviceId, std::unique_ptr<KeyCharacterMap> map)
52 : mDeviceId(deviceId), mMap(std::move(map)) {}
53
~NativeKeyCharacterMap()54 ~NativeKeyCharacterMap() {
55 }
56
getDeviceId() const57 inline int32_t getDeviceId() const {
58 return mDeviceId;
59 }
60
getMap() const61 inline const std::unique_ptr<KeyCharacterMap>& getMap() const {
62 return mMap;
63 }
64
65 private:
66 int32_t mDeviceId;
67 std::unique_ptr<KeyCharacterMap> mMap;
68 };
69
android_view_KeyCharacterMap_create(JNIEnv * env,int32_t deviceId,std::unique_ptr<KeyCharacterMap> kcm)70 jobject android_view_KeyCharacterMap_create(JNIEnv* env, int32_t deviceId,
71 std::unique_ptr<KeyCharacterMap> kcm) {
72 NativeKeyCharacterMap* nativeMap = new NativeKeyCharacterMap(deviceId, std::move(kcm));
73 if (!nativeMap) {
74 return nullptr;
75 }
76
77 return env->NewObject(gKeyCharacterMapClassInfo.clazz, gKeyCharacterMapClassInfo.ctor,
78 reinterpret_cast<jlong>(nativeMap));
79 }
80
nativeObtainEmptyKeyCharacterMap(JNIEnv * env,jobject,jint deviceId)81 static jobject nativeObtainEmptyKeyCharacterMap(JNIEnv* env, /*clazz=*/jobject, jint deviceId) {
82 return android_view_KeyCharacterMap_create(env, deviceId, nullptr);
83 }
84
nativeReadFromParcel(JNIEnv * env,jobject clazz,jobject parcelObj)85 static jlong nativeReadFromParcel(JNIEnv *env, jobject clazz, jobject parcelObj) {
86 Parcel* parcel = parcelForJavaObject(env, parcelObj);
87 if (!parcel) {
88 return 0;
89 }
90
91 int32_t deviceId = parcel->readInt32();
92 if (parcel->errorCheck()) {
93 return 0;
94 }
95
96 std::unique_ptr<KeyCharacterMap> kcm;
97 // Check if map is a null character map
98 if (parcel->readBool()) {
99 kcm = KeyCharacterMap::readFromParcel(parcel);
100 if (!kcm.get()) {
101 return 0;
102 }
103 }
104 NativeKeyCharacterMap* map = new NativeKeyCharacterMap(deviceId, std::move(kcm));
105 return reinterpret_cast<jlong>(map);
106 }
107
nativeWriteToParcel(JNIEnv * env,jobject clazz,jlong ptr,jobject parcelObj)108 static void nativeWriteToParcel(JNIEnv* env, jobject clazz, jlong ptr, jobject parcelObj) {
109 NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
110 Parcel* parcel = parcelForJavaObject(env, parcelObj);
111 if (!parcel || !map) {
112 return;
113 }
114 parcel->writeInt32(map->getDeviceId());
115 if (!map->getMap()) {
116 parcel->writeBool(false);
117 return;
118 }
119 parcel->writeBool(true);
120 map->getMap()->writeToParcel(parcel);
121 }
122
nativeDispose(JNIEnv * env,jobject clazz,jlong ptr)123 static void nativeDispose(JNIEnv *env, jobject clazz, jlong ptr) {
124 NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
125 delete map;
126 }
127
128 // Return the associated character or combining accent, or 0 if none.
nativeGetCharacter(JNIEnv * env,jobject clazz,jlong ptr,jint keyCode,jint metaState)129 static jchar nativeGetCharacter(JNIEnv *env, jobject clazz, jlong ptr,
130 jint keyCode, jint metaState) {
131 NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
132 if (!map || !map->getMap()) {
133 return static_cast<jchar>(0);
134 }
135 return map->getMap()->getCharacter(keyCode, metaState);
136 }
137
nativeGetFallbackAction(JNIEnv * env,jobject clazz,jlong ptr,jint keyCode,jint metaState,jobject fallbackActionObj)138 static jboolean nativeGetFallbackAction(JNIEnv *env, jobject clazz, jlong ptr, jint keyCode,
139 jint metaState, jobject fallbackActionObj) {
140 NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
141 if (!map || !map->getMap()) {
142 return static_cast<jboolean>(false);
143 }
144 KeyCharacterMap::FallbackAction fallbackAction;
145
146 bool result = map->getMap()->getFallbackAction(keyCode, metaState, &fallbackAction);
147 if (result) {
148 env->SetIntField(fallbackActionObj, gFallbackActionClassInfo.keyCode,
149 fallbackAction.keyCode);
150 env->SetIntField(fallbackActionObj, gFallbackActionClassInfo.metaState,
151 fallbackAction.metaState);
152 }
153 return result;
154 }
155
156 // Return the number of a key code, or 0 if none.
nativeGetNumber(JNIEnv * env,jobject clazz,jlong ptr,jint keyCode)157 static jchar nativeGetNumber(JNIEnv *env, jobject clazz, jlong ptr, jint keyCode) {
158 NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
159 if (!map || !map->getMap()) {
160 return static_cast<jchar>(0);
161 }
162 return map->getMap()->getNumber(keyCode);
163 }
164
165 // Return the matched key code and meta state, or 0 if none.
nativeGetMatch(JNIEnv * env,jobject clazz,jlong ptr,jint keyCode,jcharArray charsArray,jint metaState)166 static jchar nativeGetMatch(JNIEnv *env, jobject clazz, jlong ptr, jint keyCode,
167 jcharArray charsArray, jint metaState) {
168 NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
169 if (!map || !map->getMap()) {
170 return static_cast<jchar>(0);
171 }
172 jsize numChars = env->GetArrayLength(charsArray);
173 jchar* chars = static_cast<jchar*>(env->GetPrimitiveArrayCritical(charsArray, NULL));
174 if (!chars) {
175 return 0;
176 }
177
178 char16_t result = map->getMap()->getMatch(keyCode, reinterpret_cast<char16_t*>(chars),
179 size_t(numChars), metaState);
180
181 env->ReleasePrimitiveArrayCritical(charsArray, chars, JNI_ABORT);
182 return result;
183 }
184
185 // Return the associated display label, or 0 if none.
nativeGetDisplayLabel(JNIEnv * env,jobject clazz,jlong ptr,jint keyCode)186 static jchar nativeGetDisplayLabel(JNIEnv *env, jobject clazz, jlong ptr, jint keyCode) {
187 NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
188 if (!map || !map->getMap()) {
189 return static_cast<jchar>(0);
190 }
191 return map->getMap()->getDisplayLabel(keyCode);
192 }
193
194 // Return the associated keyboard type, or 0 if none.
nativeGetKeyboardType(JNIEnv * env,jobject clazz,jlong ptr)195 static jint nativeGetKeyboardType(JNIEnv *env, jobject clazz, jlong ptr) {
196 NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
197 if (!map || !map->getMap()) {
198 return static_cast<jint>(0);
199 }
200 return static_cast<jint>(map->getMap()->getKeyboardType());
201 }
202
nativeGetEvents(JNIEnv * env,jobject clazz,jlong ptr,jcharArray charsArray)203 static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jlong ptr,
204 jcharArray charsArray) {
205 NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
206 if (!map || !map->getMap()) {
207 return env->NewObjectArray(/*size=*/0, gKeyEventClassInfo.clazz, NULL);
208 }
209 jchar* chars = env->GetCharArrayElements(charsArray, NULL);
210 if (!chars) {
211 return NULL;
212 }
213 jsize numChars = env->GetArrayLength(charsArray);
214
215 Vector<KeyEvent> events;
216 jobjectArray result = NULL;
217 if (map->getMap()->getEvents(map->getDeviceId(), reinterpret_cast<char16_t*>(chars),
218 size_t(numChars), events)) {
219 result = env->NewObjectArray(jsize(events.size()), gKeyEventClassInfo.clazz, NULL);
220 if (result) {
221 for (size_t i = 0; i < events.size(); i++) {
222 ScopedLocalRef<jobject> keyEventObj =
223 android_view_KeyEvent_obtainAsCopy(env, events.itemAt(i));
224 if (!keyEventObj.get()) break; // threw OOM exception
225 env->SetObjectArrayElement(result, jsize(i), keyEventObj.get());
226 }
227 }
228 }
229
230 env->ReleaseCharArrayElements(charsArray, chars, JNI_ABORT);
231 return result;
232 }
233
nativeEquals(JNIEnv * env,jobject clazz,jlong ptr1,jlong ptr2)234 static jboolean nativeEquals(JNIEnv* env, jobject clazz, jlong ptr1, jlong ptr2) {
235 const std::unique_ptr<KeyCharacterMap>& map1 =
236 (reinterpret_cast<NativeKeyCharacterMap*>(ptr1))->getMap();
237 const std::unique_ptr<KeyCharacterMap>& map2 =
238 (reinterpret_cast<NativeKeyCharacterMap*>(ptr2))->getMap();
239 if (map1 == nullptr || map2 == nullptr) {
240 return map1 == map2;
241 }
242 return static_cast<jboolean>(*map1 == *map2);
243 }
244
nativeApplyOverlay(JNIEnv * env,jobject clazz,jlong ptr,jstring nameObj,jstring overlayObj)245 static void nativeApplyOverlay(JNIEnv* env, jobject clazz, jlong ptr, jstring nameObj,
246 jstring overlayObj) {
247 NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
248 if (!map || !map->getMap()) {
249 return;
250 }
251 ScopedUtfChars nameChars(env, nameObj);
252 ScopedUtfChars overlayChars(env, overlayObj);
253 base::Result<std::shared_ptr<KeyCharacterMap>> ret =
254 KeyCharacterMap::loadContents(nameChars.c_str(), overlayChars.c_str(),
255 KeyCharacterMap::Format::OVERLAY);
256 if (ret.ok()) {
257 std::shared_ptr<KeyCharacterMap> overlay = *ret;
258 map->getMap()->combine(*overlay);
259 }
260 }
261
nativeGetMappedKey(JNIEnv * env,jobject clazz,jlong ptr,jint scanCode)262 static jint nativeGetMappedKey(JNIEnv* env, jobject clazz, jlong ptr, jint scanCode) {
263 NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
264 if (!map || !map->getMap()) {
265 return 0;
266 }
267 int32_t outKeyCode;
268 status_t mapKeyRes = map->getMap()->mapKey(scanCode, /*usageCode=*/0, &outKeyCode);
269 if (mapKeyRes != OK) {
270 return 0;
271 }
272 return static_cast<jint>(outKeyCode);
273 }
274
275 /*
276 * JNI registration.
277 */
278
279 static const JNINativeMethod g_methods[] = {
280 /* name, signature, funcPtr */
281 {"nativeReadFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeReadFromParcel},
282 {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteToParcel},
283 {"nativeDispose", "(J)V", (void*)nativeDispose},
284 {"nativeGetCharacter", "(JII)C", (void*)nativeGetCharacter},
285 {"nativeGetFallbackAction", "(JIILandroid/view/KeyCharacterMap$FallbackAction;)Z",
286 (void*)nativeGetFallbackAction},
287 {"nativeGetNumber", "(JI)C", (void*)nativeGetNumber},
288 {"nativeGetMatch", "(JI[CI)C", (void*)nativeGetMatch},
289 {"nativeGetDisplayLabel", "(JI)C", (void*)nativeGetDisplayLabel},
290 {"nativeGetKeyboardType", "(J)I", (void*)nativeGetKeyboardType},
291 {"nativeGetEvents", "(J[C)[Landroid/view/KeyEvent;", (void*)nativeGetEvents},
292 {"nativeObtainEmptyKeyCharacterMap", "(I)Landroid/view/KeyCharacterMap;",
293 (void*)nativeObtainEmptyKeyCharacterMap},
294 {"nativeEquals", "(JJ)Z", (void*)nativeEquals},
295 {"nativeApplyOverlay", "(JLjava/lang/String;Ljava/lang/String;)V",
296 (void*)nativeApplyOverlay},
297 {"nativeGetMappedKey", "(JI)I", (void*)nativeGetMappedKey}};
298
register_android_view_KeyCharacterMap(JNIEnv * env)299 int register_android_view_KeyCharacterMap(JNIEnv* env)
300 {
301 gKeyCharacterMapClassInfo.clazz = FindClassOrDie(env, "android/view/KeyCharacterMap");
302 gKeyCharacterMapClassInfo.clazz = MakeGlobalRefOrDie(env, gKeyCharacterMapClassInfo.clazz);
303
304 gKeyCharacterMapClassInfo.ctor = GetMethodIDOrDie(env, gKeyCharacterMapClassInfo.clazz,
305 "<init>", "(J)V");
306
307 gKeyEventClassInfo.clazz = FindClassOrDie(env, "android/view/KeyEvent");
308 gKeyEventClassInfo.clazz = MakeGlobalRefOrDie(env, gKeyEventClassInfo.clazz);
309
310 jclass clazz = FindClassOrDie(env, "android/view/KeyCharacterMap$FallbackAction");
311
312 gFallbackActionClassInfo.keyCode = GetFieldIDOrDie(env, clazz, "keyCode", "I");
313 gFallbackActionClassInfo.metaState = GetFieldIDOrDie(env, clazz, "metaState", "I");
314
315 return RegisterMethodsOrDie(env, "android/view/KeyCharacterMap", g_methods, NELEM(g_methods));
316 }
317
318 }; // namespace android
319