Home | History | Annotate | Download | only in ui
      1 /*
      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 
     17 #define LOG_TAG "KeyCharacterMap"
     18 
     19 #include <stdlib.h>
     20 #include <string.h>
     21 #include <android/keycodes.h>
     22 #include <ui/Keyboard.h>
     23 #include <ui/KeyCharacterMap.h>
     24 #include <utils/Log.h>
     25 #include <utils/Errors.h>
     26 #include <utils/Tokenizer.h>
     27 #include <utils/Timers.h>
     28 
     29 // Enables debug output for the parser.
     30 #define DEBUG_PARSER 0
     31 
     32 // Enables debug output for parser performance.
     33 #define DEBUG_PARSER_PERFORMANCE 0
     34 
     35 // Enables debug output for mapping.
     36 #define DEBUG_MAPPING 0
     37 
     38 
     39 namespace android {
     40 
     41 static const char* WHITESPACE = " \t\r";
     42 static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:";
     43 
     44 struct Modifier {
     45     const char* label;
     46     int32_t metaState;
     47 };
     48 static const Modifier modifiers[] = {
     49         { "shift", AMETA_SHIFT_ON },
     50         { "lshift", AMETA_SHIFT_LEFT_ON },
     51         { "rshift", AMETA_SHIFT_RIGHT_ON },
     52         { "alt", AMETA_ALT_ON },
     53         { "lalt", AMETA_ALT_LEFT_ON },
     54         { "ralt", AMETA_ALT_RIGHT_ON },
     55         { "ctrl", AMETA_CTRL_ON },
     56         { "lctrl", AMETA_CTRL_LEFT_ON },
     57         { "rctrl", AMETA_CTRL_RIGHT_ON },
     58         { "meta", AMETA_META_ON },
     59         { "lmeta", AMETA_META_LEFT_ON },
     60         { "rmeta", AMETA_META_RIGHT_ON },
     61         { "sym", AMETA_SYM_ON },
     62         { "fn", AMETA_FUNCTION_ON },
     63         { "capslock", AMETA_CAPS_LOCK_ON },
     64         { "numlock", AMETA_NUM_LOCK_ON },
     65         { "scrolllock", AMETA_SCROLL_LOCK_ON },
     66 };
     67 
     68 #if DEBUG_MAPPING
     69 static String8 toString(const char16_t* chars, size_t numChars) {
     70     String8 result;
     71     for (size_t i = 0; i < numChars; i++) {
     72         result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]);
     73     }
     74     return result;
     75 }
     76 #endif
     77 
     78 
     79 // --- KeyCharacterMap ---
     80 
     81 KeyCharacterMap::KeyCharacterMap() :
     82     mType(KEYBOARD_TYPE_UNKNOWN) {
     83 }
     84 
     85 KeyCharacterMap::~KeyCharacterMap() {
     86     for (size_t i = 0; i < mKeys.size(); i++) {
     87         Key* key = mKeys.editValueAt(i);
     88         delete key;
     89     }
     90 }
     91 
     92 status_t KeyCharacterMap::load(const String8& filename, KeyCharacterMap** outMap) {
     93     *outMap = NULL;
     94 
     95     Tokenizer* tokenizer;
     96     status_t status = Tokenizer::open(filename, &tokenizer);
     97     if (status) {
     98         LOGE("Error %d opening key character map file %s.", status, filename.string());
     99     } else {
    100         KeyCharacterMap* map = new KeyCharacterMap();
    101         if (!map) {
    102             LOGE("Error allocating key character map.");
    103             status = NO_MEMORY;
    104         } else {
    105 #if DEBUG_PARSER_PERFORMANCE
    106             nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
    107 #endif
    108             Parser parser(map, tokenizer);
    109             status = parser.parse();
    110 #if DEBUG_PARSER_PERFORMANCE
    111             nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
    112             LOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
    113                     tokenizer->getFilename().string(), tokenizer->getLineNumber(),
    114                     elapsedTime / 1000000.0);
    115 #endif
    116             if (status) {
    117                 delete map;
    118             } else {
    119                 *outMap = map;
    120             }
    121         }
    122         delete tokenizer;
    123     }
    124     return status;
    125 }
    126 
    127 status_t KeyCharacterMap::loadByDeviceId(int32_t deviceId, KeyCharacterMap** outMap) {
    128     *outMap = NULL;
    129 
    130     String8 filename;
    131     status_t result = getKeyCharacterMapFile(deviceId, filename);
    132     if (!result) {
    133         result = load(filename, outMap);
    134     }
    135     return result;
    136 }
    137 
    138 int32_t KeyCharacterMap::getKeyboardType() const {
    139     return mType;
    140 }
    141 
    142 char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
    143     char16_t result = 0;
    144     const Key* key;
    145     if (getKey(keyCode, &key)) {
    146         result = key->label;
    147     }
    148 #if DEBUG_MAPPING
    149     LOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result);
    150 #endif
    151     return result;
    152 }
    153 
    154 char16_t KeyCharacterMap::getNumber(int32_t keyCode) const {
    155     char16_t result = 0;
    156     const Key* key;
    157     if (getKey(keyCode, &key)) {
    158         result = key->number;
    159     }
    160 #if DEBUG_MAPPING
    161     LOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result);
    162 #endif
    163     return result;
    164 }
    165 
    166 char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const {
    167     char16_t result = 0;
    168     const Key* key;
    169     const Behavior* behavior;
    170     if (getKeyBehavior(keyCode, metaState, &key, &behavior)) {
    171         result = behavior->character;
    172     }
    173 #if DEBUG_MAPPING
    174     LOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result);
    175 #endif
    176     return result;
    177 }
    178 
    179 bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState,
    180         FallbackAction* outFallbackAction) const {
    181     outFallbackAction->keyCode = 0;
    182     outFallbackAction->metaState = 0;
    183 
    184     bool result = false;
    185     const Key* key;
    186     const Behavior* behavior;
    187     if (getKeyBehavior(keyCode, metaState, &key, &behavior)) {
    188         if (behavior->fallbackKeyCode) {
    189             outFallbackAction->keyCode = behavior->fallbackKeyCode;
    190             outFallbackAction->metaState = metaState & ~behavior->metaState;
    191             result = true;
    192         }
    193     }
    194 #if DEBUG_MAPPING
    195     LOGD("getFallbackKeyCode: keyCode=%d, metaState=0x%08x ~ Result %s, "
    196             "fallback keyCode=%d, fallback metaState=0x%08x.",
    197             keyCode, metaState, result ? "true" : "false",
    198             outFallbackAction->keyCode, outFallbackAction->metaState);
    199 #endif
    200     return result;
    201 }
    202 
    203 char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars,
    204         int32_t metaState) const {
    205     char16_t result = 0;
    206     const Key* key;
    207     if (getKey(keyCode, &key)) {
    208         // Try to find the most general behavior that maps to this character.
    209         // For example, the base key behavior will usually be last in the list.
    210         // However, if we find a perfect meta state match for one behavior then use that one.
    211         for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
    212             if (behavior->character) {
    213                 for (size_t i = 0; i < numChars; i++) {
    214                     if (behavior->character == chars[i]) {
    215                         result = behavior->character;
    216                         if ((behavior->metaState & metaState) == behavior->metaState) {
    217                             goto ExactMatch;
    218                         }
    219                         break;
    220                     }
    221                 }
    222             }
    223         }
    224     ExactMatch: ;
    225     }
    226 #if DEBUG_MAPPING
    227     LOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.",
    228             keyCode, toString(chars, numChars).string(), metaState, result);
    229 #endif
    230     return result;
    231 }
    232 
    233 bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
    234         Vector<KeyEvent>& outEvents) const {
    235     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
    236 
    237     for (size_t i = 0; i < numChars; i++) {
    238         int32_t keyCode, metaState;
    239         char16_t ch = chars[i];
    240         if (!findKey(ch, &keyCode, &metaState)) {
    241 #if DEBUG_MAPPING
    242             LOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.",
    243                     deviceId, toString(chars, numChars).string(), ch);
    244 #endif
    245             return false;
    246         }
    247 
    248         int32_t currentMetaState = 0;
    249         addMetaKeys(outEvents, deviceId, metaState, true, now, &currentMetaState);
    250         addKey(outEvents, deviceId, keyCode, currentMetaState, true, now);
    251         addKey(outEvents, deviceId, keyCode, currentMetaState, false, now);
    252         addMetaKeys(outEvents, deviceId, metaState, false, now, &currentMetaState);
    253     }
    254 #if DEBUG_MAPPING
    255     LOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.",
    256             deviceId, toString(chars, numChars).string(), int32_t(outEvents.size()));
    257     for (size_t i = 0; i < outEvents.size(); i++) {
    258         LOGD("  Key: keyCode=%d, metaState=0x%08x, %s.",
    259                 outEvents[i].getKeyCode(), outEvents[i].getMetaState(),
    260                 outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up");
    261     }
    262 #endif
    263     return true;
    264 }
    265 
    266 bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const {
    267     ssize_t index = mKeys.indexOfKey(keyCode);
    268     if (index >= 0) {
    269         *outKey = mKeys.valueAt(index);
    270         return true;
    271     }
    272     return false;
    273 }
    274 
    275 bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState,
    276         const Key** outKey, const Behavior** outBehavior) const {
    277     const Key* key;
    278     if (getKey(keyCode, &key)) {
    279         const Behavior* behavior = key->firstBehavior;
    280         while (behavior) {
    281             if ((behavior->metaState & metaState) == behavior->metaState) {
    282                 *outKey = key;
    283                 *outBehavior = behavior;
    284                 return true;
    285             }
    286             behavior = behavior->next;
    287         }
    288     }
    289     return false;
    290 }
    291 
    292 bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const {
    293     if (!ch) {
    294         return false;
    295     }
    296 
    297     for (size_t i = 0; i < mKeys.size(); i++) {
    298         const Key* key = mKeys.valueAt(i);
    299 
    300         // Try to find the most general behavior that maps to this character.
    301         // For example, the base key behavior will usually be last in the list.
    302         const Behavior* found = NULL;
    303         for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
    304             if (behavior->character == ch) {
    305                 found = behavior;
    306             }
    307         }
    308         if (found) {
    309             *outKeyCode = mKeys.keyAt(i);
    310             *outMetaState = found->metaState;
    311             return true;
    312         }
    313     }
    314     return false;
    315 }
    316 
    317 void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents,
    318         int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
    319     outEvents.push();
    320     KeyEvent& event = outEvents.editTop();
    321     event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD,
    322             down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
    323             0, keyCode, 0, metaState, 0, time, time);
    324 }
    325 
    326 void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
    327         int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
    328         int32_t* currentMetaState) {
    329     // Add and remove meta keys symmetrically.
    330     if (down) {
    331         addLockedMetaKey(outEvents, deviceId, metaState, time,
    332                 AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
    333         addLockedMetaKey(outEvents, deviceId, metaState, time,
    334                 AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
    335         addLockedMetaKey(outEvents, deviceId, metaState, time,
    336                 AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
    337 
    338         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
    339                 AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
    340                 AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
    341                 AMETA_SHIFT_ON, currentMetaState);
    342         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
    343                 AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
    344                 AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
    345                 AMETA_ALT_ON, currentMetaState);
    346         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
    347                 AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
    348                 AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
    349                 AMETA_CTRL_ON, currentMetaState);
    350         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
    351                 AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
    352                 AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
    353                 AMETA_META_ON, currentMetaState);
    354 
    355         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
    356                 AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
    357         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
    358                 AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
    359     } else {
    360         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
    361                 AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
    362         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
    363                 AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
    364 
    365         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
    366                 AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
    367                 AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
    368                 AMETA_META_ON, currentMetaState);
    369         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
    370                 AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
    371                 AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
    372                 AMETA_CTRL_ON, currentMetaState);
    373         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
    374                 AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
    375                 AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
    376                 AMETA_ALT_ON, currentMetaState);
    377         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
    378                 AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
    379                 AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
    380                 AMETA_SHIFT_ON, currentMetaState);
    381 
    382         addLockedMetaKey(outEvents, deviceId, metaState, time,
    383                 AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
    384         addLockedMetaKey(outEvents, deviceId, metaState, time,
    385                 AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
    386         addLockedMetaKey(outEvents, deviceId, metaState, time,
    387                 AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
    388     }
    389 }
    390 
    391 bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
    392         int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
    393         int32_t keyCode, int32_t keyMetaState,
    394         int32_t* currentMetaState) {
    395     if ((metaState & keyMetaState) == keyMetaState) {
    396         *currentMetaState = updateMetaState(keyCode, down, *currentMetaState);
    397         addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time);
    398         return true;
    399     }
    400     return false;
    401 }
    402 
    403 void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
    404         int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
    405         int32_t leftKeyCode, int32_t leftKeyMetaState,
    406         int32_t rightKeyCode, int32_t rightKeyMetaState,
    407         int32_t eitherKeyMetaState,
    408         int32_t* currentMetaState) {
    409     bool specific = false;
    410     specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
    411             leftKeyCode, leftKeyMetaState, currentMetaState);
    412     specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
    413             rightKeyCode, rightKeyMetaState, currentMetaState);
    414 
    415     if (!specific) {
    416         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
    417                 leftKeyCode, eitherKeyMetaState, currentMetaState);
    418     }
    419 }
    420 
    421 void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents,
    422         int32_t deviceId, int32_t metaState, nsecs_t time,
    423         int32_t keyCode, int32_t keyMetaState,
    424         int32_t* currentMetaState) {
    425     if ((metaState & keyMetaState) == keyMetaState) {
    426         *currentMetaState = updateMetaState(keyCode, true, *currentMetaState);
    427         addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time);
    428         *currentMetaState = updateMetaState(keyCode, false, *currentMetaState);
    429         addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time);
    430     }
    431 }
    432 
    433 
    434 // --- KeyCharacterMap::Key ---
    435 
    436 KeyCharacterMap::Key::Key() :
    437         label(0), number(0), firstBehavior(NULL) {
    438 }
    439 
    440 KeyCharacterMap::Key::~Key() {
    441     Behavior* behavior = firstBehavior;
    442     while (behavior) {
    443         Behavior* next = behavior->next;
    444         delete behavior;
    445         behavior = next;
    446     }
    447 }
    448 
    449 
    450 // --- KeyCharacterMap::Behavior ---
    451 
    452 KeyCharacterMap::Behavior::Behavior() :
    453         next(NULL), metaState(0), character(0), fallbackKeyCode(0) {
    454 }
    455 
    456 
    457 // --- KeyCharacterMap::Parser ---
    458 
    459 KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer) :
    460         mMap(map), mTokenizer(tokenizer), mState(STATE_TOP) {
    461 }
    462 
    463 KeyCharacterMap::Parser::~Parser() {
    464 }
    465 
    466 status_t KeyCharacterMap::Parser::parse() {
    467     while (!mTokenizer->isEof()) {
    468 #if DEBUG_PARSER
    469         LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
    470                 mTokenizer->peekRemainderOfLine().string());
    471 #endif
    472 
    473         mTokenizer->skipDelimiters(WHITESPACE);
    474 
    475         if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
    476             switch (mState) {
    477             case STATE_TOP: {
    478                 String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
    479                 if (keywordToken == "type") {
    480                     mTokenizer->skipDelimiters(WHITESPACE);
    481                     status_t status = parseType();
    482                     if (status) return status;
    483                 } else if (keywordToken == "key") {
    484                     mTokenizer->skipDelimiters(WHITESPACE);
    485                     status_t status = parseKey();
    486                     if (status) return status;
    487                 } else {
    488                     LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
    489                             keywordToken.string());
    490                     return BAD_VALUE;
    491                 }
    492                 break;
    493             }
    494 
    495             case STATE_KEY: {
    496                 status_t status = parseKeyProperty();
    497                 if (status) return status;
    498                 break;
    499             }
    500             }
    501 
    502             mTokenizer->skipDelimiters(WHITESPACE);
    503             if (!mTokenizer->isEol()) {
    504                 LOGE("%s: Expected end of line, got '%s'.",
    505                         mTokenizer->getLocation().string(),
    506                         mTokenizer->peekRemainderOfLine().string());
    507                 return BAD_VALUE;
    508             }
    509         }
    510 
    511         mTokenizer->nextLine();
    512     }
    513 
    514     if (mState != STATE_TOP) {
    515         LOGE("%s: Unterminated key description at end of file.",
    516                 mTokenizer->getLocation().string());
    517         return BAD_VALUE;
    518     }
    519 
    520     if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
    521         LOGE("%s: Missing required keyboard 'type' declaration.",
    522                 mTokenizer->getLocation().string());
    523         return BAD_VALUE;
    524     }
    525 
    526     return NO_ERROR;
    527 }
    528 
    529 status_t KeyCharacterMap::Parser::parseType() {
    530     if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) {
    531         LOGE("%s: Duplicate keyboard 'type' declaration.",
    532                 mTokenizer->getLocation().string());
    533         return BAD_VALUE;
    534     }
    535 
    536     KeyboardType type;
    537     String8 typeToken = mTokenizer->nextToken(WHITESPACE);
    538     if (typeToken == "NUMERIC") {
    539         type = KEYBOARD_TYPE_NUMERIC;
    540     } else if (typeToken == "PREDICTIVE") {
    541         type = KEYBOARD_TYPE_PREDICTIVE;
    542     } else if (typeToken == "ALPHA") {
    543         type = KEYBOARD_TYPE_ALPHA;
    544     } else if (typeToken == "FULL") {
    545         type = KEYBOARD_TYPE_FULL;
    546     } else if (typeToken == "SPECIAL_FUNCTION") {
    547         type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
    548     } else {
    549         LOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
    550                 typeToken.string());
    551         return BAD_VALUE;
    552     }
    553 
    554 #if DEBUG_PARSER
    555     LOGD("Parsed type: type=%d.", type);
    556 #endif
    557     mMap->mType = type;
    558     return NO_ERROR;
    559 }
    560 
    561 status_t KeyCharacterMap::Parser::parseKey() {
    562     String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
    563     int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
    564     if (!keyCode) {
    565         LOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
    566                 keyCodeToken.string());
    567         return BAD_VALUE;
    568     }
    569     if (mMap->mKeys.indexOfKey(keyCode) >= 0) {
    570         LOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(),
    571                 keyCodeToken.string());
    572         return BAD_VALUE;
    573     }
    574 
    575     mTokenizer->skipDelimiters(WHITESPACE);
    576     String8 openBraceToken = mTokenizer->nextToken(WHITESPACE);
    577     if (openBraceToken != "{") {
    578         LOGE("%s: Expected '{' after key code label, got '%s'.",
    579                 mTokenizer->getLocation().string(), openBraceToken.string());
    580         return BAD_VALUE;
    581     }
    582 
    583 #if DEBUG_PARSER
    584     LOGD("Parsed beginning of key: keyCode=%d.", keyCode);
    585 #endif
    586     mKeyCode = keyCode;
    587     mMap->mKeys.add(keyCode, new Key());
    588     mState = STATE_KEY;
    589     return NO_ERROR;
    590 }
    591 
    592 status_t KeyCharacterMap::Parser::parseKeyProperty() {
    593     String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
    594     if (token == "}") {
    595         mState = STATE_TOP;
    596         return NO_ERROR;
    597     }
    598 
    599     Vector<Property> properties;
    600 
    601     // Parse all comma-delimited property names up to the first colon.
    602     for (;;) {
    603         if (token == "label") {
    604             properties.add(Property(PROPERTY_LABEL));
    605         } else if (token == "number") {
    606             properties.add(Property(PROPERTY_NUMBER));
    607         } else {
    608             int32_t metaState;
    609             status_t status = parseModifier(token, &metaState);
    610             if (status) {
    611                 LOGE("%s: Expected a property name or modifier, got '%s'.",
    612                         mTokenizer->getLocation().string(), token.string());
    613                 return status;
    614             }
    615             properties.add(Property(PROPERTY_META, metaState));
    616         }
    617 
    618         mTokenizer->skipDelimiters(WHITESPACE);
    619         if (!mTokenizer->isEol()) {
    620             char ch = mTokenizer->nextChar();
    621             if (ch == ':') {
    622                 break;
    623             } else if (ch == ',') {
    624                 mTokenizer->skipDelimiters(WHITESPACE);
    625                 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
    626                 continue;
    627             }
    628         }
    629 
    630         LOGE("%s: Expected ',' or ':' after property name.",
    631                 mTokenizer->getLocation().string());
    632         return BAD_VALUE;
    633     }
    634 
    635     // Parse behavior after the colon.
    636     mTokenizer->skipDelimiters(WHITESPACE);
    637 
    638     Behavior behavior;
    639     bool haveCharacter = false;
    640     bool haveFallback = false;
    641 
    642     do {
    643         char ch = mTokenizer->peekChar();
    644         if (ch == '\'') {
    645             char16_t character;
    646             status_t status = parseCharacterLiteral(&character);
    647             if (status || !character) {
    648                 LOGE("%s: Invalid character literal for key.",
    649                         mTokenizer->getLocation().string());
    650                 return BAD_VALUE;
    651             }
    652             if (haveCharacter) {
    653                 LOGE("%s: Cannot combine multiple character literals or 'none'.",
    654                         mTokenizer->getLocation().string());
    655                 return BAD_VALUE;
    656             }
    657             behavior.character = character;
    658             haveCharacter = true;
    659         } else {
    660             token = mTokenizer->nextToken(WHITESPACE);
    661             if (token == "none") {
    662                 if (haveCharacter) {
    663                     LOGE("%s: Cannot combine multiple character literals or 'none'.",
    664                             mTokenizer->getLocation().string());
    665                     return BAD_VALUE;
    666                 }
    667                 haveCharacter = true;
    668             } else if (token == "fallback") {
    669                 mTokenizer->skipDelimiters(WHITESPACE);
    670                 token = mTokenizer->nextToken(WHITESPACE);
    671                 int32_t keyCode = getKeyCodeByLabel(token.string());
    672                 if (!keyCode) {
    673                     LOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
    674                             mTokenizer->getLocation().string(),
    675                             token.string());
    676                     return BAD_VALUE;
    677                 }
    678                 if (haveFallback) {
    679                     LOGE("%s: Cannot combine multiple fallback key codes.",
    680                             mTokenizer->getLocation().string());
    681                     return BAD_VALUE;
    682                 }
    683                 behavior.fallbackKeyCode = keyCode;
    684                 haveFallback = true;
    685             } else {
    686                 LOGE("%s: Expected a key behavior after ':'.",
    687                         mTokenizer->getLocation().string());
    688                 return BAD_VALUE;
    689             }
    690         }
    691 
    692         mTokenizer->skipDelimiters(WHITESPACE);
    693     } while (!mTokenizer->isEol());
    694 
    695     // Add the behavior.
    696     Key* key = mMap->mKeys.valueFor(mKeyCode);
    697     for (size_t i = 0; i < properties.size(); i++) {
    698         const Property& property = properties.itemAt(i);
    699         switch (property.property) {
    700         case PROPERTY_LABEL:
    701             if (key->label) {
    702                 LOGE("%s: Duplicate label for key.",
    703                         mTokenizer->getLocation().string());
    704                 return BAD_VALUE;
    705             }
    706             key->label = behavior.character;
    707 #if DEBUG_PARSER
    708             LOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label);
    709 #endif
    710             break;
    711         case PROPERTY_NUMBER:
    712             if (key->number) {
    713                 LOGE("%s: Duplicate number for key.",
    714                         mTokenizer->getLocation().string());
    715                 return BAD_VALUE;
    716             }
    717             key->number = behavior.character;
    718 #if DEBUG_PARSER
    719             LOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number);
    720 #endif
    721             break;
    722         case PROPERTY_META: {
    723             for (Behavior* b = key->firstBehavior; b; b = b->next) {
    724                 if (b->metaState == property.metaState) {
    725                     LOGE("%s: Duplicate key behavior for modifier.",
    726                             mTokenizer->getLocation().string());
    727                     return BAD_VALUE;
    728                 }
    729             }
    730             Behavior* newBehavior = new Behavior(behavior);
    731             newBehavior->metaState = property.metaState;
    732             newBehavior->next = key->firstBehavior;
    733             key->firstBehavior = newBehavior;
    734 #if DEBUG_PARSER
    735             LOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d.", mKeyCode,
    736                     newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode);
    737 #endif
    738             break;
    739         }
    740         }
    741     }
    742     return NO_ERROR;
    743 }
    744 
    745 status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) {
    746     if (token == "base") {
    747         *outMetaState = 0;
    748         return NO_ERROR;
    749     }
    750 
    751     int32_t combinedMeta = 0;
    752 
    753     const char* str = token.string();
    754     const char* start = str;
    755     for (const char* cur = str; ; cur++) {
    756         char ch = *cur;
    757         if (ch == '+' || ch == '\0') {
    758             size_t len = cur - start;
    759             int32_t metaState = 0;
    760             for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) {
    761                 if (strlen(modifiers[i].label) == len
    762                         && strncmp(modifiers[i].label, start, len) == 0) {
    763                     metaState = modifiers[i].metaState;
    764                     break;
    765                 }
    766             }
    767             if (!metaState) {
    768                 return BAD_VALUE;
    769             }
    770             if (combinedMeta & metaState) {
    771                 LOGE("%s: Duplicate modifier combination '%s'.",
    772                         mTokenizer->getLocation().string(), token.string());
    773                 return BAD_VALUE;
    774             }
    775 
    776             combinedMeta |= metaState;
    777             start = cur + 1;
    778 
    779             if (ch == '\0') {
    780                 break;
    781             }
    782         }
    783     }
    784     *outMetaState = combinedMeta;
    785     return NO_ERROR;
    786 }
    787 
    788 status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) {
    789     char ch = mTokenizer->nextChar();
    790     if (ch != '\'') {
    791         goto Error;
    792     }
    793 
    794     ch = mTokenizer->nextChar();
    795     if (ch == '\\') {
    796         // Escape sequence.
    797         ch = mTokenizer->nextChar();
    798         if (ch == 'n') {
    799             *outCharacter = '\n';
    800         } else if (ch == 't') {
    801             *outCharacter = '\t';
    802         } else if (ch == '\\') {
    803             *outCharacter = '\\';
    804         } else if (ch == '\'') {
    805             *outCharacter = '\'';
    806         } else if (ch == '"') {
    807             *outCharacter = '"';
    808         } else if (ch == 'u') {
    809             *outCharacter = 0;
    810             for (int i = 0; i < 4; i++) {
    811                 ch = mTokenizer->nextChar();
    812                 int digit;
    813                 if (ch >= '0' && ch <= '9') {
    814                     digit = ch - '0';
    815                 } else if (ch >= 'A' && ch <= 'F') {
    816                     digit = ch - 'A' + 10;
    817                 } else if (ch >= 'a' && ch <= 'f') {
    818                     digit = ch - 'a' + 10;
    819                 } else {
    820                     goto Error;
    821                 }
    822                 *outCharacter = (*outCharacter << 4) | digit;
    823             }
    824         } else {
    825             goto Error;
    826         }
    827     } else if (ch >= 32 && ch <= 126 && ch != '\'') {
    828         // ASCII literal character.
    829         *outCharacter = ch;
    830     } else {
    831         goto Error;
    832     }
    833 
    834     ch = mTokenizer->nextChar();
    835     if (ch != '\'') {
    836         goto Error;
    837     }
    838 
    839     // Ensure that we consumed the entire token.
    840     if (mTokenizer->nextToken(WHITESPACE).isEmpty()) {
    841         return NO_ERROR;
    842     }
    843 
    844 Error:
    845     LOGE("%s: Malformed character literal.", mTokenizer->getLocation().string());
    846     return BAD_VALUE;
    847 }
    848 
    849 } // namespace android
    850