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