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