xref: /aosp_15_r20/frameworks/native/libs/input/KeyCharacterMap.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker  * Copyright (C) 2008 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker  *
4*38e8c45fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker  *
8*38e8c45fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker  *
10*38e8c45fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker  * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker  */
16*38e8c45fSAndroid Build Coastguard Worker 
17*38e8c45fSAndroid Build Coastguard Worker #define LOG_TAG "KeyCharacterMap"
18*38e8c45fSAndroid Build Coastguard Worker 
19*38e8c45fSAndroid Build Coastguard Worker #include <stdlib.h>
20*38e8c45fSAndroid Build Coastguard Worker #include <string.h>
21*38e8c45fSAndroid Build Coastguard Worker 
22*38e8c45fSAndroid Build Coastguard Worker #include <android/keycodes.h>
23*38e8c45fSAndroid Build Coastguard Worker #include <attestation/HmacKeyManager.h>
24*38e8c45fSAndroid Build Coastguard Worker #include <binder/Parcel.h>
25*38e8c45fSAndroid Build Coastguard Worker #include <input/InputEventLabels.h>
26*38e8c45fSAndroid Build Coastguard Worker #include <input/KeyCharacterMap.h>
27*38e8c45fSAndroid Build Coastguard Worker #include <input/Keyboard.h>
28*38e8c45fSAndroid Build Coastguard Worker 
29*38e8c45fSAndroid Build Coastguard Worker #include <utils/Errors.h>
30*38e8c45fSAndroid Build Coastguard Worker #include <utils/Log.h>
31*38e8c45fSAndroid Build Coastguard Worker #include <utils/Timers.h>
32*38e8c45fSAndroid Build Coastguard Worker #include <utils/Tokenizer.h>
33*38e8c45fSAndroid Build Coastguard Worker 
34*38e8c45fSAndroid Build Coastguard Worker // Enables debug output for the parser.
35*38e8c45fSAndroid Build Coastguard Worker #define DEBUG_PARSER 0
36*38e8c45fSAndroid Build Coastguard Worker 
37*38e8c45fSAndroid Build Coastguard Worker // Enables debug output for parser performance.
38*38e8c45fSAndroid Build Coastguard Worker #define DEBUG_PARSER_PERFORMANCE 0
39*38e8c45fSAndroid Build Coastguard Worker 
40*38e8c45fSAndroid Build Coastguard Worker // Enables debug output for mapping.
41*38e8c45fSAndroid Build Coastguard Worker #define DEBUG_MAPPING 0
42*38e8c45fSAndroid Build Coastguard Worker 
43*38e8c45fSAndroid Build Coastguard Worker namespace android {
44*38e8c45fSAndroid Build Coastguard Worker 
45*38e8c45fSAndroid Build Coastguard Worker static const char* WHITESPACE = " \t\r";
46*38e8c45fSAndroid Build Coastguard Worker static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:";
47*38e8c45fSAndroid Build Coastguard Worker 
48*38e8c45fSAndroid Build Coastguard Worker struct Modifier {
49*38e8c45fSAndroid Build Coastguard Worker     const char* label;
50*38e8c45fSAndroid Build Coastguard Worker     int32_t metaState;
51*38e8c45fSAndroid Build Coastguard Worker };
52*38e8c45fSAndroid Build Coastguard Worker static const Modifier modifiers[] = {
53*38e8c45fSAndroid Build Coastguard Worker         { "shift", AMETA_SHIFT_ON },
54*38e8c45fSAndroid Build Coastguard Worker         { "lshift", AMETA_SHIFT_LEFT_ON },
55*38e8c45fSAndroid Build Coastguard Worker         { "rshift", AMETA_SHIFT_RIGHT_ON },
56*38e8c45fSAndroid Build Coastguard Worker         { "alt", AMETA_ALT_ON },
57*38e8c45fSAndroid Build Coastguard Worker         { "lalt", AMETA_ALT_LEFT_ON },
58*38e8c45fSAndroid Build Coastguard Worker         { "ralt", AMETA_ALT_RIGHT_ON },
59*38e8c45fSAndroid Build Coastguard Worker         { "ctrl", AMETA_CTRL_ON },
60*38e8c45fSAndroid Build Coastguard Worker         { "lctrl", AMETA_CTRL_LEFT_ON },
61*38e8c45fSAndroid Build Coastguard Worker         { "rctrl", AMETA_CTRL_RIGHT_ON },
62*38e8c45fSAndroid Build Coastguard Worker         { "meta", AMETA_META_ON },
63*38e8c45fSAndroid Build Coastguard Worker         { "lmeta", AMETA_META_LEFT_ON },
64*38e8c45fSAndroid Build Coastguard Worker         { "rmeta", AMETA_META_RIGHT_ON },
65*38e8c45fSAndroid Build Coastguard Worker         { "sym", AMETA_SYM_ON },
66*38e8c45fSAndroid Build Coastguard Worker         { "fn", AMETA_FUNCTION_ON },
67*38e8c45fSAndroid Build Coastguard Worker         { "capslock", AMETA_CAPS_LOCK_ON },
68*38e8c45fSAndroid Build Coastguard Worker         { "numlock", AMETA_NUM_LOCK_ON },
69*38e8c45fSAndroid Build Coastguard Worker         { "scrolllock", AMETA_SCROLL_LOCK_ON },
70*38e8c45fSAndroid Build Coastguard Worker };
71*38e8c45fSAndroid Build Coastguard Worker 
72*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_MAPPING
toString(const char16_t * chars,size_t numChars)73*38e8c45fSAndroid Build Coastguard Worker static String8 toString(const char16_t* chars, size_t numChars) {
74*38e8c45fSAndroid Build Coastguard Worker     String8 result;
75*38e8c45fSAndroid Build Coastguard Worker     for (size_t i = 0; i < numChars; i++) {
76*38e8c45fSAndroid Build Coastguard Worker         result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]);
77*38e8c45fSAndroid Build Coastguard Worker     }
78*38e8c45fSAndroid Build Coastguard Worker     return result;
79*38e8c45fSAndroid Build Coastguard Worker }
80*38e8c45fSAndroid Build Coastguard Worker #endif
81*38e8c45fSAndroid Build Coastguard Worker 
82*38e8c45fSAndroid Build Coastguard Worker 
83*38e8c45fSAndroid Build Coastguard Worker // --- KeyCharacterMap ---
84*38e8c45fSAndroid Build Coastguard Worker 
KeyCharacterMap(const std::string & filename)85*38e8c45fSAndroid Build Coastguard Worker KeyCharacterMap::KeyCharacterMap(const std::string& filename) : mLoadFileName(filename) {}
86*38e8c45fSAndroid Build Coastguard Worker 
load(const std::string & filename,Format format)87*38e8c45fSAndroid Build Coastguard Worker base::Result<std::unique_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename,
88*38e8c45fSAndroid Build Coastguard Worker                                                                      Format format) {
89*38e8c45fSAndroid Build Coastguard Worker     Tokenizer* tokenizer;
90*38e8c45fSAndroid Build Coastguard Worker     status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
91*38e8c45fSAndroid Build Coastguard Worker     if (status) {
92*38e8c45fSAndroid Build Coastguard Worker         return Errorf("Error {} opening key character map file {}.", status, filename.c_str());
93*38e8c45fSAndroid Build Coastguard Worker     }
94*38e8c45fSAndroid Build Coastguard Worker     std::unique_ptr<KeyCharacterMap> map =
95*38e8c45fSAndroid Build Coastguard Worker             std::unique_ptr<KeyCharacterMap>(new KeyCharacterMap(filename));
96*38e8c45fSAndroid Build Coastguard Worker     if (!map.get()) {
97*38e8c45fSAndroid Build Coastguard Worker         ALOGE("Error allocating key character map.");
98*38e8c45fSAndroid Build Coastguard Worker         return Errorf("Error allocating key character map.");
99*38e8c45fSAndroid Build Coastguard Worker     }
100*38e8c45fSAndroid Build Coastguard Worker     std::unique_ptr<Tokenizer> t(tokenizer);
101*38e8c45fSAndroid Build Coastguard Worker     status = map->load(t.get(), format);
102*38e8c45fSAndroid Build Coastguard Worker     if (status == OK) {
103*38e8c45fSAndroid Build Coastguard Worker         return map;
104*38e8c45fSAndroid Build Coastguard Worker     }
105*38e8c45fSAndroid Build Coastguard Worker     return Errorf("Load KeyCharacterMap failed {}.", status);
106*38e8c45fSAndroid Build Coastguard Worker }
107*38e8c45fSAndroid Build Coastguard Worker 
loadContents(const std::string & filename,const char * contents,Format format)108*38e8c45fSAndroid Build Coastguard Worker base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::loadContents(
109*38e8c45fSAndroid Build Coastguard Worker         const std::string& filename, const char* contents, Format format) {
110*38e8c45fSAndroid Build Coastguard Worker     Tokenizer* tokenizer;
111*38e8c45fSAndroid Build Coastguard Worker     status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
112*38e8c45fSAndroid Build Coastguard Worker     if (status) {
113*38e8c45fSAndroid Build Coastguard Worker         ALOGE("Error %d opening key character map.", status);
114*38e8c45fSAndroid Build Coastguard Worker         return Errorf("Error {} opening key character map.", status);
115*38e8c45fSAndroid Build Coastguard Worker     }
116*38e8c45fSAndroid Build Coastguard Worker     std::shared_ptr<KeyCharacterMap> map =
117*38e8c45fSAndroid Build Coastguard Worker             std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap(filename));
118*38e8c45fSAndroid Build Coastguard Worker     if (!map.get()) {
119*38e8c45fSAndroid Build Coastguard Worker         ALOGE("Error allocating key character map.");
120*38e8c45fSAndroid Build Coastguard Worker         return Errorf("Error allocating key character map.");
121*38e8c45fSAndroid Build Coastguard Worker     }
122*38e8c45fSAndroid Build Coastguard Worker     std::unique_ptr<Tokenizer> t(tokenizer);
123*38e8c45fSAndroid Build Coastguard Worker     status = map->load(t.get(), format);
124*38e8c45fSAndroid Build Coastguard Worker     if (status == OK) {
125*38e8c45fSAndroid Build Coastguard Worker         return map;
126*38e8c45fSAndroid Build Coastguard Worker     }
127*38e8c45fSAndroid Build Coastguard Worker     return Errorf("Load KeyCharacterMap failed {}.", status);
128*38e8c45fSAndroid Build Coastguard Worker }
129*38e8c45fSAndroid Build Coastguard Worker 
load(Tokenizer * tokenizer,Format format)130*38e8c45fSAndroid Build Coastguard Worker status_t KeyCharacterMap::load(Tokenizer* tokenizer, Format format) {
131*38e8c45fSAndroid Build Coastguard Worker     status_t status = OK;
132*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_PARSER_PERFORMANCE
133*38e8c45fSAndroid Build Coastguard Worker     nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
134*38e8c45fSAndroid Build Coastguard Worker #endif
135*38e8c45fSAndroid Build Coastguard Worker     Parser parser(this, tokenizer, format);
136*38e8c45fSAndroid Build Coastguard Worker     status = parser.parse();
137*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_PARSER_PERFORMANCE
138*38e8c45fSAndroid Build Coastguard Worker     nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
139*38e8c45fSAndroid Build Coastguard Worker     ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
140*38e8c45fSAndroid Build Coastguard Worker           tokenizer->getFilename().c_str(), tokenizer->getLineNumber(), elapsedTime / 1000000.0);
141*38e8c45fSAndroid Build Coastguard Worker #endif
142*38e8c45fSAndroid Build Coastguard Worker     if (status != OK) {
143*38e8c45fSAndroid Build Coastguard Worker         ALOGE("Loading KeyCharacterMap failed with status %s", statusToString(status).c_str());
144*38e8c45fSAndroid Build Coastguard Worker     }
145*38e8c45fSAndroid Build Coastguard Worker     return status;
146*38e8c45fSAndroid Build Coastguard Worker }
147*38e8c45fSAndroid Build Coastguard Worker 
clear()148*38e8c45fSAndroid Build Coastguard Worker void KeyCharacterMap::clear() {
149*38e8c45fSAndroid Build Coastguard Worker     mKeysByScanCode.clear();
150*38e8c45fSAndroid Build Coastguard Worker     mKeysByUsageCode.clear();
151*38e8c45fSAndroid Build Coastguard Worker     mKeys.clear();
152*38e8c45fSAndroid Build Coastguard Worker     mLayoutOverlayApplied = false;
153*38e8c45fSAndroid Build Coastguard Worker     mType = KeyboardType::UNKNOWN;
154*38e8c45fSAndroid Build Coastguard Worker }
155*38e8c45fSAndroid Build Coastguard Worker 
reloadBaseFromFile()156*38e8c45fSAndroid Build Coastguard Worker status_t KeyCharacterMap::reloadBaseFromFile() {
157*38e8c45fSAndroid Build Coastguard Worker     clear();
158*38e8c45fSAndroid Build Coastguard Worker     Tokenizer* tokenizer;
159*38e8c45fSAndroid Build Coastguard Worker     status_t status = Tokenizer::open(String8(mLoadFileName.c_str()), &tokenizer);
160*38e8c45fSAndroid Build Coastguard Worker     if (status) {
161*38e8c45fSAndroid Build Coastguard Worker         ALOGE("Error %s opening key character map file %s.", statusToString(status).c_str(),
162*38e8c45fSAndroid Build Coastguard Worker               mLoadFileName.c_str());
163*38e8c45fSAndroid Build Coastguard Worker         return status;
164*38e8c45fSAndroid Build Coastguard Worker     }
165*38e8c45fSAndroid Build Coastguard Worker     std::unique_ptr<Tokenizer> t(tokenizer);
166*38e8c45fSAndroid Build Coastguard Worker     return load(t.get(), KeyCharacterMap::Format::BASE);
167*38e8c45fSAndroid Build Coastguard Worker }
168*38e8c45fSAndroid Build Coastguard Worker 
combine(const KeyCharacterMap & overlay)169*38e8c45fSAndroid Build Coastguard Worker void KeyCharacterMap::combine(const KeyCharacterMap& overlay) {
170*38e8c45fSAndroid Build Coastguard Worker     if (mLayoutOverlayApplied) {
171*38e8c45fSAndroid Build Coastguard Worker         reloadBaseFromFile();
172*38e8c45fSAndroid Build Coastguard Worker     }
173*38e8c45fSAndroid Build Coastguard Worker     for (const auto& [keyCode, key] : overlay.mKeys) {
174*38e8c45fSAndroid Build Coastguard Worker         mKeys.insert_or_assign(keyCode, key);
175*38e8c45fSAndroid Build Coastguard Worker     }
176*38e8c45fSAndroid Build Coastguard Worker 
177*38e8c45fSAndroid Build Coastguard Worker     for (const auto& [fromScanCode, toAndroidKeyCode] : overlay.mKeysByScanCode) {
178*38e8c45fSAndroid Build Coastguard Worker         mKeysByScanCode.insert_or_assign(fromScanCode, toAndroidKeyCode);
179*38e8c45fSAndroid Build Coastguard Worker     }
180*38e8c45fSAndroid Build Coastguard Worker 
181*38e8c45fSAndroid Build Coastguard Worker     for (const auto& [fromHidUsageCode, toAndroidKeyCode] : overlay.mKeysByUsageCode) {
182*38e8c45fSAndroid Build Coastguard Worker         mKeysByUsageCode.insert_or_assign(fromHidUsageCode, toAndroidKeyCode);
183*38e8c45fSAndroid Build Coastguard Worker     }
184*38e8c45fSAndroid Build Coastguard Worker     mLayoutOverlayApplied = true;
185*38e8c45fSAndroid Build Coastguard Worker }
186*38e8c45fSAndroid Build Coastguard Worker 
clearLayoutOverlay()187*38e8c45fSAndroid Build Coastguard Worker void KeyCharacterMap::clearLayoutOverlay() {
188*38e8c45fSAndroid Build Coastguard Worker     if (mLayoutOverlayApplied) {
189*38e8c45fSAndroid Build Coastguard Worker         reloadBaseFromFile();
190*38e8c45fSAndroid Build Coastguard Worker         mLayoutOverlayApplied = false;
191*38e8c45fSAndroid Build Coastguard Worker     }
192*38e8c45fSAndroid Build Coastguard Worker }
193*38e8c45fSAndroid Build Coastguard Worker 
getKeyboardType() const194*38e8c45fSAndroid Build Coastguard Worker KeyCharacterMap::KeyboardType KeyCharacterMap::getKeyboardType() const {
195*38e8c45fSAndroid Build Coastguard Worker     return mType;
196*38e8c45fSAndroid Build Coastguard Worker }
197*38e8c45fSAndroid Build Coastguard Worker 
getLoadFileName() const198*38e8c45fSAndroid Build Coastguard Worker const std::string KeyCharacterMap::getLoadFileName() const {
199*38e8c45fSAndroid Build Coastguard Worker     return mLoadFileName;
200*38e8c45fSAndroid Build Coastguard Worker }
201*38e8c45fSAndroid Build Coastguard Worker 
getDisplayLabel(int32_t keyCode) const202*38e8c45fSAndroid Build Coastguard Worker char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
203*38e8c45fSAndroid Build Coastguard Worker     char16_t result = 0;
204*38e8c45fSAndroid Build Coastguard Worker     const Key* key = getKey(keyCode);
205*38e8c45fSAndroid Build Coastguard Worker     if (key != nullptr) {
206*38e8c45fSAndroid Build Coastguard Worker         result = key->label;
207*38e8c45fSAndroid Build Coastguard Worker     }
208*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_MAPPING
209*38e8c45fSAndroid Build Coastguard Worker     ALOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result);
210*38e8c45fSAndroid Build Coastguard Worker #endif
211*38e8c45fSAndroid Build Coastguard Worker     return result;
212*38e8c45fSAndroid Build Coastguard Worker }
213*38e8c45fSAndroid Build Coastguard Worker 
getNumber(int32_t keyCode) const214*38e8c45fSAndroid Build Coastguard Worker char16_t KeyCharacterMap::getNumber(int32_t keyCode) const {
215*38e8c45fSAndroid Build Coastguard Worker     char16_t result = 0;
216*38e8c45fSAndroid Build Coastguard Worker     const Key* key = getKey(keyCode);
217*38e8c45fSAndroid Build Coastguard Worker     if (key != nullptr) {
218*38e8c45fSAndroid Build Coastguard Worker         result = key->number;
219*38e8c45fSAndroid Build Coastguard Worker     }
220*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_MAPPING
221*38e8c45fSAndroid Build Coastguard Worker     ALOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result);
222*38e8c45fSAndroid Build Coastguard Worker #endif
223*38e8c45fSAndroid Build Coastguard Worker     return result;
224*38e8c45fSAndroid Build Coastguard Worker }
225*38e8c45fSAndroid Build Coastguard Worker 
getCharacter(int32_t keyCode,int32_t metaState) const226*38e8c45fSAndroid Build Coastguard Worker char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const {
227*38e8c45fSAndroid Build Coastguard Worker     char16_t result = 0;
228*38e8c45fSAndroid Build Coastguard Worker     const Behavior* behavior = getKeyBehavior(keyCode, metaState);
229*38e8c45fSAndroid Build Coastguard Worker     if (behavior != nullptr) {
230*38e8c45fSAndroid Build Coastguard Worker         result = behavior->character;
231*38e8c45fSAndroid Build Coastguard Worker     }
232*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_MAPPING
233*38e8c45fSAndroid Build Coastguard Worker     ALOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result);
234*38e8c45fSAndroid Build Coastguard Worker #endif
235*38e8c45fSAndroid Build Coastguard Worker     return result;
236*38e8c45fSAndroid Build Coastguard Worker }
237*38e8c45fSAndroid Build Coastguard Worker 
getFallbackAction(int32_t keyCode,int32_t metaState,FallbackAction * outFallbackAction) const238*38e8c45fSAndroid Build Coastguard Worker bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState,
239*38e8c45fSAndroid Build Coastguard Worker         FallbackAction* outFallbackAction) const {
240*38e8c45fSAndroid Build Coastguard Worker     outFallbackAction->keyCode = 0;
241*38e8c45fSAndroid Build Coastguard Worker     outFallbackAction->metaState = 0;
242*38e8c45fSAndroid Build Coastguard Worker 
243*38e8c45fSAndroid Build Coastguard Worker     bool result = false;
244*38e8c45fSAndroid Build Coastguard Worker     const Behavior* behavior = getKeyBehavior(keyCode, metaState);
245*38e8c45fSAndroid Build Coastguard Worker     if (behavior != nullptr) {
246*38e8c45fSAndroid Build Coastguard Worker         if (behavior->fallbackKeyCode) {
247*38e8c45fSAndroid Build Coastguard Worker             outFallbackAction->keyCode = behavior->fallbackKeyCode;
248*38e8c45fSAndroid Build Coastguard Worker             outFallbackAction->metaState = metaState & ~behavior->metaState;
249*38e8c45fSAndroid Build Coastguard Worker             result = true;
250*38e8c45fSAndroid Build Coastguard Worker         }
251*38e8c45fSAndroid Build Coastguard Worker     }
252*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_MAPPING
253*38e8c45fSAndroid Build Coastguard Worker     ALOGD("getFallbackKeyCode: keyCode=%d, metaState=0x%08x ~ Result %s, "
254*38e8c45fSAndroid Build Coastguard Worker             "fallback keyCode=%d, fallback metaState=0x%08x.",
255*38e8c45fSAndroid Build Coastguard Worker             keyCode, metaState, result ? "true" : "false",
256*38e8c45fSAndroid Build Coastguard Worker             outFallbackAction->keyCode, outFallbackAction->metaState);
257*38e8c45fSAndroid Build Coastguard Worker #endif
258*38e8c45fSAndroid Build Coastguard Worker     return result;
259*38e8c45fSAndroid Build Coastguard Worker }
260*38e8c45fSAndroid Build Coastguard Worker 
getMatch(int32_t keyCode,const char16_t * chars,size_t numChars,int32_t metaState) const261*38e8c45fSAndroid Build Coastguard Worker char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars,
262*38e8c45fSAndroid Build Coastguard Worker         int32_t metaState) const {
263*38e8c45fSAndroid Build Coastguard Worker     char16_t result = 0;
264*38e8c45fSAndroid Build Coastguard Worker     const Key* key = getKey(keyCode);
265*38e8c45fSAndroid Build Coastguard Worker     if (key != nullptr) {
266*38e8c45fSAndroid Build Coastguard Worker         // Try to find the most general behavior that maps to this character.
267*38e8c45fSAndroid Build Coastguard Worker         // For example, the base key behavior will usually be last in the list.
268*38e8c45fSAndroid Build Coastguard Worker         // However, if we find a perfect meta state match for one behavior then use that one.
269*38e8c45fSAndroid Build Coastguard Worker         for (const Behavior& behavior : key->behaviors) {
270*38e8c45fSAndroid Build Coastguard Worker             if (behavior.character) {
271*38e8c45fSAndroid Build Coastguard Worker                 for (size_t i = 0; i < numChars; i++) {
272*38e8c45fSAndroid Build Coastguard Worker                     if (behavior.character == chars[i]) {
273*38e8c45fSAndroid Build Coastguard Worker                         result = behavior.character;
274*38e8c45fSAndroid Build Coastguard Worker                         if ((behavior.metaState & metaState) == behavior.metaState) {
275*38e8c45fSAndroid Build Coastguard Worker                             // Found exact match!
276*38e8c45fSAndroid Build Coastguard Worker                             return result;
277*38e8c45fSAndroid Build Coastguard Worker                         }
278*38e8c45fSAndroid Build Coastguard Worker                         break;
279*38e8c45fSAndroid Build Coastguard Worker                     }
280*38e8c45fSAndroid Build Coastguard Worker                 }
281*38e8c45fSAndroid Build Coastguard Worker             }
282*38e8c45fSAndroid Build Coastguard Worker         }
283*38e8c45fSAndroid Build Coastguard Worker     }
284*38e8c45fSAndroid Build Coastguard Worker     return result;
285*38e8c45fSAndroid Build Coastguard Worker }
286*38e8c45fSAndroid Build Coastguard Worker 
getEvents(int32_t deviceId,const char16_t * chars,size_t numChars,Vector<KeyEvent> & outEvents) const287*38e8c45fSAndroid Build Coastguard Worker bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
288*38e8c45fSAndroid Build Coastguard Worker         Vector<KeyEvent>& outEvents) const {
289*38e8c45fSAndroid Build Coastguard Worker     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
290*38e8c45fSAndroid Build Coastguard Worker 
291*38e8c45fSAndroid Build Coastguard Worker     for (size_t i = 0; i < numChars; i++) {
292*38e8c45fSAndroid Build Coastguard Worker         int32_t keyCode, metaState;
293*38e8c45fSAndroid Build Coastguard Worker         char16_t ch = chars[i];
294*38e8c45fSAndroid Build Coastguard Worker         if (!findKey(ch, &keyCode, &metaState)) {
295*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_MAPPING
296*38e8c45fSAndroid Build Coastguard Worker             ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.",
297*38e8c45fSAndroid Build Coastguard Worker                   deviceId, toString(chars, numChars).c_str(), ch);
298*38e8c45fSAndroid Build Coastguard Worker #endif
299*38e8c45fSAndroid Build Coastguard Worker             return false;
300*38e8c45fSAndroid Build Coastguard Worker         }
301*38e8c45fSAndroid Build Coastguard Worker 
302*38e8c45fSAndroid Build Coastguard Worker         int32_t currentMetaState = 0;
303*38e8c45fSAndroid Build Coastguard Worker         addMetaKeys(outEvents, deviceId, metaState, true, now, &currentMetaState);
304*38e8c45fSAndroid Build Coastguard Worker         addKey(outEvents, deviceId, keyCode, currentMetaState, true, now);
305*38e8c45fSAndroid Build Coastguard Worker         addKey(outEvents, deviceId, keyCode, currentMetaState, false, now);
306*38e8c45fSAndroid Build Coastguard Worker         addMetaKeys(outEvents, deviceId, metaState, false, now, &currentMetaState);
307*38e8c45fSAndroid Build Coastguard Worker     }
308*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_MAPPING
309*38e8c45fSAndroid Build Coastguard Worker     ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", deviceId,
310*38e8c45fSAndroid Build Coastguard Worker           toString(chars, numChars).c_str(), int32_t(outEvents.size()));
311*38e8c45fSAndroid Build Coastguard Worker     for (size_t i = 0; i < outEvents.size(); i++) {
312*38e8c45fSAndroid Build Coastguard Worker         ALOGD("  Key: keyCode=%d, metaState=0x%08x, %s.",
313*38e8c45fSAndroid Build Coastguard Worker                 outEvents[i].getKeyCode(), outEvents[i].getMetaState(),
314*38e8c45fSAndroid Build Coastguard Worker                 outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up");
315*38e8c45fSAndroid Build Coastguard Worker     }
316*38e8c45fSAndroid Build Coastguard Worker #endif
317*38e8c45fSAndroid Build Coastguard Worker     return true;
318*38e8c45fSAndroid Build Coastguard Worker }
319*38e8c45fSAndroid Build Coastguard Worker 
setKeyRemapping(const std::map<int32_t,int32_t> & keyRemapping)320*38e8c45fSAndroid Build Coastguard Worker void KeyCharacterMap::setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping) {
321*38e8c45fSAndroid Build Coastguard Worker     mKeyRemapping = keyRemapping;
322*38e8c45fSAndroid Build Coastguard Worker }
323*38e8c45fSAndroid Build Coastguard Worker 
mapKey(int32_t scanCode,int32_t usageCode,int32_t * outKeyCode) const324*38e8c45fSAndroid Build Coastguard Worker status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const {
325*38e8c45fSAndroid Build Coastguard Worker     if (usageCode) {
326*38e8c45fSAndroid Build Coastguard Worker         const auto it = mKeysByUsageCode.find(usageCode);
327*38e8c45fSAndroid Build Coastguard Worker         if (it != mKeysByUsageCode.end()) {
328*38e8c45fSAndroid Build Coastguard Worker             *outKeyCode = it->second;
329*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_MAPPING
330*38e8c45fSAndroid Build Coastguard Worker             ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
331*38e8c45fSAndroid Build Coastguard Worker                     scanCode, usageCode, *outKeyCode);
332*38e8c45fSAndroid Build Coastguard Worker #endif
333*38e8c45fSAndroid Build Coastguard Worker             return OK;
334*38e8c45fSAndroid Build Coastguard Worker         }
335*38e8c45fSAndroid Build Coastguard Worker     }
336*38e8c45fSAndroid Build Coastguard Worker     if (scanCode) {
337*38e8c45fSAndroid Build Coastguard Worker         const auto it = mKeysByScanCode.find(scanCode);
338*38e8c45fSAndroid Build Coastguard Worker         if (it != mKeysByScanCode.end()) {
339*38e8c45fSAndroid Build Coastguard Worker             *outKeyCode = it->second;
340*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_MAPPING
341*38e8c45fSAndroid Build Coastguard Worker             ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
342*38e8c45fSAndroid Build Coastguard Worker                     scanCode, usageCode, *outKeyCode);
343*38e8c45fSAndroid Build Coastguard Worker #endif
344*38e8c45fSAndroid Build Coastguard Worker             return OK;
345*38e8c45fSAndroid Build Coastguard Worker         }
346*38e8c45fSAndroid Build Coastguard Worker     }
347*38e8c45fSAndroid Build Coastguard Worker 
348*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_MAPPING
349*38e8c45fSAndroid Build Coastguard Worker     ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode);
350*38e8c45fSAndroid Build Coastguard Worker #endif
351*38e8c45fSAndroid Build Coastguard Worker     *outKeyCode = AKEYCODE_UNKNOWN;
352*38e8c45fSAndroid Build Coastguard Worker     return NAME_NOT_FOUND;
353*38e8c45fSAndroid Build Coastguard Worker }
354*38e8c45fSAndroid Build Coastguard Worker 
applyKeyRemapping(int32_t fromKeyCode) const355*38e8c45fSAndroid Build Coastguard Worker int32_t KeyCharacterMap::applyKeyRemapping(int32_t fromKeyCode) const {
356*38e8c45fSAndroid Build Coastguard Worker     int32_t toKeyCode = fromKeyCode;
357*38e8c45fSAndroid Build Coastguard Worker 
358*38e8c45fSAndroid Build Coastguard Worker     const auto it = mKeyRemapping.find(fromKeyCode);
359*38e8c45fSAndroid Build Coastguard Worker     if (it != mKeyRemapping.end()) {
360*38e8c45fSAndroid Build Coastguard Worker         toKeyCode = it->second;
361*38e8c45fSAndroid Build Coastguard Worker     }
362*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_MAPPING
363*38e8c45fSAndroid Build Coastguard Worker     ALOGD("applyKeyRemapping: keyCode=%d ~ replacement keyCode=%d.", fromKeyCode, toKeyCode);
364*38e8c45fSAndroid Build Coastguard Worker #endif
365*38e8c45fSAndroid Build Coastguard Worker     return toKeyCode;
366*38e8c45fSAndroid Build Coastguard Worker }
367*38e8c45fSAndroid Build Coastguard Worker 
findKeyCodesMappedToKeyCode(int32_t toKeyCode) const368*38e8c45fSAndroid Build Coastguard Worker std::vector<int32_t> KeyCharacterMap::findKeyCodesMappedToKeyCode(int32_t toKeyCode) const {
369*38e8c45fSAndroid Build Coastguard Worker     std::vector<int32_t> fromKeyCodes;
370*38e8c45fSAndroid Build Coastguard Worker 
371*38e8c45fSAndroid Build Coastguard Worker     for (const auto& [from, to] : mKeyRemapping) {
372*38e8c45fSAndroid Build Coastguard Worker         if (toKeyCode == to) {
373*38e8c45fSAndroid Build Coastguard Worker             fromKeyCodes.push_back(from);
374*38e8c45fSAndroid Build Coastguard Worker         }
375*38e8c45fSAndroid Build Coastguard Worker     }
376*38e8c45fSAndroid Build Coastguard Worker     return fromKeyCodes;
377*38e8c45fSAndroid Build Coastguard Worker }
378*38e8c45fSAndroid Build Coastguard Worker 
applyKeyBehavior(int32_t fromKeyCode,int32_t fromMetaState) const379*38e8c45fSAndroid Build Coastguard Worker std::pair<int32_t, int32_t> KeyCharacterMap::applyKeyBehavior(int32_t fromKeyCode,
380*38e8c45fSAndroid Build Coastguard Worker                                                               int32_t fromMetaState) const {
381*38e8c45fSAndroid Build Coastguard Worker     int32_t toKeyCode = fromKeyCode;
382*38e8c45fSAndroid Build Coastguard Worker     int32_t toMetaState = fromMetaState;
383*38e8c45fSAndroid Build Coastguard Worker 
384*38e8c45fSAndroid Build Coastguard Worker     const Behavior* behavior = getKeyBehavior(fromKeyCode, fromMetaState);
385*38e8c45fSAndroid Build Coastguard Worker     if (behavior != nullptr) {
386*38e8c45fSAndroid Build Coastguard Worker         if (behavior->replacementKeyCode) {
387*38e8c45fSAndroid Build Coastguard Worker             toKeyCode = behavior->replacementKeyCode;
388*38e8c45fSAndroid Build Coastguard Worker             toMetaState = fromMetaState & ~behavior->metaState;
389*38e8c45fSAndroid Build Coastguard Worker             // Reset dependent meta states.
390*38e8c45fSAndroid Build Coastguard Worker             if (behavior->metaState & AMETA_ALT_ON) {
391*38e8c45fSAndroid Build Coastguard Worker                 toMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
392*38e8c45fSAndroid Build Coastguard Worker             }
393*38e8c45fSAndroid Build Coastguard Worker             if (behavior->metaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
394*38e8c45fSAndroid Build Coastguard Worker                 toMetaState &= ~AMETA_ALT_ON;
395*38e8c45fSAndroid Build Coastguard Worker             }
396*38e8c45fSAndroid Build Coastguard Worker             if (behavior->metaState & AMETA_CTRL_ON) {
397*38e8c45fSAndroid Build Coastguard Worker                 toMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
398*38e8c45fSAndroid Build Coastguard Worker             }
399*38e8c45fSAndroid Build Coastguard Worker             if (behavior->metaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
400*38e8c45fSAndroid Build Coastguard Worker                 toMetaState &= ~AMETA_CTRL_ON;
401*38e8c45fSAndroid Build Coastguard Worker             }
402*38e8c45fSAndroid Build Coastguard Worker             if (behavior->metaState & AMETA_SHIFT_ON) {
403*38e8c45fSAndroid Build Coastguard Worker                 toMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON);
404*38e8c45fSAndroid Build Coastguard Worker             }
405*38e8c45fSAndroid Build Coastguard Worker             if (behavior->metaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
406*38e8c45fSAndroid Build Coastguard Worker                 toMetaState &= ~AMETA_SHIFT_ON;
407*38e8c45fSAndroid Build Coastguard Worker             }
408*38e8c45fSAndroid Build Coastguard Worker             // ... and put universal bits back if needed
409*38e8c45fSAndroid Build Coastguard Worker             toMetaState = normalizeMetaState(toMetaState);
410*38e8c45fSAndroid Build Coastguard Worker         }
411*38e8c45fSAndroid Build Coastguard Worker     }
412*38e8c45fSAndroid Build Coastguard Worker 
413*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_MAPPING
414*38e8c45fSAndroid Build Coastguard Worker     ALOGD("applyKeyBehavior: keyCode=%d, metaState=0x%08x ~ "
415*38e8c45fSAndroid Build Coastguard Worker           "replacement keyCode=%d, replacement metaState=0x%08x.",
416*38e8c45fSAndroid Build Coastguard Worker           fromKeyCode, fromMetaState, toKeyCode, toMetaState);
417*38e8c45fSAndroid Build Coastguard Worker #endif
418*38e8c45fSAndroid Build Coastguard Worker     return std::make_pair(toKeyCode, toMetaState);
419*38e8c45fSAndroid Build Coastguard Worker }
420*38e8c45fSAndroid Build Coastguard Worker 
getKey(int32_t keyCode) const421*38e8c45fSAndroid Build Coastguard Worker const KeyCharacterMap::Key* KeyCharacterMap::getKey(int32_t keyCode) const {
422*38e8c45fSAndroid Build Coastguard Worker     auto it = mKeys.find(keyCode);
423*38e8c45fSAndroid Build Coastguard Worker     if (it != mKeys.end()) {
424*38e8c45fSAndroid Build Coastguard Worker         return &it->second;
425*38e8c45fSAndroid Build Coastguard Worker     }
426*38e8c45fSAndroid Build Coastguard Worker     return nullptr;
427*38e8c45fSAndroid Build Coastguard Worker }
428*38e8c45fSAndroid Build Coastguard Worker 
getKeyBehavior(int32_t keyCode,int32_t metaState) const429*38e8c45fSAndroid Build Coastguard Worker const KeyCharacterMap::Behavior* KeyCharacterMap::getKeyBehavior(int32_t keyCode,
430*38e8c45fSAndroid Build Coastguard Worker                                                                  int32_t metaState) const {
431*38e8c45fSAndroid Build Coastguard Worker     const Key* key = getKey(keyCode);
432*38e8c45fSAndroid Build Coastguard Worker     if (key != nullptr) {
433*38e8c45fSAndroid Build Coastguard Worker         for (const Behavior& behavior : key->behaviors) {
434*38e8c45fSAndroid Build Coastguard Worker             if (matchesMetaState(metaState, behavior.metaState)) {
435*38e8c45fSAndroid Build Coastguard Worker                 return &behavior;
436*38e8c45fSAndroid Build Coastguard Worker             }
437*38e8c45fSAndroid Build Coastguard Worker         }
438*38e8c45fSAndroid Build Coastguard Worker     }
439*38e8c45fSAndroid Build Coastguard Worker     return nullptr;
440*38e8c45fSAndroid Build Coastguard Worker }
441*38e8c45fSAndroid Build Coastguard Worker 
matchesMetaState(int32_t eventMetaState,int32_t behaviorMetaState)442*38e8c45fSAndroid Build Coastguard Worker bool KeyCharacterMap::matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState) {
443*38e8c45fSAndroid Build Coastguard Worker     // Behavior must have at least the set of meta states specified.
444*38e8c45fSAndroid Build Coastguard Worker     // And if the key event has CTRL, ALT or META then the behavior must exactly
445*38e8c45fSAndroid Build Coastguard Worker     // match those, taking into account that a behavior can specify that it handles
446*38e8c45fSAndroid Build Coastguard Worker     // one, both or either of a left/right modifier pair.
447*38e8c45fSAndroid Build Coastguard Worker     if ((eventMetaState & behaviorMetaState) == behaviorMetaState) {
448*38e8c45fSAndroid Build Coastguard Worker         const int32_t EXACT_META_STATES =
449*38e8c45fSAndroid Build Coastguard Worker                 AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON
450*38e8c45fSAndroid Build Coastguard Worker                 | AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON
451*38e8c45fSAndroid Build Coastguard Worker                 | AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON;
452*38e8c45fSAndroid Build Coastguard Worker         int32_t unmatchedMetaState = eventMetaState & ~behaviorMetaState & EXACT_META_STATES;
453*38e8c45fSAndroid Build Coastguard Worker         if (behaviorMetaState & AMETA_CTRL_ON) {
454*38e8c45fSAndroid Build Coastguard Worker             unmatchedMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
455*38e8c45fSAndroid Build Coastguard Worker         } else if (behaviorMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
456*38e8c45fSAndroid Build Coastguard Worker             unmatchedMetaState &= ~AMETA_CTRL_ON;
457*38e8c45fSAndroid Build Coastguard Worker         }
458*38e8c45fSAndroid Build Coastguard Worker         if (behaviorMetaState & AMETA_ALT_ON) {
459*38e8c45fSAndroid Build Coastguard Worker             unmatchedMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
460*38e8c45fSAndroid Build Coastguard Worker         } else if (behaviorMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
461*38e8c45fSAndroid Build Coastguard Worker             unmatchedMetaState &= ~AMETA_ALT_ON;
462*38e8c45fSAndroid Build Coastguard Worker         }
463*38e8c45fSAndroid Build Coastguard Worker         if (behaviorMetaState & AMETA_META_ON) {
464*38e8c45fSAndroid Build Coastguard Worker             unmatchedMetaState &= ~(AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
465*38e8c45fSAndroid Build Coastguard Worker         } else if (behaviorMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
466*38e8c45fSAndroid Build Coastguard Worker             unmatchedMetaState &= ~AMETA_META_ON;
467*38e8c45fSAndroid Build Coastguard Worker         }
468*38e8c45fSAndroid Build Coastguard Worker         return !unmatchedMetaState;
469*38e8c45fSAndroid Build Coastguard Worker     }
470*38e8c45fSAndroid Build Coastguard Worker     return false;
471*38e8c45fSAndroid Build Coastguard Worker }
472*38e8c45fSAndroid Build Coastguard Worker 
findKey(char16_t ch,int32_t * outKeyCode,int32_t * outMetaState) const473*38e8c45fSAndroid Build Coastguard Worker bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const {
474*38e8c45fSAndroid Build Coastguard Worker     if (!ch) {
475*38e8c45fSAndroid Build Coastguard Worker         return false;
476*38e8c45fSAndroid Build Coastguard Worker     }
477*38e8c45fSAndroid Build Coastguard Worker 
478*38e8c45fSAndroid Build Coastguard Worker     for (const auto& [keyCode, key] : mKeys) {
479*38e8c45fSAndroid Build Coastguard Worker         // Try to find the most general behavior that maps to this character.
480*38e8c45fSAndroid Build Coastguard Worker         // For example, the base key behavior will usually be last in the list.
481*38e8c45fSAndroid Build Coastguard Worker         const Behavior* found = nullptr;
482*38e8c45fSAndroid Build Coastguard Worker         for (const Behavior& behavior : key.behaviors) {
483*38e8c45fSAndroid Build Coastguard Worker             if (behavior.character == ch) {
484*38e8c45fSAndroid Build Coastguard Worker                 found = &behavior;
485*38e8c45fSAndroid Build Coastguard Worker             }
486*38e8c45fSAndroid Build Coastguard Worker         }
487*38e8c45fSAndroid Build Coastguard Worker         if (found != nullptr) {
488*38e8c45fSAndroid Build Coastguard Worker             *outKeyCode = keyCode;
489*38e8c45fSAndroid Build Coastguard Worker             *outMetaState = found->metaState;
490*38e8c45fSAndroid Build Coastguard Worker             return true;
491*38e8c45fSAndroid Build Coastguard Worker         }
492*38e8c45fSAndroid Build Coastguard Worker     }
493*38e8c45fSAndroid Build Coastguard Worker     return false;
494*38e8c45fSAndroid Build Coastguard Worker }
495*38e8c45fSAndroid Build Coastguard Worker 
addKey(Vector<KeyEvent> & outEvents,int32_t deviceId,int32_t keyCode,int32_t metaState,bool down,nsecs_t time)496*38e8c45fSAndroid Build Coastguard Worker void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents, int32_t deviceId, int32_t keyCode,
497*38e8c45fSAndroid Build Coastguard Worker                              int32_t metaState, bool down, nsecs_t time) {
498*38e8c45fSAndroid Build Coastguard Worker     outEvents.push();
499*38e8c45fSAndroid Build Coastguard Worker     KeyEvent& event = outEvents.editTop();
500*38e8c45fSAndroid Build Coastguard Worker     event.initialize(InputEvent::nextId(), deviceId, AINPUT_SOURCE_KEYBOARD,
501*38e8c45fSAndroid Build Coastguard Worker                      ui::LogicalDisplayId::INVALID, INVALID_HMAC,
502*38e8c45fSAndroid Build Coastguard Worker                      down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState,
503*38e8c45fSAndroid Build Coastguard Worker                      0, time, time);
504*38e8c45fSAndroid Build Coastguard Worker }
505*38e8c45fSAndroid Build Coastguard Worker 
addMetaKeys(Vector<KeyEvent> & outEvents,int32_t deviceId,int32_t metaState,bool down,nsecs_t time,int32_t * currentMetaState)506*38e8c45fSAndroid Build Coastguard Worker void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
507*38e8c45fSAndroid Build Coastguard Worker         int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
508*38e8c45fSAndroid Build Coastguard Worker         int32_t* currentMetaState) {
509*38e8c45fSAndroid Build Coastguard Worker     // Add and remove meta keys symmetrically.
510*38e8c45fSAndroid Build Coastguard Worker     if (down) {
511*38e8c45fSAndroid Build Coastguard Worker         addLockedMetaKey(outEvents, deviceId, metaState, time,
512*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
513*38e8c45fSAndroid Build Coastguard Worker         addLockedMetaKey(outEvents, deviceId, metaState, time,
514*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
515*38e8c45fSAndroid Build Coastguard Worker         addLockedMetaKey(outEvents, deviceId, metaState, time,
516*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
517*38e8c45fSAndroid Build Coastguard Worker 
518*38e8c45fSAndroid Build Coastguard Worker         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
519*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
520*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
521*38e8c45fSAndroid Build Coastguard Worker                 AMETA_SHIFT_ON, currentMetaState);
522*38e8c45fSAndroid Build Coastguard Worker         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
523*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
524*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
525*38e8c45fSAndroid Build Coastguard Worker                 AMETA_ALT_ON, currentMetaState);
526*38e8c45fSAndroid Build Coastguard Worker         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
527*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
528*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
529*38e8c45fSAndroid Build Coastguard Worker                 AMETA_CTRL_ON, currentMetaState);
530*38e8c45fSAndroid Build Coastguard Worker         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
531*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
532*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
533*38e8c45fSAndroid Build Coastguard Worker                 AMETA_META_ON, currentMetaState);
534*38e8c45fSAndroid Build Coastguard Worker 
535*38e8c45fSAndroid Build Coastguard Worker         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
536*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
537*38e8c45fSAndroid Build Coastguard Worker         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
538*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
539*38e8c45fSAndroid Build Coastguard Worker     } else {
540*38e8c45fSAndroid Build Coastguard Worker         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
541*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
542*38e8c45fSAndroid Build Coastguard Worker         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
543*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
544*38e8c45fSAndroid Build Coastguard Worker 
545*38e8c45fSAndroid Build Coastguard Worker         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
546*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
547*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
548*38e8c45fSAndroid Build Coastguard Worker                 AMETA_META_ON, currentMetaState);
549*38e8c45fSAndroid Build Coastguard Worker         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
550*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
551*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
552*38e8c45fSAndroid Build Coastguard Worker                 AMETA_CTRL_ON, currentMetaState);
553*38e8c45fSAndroid Build Coastguard Worker         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
554*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
555*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
556*38e8c45fSAndroid Build Coastguard Worker                 AMETA_ALT_ON, currentMetaState);
557*38e8c45fSAndroid Build Coastguard Worker         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
558*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
559*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
560*38e8c45fSAndroid Build Coastguard Worker                 AMETA_SHIFT_ON, currentMetaState);
561*38e8c45fSAndroid Build Coastguard Worker 
562*38e8c45fSAndroid Build Coastguard Worker         addLockedMetaKey(outEvents, deviceId, metaState, time,
563*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
564*38e8c45fSAndroid Build Coastguard Worker         addLockedMetaKey(outEvents, deviceId, metaState, time,
565*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
566*38e8c45fSAndroid Build Coastguard Worker         addLockedMetaKey(outEvents, deviceId, metaState, time,
567*38e8c45fSAndroid Build Coastguard Worker                 AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
568*38e8c45fSAndroid Build Coastguard Worker     }
569*38e8c45fSAndroid Build Coastguard Worker }
570*38e8c45fSAndroid Build Coastguard Worker 
addSingleEphemeralMetaKey(Vector<KeyEvent> & outEvents,int32_t deviceId,int32_t metaState,bool down,nsecs_t time,int32_t keyCode,int32_t keyMetaState,int32_t * currentMetaState)571*38e8c45fSAndroid Build Coastguard Worker bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
572*38e8c45fSAndroid Build Coastguard Worker         int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
573*38e8c45fSAndroid Build Coastguard Worker         int32_t keyCode, int32_t keyMetaState,
574*38e8c45fSAndroid Build Coastguard Worker         int32_t* currentMetaState) {
575*38e8c45fSAndroid Build Coastguard Worker     if ((metaState & keyMetaState) == keyMetaState) {
576*38e8c45fSAndroid Build Coastguard Worker         *currentMetaState = updateMetaState(keyCode, down, *currentMetaState);
577*38e8c45fSAndroid Build Coastguard Worker         addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time);
578*38e8c45fSAndroid Build Coastguard Worker         return true;
579*38e8c45fSAndroid Build Coastguard Worker     }
580*38e8c45fSAndroid Build Coastguard Worker     return false;
581*38e8c45fSAndroid Build Coastguard Worker }
582*38e8c45fSAndroid Build Coastguard Worker 
addDoubleEphemeralMetaKey(Vector<KeyEvent> & outEvents,int32_t deviceId,int32_t metaState,bool down,nsecs_t time,int32_t leftKeyCode,int32_t leftKeyMetaState,int32_t rightKeyCode,int32_t rightKeyMetaState,int32_t eitherKeyMetaState,int32_t * currentMetaState)583*38e8c45fSAndroid Build Coastguard Worker void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
584*38e8c45fSAndroid Build Coastguard Worker         int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
585*38e8c45fSAndroid Build Coastguard Worker         int32_t leftKeyCode, int32_t leftKeyMetaState,
586*38e8c45fSAndroid Build Coastguard Worker         int32_t rightKeyCode, int32_t rightKeyMetaState,
587*38e8c45fSAndroid Build Coastguard Worker         int32_t eitherKeyMetaState,
588*38e8c45fSAndroid Build Coastguard Worker         int32_t* currentMetaState) {
589*38e8c45fSAndroid Build Coastguard Worker     bool specific = false;
590*38e8c45fSAndroid Build Coastguard Worker     specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
591*38e8c45fSAndroid Build Coastguard Worker             leftKeyCode, leftKeyMetaState, currentMetaState);
592*38e8c45fSAndroid Build Coastguard Worker     specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
593*38e8c45fSAndroid Build Coastguard Worker             rightKeyCode, rightKeyMetaState, currentMetaState);
594*38e8c45fSAndroid Build Coastguard Worker 
595*38e8c45fSAndroid Build Coastguard Worker     if (!specific) {
596*38e8c45fSAndroid Build Coastguard Worker         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
597*38e8c45fSAndroid Build Coastguard Worker                 leftKeyCode, eitherKeyMetaState, currentMetaState);
598*38e8c45fSAndroid Build Coastguard Worker     }
599*38e8c45fSAndroid Build Coastguard Worker }
600*38e8c45fSAndroid Build Coastguard Worker 
addLockedMetaKey(Vector<KeyEvent> & outEvents,int32_t deviceId,int32_t metaState,nsecs_t time,int32_t keyCode,int32_t keyMetaState,int32_t * currentMetaState)601*38e8c45fSAndroid Build Coastguard Worker void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents,
602*38e8c45fSAndroid Build Coastguard Worker         int32_t deviceId, int32_t metaState, nsecs_t time,
603*38e8c45fSAndroid Build Coastguard Worker         int32_t keyCode, int32_t keyMetaState,
604*38e8c45fSAndroid Build Coastguard Worker         int32_t* currentMetaState) {
605*38e8c45fSAndroid Build Coastguard Worker     if ((metaState & keyMetaState) == keyMetaState) {
606*38e8c45fSAndroid Build Coastguard Worker         *currentMetaState = updateMetaState(keyCode, true, *currentMetaState);
607*38e8c45fSAndroid Build Coastguard Worker         addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time);
608*38e8c45fSAndroid Build Coastguard Worker         *currentMetaState = updateMetaState(keyCode, false, *currentMetaState);
609*38e8c45fSAndroid Build Coastguard Worker         addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time);
610*38e8c45fSAndroid Build Coastguard Worker     }
611*38e8c45fSAndroid Build Coastguard Worker }
612*38e8c45fSAndroid Build Coastguard Worker 
readFromParcel(Parcel * parcel)613*38e8c45fSAndroid Build Coastguard Worker std::unique_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
614*38e8c45fSAndroid Build Coastguard Worker     if (parcel == nullptr) {
615*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Null parcel", __func__);
616*38e8c45fSAndroid Build Coastguard Worker         return nullptr;
617*38e8c45fSAndroid Build Coastguard Worker     }
618*38e8c45fSAndroid Build Coastguard Worker     std::string loadFileName = parcel->readCString();
619*38e8c45fSAndroid Build Coastguard Worker     std::unique_ptr<KeyCharacterMap> map =
620*38e8c45fSAndroid Build Coastguard Worker             std::make_unique<KeyCharacterMap>(KeyCharacterMap(loadFileName));
621*38e8c45fSAndroid Build Coastguard Worker     map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32());
622*38e8c45fSAndroid Build Coastguard Worker     map->mLayoutOverlayApplied = parcel->readBool();
623*38e8c45fSAndroid Build Coastguard Worker     size_t numKeys = parcel->readInt32();
624*38e8c45fSAndroid Build Coastguard Worker     if (parcel->errorCheck()) {
625*38e8c45fSAndroid Build Coastguard Worker         return nullptr;
626*38e8c45fSAndroid Build Coastguard Worker     }
627*38e8c45fSAndroid Build Coastguard Worker     if (numKeys > MAX_KEYS) {
628*38e8c45fSAndroid Build Coastguard Worker         ALOGE("Too many keys in KeyCharacterMap (%zu > %d)", numKeys, MAX_KEYS);
629*38e8c45fSAndroid Build Coastguard Worker         return nullptr;
630*38e8c45fSAndroid Build Coastguard Worker     }
631*38e8c45fSAndroid Build Coastguard Worker 
632*38e8c45fSAndroid Build Coastguard Worker     for (size_t i = 0; i < numKeys; i++) {
633*38e8c45fSAndroid Build Coastguard Worker         int32_t keyCode = parcel->readInt32();
634*38e8c45fSAndroid Build Coastguard Worker         char16_t label = parcel->readInt32();
635*38e8c45fSAndroid Build Coastguard Worker         char16_t number = parcel->readInt32();
636*38e8c45fSAndroid Build Coastguard Worker         if (parcel->errorCheck()) {
637*38e8c45fSAndroid Build Coastguard Worker             return nullptr;
638*38e8c45fSAndroid Build Coastguard Worker         }
639*38e8c45fSAndroid Build Coastguard Worker 
640*38e8c45fSAndroid Build Coastguard Worker         Key key{.label = label, .number = number};
641*38e8c45fSAndroid Build Coastguard Worker         while (parcel->readInt32()) {
642*38e8c45fSAndroid Build Coastguard Worker             int32_t metaState = parcel->readInt32();
643*38e8c45fSAndroid Build Coastguard Worker             char16_t character = parcel->readInt32();
644*38e8c45fSAndroid Build Coastguard Worker             int32_t fallbackKeyCode = parcel->readInt32();
645*38e8c45fSAndroid Build Coastguard Worker             int32_t replacementKeyCode = parcel->readInt32();
646*38e8c45fSAndroid Build Coastguard Worker             if (parcel->errorCheck()) {
647*38e8c45fSAndroid Build Coastguard Worker                 return nullptr;
648*38e8c45fSAndroid Build Coastguard Worker             }
649*38e8c45fSAndroid Build Coastguard Worker 
650*38e8c45fSAndroid Build Coastguard Worker             key.behaviors.push_back({
651*38e8c45fSAndroid Build Coastguard Worker                     .metaState = metaState,
652*38e8c45fSAndroid Build Coastguard Worker                     .character = character,
653*38e8c45fSAndroid Build Coastguard Worker                     .fallbackKeyCode = fallbackKeyCode,
654*38e8c45fSAndroid Build Coastguard Worker                     .replacementKeyCode = replacementKeyCode,
655*38e8c45fSAndroid Build Coastguard Worker             });
656*38e8c45fSAndroid Build Coastguard Worker         }
657*38e8c45fSAndroid Build Coastguard Worker         map->mKeys.emplace(keyCode, std::move(key));
658*38e8c45fSAndroid Build Coastguard Worker 
659*38e8c45fSAndroid Build Coastguard Worker         if (parcel->errorCheck()) {
660*38e8c45fSAndroid Build Coastguard Worker             return nullptr;
661*38e8c45fSAndroid Build Coastguard Worker         }
662*38e8c45fSAndroid Build Coastguard Worker     }
663*38e8c45fSAndroid Build Coastguard Worker     size_t numKeyRemapping = parcel->readInt32();
664*38e8c45fSAndroid Build Coastguard Worker     if (parcel->errorCheck()) {
665*38e8c45fSAndroid Build Coastguard Worker         return nullptr;
666*38e8c45fSAndroid Build Coastguard Worker     }
667*38e8c45fSAndroid Build Coastguard Worker     for (size_t i = 0; i < numKeyRemapping; i++) {
668*38e8c45fSAndroid Build Coastguard Worker         int32_t key = parcel->readInt32();
669*38e8c45fSAndroid Build Coastguard Worker         int32_t value = parcel->readInt32();
670*38e8c45fSAndroid Build Coastguard Worker         map->mKeyRemapping.insert_or_assign(key, value);
671*38e8c45fSAndroid Build Coastguard Worker         if (parcel->errorCheck()) {
672*38e8c45fSAndroid Build Coastguard Worker             return nullptr;
673*38e8c45fSAndroid Build Coastguard Worker         }
674*38e8c45fSAndroid Build Coastguard Worker     }
675*38e8c45fSAndroid Build Coastguard Worker     size_t numKeysByScanCode = parcel->readInt32();
676*38e8c45fSAndroid Build Coastguard Worker     if (parcel->errorCheck()) {
677*38e8c45fSAndroid Build Coastguard Worker         return nullptr;
678*38e8c45fSAndroid Build Coastguard Worker     }
679*38e8c45fSAndroid Build Coastguard Worker     for (size_t i = 0; i < numKeysByScanCode; i++) {
680*38e8c45fSAndroid Build Coastguard Worker         int32_t key = parcel->readInt32();
681*38e8c45fSAndroid Build Coastguard Worker         int32_t value = parcel->readInt32();
682*38e8c45fSAndroid Build Coastguard Worker         map->mKeysByScanCode.insert_or_assign(key, value);
683*38e8c45fSAndroid Build Coastguard Worker         if (parcel->errorCheck()) {
684*38e8c45fSAndroid Build Coastguard Worker             return nullptr;
685*38e8c45fSAndroid Build Coastguard Worker         }
686*38e8c45fSAndroid Build Coastguard Worker     }
687*38e8c45fSAndroid Build Coastguard Worker     size_t numKeysByUsageCode = parcel->readInt32();
688*38e8c45fSAndroid Build Coastguard Worker     if (parcel->errorCheck()) {
689*38e8c45fSAndroid Build Coastguard Worker         return nullptr;
690*38e8c45fSAndroid Build Coastguard Worker     }
691*38e8c45fSAndroid Build Coastguard Worker     for (size_t i = 0; i < numKeysByUsageCode; i++) {
692*38e8c45fSAndroid Build Coastguard Worker         int32_t key = parcel->readInt32();
693*38e8c45fSAndroid Build Coastguard Worker         int32_t value = parcel->readInt32();
694*38e8c45fSAndroid Build Coastguard Worker         map->mKeysByUsageCode.insert_or_assign(key, value);
695*38e8c45fSAndroid Build Coastguard Worker         if (parcel->errorCheck()) {
696*38e8c45fSAndroid Build Coastguard Worker             return nullptr;
697*38e8c45fSAndroid Build Coastguard Worker         }
698*38e8c45fSAndroid Build Coastguard Worker     }
699*38e8c45fSAndroid Build Coastguard Worker     return map;
700*38e8c45fSAndroid Build Coastguard Worker }
701*38e8c45fSAndroid Build Coastguard Worker 
writeToParcel(Parcel * parcel) const702*38e8c45fSAndroid Build Coastguard Worker void KeyCharacterMap::writeToParcel(Parcel* parcel) const {
703*38e8c45fSAndroid Build Coastguard Worker     if (parcel == nullptr) {
704*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Null parcel", __func__);
705*38e8c45fSAndroid Build Coastguard Worker         return;
706*38e8c45fSAndroid Build Coastguard Worker     }
707*38e8c45fSAndroid Build Coastguard Worker     parcel->writeCString(mLoadFileName.c_str());
708*38e8c45fSAndroid Build Coastguard Worker     parcel->writeInt32(static_cast<int32_t>(mType));
709*38e8c45fSAndroid Build Coastguard Worker     parcel->writeBool(mLayoutOverlayApplied);
710*38e8c45fSAndroid Build Coastguard Worker 
711*38e8c45fSAndroid Build Coastguard Worker     size_t numKeys = mKeys.size();
712*38e8c45fSAndroid Build Coastguard Worker     parcel->writeInt32(numKeys);
713*38e8c45fSAndroid Build Coastguard Worker     for (const auto& [keyCode, key] : mKeys) {
714*38e8c45fSAndroid Build Coastguard Worker         parcel->writeInt32(keyCode);
715*38e8c45fSAndroid Build Coastguard Worker         parcel->writeInt32(key.label);
716*38e8c45fSAndroid Build Coastguard Worker         parcel->writeInt32(key.number);
717*38e8c45fSAndroid Build Coastguard Worker         for (const Behavior& behavior : key.behaviors) {
718*38e8c45fSAndroid Build Coastguard Worker             parcel->writeInt32(1);
719*38e8c45fSAndroid Build Coastguard Worker             parcel->writeInt32(behavior.metaState);
720*38e8c45fSAndroid Build Coastguard Worker             parcel->writeInt32(behavior.character);
721*38e8c45fSAndroid Build Coastguard Worker             parcel->writeInt32(behavior.fallbackKeyCode);
722*38e8c45fSAndroid Build Coastguard Worker             parcel->writeInt32(behavior.replacementKeyCode);
723*38e8c45fSAndroid Build Coastguard Worker         }
724*38e8c45fSAndroid Build Coastguard Worker         parcel->writeInt32(0);
725*38e8c45fSAndroid Build Coastguard Worker     }
726*38e8c45fSAndroid Build Coastguard Worker     size_t numKeyRemapping = mKeyRemapping.size();
727*38e8c45fSAndroid Build Coastguard Worker     parcel->writeInt32(numKeyRemapping);
728*38e8c45fSAndroid Build Coastguard Worker     for (auto const& [fromAndroidKeyCode, toAndroidKeyCode] : mKeyRemapping) {
729*38e8c45fSAndroid Build Coastguard Worker         parcel->writeInt32(fromAndroidKeyCode);
730*38e8c45fSAndroid Build Coastguard Worker         parcel->writeInt32(toAndroidKeyCode);
731*38e8c45fSAndroid Build Coastguard Worker     }
732*38e8c45fSAndroid Build Coastguard Worker     size_t numKeysByScanCode = mKeysByScanCode.size();
733*38e8c45fSAndroid Build Coastguard Worker     parcel->writeInt32(numKeysByScanCode);
734*38e8c45fSAndroid Build Coastguard Worker     for (auto const& [fromScanCode, toAndroidKeyCode] : mKeysByScanCode) {
735*38e8c45fSAndroid Build Coastguard Worker         parcel->writeInt32(fromScanCode);
736*38e8c45fSAndroid Build Coastguard Worker         parcel->writeInt32(toAndroidKeyCode);
737*38e8c45fSAndroid Build Coastguard Worker     }
738*38e8c45fSAndroid Build Coastguard Worker     size_t numKeysByUsageCode = mKeysByUsageCode.size();
739*38e8c45fSAndroid Build Coastguard Worker     parcel->writeInt32(numKeysByUsageCode);
740*38e8c45fSAndroid Build Coastguard Worker     for (auto const& [fromUsageCode, toAndroidKeyCode] : mKeysByUsageCode) {
741*38e8c45fSAndroid Build Coastguard Worker         parcel->writeInt32(fromUsageCode);
742*38e8c45fSAndroid Build Coastguard Worker         parcel->writeInt32(toAndroidKeyCode);
743*38e8c45fSAndroid Build Coastguard Worker     }
744*38e8c45fSAndroid Build Coastguard Worker }
745*38e8c45fSAndroid Build Coastguard Worker 
746*38e8c45fSAndroid Build Coastguard Worker // --- KeyCharacterMap::Parser ---
747*38e8c45fSAndroid Build Coastguard Worker 
Parser(KeyCharacterMap * map,Tokenizer * tokenizer,Format format)748*38e8c45fSAndroid Build Coastguard Worker KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) :
749*38e8c45fSAndroid Build Coastguard Worker         mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) {
750*38e8c45fSAndroid Build Coastguard Worker }
751*38e8c45fSAndroid Build Coastguard Worker 
parse()752*38e8c45fSAndroid Build Coastguard Worker status_t KeyCharacterMap::Parser::parse() {
753*38e8c45fSAndroid Build Coastguard Worker     while (!mTokenizer->isEof()) {
754*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_PARSER
755*38e8c45fSAndroid Build Coastguard Worker         ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(),
756*38e8c45fSAndroid Build Coastguard Worker               mTokenizer->peekRemainderOfLine().c_str());
757*38e8c45fSAndroid Build Coastguard Worker #endif
758*38e8c45fSAndroid Build Coastguard Worker 
759*38e8c45fSAndroid Build Coastguard Worker         mTokenizer->skipDelimiters(WHITESPACE);
760*38e8c45fSAndroid Build Coastguard Worker 
761*38e8c45fSAndroid Build Coastguard Worker         if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
762*38e8c45fSAndroid Build Coastguard Worker             switch (mState) {
763*38e8c45fSAndroid Build Coastguard Worker             case STATE_TOP: {
764*38e8c45fSAndroid Build Coastguard Worker                 String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
765*38e8c45fSAndroid Build Coastguard Worker                 if (keywordToken == "type") {
766*38e8c45fSAndroid Build Coastguard Worker                     mTokenizer->skipDelimiters(WHITESPACE);
767*38e8c45fSAndroid Build Coastguard Worker                     status_t status = parseType();
768*38e8c45fSAndroid Build Coastguard Worker                     if (status) return status;
769*38e8c45fSAndroid Build Coastguard Worker                 } else if (keywordToken == "map") {
770*38e8c45fSAndroid Build Coastguard Worker                     mTokenizer->skipDelimiters(WHITESPACE);
771*38e8c45fSAndroid Build Coastguard Worker                     status_t status = parseMap();
772*38e8c45fSAndroid Build Coastguard Worker                     if (status) return status;
773*38e8c45fSAndroid Build Coastguard Worker                 } else if (keywordToken == "key") {
774*38e8c45fSAndroid Build Coastguard Worker                     mTokenizer->skipDelimiters(WHITESPACE);
775*38e8c45fSAndroid Build Coastguard Worker                     status_t status = parseKey();
776*38e8c45fSAndroid Build Coastguard Worker                     if (status) return status;
777*38e8c45fSAndroid Build Coastguard Worker                 } else {
778*38e8c45fSAndroid Build Coastguard Worker                     ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().c_str(),
779*38e8c45fSAndroid Build Coastguard Worker                           keywordToken.c_str());
780*38e8c45fSAndroid Build Coastguard Worker                     return BAD_VALUE;
781*38e8c45fSAndroid Build Coastguard Worker                 }
782*38e8c45fSAndroid Build Coastguard Worker                 break;
783*38e8c45fSAndroid Build Coastguard Worker             }
784*38e8c45fSAndroid Build Coastguard Worker 
785*38e8c45fSAndroid Build Coastguard Worker             case STATE_KEY: {
786*38e8c45fSAndroid Build Coastguard Worker                 status_t status = parseKeyProperty();
787*38e8c45fSAndroid Build Coastguard Worker                 if (status) return status;
788*38e8c45fSAndroid Build Coastguard Worker                 break;
789*38e8c45fSAndroid Build Coastguard Worker             }
790*38e8c45fSAndroid Build Coastguard Worker             }
791*38e8c45fSAndroid Build Coastguard Worker 
792*38e8c45fSAndroid Build Coastguard Worker             mTokenizer->skipDelimiters(WHITESPACE);
793*38e8c45fSAndroid Build Coastguard Worker             if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
794*38e8c45fSAndroid Build Coastguard Worker             ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
795*38e8c45fSAndroid Build Coastguard Worker                   mTokenizer->getLocation().c_str(), mTokenizer->peekRemainderOfLine().c_str());
796*38e8c45fSAndroid Build Coastguard Worker             return BAD_VALUE;
797*38e8c45fSAndroid Build Coastguard Worker             }
798*38e8c45fSAndroid Build Coastguard Worker         }
799*38e8c45fSAndroid Build Coastguard Worker 
800*38e8c45fSAndroid Build Coastguard Worker         mTokenizer->nextLine();
801*38e8c45fSAndroid Build Coastguard Worker     }
802*38e8c45fSAndroid Build Coastguard Worker 
803*38e8c45fSAndroid Build Coastguard Worker     if (mState != STATE_TOP) {
804*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Unterminated key description at end of file.",
805*38e8c45fSAndroid Build Coastguard Worker               mTokenizer->getLocation().c_str());
806*38e8c45fSAndroid Build Coastguard Worker         return BAD_VALUE;
807*38e8c45fSAndroid Build Coastguard Worker     }
808*38e8c45fSAndroid Build Coastguard Worker 
809*38e8c45fSAndroid Build Coastguard Worker     if (mMap->mType == KeyboardType::UNKNOWN) {
810*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.",
811*38e8c45fSAndroid Build Coastguard Worker               mTokenizer->getLocation().c_str());
812*38e8c45fSAndroid Build Coastguard Worker         return BAD_VALUE;
813*38e8c45fSAndroid Build Coastguard Worker     }
814*38e8c45fSAndroid Build Coastguard Worker 
815*38e8c45fSAndroid Build Coastguard Worker     if (mFormat == Format::BASE) {
816*38e8c45fSAndroid Build Coastguard Worker         if (mMap->mType == KeyboardType::OVERLAY) {
817*38e8c45fSAndroid Build Coastguard Worker             ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
818*38e8c45fSAndroid Build Coastguard Worker                   mTokenizer->getLocation().c_str());
819*38e8c45fSAndroid Build Coastguard Worker             return BAD_VALUE;
820*38e8c45fSAndroid Build Coastguard Worker         }
821*38e8c45fSAndroid Build Coastguard Worker     } else if (mFormat == Format::OVERLAY) {
822*38e8c45fSAndroid Build Coastguard Worker         if (mMap->mType != KeyboardType::OVERLAY) {
823*38e8c45fSAndroid Build Coastguard Worker             ALOGE("%s: Overlay keyboard layout missing required keyboard "
824*38e8c45fSAndroid Build Coastguard Worker                   "'type OVERLAY' declaration.",
825*38e8c45fSAndroid Build Coastguard Worker                   mTokenizer->getLocation().c_str());
826*38e8c45fSAndroid Build Coastguard Worker             return BAD_VALUE;
827*38e8c45fSAndroid Build Coastguard Worker         }
828*38e8c45fSAndroid Build Coastguard Worker     }
829*38e8c45fSAndroid Build Coastguard Worker 
830*38e8c45fSAndroid Build Coastguard Worker     return NO_ERROR;
831*38e8c45fSAndroid Build Coastguard Worker }
832*38e8c45fSAndroid Build Coastguard Worker 
parseType()833*38e8c45fSAndroid Build Coastguard Worker status_t KeyCharacterMap::Parser::parseType() {
834*38e8c45fSAndroid Build Coastguard Worker     if (mMap->mType != KeyboardType::UNKNOWN) {
835*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Duplicate keyboard 'type' declaration.", mTokenizer->getLocation().c_str());
836*38e8c45fSAndroid Build Coastguard Worker         return BAD_VALUE;
837*38e8c45fSAndroid Build Coastguard Worker     }
838*38e8c45fSAndroid Build Coastguard Worker 
839*38e8c45fSAndroid Build Coastguard Worker     KeyboardType type;
840*38e8c45fSAndroid Build Coastguard Worker     String8 typeToken = mTokenizer->nextToken(WHITESPACE);
841*38e8c45fSAndroid Build Coastguard Worker     if (typeToken == "NUMERIC") {
842*38e8c45fSAndroid Build Coastguard Worker         type = KeyboardType::NUMERIC;
843*38e8c45fSAndroid Build Coastguard Worker     } else if (typeToken == "PREDICTIVE") {
844*38e8c45fSAndroid Build Coastguard Worker         type = KeyboardType::PREDICTIVE;
845*38e8c45fSAndroid Build Coastguard Worker     } else if (typeToken == "ALPHA") {
846*38e8c45fSAndroid Build Coastguard Worker         type = KeyboardType::ALPHA;
847*38e8c45fSAndroid Build Coastguard Worker     } else if (typeToken == "FULL") {
848*38e8c45fSAndroid Build Coastguard Worker         type = KeyboardType::FULL;
849*38e8c45fSAndroid Build Coastguard Worker     } else if (typeToken == "SPECIAL_FUNCTION") {
850*38e8c45fSAndroid Build Coastguard Worker         ALOGW("The SPECIAL_FUNCTION type is now declared in the device's IDC file, please set "
851*38e8c45fSAndroid Build Coastguard Worker                 "the property 'keyboard.specialFunction' to '1' there instead.");
852*38e8c45fSAndroid Build Coastguard Worker         // TODO: return BAD_VALUE here in Q
853*38e8c45fSAndroid Build Coastguard Worker         type = KeyboardType::SPECIAL_FUNCTION;
854*38e8c45fSAndroid Build Coastguard Worker     } else if (typeToken == "OVERLAY") {
855*38e8c45fSAndroid Build Coastguard Worker         type = KeyboardType::OVERLAY;
856*38e8c45fSAndroid Build Coastguard Worker     } else {
857*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().c_str(),
858*38e8c45fSAndroid Build Coastguard Worker               typeToken.c_str());
859*38e8c45fSAndroid Build Coastguard Worker         return BAD_VALUE;
860*38e8c45fSAndroid Build Coastguard Worker     }
861*38e8c45fSAndroid Build Coastguard Worker 
862*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_PARSER
863*38e8c45fSAndroid Build Coastguard Worker     ALOGD("Parsed type: type=%d.", type);
864*38e8c45fSAndroid Build Coastguard Worker #endif
865*38e8c45fSAndroid Build Coastguard Worker     mMap->mType = type;
866*38e8c45fSAndroid Build Coastguard Worker     return NO_ERROR;
867*38e8c45fSAndroid Build Coastguard Worker }
868*38e8c45fSAndroid Build Coastguard Worker 
parseMap()869*38e8c45fSAndroid Build Coastguard Worker status_t KeyCharacterMap::Parser::parseMap() {
870*38e8c45fSAndroid Build Coastguard Worker     String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
871*38e8c45fSAndroid Build Coastguard Worker     if (keywordToken == "key") {
872*38e8c45fSAndroid Build Coastguard Worker         mTokenizer->skipDelimiters(WHITESPACE);
873*38e8c45fSAndroid Build Coastguard Worker         return parseMapKey();
874*38e8c45fSAndroid Build Coastguard Worker     }
875*38e8c45fSAndroid Build Coastguard Worker     ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().c_str(),
876*38e8c45fSAndroid Build Coastguard Worker           keywordToken.c_str());
877*38e8c45fSAndroid Build Coastguard Worker     return BAD_VALUE;
878*38e8c45fSAndroid Build Coastguard Worker }
879*38e8c45fSAndroid Build Coastguard Worker 
parseMapKey()880*38e8c45fSAndroid Build Coastguard Worker status_t KeyCharacterMap::Parser::parseMapKey() {
881*38e8c45fSAndroid Build Coastguard Worker     String8 codeToken = mTokenizer->nextToken(WHITESPACE);
882*38e8c45fSAndroid Build Coastguard Worker     bool mapUsage = false;
883*38e8c45fSAndroid Build Coastguard Worker     if (codeToken == "usage") {
884*38e8c45fSAndroid Build Coastguard Worker         mapUsage = true;
885*38e8c45fSAndroid Build Coastguard Worker         mTokenizer->skipDelimiters(WHITESPACE);
886*38e8c45fSAndroid Build Coastguard Worker         codeToken = mTokenizer->nextToken(WHITESPACE);
887*38e8c45fSAndroid Build Coastguard Worker     }
888*38e8c45fSAndroid Build Coastguard Worker 
889*38e8c45fSAndroid Build Coastguard Worker     char* end;
890*38e8c45fSAndroid Build Coastguard Worker     int32_t code = int32_t(strtol(codeToken.c_str(), &end, 0));
891*38e8c45fSAndroid Build Coastguard Worker     if (*end) {
892*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().c_str(),
893*38e8c45fSAndroid Build Coastguard Worker               mapUsage ? "usage" : "scan code", codeToken.c_str());
894*38e8c45fSAndroid Build Coastguard Worker         return BAD_VALUE;
895*38e8c45fSAndroid Build Coastguard Worker     }
896*38e8c45fSAndroid Build Coastguard Worker     std::map<int32_t, int32_t>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
897*38e8c45fSAndroid Build Coastguard Worker     const auto it = map.find(code);
898*38e8c45fSAndroid Build Coastguard Worker     if (it != map.end()) {
899*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().c_str(),
900*38e8c45fSAndroid Build Coastguard Worker                 mapUsage ? "usage" : "scan code", codeToken.c_str());
901*38e8c45fSAndroid Build Coastguard Worker         return BAD_VALUE;
902*38e8c45fSAndroid Build Coastguard Worker     }
903*38e8c45fSAndroid Build Coastguard Worker 
904*38e8c45fSAndroid Build Coastguard Worker     mTokenizer->skipDelimiters(WHITESPACE);
905*38e8c45fSAndroid Build Coastguard Worker     String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
906*38e8c45fSAndroid Build Coastguard Worker     std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str());
907*38e8c45fSAndroid Build Coastguard Worker     if (!keyCode) {
908*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(),
909*38e8c45fSAndroid Build Coastguard Worker               keyCodeToken.c_str());
910*38e8c45fSAndroid Build Coastguard Worker         return BAD_VALUE;
911*38e8c45fSAndroid Build Coastguard Worker     }
912*38e8c45fSAndroid Build Coastguard Worker 
913*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_PARSER
914*38e8c45fSAndroid Build Coastguard Worker     ALOGD("Parsed map key %s: code=%d, keyCode=%d.",
915*38e8c45fSAndroid Build Coastguard Worker             mapUsage ? "usage" : "scan code", code, keyCode);
916*38e8c45fSAndroid Build Coastguard Worker #endif
917*38e8c45fSAndroid Build Coastguard Worker     map.insert_or_assign(code, *keyCode);
918*38e8c45fSAndroid Build Coastguard Worker     return NO_ERROR;
919*38e8c45fSAndroid Build Coastguard Worker }
920*38e8c45fSAndroid Build Coastguard Worker 
parseKey()921*38e8c45fSAndroid Build Coastguard Worker status_t KeyCharacterMap::Parser::parseKey() {
922*38e8c45fSAndroid Build Coastguard Worker     String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
923*38e8c45fSAndroid Build Coastguard Worker     std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str());
924*38e8c45fSAndroid Build Coastguard Worker     if (!keyCode) {
925*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(),
926*38e8c45fSAndroid Build Coastguard Worker               keyCodeToken.c_str());
927*38e8c45fSAndroid Build Coastguard Worker         return BAD_VALUE;
928*38e8c45fSAndroid Build Coastguard Worker     }
929*38e8c45fSAndroid Build Coastguard Worker     if (mMap->mKeys.find(*keyCode) != mMap->mKeys.end()) {
930*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().c_str(),
931*38e8c45fSAndroid Build Coastguard Worker                 keyCodeToken.c_str());
932*38e8c45fSAndroid Build Coastguard Worker         return BAD_VALUE;
933*38e8c45fSAndroid Build Coastguard Worker     }
934*38e8c45fSAndroid Build Coastguard Worker 
935*38e8c45fSAndroid Build Coastguard Worker     mTokenizer->skipDelimiters(WHITESPACE);
936*38e8c45fSAndroid Build Coastguard Worker     String8 openBraceToken = mTokenizer->nextToken(WHITESPACE);
937*38e8c45fSAndroid Build Coastguard Worker     if (openBraceToken != "{") {
938*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Expected '{' after key code label, got '%s'.", mTokenizer->getLocation().c_str(),
939*38e8c45fSAndroid Build Coastguard Worker               openBraceToken.c_str());
940*38e8c45fSAndroid Build Coastguard Worker         return BAD_VALUE;
941*38e8c45fSAndroid Build Coastguard Worker     }
942*38e8c45fSAndroid Build Coastguard Worker 
943*38e8c45fSAndroid Build Coastguard Worker     ALOGD_IF(DEBUG_PARSER, "Parsed beginning of key: keyCode=%d.", *keyCode);
944*38e8c45fSAndroid Build Coastguard Worker     mKeyCode = *keyCode;
945*38e8c45fSAndroid Build Coastguard Worker     mMap->mKeys.emplace(*keyCode, Key{});
946*38e8c45fSAndroid Build Coastguard Worker     mState = STATE_KEY;
947*38e8c45fSAndroid Build Coastguard Worker     return NO_ERROR;
948*38e8c45fSAndroid Build Coastguard Worker }
949*38e8c45fSAndroid Build Coastguard Worker 
parseKeyProperty()950*38e8c45fSAndroid Build Coastguard Worker status_t KeyCharacterMap::Parser::parseKeyProperty() {
951*38e8c45fSAndroid Build Coastguard Worker     Key& key = mMap->mKeys[mKeyCode];
952*38e8c45fSAndroid Build Coastguard Worker     String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
953*38e8c45fSAndroid Build Coastguard Worker     if (token == "}") {
954*38e8c45fSAndroid Build Coastguard Worker         mState = STATE_TOP;
955*38e8c45fSAndroid Build Coastguard Worker         return finishKey(key);
956*38e8c45fSAndroid Build Coastguard Worker     }
957*38e8c45fSAndroid Build Coastguard Worker 
958*38e8c45fSAndroid Build Coastguard Worker     std::vector<Property> properties;
959*38e8c45fSAndroid Build Coastguard Worker 
960*38e8c45fSAndroid Build Coastguard Worker     // Parse all comma-delimited property names up to the first colon.
961*38e8c45fSAndroid Build Coastguard Worker     for (;;) {
962*38e8c45fSAndroid Build Coastguard Worker         if (token == "label") {
963*38e8c45fSAndroid Build Coastguard Worker             properties.emplace_back(PROPERTY_LABEL);
964*38e8c45fSAndroid Build Coastguard Worker         } else if (token == "number") {
965*38e8c45fSAndroid Build Coastguard Worker             properties.emplace_back(PROPERTY_NUMBER);
966*38e8c45fSAndroid Build Coastguard Worker         } else {
967*38e8c45fSAndroid Build Coastguard Worker             int32_t metaState;
968*38e8c45fSAndroid Build Coastguard Worker             status_t status = parseModifier(token.c_str(), &metaState);
969*38e8c45fSAndroid Build Coastguard Worker             if (status) {
970*38e8c45fSAndroid Build Coastguard Worker                 ALOGE("%s: Expected a property name or modifier, got '%s'.",
971*38e8c45fSAndroid Build Coastguard Worker                       mTokenizer->getLocation().c_str(), token.c_str());
972*38e8c45fSAndroid Build Coastguard Worker                 return status;
973*38e8c45fSAndroid Build Coastguard Worker             }
974*38e8c45fSAndroid Build Coastguard Worker             properties.emplace_back(PROPERTY_META, metaState);
975*38e8c45fSAndroid Build Coastguard Worker         }
976*38e8c45fSAndroid Build Coastguard Worker 
977*38e8c45fSAndroid Build Coastguard Worker         mTokenizer->skipDelimiters(WHITESPACE);
978*38e8c45fSAndroid Build Coastguard Worker         if (!mTokenizer->isEol()) {
979*38e8c45fSAndroid Build Coastguard Worker             char ch = mTokenizer->nextChar();
980*38e8c45fSAndroid Build Coastguard Worker             if (ch == ':') {
981*38e8c45fSAndroid Build Coastguard Worker                 break;
982*38e8c45fSAndroid Build Coastguard Worker             } else if (ch == ',') {
983*38e8c45fSAndroid Build Coastguard Worker                 mTokenizer->skipDelimiters(WHITESPACE);
984*38e8c45fSAndroid Build Coastguard Worker                 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
985*38e8c45fSAndroid Build Coastguard Worker                 continue;
986*38e8c45fSAndroid Build Coastguard Worker             }
987*38e8c45fSAndroid Build Coastguard Worker         }
988*38e8c45fSAndroid Build Coastguard Worker 
989*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Expected ',' or ':' after property name.", mTokenizer->getLocation().c_str());
990*38e8c45fSAndroid Build Coastguard Worker         return BAD_VALUE;
991*38e8c45fSAndroid Build Coastguard Worker     }
992*38e8c45fSAndroid Build Coastguard Worker 
993*38e8c45fSAndroid Build Coastguard Worker     // Parse behavior after the colon.
994*38e8c45fSAndroid Build Coastguard Worker     mTokenizer->skipDelimiters(WHITESPACE);
995*38e8c45fSAndroid Build Coastguard Worker 
996*38e8c45fSAndroid Build Coastguard Worker     Behavior behavior;
997*38e8c45fSAndroid Build Coastguard Worker     bool haveCharacter = false;
998*38e8c45fSAndroid Build Coastguard Worker     bool haveFallback = false;
999*38e8c45fSAndroid Build Coastguard Worker     bool haveReplacement = false;
1000*38e8c45fSAndroid Build Coastguard Worker 
1001*38e8c45fSAndroid Build Coastguard Worker     do {
1002*38e8c45fSAndroid Build Coastguard Worker         char ch = mTokenizer->peekChar();
1003*38e8c45fSAndroid Build Coastguard Worker         if (ch == '\'') {
1004*38e8c45fSAndroid Build Coastguard Worker             char16_t character;
1005*38e8c45fSAndroid Build Coastguard Worker             status_t status = parseCharacterLiteral(&character);
1006*38e8c45fSAndroid Build Coastguard Worker             if (status || !character) {
1007*38e8c45fSAndroid Build Coastguard Worker                 ALOGE("%s: Invalid character literal for key.", mTokenizer->getLocation().c_str());
1008*38e8c45fSAndroid Build Coastguard Worker                 return BAD_VALUE;
1009*38e8c45fSAndroid Build Coastguard Worker             }
1010*38e8c45fSAndroid Build Coastguard Worker             if (haveCharacter) {
1011*38e8c45fSAndroid Build Coastguard Worker                 ALOGE("%s: Cannot combine multiple character literals or 'none'.",
1012*38e8c45fSAndroid Build Coastguard Worker                       mTokenizer->getLocation().c_str());
1013*38e8c45fSAndroid Build Coastguard Worker                 return BAD_VALUE;
1014*38e8c45fSAndroid Build Coastguard Worker             }
1015*38e8c45fSAndroid Build Coastguard Worker             if (haveReplacement) {
1016*38e8c45fSAndroid Build Coastguard Worker                 ALOGE("%s: Cannot combine character literal with replace action.",
1017*38e8c45fSAndroid Build Coastguard Worker                       mTokenizer->getLocation().c_str());
1018*38e8c45fSAndroid Build Coastguard Worker                 return BAD_VALUE;
1019*38e8c45fSAndroid Build Coastguard Worker             }
1020*38e8c45fSAndroid Build Coastguard Worker             behavior.character = character;
1021*38e8c45fSAndroid Build Coastguard Worker             haveCharacter = true;
1022*38e8c45fSAndroid Build Coastguard Worker         } else {
1023*38e8c45fSAndroid Build Coastguard Worker             token = mTokenizer->nextToken(WHITESPACE);
1024*38e8c45fSAndroid Build Coastguard Worker             if (token == "none") {
1025*38e8c45fSAndroid Build Coastguard Worker                 if (haveCharacter) {
1026*38e8c45fSAndroid Build Coastguard Worker                     ALOGE("%s: Cannot combine multiple character literals or 'none'.",
1027*38e8c45fSAndroid Build Coastguard Worker                           mTokenizer->getLocation().c_str());
1028*38e8c45fSAndroid Build Coastguard Worker                     return BAD_VALUE;
1029*38e8c45fSAndroid Build Coastguard Worker                 }
1030*38e8c45fSAndroid Build Coastguard Worker                 if (haveReplacement) {
1031*38e8c45fSAndroid Build Coastguard Worker                     ALOGE("%s: Cannot combine 'none' with replace action.",
1032*38e8c45fSAndroid Build Coastguard Worker                           mTokenizer->getLocation().c_str());
1033*38e8c45fSAndroid Build Coastguard Worker                     return BAD_VALUE;
1034*38e8c45fSAndroid Build Coastguard Worker                 }
1035*38e8c45fSAndroid Build Coastguard Worker                 haveCharacter = true;
1036*38e8c45fSAndroid Build Coastguard Worker             } else if (token == "fallback") {
1037*38e8c45fSAndroid Build Coastguard Worker                 mTokenizer->skipDelimiters(WHITESPACE);
1038*38e8c45fSAndroid Build Coastguard Worker                 token = mTokenizer->nextToken(WHITESPACE);
1039*38e8c45fSAndroid Build Coastguard Worker                 std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.c_str());
1040*38e8c45fSAndroid Build Coastguard Worker                 if (!keyCode) {
1041*38e8c45fSAndroid Build Coastguard Worker                     ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
1042*38e8c45fSAndroid Build Coastguard Worker                           mTokenizer->getLocation().c_str(), token.c_str());
1043*38e8c45fSAndroid Build Coastguard Worker                     return BAD_VALUE;
1044*38e8c45fSAndroid Build Coastguard Worker                 }
1045*38e8c45fSAndroid Build Coastguard Worker                 if (haveFallback || haveReplacement) {
1046*38e8c45fSAndroid Build Coastguard Worker                     ALOGE("%s: Cannot combine multiple fallback/replacement key codes.",
1047*38e8c45fSAndroid Build Coastguard Worker                           mTokenizer->getLocation().c_str());
1048*38e8c45fSAndroid Build Coastguard Worker                     return BAD_VALUE;
1049*38e8c45fSAndroid Build Coastguard Worker                 }
1050*38e8c45fSAndroid Build Coastguard Worker                 behavior.fallbackKeyCode = *keyCode;
1051*38e8c45fSAndroid Build Coastguard Worker                 haveFallback = true;
1052*38e8c45fSAndroid Build Coastguard Worker             } else if (token == "replace") {
1053*38e8c45fSAndroid Build Coastguard Worker                 mTokenizer->skipDelimiters(WHITESPACE);
1054*38e8c45fSAndroid Build Coastguard Worker                 token = mTokenizer->nextToken(WHITESPACE);
1055*38e8c45fSAndroid Build Coastguard Worker                 std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.c_str());
1056*38e8c45fSAndroid Build Coastguard Worker                 if (!keyCode) {
1057*38e8c45fSAndroid Build Coastguard Worker                     ALOGE("%s: Invalid key code label for replace, got '%s'.",
1058*38e8c45fSAndroid Build Coastguard Worker                           mTokenizer->getLocation().c_str(), token.c_str());
1059*38e8c45fSAndroid Build Coastguard Worker                     return BAD_VALUE;
1060*38e8c45fSAndroid Build Coastguard Worker                 }
1061*38e8c45fSAndroid Build Coastguard Worker                 if (haveCharacter) {
1062*38e8c45fSAndroid Build Coastguard Worker                     ALOGE("%s: Cannot combine character literal with replace action.",
1063*38e8c45fSAndroid Build Coastguard Worker                           mTokenizer->getLocation().c_str());
1064*38e8c45fSAndroid Build Coastguard Worker                     return BAD_VALUE;
1065*38e8c45fSAndroid Build Coastguard Worker                 }
1066*38e8c45fSAndroid Build Coastguard Worker                 if (haveFallback || haveReplacement) {
1067*38e8c45fSAndroid Build Coastguard Worker                     ALOGE("%s: Cannot combine multiple fallback/replacement key codes.",
1068*38e8c45fSAndroid Build Coastguard Worker                           mTokenizer->getLocation().c_str());
1069*38e8c45fSAndroid Build Coastguard Worker                     return BAD_VALUE;
1070*38e8c45fSAndroid Build Coastguard Worker                 }
1071*38e8c45fSAndroid Build Coastguard Worker                 behavior.replacementKeyCode = *keyCode;
1072*38e8c45fSAndroid Build Coastguard Worker                 haveReplacement = true;
1073*38e8c45fSAndroid Build Coastguard Worker 
1074*38e8c45fSAndroid Build Coastguard Worker             } else {
1075*38e8c45fSAndroid Build Coastguard Worker                 ALOGE("%s: Expected a key behavior after ':'.", mTokenizer->getLocation().c_str());
1076*38e8c45fSAndroid Build Coastguard Worker                 return BAD_VALUE;
1077*38e8c45fSAndroid Build Coastguard Worker             }
1078*38e8c45fSAndroid Build Coastguard Worker         }
1079*38e8c45fSAndroid Build Coastguard Worker 
1080*38e8c45fSAndroid Build Coastguard Worker         mTokenizer->skipDelimiters(WHITESPACE);
1081*38e8c45fSAndroid Build Coastguard Worker     } while (!mTokenizer->isEol() && mTokenizer->peekChar() != '#');
1082*38e8c45fSAndroid Build Coastguard Worker 
1083*38e8c45fSAndroid Build Coastguard Worker     // Add the behavior.
1084*38e8c45fSAndroid Build Coastguard Worker     for (const Property& property : properties) {
1085*38e8c45fSAndroid Build Coastguard Worker         switch (property.property) {
1086*38e8c45fSAndroid Build Coastguard Worker         case PROPERTY_LABEL:
1087*38e8c45fSAndroid Build Coastguard Worker                 if (key.label) {
1088*38e8c45fSAndroid Build Coastguard Worker                     ALOGE("%s: Duplicate label for key.", mTokenizer->getLocation().c_str());
1089*38e8c45fSAndroid Build Coastguard Worker                     return BAD_VALUE;
1090*38e8c45fSAndroid Build Coastguard Worker                 }
1091*38e8c45fSAndroid Build Coastguard Worker                 key.label = behavior.character;
1092*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_PARSER
1093*38e8c45fSAndroid Build Coastguard Worker                 ALOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key.label);
1094*38e8c45fSAndroid Build Coastguard Worker #endif
1095*38e8c45fSAndroid Build Coastguard Worker             break;
1096*38e8c45fSAndroid Build Coastguard Worker         case PROPERTY_NUMBER:
1097*38e8c45fSAndroid Build Coastguard Worker             if (key.number) {
1098*38e8c45fSAndroid Build Coastguard Worker                     ALOGE("%s: Duplicate number for key.", mTokenizer->getLocation().c_str());
1099*38e8c45fSAndroid Build Coastguard Worker                     return BAD_VALUE;
1100*38e8c45fSAndroid Build Coastguard Worker             }
1101*38e8c45fSAndroid Build Coastguard Worker             key.number = behavior.character;
1102*38e8c45fSAndroid Build Coastguard Worker #if DEBUG_PARSER
1103*38e8c45fSAndroid Build Coastguard Worker             ALOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key.number);
1104*38e8c45fSAndroid Build Coastguard Worker #endif
1105*38e8c45fSAndroid Build Coastguard Worker             break;
1106*38e8c45fSAndroid Build Coastguard Worker         case PROPERTY_META: {
1107*38e8c45fSAndroid Build Coastguard Worker             for (const Behavior& b : key.behaviors) {
1108*38e8c45fSAndroid Build Coastguard Worker                     if (b.metaState == property.metaState) {
1109*38e8c45fSAndroid Build Coastguard Worker                     ALOGE("%s: Duplicate key behavior for modifier.",
1110*38e8c45fSAndroid Build Coastguard Worker                           mTokenizer->getLocation().c_str());
1111*38e8c45fSAndroid Build Coastguard Worker                     return BAD_VALUE;
1112*38e8c45fSAndroid Build Coastguard Worker                     }
1113*38e8c45fSAndroid Build Coastguard Worker             }
1114*38e8c45fSAndroid Build Coastguard Worker             Behavior newBehavior = behavior;
1115*38e8c45fSAndroid Build Coastguard Worker             newBehavior.metaState = property.metaState;
1116*38e8c45fSAndroid Build Coastguard Worker             key.behaviors.push_front(newBehavior);
1117*38e8c45fSAndroid Build Coastguard Worker             ALOGD_IF(DEBUG_PARSER,
1118*38e8c45fSAndroid Build Coastguard Worker                      "Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d replace=%d.",
1119*38e8c45fSAndroid Build Coastguard Worker                      mKeyCode, key.behaviors.front().metaState, key.behaviors.front().character,
1120*38e8c45fSAndroid Build Coastguard Worker                      key.behaviors.front().fallbackKeyCode,
1121*38e8c45fSAndroid Build Coastguard Worker                      key.behaviors.front().replacementKeyCode);
1122*38e8c45fSAndroid Build Coastguard Worker             break;
1123*38e8c45fSAndroid Build Coastguard Worker         }
1124*38e8c45fSAndroid Build Coastguard Worker         }
1125*38e8c45fSAndroid Build Coastguard Worker     }
1126*38e8c45fSAndroid Build Coastguard Worker     return NO_ERROR;
1127*38e8c45fSAndroid Build Coastguard Worker }
1128*38e8c45fSAndroid Build Coastguard Worker 
finishKey(Key & key)1129*38e8c45fSAndroid Build Coastguard Worker status_t KeyCharacterMap::Parser::finishKey(Key& key) {
1130*38e8c45fSAndroid Build Coastguard Worker     // Fill in default number property.
1131*38e8c45fSAndroid Build Coastguard Worker     if (!key.number) {
1132*38e8c45fSAndroid Build Coastguard Worker         char16_t digit = 0;
1133*38e8c45fSAndroid Build Coastguard Worker         char16_t symbol = 0;
1134*38e8c45fSAndroid Build Coastguard Worker         for (const Behavior& b : key.behaviors) {
1135*38e8c45fSAndroid Build Coastguard Worker             char16_t ch = b.character;
1136*38e8c45fSAndroid Build Coastguard Worker             if (ch) {
1137*38e8c45fSAndroid Build Coastguard Worker                 if (ch >= '0' && ch <= '9') {
1138*38e8c45fSAndroid Build Coastguard Worker                     digit = ch;
1139*38e8c45fSAndroid Build Coastguard Worker                 } else if (ch == '(' || ch == ')' || ch == '#' || ch == '*'
1140*38e8c45fSAndroid Build Coastguard Worker                         || ch == '-' || ch == '+' || ch == ',' || ch == '.'
1141*38e8c45fSAndroid Build Coastguard Worker                         || ch == '\'' || ch == ':' || ch == ';' || ch == '/') {
1142*38e8c45fSAndroid Build Coastguard Worker                     symbol = ch;
1143*38e8c45fSAndroid Build Coastguard Worker                 }
1144*38e8c45fSAndroid Build Coastguard Worker             }
1145*38e8c45fSAndroid Build Coastguard Worker         }
1146*38e8c45fSAndroid Build Coastguard Worker         key.number = digit ? digit : symbol;
1147*38e8c45fSAndroid Build Coastguard Worker     }
1148*38e8c45fSAndroid Build Coastguard Worker     return NO_ERROR;
1149*38e8c45fSAndroid Build Coastguard Worker }
1150*38e8c45fSAndroid Build Coastguard Worker 
parseModifier(const std::string & token,int32_t * outMetaState)1151*38e8c45fSAndroid Build Coastguard Worker status_t KeyCharacterMap::Parser::parseModifier(const std::string& token, int32_t* outMetaState) {
1152*38e8c45fSAndroid Build Coastguard Worker     if (token == "base") {
1153*38e8c45fSAndroid Build Coastguard Worker         *outMetaState = 0;
1154*38e8c45fSAndroid Build Coastguard Worker         return NO_ERROR;
1155*38e8c45fSAndroid Build Coastguard Worker     }
1156*38e8c45fSAndroid Build Coastguard Worker 
1157*38e8c45fSAndroid Build Coastguard Worker     int32_t combinedMeta = 0;
1158*38e8c45fSAndroid Build Coastguard Worker 
1159*38e8c45fSAndroid Build Coastguard Worker     const char* str = token.c_str();
1160*38e8c45fSAndroid Build Coastguard Worker     const char* start = str;
1161*38e8c45fSAndroid Build Coastguard Worker     for (const char* cur = str; ; cur++) {
1162*38e8c45fSAndroid Build Coastguard Worker         char ch = *cur;
1163*38e8c45fSAndroid Build Coastguard Worker         if (ch == '+' || ch == '\0') {
1164*38e8c45fSAndroid Build Coastguard Worker             size_t len = cur - start;
1165*38e8c45fSAndroid Build Coastguard Worker             int32_t metaState = 0;
1166*38e8c45fSAndroid Build Coastguard Worker             for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) {
1167*38e8c45fSAndroid Build Coastguard Worker                 if (strlen(modifiers[i].label) == len
1168*38e8c45fSAndroid Build Coastguard Worker                         && strncmp(modifiers[i].label, start, len) == 0) {
1169*38e8c45fSAndroid Build Coastguard Worker                     metaState = modifiers[i].metaState;
1170*38e8c45fSAndroid Build Coastguard Worker                     break;
1171*38e8c45fSAndroid Build Coastguard Worker                 }
1172*38e8c45fSAndroid Build Coastguard Worker             }
1173*38e8c45fSAndroid Build Coastguard Worker             if (!metaState) {
1174*38e8c45fSAndroid Build Coastguard Worker                 return BAD_VALUE;
1175*38e8c45fSAndroid Build Coastguard Worker             }
1176*38e8c45fSAndroid Build Coastguard Worker             if (combinedMeta & metaState) {
1177*38e8c45fSAndroid Build Coastguard Worker                 ALOGE("%s: Duplicate modifier combination '%s'.", mTokenizer->getLocation().c_str(),
1178*38e8c45fSAndroid Build Coastguard Worker                       token.c_str());
1179*38e8c45fSAndroid Build Coastguard Worker                 return BAD_VALUE;
1180*38e8c45fSAndroid Build Coastguard Worker             }
1181*38e8c45fSAndroid Build Coastguard Worker 
1182*38e8c45fSAndroid Build Coastguard Worker             combinedMeta |= metaState;
1183*38e8c45fSAndroid Build Coastguard Worker             start = cur + 1;
1184*38e8c45fSAndroid Build Coastguard Worker 
1185*38e8c45fSAndroid Build Coastguard Worker             if (ch == '\0') {
1186*38e8c45fSAndroid Build Coastguard Worker                 break;
1187*38e8c45fSAndroid Build Coastguard Worker             }
1188*38e8c45fSAndroid Build Coastguard Worker         }
1189*38e8c45fSAndroid Build Coastguard Worker     }
1190*38e8c45fSAndroid Build Coastguard Worker     *outMetaState = combinedMeta;
1191*38e8c45fSAndroid Build Coastguard Worker     return NO_ERROR;
1192*38e8c45fSAndroid Build Coastguard Worker }
1193*38e8c45fSAndroid Build Coastguard Worker 
parseCharacterLiteral(char16_t * outCharacter)1194*38e8c45fSAndroid Build Coastguard Worker status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) {
1195*38e8c45fSAndroid Build Coastguard Worker     char ch = mTokenizer->nextChar();
1196*38e8c45fSAndroid Build Coastguard Worker     if (ch != '\'') {
1197*38e8c45fSAndroid Build Coastguard Worker         goto Error;
1198*38e8c45fSAndroid Build Coastguard Worker     }
1199*38e8c45fSAndroid Build Coastguard Worker 
1200*38e8c45fSAndroid Build Coastguard Worker     ch = mTokenizer->nextChar();
1201*38e8c45fSAndroid Build Coastguard Worker     if (ch == '\\') {
1202*38e8c45fSAndroid Build Coastguard Worker         // Escape sequence.
1203*38e8c45fSAndroid Build Coastguard Worker         ch = mTokenizer->nextChar();
1204*38e8c45fSAndroid Build Coastguard Worker         if (ch == 'n') {
1205*38e8c45fSAndroid Build Coastguard Worker             *outCharacter = '\n';
1206*38e8c45fSAndroid Build Coastguard Worker         } else if (ch == 't') {
1207*38e8c45fSAndroid Build Coastguard Worker             *outCharacter = '\t';
1208*38e8c45fSAndroid Build Coastguard Worker         } else if (ch == '\\') {
1209*38e8c45fSAndroid Build Coastguard Worker             *outCharacter = '\\';
1210*38e8c45fSAndroid Build Coastguard Worker         } else if (ch == '\'') {
1211*38e8c45fSAndroid Build Coastguard Worker             *outCharacter = '\'';
1212*38e8c45fSAndroid Build Coastguard Worker         } else if (ch == '"') {
1213*38e8c45fSAndroid Build Coastguard Worker             *outCharacter = '"';
1214*38e8c45fSAndroid Build Coastguard Worker         } else if (ch == 'u') {
1215*38e8c45fSAndroid Build Coastguard Worker             *outCharacter = 0;
1216*38e8c45fSAndroid Build Coastguard Worker             for (int i = 0; i < 4; i++) {
1217*38e8c45fSAndroid Build Coastguard Worker                 ch = mTokenizer->nextChar();
1218*38e8c45fSAndroid Build Coastguard Worker                 int digit;
1219*38e8c45fSAndroid Build Coastguard Worker                 if (ch >= '0' && ch <= '9') {
1220*38e8c45fSAndroid Build Coastguard Worker                     digit = ch - '0';
1221*38e8c45fSAndroid Build Coastguard Worker                 } else if (ch >= 'A' && ch <= 'F') {
1222*38e8c45fSAndroid Build Coastguard Worker                     digit = ch - 'A' + 10;
1223*38e8c45fSAndroid Build Coastguard Worker                 } else if (ch >= 'a' && ch <= 'f') {
1224*38e8c45fSAndroid Build Coastguard Worker                     digit = ch - 'a' + 10;
1225*38e8c45fSAndroid Build Coastguard Worker                 } else {
1226*38e8c45fSAndroid Build Coastguard Worker                     goto Error;
1227*38e8c45fSAndroid Build Coastguard Worker                 }
1228*38e8c45fSAndroid Build Coastguard Worker                 *outCharacter = (*outCharacter << 4) | digit;
1229*38e8c45fSAndroid Build Coastguard Worker             }
1230*38e8c45fSAndroid Build Coastguard Worker         } else {
1231*38e8c45fSAndroid Build Coastguard Worker             goto Error;
1232*38e8c45fSAndroid Build Coastguard Worker         }
1233*38e8c45fSAndroid Build Coastguard Worker     } else if (ch >= 32 && ch <= 126 && ch != '\'') {
1234*38e8c45fSAndroid Build Coastguard Worker         // ASCII literal character.
1235*38e8c45fSAndroid Build Coastguard Worker         *outCharacter = ch;
1236*38e8c45fSAndroid Build Coastguard Worker     } else {
1237*38e8c45fSAndroid Build Coastguard Worker         goto Error;
1238*38e8c45fSAndroid Build Coastguard Worker     }
1239*38e8c45fSAndroid Build Coastguard Worker 
1240*38e8c45fSAndroid Build Coastguard Worker     ch = mTokenizer->nextChar();
1241*38e8c45fSAndroid Build Coastguard Worker     if (ch != '\'') {
1242*38e8c45fSAndroid Build Coastguard Worker         goto Error;
1243*38e8c45fSAndroid Build Coastguard Worker     }
1244*38e8c45fSAndroid Build Coastguard Worker 
1245*38e8c45fSAndroid Build Coastguard Worker     // Ensure that we consumed the entire token.
1246*38e8c45fSAndroid Build Coastguard Worker     if (mTokenizer->nextToken(WHITESPACE).empty()) {
1247*38e8c45fSAndroid Build Coastguard Worker         return NO_ERROR;
1248*38e8c45fSAndroid Build Coastguard Worker     }
1249*38e8c45fSAndroid Build Coastguard Worker 
1250*38e8c45fSAndroid Build Coastguard Worker Error:
1251*38e8c45fSAndroid Build Coastguard Worker     ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().c_str());
1252*38e8c45fSAndroid Build Coastguard Worker     return BAD_VALUE;
1253*38e8c45fSAndroid Build Coastguard Worker }
1254*38e8c45fSAndroid Build Coastguard Worker 
1255*38e8c45fSAndroid Build Coastguard Worker } // namespace android
1256