1 /*
2 * Copyright (C) 2010 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 #define LOG_TAG "Keyboard"
18
19 #include <limits.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <optional>
23
24 #include <input/InputDevice.h>
25 #include <input/InputEventLabels.h>
26 #include <input/KeyCharacterMap.h>
27 #include <input/KeyLayoutMap.h>
28 #include <input/Keyboard.h>
29 #include <log/log.h>
30 #include <utils/Errors.h>
31
32 namespace android {
33
getPath(const InputDeviceIdentifier & deviceIdentifier,const std::string & name,InputDeviceConfigurationFileType type)34 static std::string getPath(const InputDeviceIdentifier& deviceIdentifier, const std::string& name,
35 InputDeviceConfigurationFileType type) {
36 return name.empty()
37 ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
38 : getInputDeviceConfigurationFilePathByName(name, type);
39 }
40
41 // --- KeyMap ---
42
KeyMap()43 KeyMap::KeyMap() {
44 }
45
~KeyMap()46 KeyMap::~KeyMap() {
47 }
48
load(const InputDeviceIdentifier & deviceIdentifier,const PropertyMap * deviceConfiguration)49 status_t KeyMap::load(const InputDeviceIdentifier& deviceIdentifier,
50 const PropertyMap* deviceConfiguration) {
51 // Use the configured key layout if available.
52 if (deviceConfiguration) {
53 std::optional<std::string> keyLayoutName =
54 deviceConfiguration->getString("keyboard.layout");
55 if (keyLayoutName.has_value()) {
56 status_t status = loadKeyLayout(deviceIdentifier, *keyLayoutName);
57 if (status == NAME_NOT_FOUND) {
58 ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
59 "it was not found.",
60 deviceIdentifier.name.c_str(), keyLayoutName->c_str());
61 }
62 }
63
64 std::optional<std::string> keyCharacterMapName =
65 deviceConfiguration->getString("keyboard.characterMap");
66 if (keyCharacterMapName.has_value()) {
67 status_t status = loadKeyCharacterMap(deviceIdentifier, *keyCharacterMapName);
68 if (status == NAME_NOT_FOUND) {
69 ALOGE("Configuration for keyboard device '%s' requested keyboard character "
70 "map '%s' but it was not found.",
71 deviceIdentifier.name.c_str(), keyCharacterMapName->c_str());
72 }
73 }
74
75 if (isComplete()) {
76 return OK;
77 }
78 }
79
80 // Try searching by device identifier.
81 if (probeKeyMap(deviceIdentifier, "")) {
82 return OK;
83 }
84
85 // Fall back on the Generic key map.
86 // TODO Apply some additional heuristics here to figure out what kind of
87 // generic key map to use (US English, etc.) for typical external keyboards.
88 if (probeKeyMap(deviceIdentifier, "Generic")) {
89 return OK;
90 }
91
92 // Try the Virtual key map as a last resort.
93 if (probeKeyMap(deviceIdentifier, "Virtual")) {
94 return OK;
95 }
96
97 // Give up!
98 ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
99 deviceIdentifier.name.c_str());
100 return NAME_NOT_FOUND;
101 }
102
probeKeyMap(const InputDeviceIdentifier & deviceIdentifier,const std::string & keyMapName)103 bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
104 const std::string& keyMapName) {
105 if (!haveKeyLayout()) {
106 loadKeyLayout(deviceIdentifier, keyMapName);
107 }
108 if (!haveKeyCharacterMap()) {
109 loadKeyCharacterMap(deviceIdentifier, keyMapName);
110 }
111 return isComplete();
112 }
113
loadKeyLayout(const InputDeviceIdentifier & deviceIdentifier,const std::string & name)114 status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
115 const std::string& name) {
116 std::string path(getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_LAYOUT));
117 if (path.empty()) {
118 return NAME_NOT_FOUND;
119 }
120
121 base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path);
122 if (ret.ok()) {
123 keyLayoutMap = *ret;
124 keyLayoutFile = path;
125 return OK;
126 }
127
128 // Try to load fallback layout if the regular layout could not be loaded due to missing
129 // kernel modules
130 std::string fallbackPath(
131 getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier,
132 InputDeviceConfigurationFileType::
133 KEY_LAYOUT,
134 "_fallback"));
135 ret = KeyLayoutMap::load(fallbackPath);
136 if (!ret.ok()) {
137 return ret.error().code();
138 }
139 keyLayoutMap = *ret;
140 keyLayoutFile = fallbackPath;
141 return OK;
142 }
143
loadKeyCharacterMap(const InputDeviceIdentifier & deviceIdentifier,const std::string & name)144 status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
145 const std::string& name) {
146 std::string path =
147 getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_CHARACTER_MAP);
148 if (path.empty()) {
149 return NAME_NOT_FOUND;
150 }
151
152 base::Result<std::shared_ptr<KeyCharacterMap>> ret =
153 KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
154 if (!ret.ok()) {
155 return ret.error().code();
156 }
157 keyCharacterMap = *ret;
158 keyCharacterMapFile = path;
159 return OK;
160 }
161
162 // --- Global functions ---
163
isKeyboardSpecialFunction(const PropertyMap * config)164 bool isKeyboardSpecialFunction(const PropertyMap* config) {
165 if (config == nullptr) {
166 return false;
167 }
168 return config->getBool("keyboard.specialFunction").value_or(false);
169 }
170
isEligibleBuiltInKeyboard(const InputDeviceIdentifier & deviceIdentifier,const PropertyMap * deviceConfiguration,const KeyMap * keyMap)171 bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
172 const PropertyMap* deviceConfiguration, const KeyMap* keyMap) {
173 // TODO: remove the third OR statement (SPECIAL_FUNCTION) in Q
174 if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration) ||
175 keyMap->keyCharacterMap->getKeyboardType() ==
176 KeyCharacterMap::KeyboardType::SPECIAL_FUNCTION) {
177 return false;
178 }
179
180 if (deviceConfiguration) {
181 if (deviceConfiguration->getBool("keyboard.builtIn").value_or(false)) {
182 return true;
183 }
184 }
185
186 return strstr(deviceIdentifier.name.c_str(), "-keypad");
187 }
188
setEphemeralMetaState(int32_t mask,bool down,int32_t oldMetaState)189 static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
190 int32_t newMetaState;
191 if (down) {
192 newMetaState = oldMetaState | mask;
193 } else {
194 newMetaState = oldMetaState &
195 ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON);
196 }
197
198 return normalizeMetaState(newMetaState);
199 }
200
normalizeMetaState(int32_t oldMetaState)201 int32_t normalizeMetaState(int32_t oldMetaState) {
202 int32_t newMetaState = oldMetaState;
203 if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
204 newMetaState |= AMETA_ALT_ON;
205 }
206
207 if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
208 newMetaState |= AMETA_SHIFT_ON;
209 }
210
211 if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
212 newMetaState |= AMETA_CTRL_ON;
213 }
214
215 if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
216 newMetaState |= AMETA_META_ON;
217 }
218 return newMetaState;
219 }
220
toggleLockedMetaState(int32_t mask,bool down,int32_t oldMetaState)221 static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) {
222 if (down) {
223 return oldMetaState;
224 } else {
225 return oldMetaState ^ mask;
226 }
227 }
228
updateMetaState(int32_t keyCode,bool down,int32_t oldMetaState)229 int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
230 switch (keyCode) {
231 case AKEYCODE_ALT_LEFT:
232 return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
233 case AKEYCODE_ALT_RIGHT:
234 return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState);
235 case AKEYCODE_SHIFT_LEFT:
236 return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState);
237 case AKEYCODE_SHIFT_RIGHT:
238 return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState);
239 case AKEYCODE_SYM:
240 return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState);
241 case AKEYCODE_FUNCTION:
242 return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState);
243 case AKEYCODE_CTRL_LEFT:
244 return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState);
245 case AKEYCODE_CTRL_RIGHT:
246 return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState);
247 case AKEYCODE_META_LEFT:
248 return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState);
249 case AKEYCODE_META_RIGHT:
250 return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState);
251 case AKEYCODE_CAPS_LOCK:
252 return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState);
253 case AKEYCODE_NUM_LOCK:
254 return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState);
255 case AKEYCODE_SCROLL_LOCK:
256 return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState);
257 default:
258 return oldMetaState;
259 }
260 }
261
isMetaKey(int32_t keyCode)262 bool isMetaKey(int32_t keyCode) {
263 switch (keyCode) {
264 case AKEYCODE_ALT_LEFT:
265 case AKEYCODE_ALT_RIGHT:
266 case AKEYCODE_SHIFT_LEFT:
267 case AKEYCODE_SHIFT_RIGHT:
268 case AKEYCODE_SYM:
269 case AKEYCODE_FUNCTION:
270 case AKEYCODE_CTRL_LEFT:
271 case AKEYCODE_CTRL_RIGHT:
272 case AKEYCODE_META_LEFT:
273 case AKEYCODE_META_RIGHT:
274 case AKEYCODE_CAPS_LOCK:
275 case AKEYCODE_NUM_LOCK:
276 case AKEYCODE_SCROLL_LOCK:
277 return true;
278 default:
279 return false;
280 }
281 }
282
283
284 } // namespace android
285