blob: 66fdfd7828ecddfa039b90c2dd3015e994552bc5 [file] [log] [blame]
Jeff Brown6b53e8d2010-11-10 16:03:06 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080017#define LOG_TAG "KeyCharacterMap"
18
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019#include <stdlib.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020#include <string.h>
Jeff Brown6b53e8d2010-11-10 16:03:06 -080021#include <android/keycodes.h>
Mathias Agopianb93a03f82012-02-17 15:34:57 -080022#include <androidfw/Keyboard.h>
23#include <androidfw/KeyCharacterMap.h>
Jeff Brown9f25b7f2012-04-10 14:30:49 -070024
25#if HAVE_ANDROID_OS
26#include <binder/Parcel.h>
27#endif
28
Jeff Brown6b53e8d2010-11-10 16:03:06 -080029#include <utils/Log.h>
30#include <utils/Errors.h>
31#include <utils/Tokenizer.h>
32#include <utils/Timers.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033
Jeff Brown6b53e8d2010-11-10 16:03:06 -080034// Enables debug output for the parser.
35#define DEBUG_PARSER 0
36
37// Enables debug output for parser performance.
38#define DEBUG_PARSER_PERFORMANCE 0
39
40// Enables debug output for mapping.
41#define DEBUG_MAPPING 0
42
43
44namespace android {
45
46static const char* WHITESPACE = " \t\r";
47static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:";
48
49struct Modifier {
50 const char* label;
51 int32_t metaState;
52};
53static const Modifier modifiers[] = {
54 { "shift", AMETA_SHIFT_ON },
55 { "lshift", AMETA_SHIFT_LEFT_ON },
56 { "rshift", AMETA_SHIFT_RIGHT_ON },
57 { "alt", AMETA_ALT_ON },
58 { "lalt", AMETA_ALT_LEFT_ON },
59 { "ralt", AMETA_ALT_RIGHT_ON },
60 { "ctrl", AMETA_CTRL_ON },
61 { "lctrl", AMETA_CTRL_LEFT_ON },
62 { "rctrl", AMETA_CTRL_RIGHT_ON },
63 { "meta", AMETA_META_ON },
64 { "lmeta", AMETA_META_LEFT_ON },
65 { "rmeta", AMETA_META_RIGHT_ON },
66 { "sym", AMETA_SYM_ON },
67 { "fn", AMETA_FUNCTION_ON },
68 { "capslock", AMETA_CAPS_LOCK_ON },
69 { "numlock", AMETA_NUM_LOCK_ON },
70 { "scrolllock", AMETA_SCROLL_LOCK_ON },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071};
72
Jeff Brown6b53e8d2010-11-10 16:03:06 -080073#if DEBUG_MAPPING
74static String8 toString(const char16_t* chars, size_t numChars) {
75 String8 result;
76 for (size_t i = 0; i < numChars; i++) {
77 result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -080079 return result;
80}
81#endif
82
83
84// --- KeyCharacterMap ---
85
Jeff Brown9f25b7f2012-04-10 14:30:49 -070086sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap();
87
Jeff Brown6b53e8d2010-11-10 16:03:06 -080088KeyCharacterMap::KeyCharacterMap() :
89 mType(KEYBOARD_TYPE_UNKNOWN) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090}
91
Jeff Brown6ec6f792012-04-17 16:52:41 -070092KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) :
93 RefBase(), mType(other.mType) {
94 for (size_t i = 0; i < other.mKeys.size(); i++) {
95 mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i)));
96 }
97}
98
Jeff Brown6b53e8d2010-11-10 16:03:06 -080099KeyCharacterMap::~KeyCharacterMap() {
100 for (size_t i = 0; i < mKeys.size(); i++) {
101 Key* key = mKeys.editValueAt(i);
102 delete key;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104}
105
Jeff Brown6ec6f792012-04-17 16:52:41 -0700106status_t KeyCharacterMap::load(const String8& filename,
107 Format format, sp<KeyCharacterMap>* outMap) {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700108 outMap->clear();
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800109
110 Tokenizer* tokenizer;
111 status_t status = Tokenizer::open(filename, &tokenizer);
112 if (status) {
Steve Block3762c312012-01-06 19:20:56 +0000113 ALOGE("Error %d opening key character map file %s.", status, filename.string());
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800114 } else {
Jeff Brown6ec6f792012-04-17 16:52:41 -0700115 status = load(tokenizer, format, outMap);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800116 delete tokenizer;
117 }
118 return status;
119}
120
Jeff Brown6ec6f792012-04-17 16:52:41 -0700121status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents,
122 Format format, sp<KeyCharacterMap>* outMap) {
123 outMap->clear();
124
125 Tokenizer* tokenizer;
126 status_t status = Tokenizer::fromContents(filename, contents, &tokenizer);
127 if (status) {
128 ALOGE("Error %d opening key character map.", status);
129 } else {
130 status = load(tokenizer, format, outMap);
131 delete tokenizer;
132 }
133 return status;
134}
135
136status_t KeyCharacterMap::load(Tokenizer* tokenizer,
137 Format format, sp<KeyCharacterMap>* outMap) {
138 status_t status = OK;
139 sp<KeyCharacterMap> map = new KeyCharacterMap();
140 if (!map.get()) {
141 ALOGE("Error allocating key character map.");
142 status = NO_MEMORY;
143 } else {
144#if DEBUG_PARSER_PERFORMANCE
145 nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
146#endif
147 Parser parser(map.get(), tokenizer, format);
148 status = parser.parse();
149#if DEBUG_PARSER_PERFORMANCE
150 nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
151 ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
152 tokenizer->getFilename().string(), tokenizer->getLineNumber(),
153 elapsedTime / 1000000.0);
154#endif
155 if (!status) {
156 *outMap = map;
157 }
158 }
159 return status;
160}
161
162sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
163 const sp<KeyCharacterMap>& overlay) {
164 if (overlay == NULL) {
165 return base;
166 }
167 if (base == NULL) {
168 return overlay;
169 }
170
171 sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get());
172 for (size_t i = 0; i < overlay->mKeys.size(); i++) {
173 int32_t keyCode = overlay->mKeys.keyAt(i);
174 Key* key = overlay->mKeys.valueAt(i);
175 ssize_t oldIndex = map->mKeys.indexOfKey(keyCode);
176 if (oldIndex >= 0) {
177 delete map->mKeys.valueAt(oldIndex);
178 map->mKeys.editValueAt(oldIndex) = new Key(*key);
179 } else {
180 map->mKeys.add(keyCode, new Key(*key));
181 }
182 }
183 return map;
184}
185
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700186sp<KeyCharacterMap> KeyCharacterMap::empty() {
187 return sEmpty;
188}
189
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800190int32_t KeyCharacterMap::getKeyboardType() const {
191 return mType;
192}
193
194char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
195 char16_t result = 0;
Jeff Brown49ed71d2010-12-06 17:13:33 -0800196 const Key* key;
197 if (getKey(keyCode, &key)) {
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800198 result = key->label;
199 }
200#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000201 ALOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800202#endif
203 return result;
204}
205
206char16_t KeyCharacterMap::getNumber(int32_t keyCode) const {
207 char16_t result = 0;
Jeff Brown49ed71d2010-12-06 17:13:33 -0800208 const Key* key;
209 if (getKey(keyCode, &key)) {
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800210 result = key->number;
211 }
212#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000213 ALOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800214#endif
215 return result;
216}
217
218char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const {
219 char16_t result = 0;
Jeff Brown49ed71d2010-12-06 17:13:33 -0800220 const Key* key;
221 const Behavior* behavior;
222 if (getKeyBehavior(keyCode, metaState, &key, &behavior)) {
223 result = behavior->character;
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800224 }
225#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000226 ALOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800227#endif
228 return result;
229}
230
Jeff Brown49ed71d2010-12-06 17:13:33 -0800231bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState,
232 FallbackAction* outFallbackAction) const {
233 outFallbackAction->keyCode = 0;
234 outFallbackAction->metaState = 0;
235
236 bool result = false;
237 const Key* key;
238 const Behavior* behavior;
239 if (getKeyBehavior(keyCode, metaState, &key, &behavior)) {
Jeff Brownfca66d32011-01-14 17:54:38 -0800240 if (behavior->fallbackKeyCode) {
241 outFallbackAction->keyCode = behavior->fallbackKeyCode;
242 outFallbackAction->metaState = metaState & ~behavior->metaState;
243 result = true;
244 }
Jeff Brown49ed71d2010-12-06 17:13:33 -0800245 }
246#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000247 ALOGD("getFallbackKeyCode: keyCode=%d, metaState=0x%08x ~ Result %s, "
Jeff Brown49ed71d2010-12-06 17:13:33 -0800248 "fallback keyCode=%d, fallback metaState=0x%08x.",
249 keyCode, metaState, result ? "true" : "false",
250 outFallbackAction->keyCode, outFallbackAction->metaState);
251#endif
252 return result;
253}
254
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800255char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars,
256 int32_t metaState) const {
257 char16_t result = 0;
Jeff Brown49ed71d2010-12-06 17:13:33 -0800258 const Key* key;
259 if (getKey(keyCode, &key)) {
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800260 // Try to find the most general behavior that maps to this character.
261 // For example, the base key behavior will usually be last in the list.
262 // However, if we find a perfect meta state match for one behavior then use that one.
263 for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
264 if (behavior->character) {
265 for (size_t i = 0; i < numChars; i++) {
266 if (behavior->character == chars[i]) {
267 result = behavior->character;
268 if ((behavior->metaState & metaState) == behavior->metaState) {
269 goto ExactMatch;
270 }
271 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 }
273 }
274 }
275 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800276 ExactMatch: ;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800278#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000279 ALOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800280 keyCode, toString(chars, numChars).string(), metaState, result);
281#endif
282 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283}
284
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800285bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
286 Vector<KeyEvent>& outEvents) const {
287 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
288
289 for (size_t i = 0; i < numChars; i++) {
290 int32_t keyCode, metaState;
291 char16_t ch = chars[i];
292 if (!findKey(ch, &keyCode, &metaState)) {
293#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000294 ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800295 deviceId, toString(chars, numChars).string(), ch);
296#endif
297 return false;
298 }
299
300 int32_t currentMetaState = 0;
301 addMetaKeys(outEvents, deviceId, metaState, true, now, &currentMetaState);
302 addKey(outEvents, deviceId, keyCode, currentMetaState, true, now);
303 addKey(outEvents, deviceId, keyCode, currentMetaState, false, now);
304 addMetaKeys(outEvents, deviceId, metaState, false, now, &currentMetaState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800306#if DEBUG_MAPPING
Steve Block5baa3a62011-12-20 16:23:08 +0000307 ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.",
Jeff Brown49ed71d2010-12-06 17:13:33 -0800308 deviceId, toString(chars, numChars).string(), int32_t(outEvents.size()));
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800309 for (size_t i = 0; i < outEvents.size(); i++) {
Steve Block5baa3a62011-12-20 16:23:08 +0000310 ALOGD(" Key: keyCode=%d, metaState=0x%08x, %s.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800311 outEvents[i].getKeyCode(), outEvents[i].getMetaState(),
312 outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up");
313 }
314#endif
315 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316}
317
Jeff Brown49ed71d2010-12-06 17:13:33 -0800318bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const {
319 ssize_t index = mKeys.indexOfKey(keyCode);
320 if (index >= 0) {
321 *outKey = mKeys.valueAt(index);
322 return true;
323 }
324 return false;
325}
326
327bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState,
328 const Key** outKey, const Behavior** outBehavior) const {
329 const Key* key;
330 if (getKey(keyCode, &key)) {
331 const Behavior* behavior = key->firstBehavior;
332 while (behavior) {
333 if ((behavior->metaState & metaState) == behavior->metaState) {
334 *outKey = key;
335 *outBehavior = behavior;
336 return true;
337 }
338 behavior = behavior->next;
339 }
340 }
341 return false;
342}
343
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800344bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const {
345 if (!ch) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 return false;
347 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800349 for (size_t i = 0; i < mKeys.size(); i++) {
350 const Key* key = mKeys.valueAt(i);
351
352 // Try to find the most general behavior that maps to this character.
353 // For example, the base key behavior will usually be last in the list.
354 const Behavior* found = NULL;
355 for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
356 if (behavior->character == ch) {
357 found = behavior;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800359 }
360 if (found) {
361 *outKeyCode = mKeys.keyAt(i);
362 *outMetaState = found->metaState;
363 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 }
365 }
366 return false;
367}
368
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800369void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents,
370 int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
371 outEvents.push();
372 KeyEvent& event = outEvents.editTop();
373 event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD,
374 down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
375 0, keyCode, 0, metaState, 0, time, time);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376}
377
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800378void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
379 int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
380 int32_t* currentMetaState) {
381 // Add and remove meta keys symmetrically.
382 if (down) {
383 addLockedMetaKey(outEvents, deviceId, metaState, time,
384 AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
385 addLockedMetaKey(outEvents, deviceId, metaState, time,
386 AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
387 addLockedMetaKey(outEvents, deviceId, metaState, time,
388 AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800390 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
391 AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
392 AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
393 AMETA_SHIFT_ON, currentMetaState);
394 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
395 AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
396 AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
397 AMETA_ALT_ON, currentMetaState);
398 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
399 AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
400 AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
401 AMETA_CTRL_ON, currentMetaState);
402 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
403 AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
404 AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
405 AMETA_META_ON, currentMetaState);
Jeff Brown497a92c2010-09-12 17:55:08 -0700406
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800407 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
408 AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
409 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
410 AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 } else {
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800412 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
413 AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
414 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
415 AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800417 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
418 AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
419 AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
420 AMETA_META_ON, currentMetaState);
421 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
422 AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
423 AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
424 AMETA_CTRL_ON, currentMetaState);
425 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
426 AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
427 AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
428 AMETA_ALT_ON, currentMetaState);
429 addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
430 AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
431 AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
432 AMETA_SHIFT_ON, currentMetaState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800434 addLockedMetaKey(outEvents, deviceId, metaState, time,
435 AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
436 addLockedMetaKey(outEvents, deviceId, metaState, time,
437 AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
438 addLockedMetaKey(outEvents, deviceId, metaState, time,
439 AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
440 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441}
442
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800443bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
444 int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
445 int32_t keyCode, int32_t keyMetaState,
446 int32_t* currentMetaState) {
447 if ((metaState & keyMetaState) == keyMetaState) {
448 *currentMetaState = updateMetaState(keyCode, down, *currentMetaState);
449 addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time);
450 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800452 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453}
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800454
455void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
456 int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
457 int32_t leftKeyCode, int32_t leftKeyMetaState,
458 int32_t rightKeyCode, int32_t rightKeyMetaState,
459 int32_t eitherKeyMetaState,
460 int32_t* currentMetaState) {
461 bool specific = false;
462 specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
463 leftKeyCode, leftKeyMetaState, currentMetaState);
464 specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
465 rightKeyCode, rightKeyMetaState, currentMetaState);
466
467 if (!specific) {
468 addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
469 leftKeyCode, eitherKeyMetaState, currentMetaState);
470 }
471}
472
473void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents,
474 int32_t deviceId, int32_t metaState, nsecs_t time,
475 int32_t keyCode, int32_t keyMetaState,
476 int32_t* currentMetaState) {
477 if ((metaState & keyMetaState) == keyMetaState) {
478 *currentMetaState = updateMetaState(keyCode, true, *currentMetaState);
479 addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time);
480 *currentMetaState = updateMetaState(keyCode, false, *currentMetaState);
481 addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time);
482 }
483}
484
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700485#if HAVE_ANDROID_OS
486sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
487 sp<KeyCharacterMap> map = new KeyCharacterMap();
488 map->mType = parcel->readInt32();
489 size_t numKeys = parcel->readInt32();
490 if (parcel->errorCheck()) {
491 return NULL;
492 }
493
494 for (size_t i = 0; i < numKeys; i++) {
495 int32_t keyCode = parcel->readInt32();
496 char16_t label = parcel->readInt32();
497 char16_t number = parcel->readInt32();
498 if (parcel->errorCheck()) {
499 return NULL;
500 }
501
502 Key* key = new Key();
503 key->label = label;
504 key->number = number;
505 map->mKeys.add(keyCode, key);
506
507 Behavior* lastBehavior = NULL;
508 while (parcel->readInt32()) {
509 int32_t metaState = parcel->readInt32();
510 char16_t character = parcel->readInt32();
511 int32_t fallbackKeyCode = parcel->readInt32();
512 if (parcel->errorCheck()) {
513 return NULL;
514 }
515
516 Behavior* behavior = new Behavior();
517 behavior->metaState = metaState;
518 behavior->character = character;
519 behavior->fallbackKeyCode = fallbackKeyCode;
520 if (lastBehavior) {
521 lastBehavior->next = behavior;
522 } else {
523 key->firstBehavior = behavior;
524 }
525 lastBehavior = behavior;
526 }
527
528 if (parcel->errorCheck()) {
529 return NULL;
530 }
531 }
532 return map;
533}
534
535void KeyCharacterMap::writeToParcel(Parcel* parcel) const {
536 parcel->writeInt32(mType);
537
538 size_t numKeys = mKeys.size();
539 parcel->writeInt32(numKeys);
540 for (size_t i = 0; i < numKeys; i++) {
541 int32_t keyCode = mKeys.keyAt(i);
542 const Key* key = mKeys.valueAt(i);
543 parcel->writeInt32(keyCode);
544 parcel->writeInt32(key->label);
545 parcel->writeInt32(key->number);
546 for (const Behavior* behavior = key->firstBehavior; behavior != NULL;
547 behavior = behavior->next) {
548 parcel->writeInt32(1);
549 parcel->writeInt32(behavior->metaState);
550 parcel->writeInt32(behavior->character);
551 parcel->writeInt32(behavior->fallbackKeyCode);
552 }
553 parcel->writeInt32(0);
554 }
555}
556#endif
557
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800558
559// --- KeyCharacterMap::Key ---
560
561KeyCharacterMap::Key::Key() :
562 label(0), number(0), firstBehavior(NULL) {
563}
564
Jeff Brown6ec6f792012-04-17 16:52:41 -0700565KeyCharacterMap::Key::Key(const Key& other) :
566 label(other.label), number(other.number),
567 firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) {
568}
569
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800570KeyCharacterMap::Key::~Key() {
571 Behavior* behavior = firstBehavior;
572 while (behavior) {
573 Behavior* next = behavior->next;
574 delete behavior;
575 behavior = next;
576 }
577}
578
579
580// --- KeyCharacterMap::Behavior ---
581
582KeyCharacterMap::Behavior::Behavior() :
583 next(NULL), metaState(0), character(0), fallbackKeyCode(0) {
584}
585
Jeff Brown6ec6f792012-04-17 16:52:41 -0700586KeyCharacterMap::Behavior::Behavior(const Behavior& other) :
587 next(other.next ? new Behavior(*other.next) : NULL),
588 metaState(other.metaState), character(other.character),
589 fallbackKeyCode(other.fallbackKeyCode) {
590}
591
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800592
593// --- KeyCharacterMap::Parser ---
594
Jeff Brown6ec6f792012-04-17 16:52:41 -0700595KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) :
596 mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) {
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800597}
598
599KeyCharacterMap::Parser::~Parser() {
600}
601
602status_t KeyCharacterMap::Parser::parse() {
603 while (!mTokenizer->isEof()) {
604#if DEBUG_PARSER
Steve Block5baa3a62011-12-20 16:23:08 +0000605 ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800606 mTokenizer->peekRemainderOfLine().string());
607#endif
608
609 mTokenizer->skipDelimiters(WHITESPACE);
610
611 if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
612 switch (mState) {
613 case STATE_TOP: {
614 String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
615 if (keywordToken == "type") {
616 mTokenizer->skipDelimiters(WHITESPACE);
617 status_t status = parseType();
618 if (status) return status;
619 } else if (keywordToken == "key") {
620 mTokenizer->skipDelimiters(WHITESPACE);
621 status_t status = parseKey();
622 if (status) return status;
623 } else {
Steve Block3762c312012-01-06 19:20:56 +0000624 ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800625 keywordToken.string());
626 return BAD_VALUE;
627 }
628 break;
629 }
630
631 case STATE_KEY: {
632 status_t status = parseKeyProperty();
633 if (status) return status;
634 break;
635 }
636 }
637
638 mTokenizer->skipDelimiters(WHITESPACE);
639 if (!mTokenizer->isEol()) {
Steve Block3762c312012-01-06 19:20:56 +0000640 ALOGE("%s: Expected end of line, got '%s'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800641 mTokenizer->getLocation().string(),
642 mTokenizer->peekRemainderOfLine().string());
643 return BAD_VALUE;
644 }
645 }
646
647 mTokenizer->nextLine();
648 }
649
650 if (mState != STATE_TOP) {
Steve Block3762c312012-01-06 19:20:56 +0000651 ALOGE("%s: Unterminated key description at end of file.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800652 mTokenizer->getLocation().string());
653 return BAD_VALUE;
654 }
655
Jeff Brown6ec6f792012-04-17 16:52:41 -0700656 if (mFormat == FORMAT_BASE) {
657 if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
658 ALOGE("%s: Base keyboard layout missing required keyboard 'type' declaration.",
659 mTokenizer->getLocation().string());
660 return BAD_VALUE;
661 }
662 if (mMap->mType == KEYBOARD_TYPE_OVERLAY) {
663 ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
664 mTokenizer->getLocation().string());
665 return BAD_VALUE;
666 }
667 } else if (mFormat == FORMAT_OVERLAY) {
668 if (mMap->mType != KEYBOARD_TYPE_OVERLAY) {
669 ALOGE("%s: Overlay keyboard layout missing required keyboard "
670 "'type OVERLAY' declaration.",
671 mTokenizer->getLocation().string());
672 return BAD_VALUE;
673 }
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800674 }
675
676 return NO_ERROR;
677}
678
679status_t KeyCharacterMap::Parser::parseType() {
680 if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) {
Steve Block3762c312012-01-06 19:20:56 +0000681 ALOGE("%s: Duplicate keyboard 'type' declaration.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800682 mTokenizer->getLocation().string());
683 return BAD_VALUE;
684 }
685
686 KeyboardType type;
687 String8 typeToken = mTokenizer->nextToken(WHITESPACE);
688 if (typeToken == "NUMERIC") {
689 type = KEYBOARD_TYPE_NUMERIC;
690 } else if (typeToken == "PREDICTIVE") {
691 type = KEYBOARD_TYPE_PREDICTIVE;
692 } else if (typeToken == "ALPHA") {
693 type = KEYBOARD_TYPE_ALPHA;
694 } else if (typeToken == "FULL") {
695 type = KEYBOARD_TYPE_FULL;
696 } else if (typeToken == "SPECIAL_FUNCTION") {
697 type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
Jeff Brown6ec6f792012-04-17 16:52:41 -0700698 } else if (typeToken == "OVERLAY") {
699 type = KEYBOARD_TYPE_OVERLAY;
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800700 } else {
Steve Block3762c312012-01-06 19:20:56 +0000701 ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800702 typeToken.string());
703 return BAD_VALUE;
704 }
705
706#if DEBUG_PARSER
Steve Block5baa3a62011-12-20 16:23:08 +0000707 ALOGD("Parsed type: type=%d.", type);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800708#endif
709 mMap->mType = type;
710 return NO_ERROR;
711}
712
713status_t KeyCharacterMap::Parser::parseKey() {
714 String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
715 int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
716 if (!keyCode) {
Steve Block3762c312012-01-06 19:20:56 +0000717 ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800718 keyCodeToken.string());
719 return BAD_VALUE;
720 }
721 if (mMap->mKeys.indexOfKey(keyCode) >= 0) {
Steve Block3762c312012-01-06 19:20:56 +0000722 ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(),
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800723 keyCodeToken.string());
724 return BAD_VALUE;
725 }
726
727 mTokenizer->skipDelimiters(WHITESPACE);
728 String8 openBraceToken = mTokenizer->nextToken(WHITESPACE);
729 if (openBraceToken != "{") {
Steve Block3762c312012-01-06 19:20:56 +0000730 ALOGE("%s: Expected '{' after key code label, got '%s'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800731 mTokenizer->getLocation().string(), openBraceToken.string());
732 return BAD_VALUE;
733 }
734
735#if DEBUG_PARSER
Steve Block5baa3a62011-12-20 16:23:08 +0000736 ALOGD("Parsed beginning of key: keyCode=%d.", keyCode);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800737#endif
738 mKeyCode = keyCode;
739 mMap->mKeys.add(keyCode, new Key());
740 mState = STATE_KEY;
741 return NO_ERROR;
742}
743
744status_t KeyCharacterMap::Parser::parseKeyProperty() {
745 String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
746 if (token == "}") {
747 mState = STATE_TOP;
748 return NO_ERROR;
749 }
750
751 Vector<Property> properties;
752
753 // Parse all comma-delimited property names up to the first colon.
754 for (;;) {
755 if (token == "label") {
756 properties.add(Property(PROPERTY_LABEL));
757 } else if (token == "number") {
758 properties.add(Property(PROPERTY_NUMBER));
759 } else {
760 int32_t metaState;
761 status_t status = parseModifier(token, &metaState);
762 if (status) {
Steve Block3762c312012-01-06 19:20:56 +0000763 ALOGE("%s: Expected a property name or modifier, got '%s'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800764 mTokenizer->getLocation().string(), token.string());
765 return status;
766 }
767 properties.add(Property(PROPERTY_META, metaState));
768 }
769
770 mTokenizer->skipDelimiters(WHITESPACE);
771 if (!mTokenizer->isEol()) {
772 char ch = mTokenizer->nextChar();
773 if (ch == ':') {
774 break;
775 } else if (ch == ',') {
776 mTokenizer->skipDelimiters(WHITESPACE);
777 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
778 continue;
779 }
780 }
781
Steve Block3762c312012-01-06 19:20:56 +0000782 ALOGE("%s: Expected ',' or ':' after property name.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800783 mTokenizer->getLocation().string());
784 return BAD_VALUE;
785 }
786
787 // Parse behavior after the colon.
788 mTokenizer->skipDelimiters(WHITESPACE);
789
790 Behavior behavior;
791 bool haveCharacter = false;
792 bool haveFallback = false;
793
794 do {
795 char ch = mTokenizer->peekChar();
796 if (ch == '\'') {
797 char16_t character;
798 status_t status = parseCharacterLiteral(&character);
799 if (status || !character) {
Steve Block3762c312012-01-06 19:20:56 +0000800 ALOGE("%s: Invalid character literal for key.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800801 mTokenizer->getLocation().string());
802 return BAD_VALUE;
803 }
804 if (haveCharacter) {
Steve Block3762c312012-01-06 19:20:56 +0000805 ALOGE("%s: Cannot combine multiple character literals or 'none'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800806 mTokenizer->getLocation().string());
807 return BAD_VALUE;
808 }
809 behavior.character = character;
810 haveCharacter = true;
811 } else {
812 token = mTokenizer->nextToken(WHITESPACE);
813 if (token == "none") {
814 if (haveCharacter) {
Steve Block3762c312012-01-06 19:20:56 +0000815 ALOGE("%s: Cannot combine multiple character literals or 'none'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800816 mTokenizer->getLocation().string());
817 return BAD_VALUE;
818 }
819 haveCharacter = true;
820 } else if (token == "fallback") {
821 mTokenizer->skipDelimiters(WHITESPACE);
822 token = mTokenizer->nextToken(WHITESPACE);
823 int32_t keyCode = getKeyCodeByLabel(token.string());
824 if (!keyCode) {
Steve Block3762c312012-01-06 19:20:56 +0000825 ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800826 mTokenizer->getLocation().string(),
827 token.string());
828 return BAD_VALUE;
829 }
830 if (haveFallback) {
Steve Block3762c312012-01-06 19:20:56 +0000831 ALOGE("%s: Cannot combine multiple fallback key codes.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800832 mTokenizer->getLocation().string());
833 return BAD_VALUE;
834 }
835 behavior.fallbackKeyCode = keyCode;
836 haveFallback = true;
837 } else {
Steve Block3762c312012-01-06 19:20:56 +0000838 ALOGE("%s: Expected a key behavior after ':'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800839 mTokenizer->getLocation().string());
840 return BAD_VALUE;
841 }
842 }
843
844 mTokenizer->skipDelimiters(WHITESPACE);
845 } while (!mTokenizer->isEol());
846
847 // Add the behavior.
848 Key* key = mMap->mKeys.valueFor(mKeyCode);
849 for (size_t i = 0; i < properties.size(); i++) {
850 const Property& property = properties.itemAt(i);
851 switch (property.property) {
852 case PROPERTY_LABEL:
853 if (key->label) {
Steve Block3762c312012-01-06 19:20:56 +0000854 ALOGE("%s: Duplicate label for key.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800855 mTokenizer->getLocation().string());
856 return BAD_VALUE;
857 }
858 key->label = behavior.character;
859#if DEBUG_PARSER
Steve Block5baa3a62011-12-20 16:23:08 +0000860 ALOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800861#endif
862 break;
863 case PROPERTY_NUMBER:
864 if (key->number) {
Steve Block3762c312012-01-06 19:20:56 +0000865 ALOGE("%s: Duplicate number for key.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800866 mTokenizer->getLocation().string());
867 return BAD_VALUE;
868 }
869 key->number = behavior.character;
870#if DEBUG_PARSER
Steve Block5baa3a62011-12-20 16:23:08 +0000871 ALOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number);
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800872#endif
873 break;
874 case PROPERTY_META: {
875 for (Behavior* b = key->firstBehavior; b; b = b->next) {
876 if (b->metaState == property.metaState) {
Steve Block3762c312012-01-06 19:20:56 +0000877 ALOGE("%s: Duplicate key behavior for modifier.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800878 mTokenizer->getLocation().string());
879 return BAD_VALUE;
880 }
881 }
882 Behavior* newBehavior = new Behavior(behavior);
883 newBehavior->metaState = property.metaState;
884 newBehavior->next = key->firstBehavior;
885 key->firstBehavior = newBehavior;
886#if DEBUG_PARSER
Steve Block5baa3a62011-12-20 16:23:08 +0000887 ALOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d.", mKeyCode,
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800888 newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode);
889#endif
890 break;
891 }
892 }
893 }
894 return NO_ERROR;
895}
896
897status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) {
898 if (token == "base") {
899 *outMetaState = 0;
900 return NO_ERROR;
901 }
902
903 int32_t combinedMeta = 0;
904
905 const char* str = token.string();
906 const char* start = str;
907 for (const char* cur = str; ; cur++) {
908 char ch = *cur;
909 if (ch == '+' || ch == '\0') {
910 size_t len = cur - start;
911 int32_t metaState = 0;
912 for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) {
913 if (strlen(modifiers[i].label) == len
914 && strncmp(modifiers[i].label, start, len) == 0) {
915 metaState = modifiers[i].metaState;
916 break;
917 }
918 }
919 if (!metaState) {
920 return BAD_VALUE;
921 }
922 if (combinedMeta & metaState) {
Steve Block3762c312012-01-06 19:20:56 +0000923 ALOGE("%s: Duplicate modifier combination '%s'.",
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800924 mTokenizer->getLocation().string(), token.string());
925 return BAD_VALUE;
926 }
927
928 combinedMeta |= metaState;
Jeff Brown061cf752010-11-18 20:52:43 -0800929 start = cur + 1;
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800930
931 if (ch == '\0') {
932 break;
933 }
934 }
935 }
936 *outMetaState = combinedMeta;
937 return NO_ERROR;
938}
939
940status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) {
941 char ch = mTokenizer->nextChar();
942 if (ch != '\'') {
943 goto Error;
944 }
945
946 ch = mTokenizer->nextChar();
947 if (ch == '\\') {
948 // Escape sequence.
949 ch = mTokenizer->nextChar();
950 if (ch == 'n') {
951 *outCharacter = '\n';
952 } else if (ch == 't') {
953 *outCharacter = '\t';
954 } else if (ch == '\\') {
955 *outCharacter = '\\';
956 } else if (ch == '\'') {
957 *outCharacter = '\'';
958 } else if (ch == '"') {
959 *outCharacter = '"';
960 } else if (ch == 'u') {
961 *outCharacter = 0;
962 for (int i = 0; i < 4; i++) {
963 ch = mTokenizer->nextChar();
964 int digit;
965 if (ch >= '0' && ch <= '9') {
966 digit = ch - '0';
967 } else if (ch >= 'A' && ch <= 'F') {
968 digit = ch - 'A' + 10;
969 } else if (ch >= 'a' && ch <= 'f') {
970 digit = ch - 'a' + 10;
971 } else {
972 goto Error;
973 }
974 *outCharacter = (*outCharacter << 4) | digit;
975 }
976 } else {
977 goto Error;
978 }
979 } else if (ch >= 32 && ch <= 126 && ch != '\'') {
980 // ASCII literal character.
981 *outCharacter = ch;
982 } else {
983 goto Error;
984 }
985
986 ch = mTokenizer->nextChar();
987 if (ch != '\'') {
988 goto Error;
989 }
990
991 // Ensure that we consumed the entire token.
992 if (mTokenizer->nextToken(WHITESPACE).isEmpty()) {
993 return NO_ERROR;
994 }
995
996Error:
Steve Block3762c312012-01-06 19:20:56 +0000997 ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().string());
Jeff Brown6b53e8d2010-11-10 16:03:06 -0800998 return BAD_VALUE;
999}
1000
1001} // namespace android