1 /*
2 * Copyright (C) 2020 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
18 #include <android/input.h>
19 #include <jni.h>
20 #include <nativehelper/ScopedLocalFrame.h>
21 #include <nativehelper/ScopedLocalRef.h>
22 #include <nativehelper/ScopedUtfChars.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <map>
29 #include <sstream>
30 #include <unordered_map>
31 #include <vector>
32
33 #define LOG_TAG "InputDeviceKeyLayoutMapTest"
34
35 namespace android {
36
37 // Loads Generic.kl file and returns it as a std::map from scancode to key code.
loadGenericKl(std::string genericKl)38 std::map<int, std::string> loadGenericKl(std::string genericKl) {
39 std::map<int, std::string> result;
40 std::istringstream ssFile(genericKl);
41
42 for (std::string line; std::getline(ssFile, line);) {
43 if (line.empty() || line[0] == '#') {
44 // Skip the comment lines
45 continue;
46 }
47
48 std::string type, code, label, flags;
49 std::istringstream ssLine(line);
50 ssLine >> type >> code >> label >> flags;
51
52 // Skip non-key mappings.
53 if (type != "key") {
54 continue;
55 }
56
57 // Skip HID usage keys.
58 if (code == "usage") {
59 continue;
60 }
61
62 // Skip keys with flags like "FUNCTION"
63 if (!flags.empty()) {
64 continue;
65 }
66
67 result.emplace(std::stoi(code), label);
68 }
69
70 return result;
71 }
72
android_view_cts_nativeLoadKeyLayout(JNIEnv * env,jclass,jstring genericKl)73 static jobject android_view_cts_nativeLoadKeyLayout(JNIEnv* env, jclass, jstring genericKl) {
74 ScopedUtfChars keyLayout(env, genericKl);
75 if (keyLayout.c_str() == nullptr) {
76 return nullptr;
77 }
78 std::map<int, std::string> map = loadGenericKl(keyLayout.c_str());
79
80 ScopedLocalRef<jclass> hashMapClazz(env, env->FindClass("java/util/HashMap"));
81
82 jmethodID hashMapConstructID = env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
83
84 jmethodID hashMapPutID =
85 env->GetMethodID(hashMapClazz.get(), "put",
86 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
87
88 ScopedLocalRef<jclass> integerClazz(env, env->FindClass("java/lang/Integer"));
89
90 jmethodID integerConstructID = env->GetMethodID(integerClazz.get(), "<init>", "(I)V");
91
92 jobject keyLayoutMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
93
94 {
95 ScopedLocalFrame localFrame(env);
96 for (const auto& [key, label] : map) {
97 env->CallObjectMethod(keyLayoutMap, hashMapPutID, env->NewStringUTF(label.c_str()),
98 env->NewObject(integerClazz.get(), integerConstructID, key));
99 }
100 }
101 return keyLayoutMap;
102 }
103
104 } // namespace android
105
106 static JNINativeMethod gMethods[] = {
107 {"nativeLoadKeyLayout", "(Ljava/lang/String;)Ljava/util/Map;",
108 (void*)android::android_view_cts_nativeLoadKeyLayout},
109 };
110
register_android_view_cts_InputDeviceKeyLayoutMapTest(JNIEnv * env)111 int register_android_view_cts_InputDeviceKeyLayoutMapTest(JNIEnv* env) {
112 jclass clazz = env->FindClass("android/view/cts/input/InputDeviceKeyLayoutMapTest");
113 return env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(JNINativeMethod));
114 }
115