widget/gonk/libui/KeyCharacterMap.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     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  */
    17 #define LOG_TAG "KeyCharacterMap"
    18 #include "cutils_log.h"
    20 #include <stdlib.h>
    21 #include <string.h>
    22 #include "android_keycodes.h"
    23 #include "Keyboard.h"
    24 #include "KeyCharacterMap.h"
    26 #if HAVE_ANDROID_OS
    27 #include <binder/Parcel.h>
    28 #endif
    30 #include <utils/Errors.h>
    31 #include "Tokenizer.h"
    32 #include <utils/Timers.h>
    34 // Enables debug output for the parser.
    35 #define DEBUG_PARSER 0
    37 // Enables debug output for parser performance.
    38 #define DEBUG_PARSER_PERFORMANCE 0
    40 // Enables debug output for mapping.
    41 #define DEBUG_MAPPING 0
    44 namespace android {
    46 static const char* WHITESPACE = " \t\r";
    47 static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:";
    49 struct Modifier {
    50     const char* label;
    51     int32_t metaState;
    52 };
    53 static 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 },
    71 };
    73 #if DEBUG_MAPPING
    74 static 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]);
    78     }
    79     return result;
    80 }
    81 #endif
    84 // --- KeyCharacterMap ---
    86 sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap();
    88 KeyCharacterMap::KeyCharacterMap() :
    89     mType(KEYBOARD_TYPE_UNKNOWN) {
    90 }
    92 KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) :
    93     RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode),
    94     mKeysByUsageCode(other.mKeysByUsageCode) {
    95     for (size_t i = 0; i < other.mKeys.size(); i++) {
    96         mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i)));
    97     }
    98 }
   100 KeyCharacterMap::~KeyCharacterMap() {
   101     for (size_t i = 0; i < mKeys.size(); i++) {
   102         Key* key = mKeys.editValueAt(i);
   103         delete key;
   104     }
   105 }
   107 status_t KeyCharacterMap::load(const String8& filename,
   108         Format format, sp<KeyCharacterMap>* outMap) {
   109     outMap->clear();
   111     Tokenizer* tokenizer;
   112     status_t status = Tokenizer::open(filename, &tokenizer);
   113     if (status) {
   114         ALOGE("Error %d opening key character map file %s.", status, filename.string());
   115     } else {
   116         status = load(tokenizer, format, outMap);
   117         delete tokenizer;
   118     }
   119     return status;
   120 }
   122 status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents,
   123         Format format, sp<KeyCharacterMap>* outMap) {
   124     outMap->clear();
   126     Tokenizer* tokenizer;
   127     status_t status = Tokenizer::fromContents(filename, contents, &tokenizer);
   128     if (status) {
   129         ALOGE("Error %d opening key character map.", status);
   130     } else {
   131         status = load(tokenizer, format, outMap);
   132         delete tokenizer;
   133     }
   134     return status;
   135 }
   137 status_t KeyCharacterMap::load(Tokenizer* tokenizer,
   138         Format format, sp<KeyCharacterMap>* outMap) {
   139     status_t status = OK;
   140     sp<KeyCharacterMap> map = new KeyCharacterMap();
   141     if (!map.get()) {
   142         ALOGE("Error allocating key character map.");
   143         status = NO_MEMORY;
   144     } else {
   145 #if DEBUG_PARSER_PERFORMANCE
   146         nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
   147 #endif
   148         Parser parser(map.get(), tokenizer, format);
   149         status = parser.parse();
   150 #if DEBUG_PARSER_PERFORMANCE
   151         nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
   152         ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
   153                 tokenizer->getFilename().string(), tokenizer->getLineNumber(),
   154                 elapsedTime / 1000000.0);
   155 #endif
   156         if (!status) {
   157             *outMap = map;
   158         }
   159     }
   160     return status;
   161 }
   163 sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
   164         const sp<KeyCharacterMap>& overlay) {
   165     if (overlay == NULL) {
   166         return base;
   167     }
   168     if (base == NULL) {
   169         return overlay;
   170     }
   172     sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get());
   173     for (size_t i = 0; i < overlay->mKeys.size(); i++) {
   174         int32_t keyCode = overlay->mKeys.keyAt(i);
   175         Key* key = overlay->mKeys.valueAt(i);
   176         ssize_t oldIndex = map->mKeys.indexOfKey(keyCode);
   177         if (oldIndex >= 0) {
   178             delete map->mKeys.valueAt(oldIndex);
   179             map->mKeys.editValueAt(oldIndex) = new Key(*key);
   180         } else {
   181             map->mKeys.add(keyCode, new Key(*key));
   182         }
   183     }
   185     for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) {
   186         map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i),
   187                 overlay->mKeysByScanCode.valueAt(i));
   188     }
   190     for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) {
   191         map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i),
   192                 overlay->mKeysByUsageCode.valueAt(i));
   193     }
   194     return map;
   195 }
   197 sp<KeyCharacterMap> KeyCharacterMap::empty() {
   198     return sEmpty;
   199 }
   201 int32_t KeyCharacterMap::getKeyboardType() const {
   202     return mType;
   203 }
   205 char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
   206     char16_t result = 0;
   207     const Key* key;
   208     if (getKey(keyCode, &key)) {
   209         result = key->label;
   210     }
   211 #if DEBUG_MAPPING
   212     ALOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result);
   213 #endif
   214     return result;
   215 }
   217 char16_t KeyCharacterMap::getNumber(int32_t keyCode) const {
   218     char16_t result = 0;
   219     const Key* key;
   220     if (getKey(keyCode, &key)) {
   221         result = key->number;
   222     }
   223 #if DEBUG_MAPPING
   224     ALOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result);
   225 #endif
   226     return result;
   227 }
   229 char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const {
   230     char16_t result = 0;
   231     const Key* key;
   232     const Behavior* behavior;
   233     if (getKeyBehavior(keyCode, metaState, &key, &behavior)) {
   234         result = behavior->character;
   235     }
   236 #if DEBUG_MAPPING
   237     ALOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result);
   238 #endif
   239     return result;
   240 }
   242 bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState,
   243         FallbackAction* outFallbackAction) const {
   244     outFallbackAction->keyCode = 0;
   245     outFallbackAction->metaState = 0;
   247     bool result = false;
   248     const Key* key;
   249     const Behavior* behavior;
   250     if (getKeyBehavior(keyCode, metaState, &key, &behavior)) {
   251         if (behavior->fallbackKeyCode) {
   252             outFallbackAction->keyCode = behavior->fallbackKeyCode;
   253             outFallbackAction->metaState = metaState & ~behavior->metaState;
   254             result = true;
   255         }
   256     }
   257 #if DEBUG_MAPPING
   258     ALOGD("getFallbackKeyCode: keyCode=%d, metaState=0x%08x ~ Result %s, "
   259             "fallback keyCode=%d, fallback metaState=0x%08x.",
   260             keyCode, metaState, result ? "true" : "false",
   261             outFallbackAction->keyCode, outFallbackAction->metaState);
   262 #endif
   263     return result;
   264 }
   266 char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars,
   267         int32_t metaState) const {
   268     char16_t result = 0;
   269     const Key* key;
   270     if (getKey(keyCode, &key)) {
   271         // Try to find the most general behavior that maps to this character.
   272         // For example, the base key behavior will usually be last in the list.
   273         // However, if we find a perfect meta state match for one behavior then use that one.
   274         for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
   275             if (behavior->character) {
   276                 for (size_t i = 0; i < numChars; i++) {
   277                     if (behavior->character == chars[i]) {
   278                         result = behavior->character;
   279                         if ((behavior->metaState & metaState) == behavior->metaState) {
   280                             goto ExactMatch;
   281                         }
   282                         break;
   283                     }
   284                 }
   285             }
   286         }
   287     ExactMatch: ;
   288     }
   289 #if DEBUG_MAPPING
   290     ALOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.",
   291             keyCode, toString(chars, numChars).string(), metaState, result);
   292 #endif
   293     return result;
   294 }
   296 bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
   297         Vector<KeyEvent>& outEvents) const {
   298     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
   300     for (size_t i = 0; i < numChars; i++) {
   301         int32_t keyCode, metaState;
   302         char16_t ch = chars[i];
   303         if (!findKey(ch, &keyCode, &metaState)) {
   304 #if DEBUG_MAPPING
   305             ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.",
   306                     deviceId, toString(chars, numChars).string(), ch);
   307 #endif
   308             return false;
   309         }
   311         int32_t currentMetaState = 0;
   312         addMetaKeys(outEvents, deviceId, metaState, true, now, &currentMetaState);
   313         addKey(outEvents, deviceId, keyCode, currentMetaState, true, now);
   314         addKey(outEvents, deviceId, keyCode, currentMetaState, false, now);
   315         addMetaKeys(outEvents, deviceId, metaState, false, now, &currentMetaState);
   316     }
   317 #if DEBUG_MAPPING
   318     ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.",
   319             deviceId, toString(chars, numChars).string(), int32_t(outEvents.size()));
   320     for (size_t i = 0; i < outEvents.size(); i++) {
   321         ALOGD("  Key: keyCode=%d, metaState=0x%08x, %s.",
   322                 outEvents[i].getKeyCode(), outEvents[i].getMetaState(),
   323                 outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up");
   324     }
   325 #endif
   326     return true;
   327 }
   329 status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const {
   330     if (usageCode) {
   331         ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
   332         if (index >= 0) {
   333 #if DEBUG_MAPPING
   334     ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
   335             scanCode, usageCode, *outKeyCode);
   336 #endif
   337             *outKeyCode = mKeysByUsageCode.valueAt(index);
   338             return OK;
   339         }
   340     }
   341     if (scanCode) {
   342         ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
   343         if (index >= 0) {
   344 #if DEBUG_MAPPING
   345     ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
   346             scanCode, usageCode, *outKeyCode);
   347 #endif
   348             *outKeyCode = mKeysByScanCode.valueAt(index);
   349             return OK;
   350         }
   351     }
   353 #if DEBUG_MAPPING
   354         ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode);
   355 #endif
   356     *outKeyCode = AKEYCODE_UNKNOWN;
   357     return NAME_NOT_FOUND;
   358 }
   360 bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const {
   361     ssize_t index = mKeys.indexOfKey(keyCode);
   362     if (index >= 0) {
   363         *outKey = mKeys.valueAt(index);
   364         return true;
   365     }
   366     return false;
   367 }
   369 bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState,
   370         const Key** outKey, const Behavior** outBehavior) const {
   371     const Key* key;
   372     if (getKey(keyCode, &key)) {
   373         const Behavior* behavior = key->firstBehavior;
   374         while (behavior) {
   375             if (matchesMetaState(metaState, behavior->metaState)) {
   376                 *outKey = key;
   377                 *outBehavior = behavior;
   378                 return true;
   379             }
   380             behavior = behavior->next;
   381         }
   382     }
   383     return false;
   384 }
   386 bool KeyCharacterMap::matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState) {
   387     // Behavior must have at least the set of meta states specified.
   388     // And if the key event has CTRL, ALT or META then the behavior must exactly
   389     // match those, taking into account that a behavior can specify that it handles
   390     // one, both or either of a left/right modifier pair.
   391     if ((eventMetaState & behaviorMetaState) == behaviorMetaState) {
   392         const int32_t EXACT_META_STATES =
   393                 AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON
   394                 | AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON
   395                 | AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON;
   396         int32_t unmatchedMetaState = eventMetaState & ~behaviorMetaState & EXACT_META_STATES;
   397         if (behaviorMetaState & AMETA_CTRL_ON) {
   398             unmatchedMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
   399         } else if (behaviorMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
   400             unmatchedMetaState &= ~AMETA_CTRL_ON;
   401         }
   402         if (behaviorMetaState & AMETA_ALT_ON) {
   403             unmatchedMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
   404         } else if (behaviorMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
   405             unmatchedMetaState &= ~AMETA_ALT_ON;
   406         }
   407         if (behaviorMetaState & AMETA_META_ON) {
   408             unmatchedMetaState &= ~(AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
   409         } else if (behaviorMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
   410             unmatchedMetaState &= ~AMETA_META_ON;
   411         }
   412         return !unmatchedMetaState;
   413     }
   414     return false;
   415 }
   417 bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const {
   418     if (!ch) {
   419         return false;
   420     }
   422     for (size_t i = 0; i < mKeys.size(); i++) {
   423         const Key* key = mKeys.valueAt(i);
   425         // Try to find the most general behavior that maps to this character.
   426         // For example, the base key behavior will usually be last in the list.
   427         const Behavior* found = NULL;
   428         for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
   429             if (behavior->character == ch) {
   430                 found = behavior;
   431             }
   432         }
   433         if (found) {
   434             *outKeyCode = mKeys.keyAt(i);
   435             *outMetaState = found->metaState;
   436             return true;
   437         }
   438     }
   439     return false;
   440 }
   442 void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents,
   443         int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
   444     outEvents.push();
   445     KeyEvent& event = outEvents.editTop();
   446     event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD,
   447             down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
   448             0, keyCode, 0, metaState, 0, time, time);
   449 }
   451 void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
   452         int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
   453         int32_t* currentMetaState) {
   454     // Add and remove meta keys symmetrically.
   455     if (down) {
   456         addLockedMetaKey(outEvents, deviceId, metaState, time,
   457                 AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
   458         addLockedMetaKey(outEvents, deviceId, metaState, time,
   459                 AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
   460         addLockedMetaKey(outEvents, deviceId, metaState, time,
   461                 AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
   463         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
   464                 AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
   465                 AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
   466                 AMETA_SHIFT_ON, currentMetaState);
   467         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
   468                 AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
   469                 AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
   470                 AMETA_ALT_ON, currentMetaState);
   471         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
   472                 AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
   473                 AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
   474                 AMETA_CTRL_ON, currentMetaState);
   475         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
   476                 AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
   477                 AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
   478                 AMETA_META_ON, currentMetaState);
   480         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
   481                 AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
   482         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
   483                 AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
   484     } else {
   485         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
   486                 AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
   487         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
   488                 AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
   490         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
   491                 AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
   492                 AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
   493                 AMETA_META_ON, currentMetaState);
   494         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
   495                 AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
   496                 AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
   497                 AMETA_CTRL_ON, currentMetaState);
   498         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
   499                 AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
   500                 AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
   501                 AMETA_ALT_ON, currentMetaState);
   502         addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
   503                 AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
   504                 AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
   505                 AMETA_SHIFT_ON, currentMetaState);
   507         addLockedMetaKey(outEvents, deviceId, metaState, time,
   508                 AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
   509         addLockedMetaKey(outEvents, deviceId, metaState, time,
   510                 AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
   511         addLockedMetaKey(outEvents, deviceId, metaState, time,
   512                 AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
   513     }
   514 }
   516 bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
   517         int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
   518         int32_t keyCode, int32_t keyMetaState,
   519         int32_t* currentMetaState) {
   520     if ((metaState & keyMetaState) == keyMetaState) {
   521         *currentMetaState = updateMetaState(keyCode, down, *currentMetaState);
   522         addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time);
   523         return true;
   524     }
   525     return false;
   526 }
   528 void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
   529         int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
   530         int32_t leftKeyCode, int32_t leftKeyMetaState,
   531         int32_t rightKeyCode, int32_t rightKeyMetaState,
   532         int32_t eitherKeyMetaState,
   533         int32_t* currentMetaState) {
   534     bool specific = false;
   535     specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
   536             leftKeyCode, leftKeyMetaState, currentMetaState);
   537     specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
   538             rightKeyCode, rightKeyMetaState, currentMetaState);
   540     if (!specific) {
   541         addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
   542                 leftKeyCode, eitherKeyMetaState, currentMetaState);
   543     }
   544 }
   546 void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents,
   547         int32_t deviceId, int32_t metaState, nsecs_t time,
   548         int32_t keyCode, int32_t keyMetaState,
   549         int32_t* currentMetaState) {
   550     if ((metaState & keyMetaState) == keyMetaState) {
   551         *currentMetaState = updateMetaState(keyCode, true, *currentMetaState);
   552         addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time);
   553         *currentMetaState = updateMetaState(keyCode, false, *currentMetaState);
   554         addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time);
   555     }
   556 }
   558 #if HAVE_ANDROID_OS
   559 sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
   560     sp<KeyCharacterMap> map = new KeyCharacterMap();
   561     map->mType = parcel->readInt32();
   562     size_t numKeys = parcel->readInt32();
   563     if (parcel->errorCheck()) {
   564         return NULL;
   565     }
   567     for (size_t i = 0; i < numKeys; i++) {
   568         int32_t keyCode = parcel->readInt32();
   569         char16_t label = parcel->readInt32();
   570         char16_t number = parcel->readInt32();
   571         if (parcel->errorCheck()) {
   572             return NULL;
   573         }
   575         Key* key = new Key();
   576         key->label = label;
   577         key->number = number;
   578         map->mKeys.add(keyCode, key);
   580         Behavior* lastBehavior = NULL;
   581         while (parcel->readInt32()) {
   582             int32_t metaState = parcel->readInt32();
   583             char16_t character = parcel->readInt32();
   584             int32_t fallbackKeyCode = parcel->readInt32();
   585             if (parcel->errorCheck()) {
   586                 return NULL;
   587             }
   589             Behavior* behavior = new Behavior();
   590             behavior->metaState = metaState;
   591             behavior->character = character;
   592             behavior->fallbackKeyCode = fallbackKeyCode;
   593             if (lastBehavior) {
   594                 lastBehavior->next = behavior;
   595             } else {
   596                 key->firstBehavior = behavior;
   597             }
   598             lastBehavior = behavior;
   599         }
   601         if (parcel->errorCheck()) {
   602             return NULL;
   603         }
   604     }
   605     return map;
   606 }
   608 void KeyCharacterMap::writeToParcel(Parcel* parcel) const {
   609     parcel->writeInt32(mType);
   611     size_t numKeys = mKeys.size();
   612     parcel->writeInt32(numKeys);
   613     for (size_t i = 0; i < numKeys; i++) {
   614         int32_t keyCode = mKeys.keyAt(i);
   615         const Key* key = mKeys.valueAt(i);
   616         parcel->writeInt32(keyCode);
   617         parcel->writeInt32(key->label);
   618         parcel->writeInt32(key->number);
   619         for (const Behavior* behavior = key->firstBehavior; behavior != NULL;
   620                 behavior = behavior->next) {
   621             parcel->writeInt32(1);
   622             parcel->writeInt32(behavior->metaState);
   623             parcel->writeInt32(behavior->character);
   624             parcel->writeInt32(behavior->fallbackKeyCode);
   625         }
   626         parcel->writeInt32(0);
   627     }
   628 }
   629 #endif
   632 // --- KeyCharacterMap::Key ---
   634 KeyCharacterMap::Key::Key() :
   635         label(0), number(0), firstBehavior(NULL) {
   636 }
   638 KeyCharacterMap::Key::Key(const Key& other) :
   639         label(other.label), number(other.number),
   640         firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) {
   641 }
   643 KeyCharacterMap::Key::~Key() {
   644     Behavior* behavior = firstBehavior;
   645     while (behavior) {
   646         Behavior* next = behavior->next;
   647         delete behavior;
   648         behavior = next;
   649     }
   650 }
   653 // --- KeyCharacterMap::Behavior ---
   655 KeyCharacterMap::Behavior::Behavior() :
   656         next(NULL), metaState(0), character(0), fallbackKeyCode(0) {
   657 }
   659 KeyCharacterMap::Behavior::Behavior(const Behavior& other) :
   660         next(other.next ? new Behavior(*other.next) : NULL),
   661         metaState(other.metaState), character(other.character),
   662         fallbackKeyCode(other.fallbackKeyCode) {
   663 }
   666 // --- KeyCharacterMap::Parser ---
   668 KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) :
   669         mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) {
   670 }
   672 KeyCharacterMap::Parser::~Parser() {
   673 }
   675 status_t KeyCharacterMap::Parser::parse() {
   676     while (!mTokenizer->isEof()) {
   677 #if DEBUG_PARSER
   678         ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
   679                 mTokenizer->peekRemainderOfLine().string());
   680 #endif
   682         mTokenizer->skipDelimiters(WHITESPACE);
   684         if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
   685             switch (mState) {
   686             case STATE_TOP: {
   687                 String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
   688                 if (keywordToken == "type") {
   689                     mTokenizer->skipDelimiters(WHITESPACE);
   690                     status_t status = parseType();
   691                     if (status) return status;
   692                 } else if (keywordToken == "map") {
   693                     mTokenizer->skipDelimiters(WHITESPACE);
   694                     status_t status = parseMap();
   695                     if (status) return status;
   696                 } else if (keywordToken == "key") {
   697                     mTokenizer->skipDelimiters(WHITESPACE);
   698                     status_t status = parseKey();
   699                     if (status) return status;
   700                 } else {
   701                     ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
   702                             keywordToken.string());
   703                     return BAD_VALUE;
   704                 }
   705                 break;
   706             }
   708             case STATE_KEY: {
   709                 status_t status = parseKeyProperty();
   710                 if (status) return status;
   711                 break;
   712             }
   713             }
   715             mTokenizer->skipDelimiters(WHITESPACE);
   716             if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
   717                 ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
   718                         mTokenizer->getLocation().string(),
   719                         mTokenizer->peekRemainderOfLine().string());
   720                 return BAD_VALUE;
   721             }
   722         }
   724         mTokenizer->nextLine();
   725     }
   727     if (mState != STATE_TOP) {
   728         ALOGE("%s: Unterminated key description at end of file.",
   729                 mTokenizer->getLocation().string());
   730         return BAD_VALUE;
   731     }
   733     if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
   734         ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.",
   735                 mTokenizer->getLocation().string());
   736         return BAD_VALUE;
   737     }
   739     if (mFormat == FORMAT_BASE) {
   740         if (mMap->mType == KEYBOARD_TYPE_OVERLAY) {
   741             ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
   742                     mTokenizer->getLocation().string());
   743             return BAD_VALUE;
   744         }
   745     } else if (mFormat == FORMAT_OVERLAY) {
   746         if (mMap->mType != KEYBOARD_TYPE_OVERLAY) {
   747             ALOGE("%s: Overlay keyboard layout missing required keyboard "
   748                     "'type OVERLAY' declaration.",
   749                     mTokenizer->getLocation().string());
   750             return BAD_VALUE;
   751         }
   752     }
   754     return NO_ERROR;
   755 }
   757 status_t KeyCharacterMap::Parser::parseType() {
   758     if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) {
   759         ALOGE("%s: Duplicate keyboard 'type' declaration.",
   760                 mTokenizer->getLocation().string());
   761         return BAD_VALUE;
   762     }
   764     KeyboardType type;
   765     String8 typeToken = mTokenizer->nextToken(WHITESPACE);
   766     if (typeToken == "NUMERIC") {
   767         type = KEYBOARD_TYPE_NUMERIC;
   768     } else if (typeToken == "PREDICTIVE") {
   769         type = KEYBOARD_TYPE_PREDICTIVE;
   770     } else if (typeToken == "ALPHA") {
   771         type = KEYBOARD_TYPE_ALPHA;
   772     } else if (typeToken == "FULL") {
   773         type = KEYBOARD_TYPE_FULL;
   774     } else if (typeToken == "SPECIAL_FUNCTION") {
   775         type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
   776     } else if (typeToken == "OVERLAY") {
   777         type = KEYBOARD_TYPE_OVERLAY;
   778     } else {
   779         ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
   780                 typeToken.string());
   781         return BAD_VALUE;
   782     }
   784 #if DEBUG_PARSER
   785     ALOGD("Parsed type: type=%d.", type);
   786 #endif
   787     mMap->mType = type;
   788     return NO_ERROR;
   789 }
   791 status_t KeyCharacterMap::Parser::parseMap() {
   792     String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
   793     if (keywordToken == "key") {
   794         mTokenizer->skipDelimiters(WHITESPACE);
   795         return parseMapKey();
   796     }
   797     ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().string(),
   798             keywordToken.string());
   799     return BAD_VALUE;
   800 }
   802 status_t KeyCharacterMap::Parser::parseMapKey() {
   803     String8 codeToken = mTokenizer->nextToken(WHITESPACE);
   804     bool mapUsage = false;
   805     if (codeToken == "usage") {
   806         mapUsage = true;
   807         mTokenizer->skipDelimiters(WHITESPACE);
   808         codeToken = mTokenizer->nextToken(WHITESPACE);
   809     }
   811     char* end;
   812     int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
   813     if (*end) {
   814         ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
   815                 mapUsage ? "usage" : "scan code", codeToken.string());
   816         return BAD_VALUE;
   817     }
   818     KeyedVector<int32_t, int32_t>& map =
   819             mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
   820     if (map.indexOfKey(code) >= 0) {
   821         ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
   822                 mapUsage ? "usage" : "scan code", codeToken.string());
   823         return BAD_VALUE;
   824     }
   826     mTokenizer->skipDelimiters(WHITESPACE);
   827     String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
   828     int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
   829     if (!keyCode) {
   830         ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
   831                 keyCodeToken.string());
   832         return BAD_VALUE;
   833     }
   835 #if DEBUG_PARSER
   836     ALOGD("Parsed map key %s: code=%d, keyCode=%d.",
   837             mapUsage ? "usage" : "scan code", code, keyCode);
   838 #endif
   839     map.add(code, keyCode);
   840     return NO_ERROR;
   841 }
   843 status_t KeyCharacterMap::Parser::parseKey() {
   844     String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
   845     int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
   846     if (!keyCode) {
   847         ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
   848                 keyCodeToken.string());
   849         return BAD_VALUE;
   850     }
   851     if (mMap->mKeys.indexOfKey(keyCode) >= 0) {
   852         ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(),
   853                 keyCodeToken.string());
   854         return BAD_VALUE;
   855     }
   857     mTokenizer->skipDelimiters(WHITESPACE);
   858     String8 openBraceToken = mTokenizer->nextToken(WHITESPACE);
   859     if (openBraceToken != "{") {
   860         ALOGE("%s: Expected '{' after key code label, got '%s'.",
   861                 mTokenizer->getLocation().string(), openBraceToken.string());
   862         return BAD_VALUE;
   863     }
   865 #if DEBUG_PARSER
   866     ALOGD("Parsed beginning of key: keyCode=%d.", keyCode);
   867 #endif
   868     mKeyCode = keyCode;
   869     mMap->mKeys.add(keyCode, new Key());
   870     mState = STATE_KEY;
   871     return NO_ERROR;
   872 }
   874 status_t KeyCharacterMap::Parser::parseKeyProperty() {
   875     Key* key = mMap->mKeys.valueFor(mKeyCode);
   876     String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
   877     if (token == "}") {
   878         mState = STATE_TOP;
   879         return finishKey(key);
   880     }
   882     Vector<Property> properties;
   884     // Parse all comma-delimited property names up to the first colon.
   885     for (;;) {
   886         if (token == "label") {
   887             properties.add(Property(PROPERTY_LABEL));
   888         } else if (token == "number") {
   889             properties.add(Property(PROPERTY_NUMBER));
   890         } else {
   891             int32_t metaState;
   892             status_t status = parseModifier(token, &metaState);
   893             if (status) {
   894                 ALOGE("%s: Expected a property name or modifier, got '%s'.",
   895                         mTokenizer->getLocation().string(), token.string());
   896                 return status;
   897             }
   898             properties.add(Property(PROPERTY_META, metaState));
   899         }
   901         mTokenizer->skipDelimiters(WHITESPACE);
   902         if (!mTokenizer->isEol()) {
   903             char ch = mTokenizer->nextChar();
   904             if (ch == ':') {
   905                 break;
   906             } else if (ch == ',') {
   907                 mTokenizer->skipDelimiters(WHITESPACE);
   908                 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
   909                 continue;
   910             }
   911         }
   913         ALOGE("%s: Expected ',' or ':' after property name.",
   914                 mTokenizer->getLocation().string());
   915         return BAD_VALUE;
   916     }
   918     // Parse behavior after the colon.
   919     mTokenizer->skipDelimiters(WHITESPACE);
   921     Behavior behavior;
   922     bool haveCharacter = false;
   923     bool haveFallback = false;
   925     do {
   926         char ch = mTokenizer->peekChar();
   927         if (ch == '\'') {
   928             char16_t character;
   929             status_t status = parseCharacterLiteral(&character);
   930             if (status || !character) {
   931                 ALOGE("%s: Invalid character literal for key.",
   932                         mTokenizer->getLocation().string());
   933                 return BAD_VALUE;
   934             }
   935             if (haveCharacter) {
   936                 ALOGE("%s: Cannot combine multiple character literals or 'none'.",
   937                         mTokenizer->getLocation().string());
   938                 return BAD_VALUE;
   939             }
   940             behavior.character = character;
   941             haveCharacter = true;
   942         } else {
   943             token = mTokenizer->nextToken(WHITESPACE);
   944             if (token == "none") {
   945                 if (haveCharacter) {
   946                     ALOGE("%s: Cannot combine multiple character literals or 'none'.",
   947                             mTokenizer->getLocation().string());
   948                     return BAD_VALUE;
   949                 }
   950                 haveCharacter = true;
   951             } else if (token == "fallback") {
   952                 mTokenizer->skipDelimiters(WHITESPACE);
   953                 token = mTokenizer->nextToken(WHITESPACE);
   954                 int32_t keyCode = getKeyCodeByLabel(token.string());
   955                 if (!keyCode) {
   956                     ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
   957                             mTokenizer->getLocation().string(),
   958                             token.string());
   959                     return BAD_VALUE;
   960                 }
   961                 if (haveFallback) {
   962                     ALOGE("%s: Cannot combine multiple fallback key codes.",
   963                             mTokenizer->getLocation().string());
   964                     return BAD_VALUE;
   965                 }
   966                 behavior.fallbackKeyCode = keyCode;
   967                 haveFallback = true;
   968             } else {
   969                 ALOGE("%s: Expected a key behavior after ':'.",
   970                         mTokenizer->getLocation().string());
   971                 return BAD_VALUE;
   972             }
   973         }
   975         mTokenizer->skipDelimiters(WHITESPACE);
   976     } while (!mTokenizer->isEol() && mTokenizer->peekChar() != '#');
   978     // Add the behavior.
   979     for (size_t i = 0; i < properties.size(); i++) {
   980         const Property& property = properties.itemAt(i);
   981         switch (property.property) {
   982         case PROPERTY_LABEL:
   983             if (key->label) {
   984                 ALOGE("%s: Duplicate label for key.",
   985                         mTokenizer->getLocation().string());
   986                 return BAD_VALUE;
   987             }
   988             key->label = behavior.character;
   989 #if DEBUG_PARSER
   990             ALOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label);
   991 #endif
   992             break;
   993         case PROPERTY_NUMBER:
   994             if (key->number) {
   995                 ALOGE("%s: Duplicate number for key.",
   996                         mTokenizer->getLocation().string());
   997                 return BAD_VALUE;
   998             }
   999             key->number = behavior.character;
  1000 #if DEBUG_PARSER
  1001             ALOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number);
  1002 #endif
  1003             break;
  1004         case PROPERTY_META: {
  1005             for (Behavior* b = key->firstBehavior; b; b = b->next) {
  1006                 if (b->metaState == property.metaState) {
  1007                     ALOGE("%s: Duplicate key behavior for modifier.",
  1008                             mTokenizer->getLocation().string());
  1009                     return BAD_VALUE;
  1012             Behavior* newBehavior = new Behavior(behavior);
  1013             newBehavior->metaState = property.metaState;
  1014             newBehavior->next = key->firstBehavior;
  1015             key->firstBehavior = newBehavior;
  1016 #if DEBUG_PARSER
  1017             ALOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d.", mKeyCode,
  1018                     newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode);
  1019 #endif
  1020             break;
  1024     return NO_ERROR;
  1027 status_t KeyCharacterMap::Parser::finishKey(Key* key) {
  1028     // Fill in default number property.
  1029     if (!key->number) {
  1030         char16_t digit = 0;
  1031         char16_t symbol = 0;
  1032         for (Behavior* b = key->firstBehavior; b; b = b->next) {
  1033             char16_t ch = b->character;
  1034             if (ch) {
  1035                 if (ch >= '0' && ch <= '9') {
  1036                     digit = ch;
  1037                 } else if (ch == '(' || ch == ')' || ch == '#' || ch == '*'
  1038                         || ch == '-' || ch == '+' || ch == ',' || ch == '.'
  1039                         || ch == '\'' || ch == ':' || ch == ';' || ch == '/') {
  1040                     symbol = ch;
  1044         key->number = digit ? digit : symbol;
  1046     return NO_ERROR;
  1049 status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) {
  1050     if (token == "base") {
  1051         *outMetaState = 0;
  1052         return NO_ERROR;
  1055     int32_t combinedMeta = 0;
  1057     const char* str = token.string();
  1058     const char* start = str;
  1059     for (const char* cur = str; ; cur++) {
  1060         char ch = *cur;
  1061         if (ch == '+' || ch == '\0') {
  1062             size_t len = cur - start;
  1063             int32_t metaState = 0;
  1064             for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) {
  1065                 if (strlen(modifiers[i].label) == len
  1066                         && strncmp(modifiers[i].label, start, len) == 0) {
  1067                     metaState = modifiers[i].metaState;
  1068                     break;
  1071             if (!metaState) {
  1072                 return BAD_VALUE;
  1074             if (combinedMeta & metaState) {
  1075                 ALOGE("%s: Duplicate modifier combination '%s'.",
  1076                         mTokenizer->getLocation().string(), token.string());
  1077                 return BAD_VALUE;
  1080             combinedMeta |= metaState;
  1081             start = cur + 1;
  1083             if (ch == '\0') {
  1084                 break;
  1088     *outMetaState = combinedMeta;
  1089     return NO_ERROR;
  1092 status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) {
  1093     char ch = mTokenizer->nextChar();
  1094     if (ch != '\'') {
  1095         goto Error;
  1098     ch = mTokenizer->nextChar();
  1099     if (ch == '\\') {
  1100         // Escape sequence.
  1101         ch = mTokenizer->nextChar();
  1102         if (ch == 'n') {
  1103             *outCharacter = '\n';
  1104         } else if (ch == 't') {
  1105             *outCharacter = '\t';
  1106         } else if (ch == '\\') {
  1107             *outCharacter = '\\';
  1108         } else if (ch == '\'') {
  1109             *outCharacter = '\'';
  1110         } else if (ch == '"') {
  1111             *outCharacter = '"';
  1112         } else if (ch == 'u') {
  1113             *outCharacter = 0;
  1114             for (int i = 0; i < 4; i++) {
  1115                 ch = mTokenizer->nextChar();
  1116                 int digit;
  1117                 if (ch >= '0' && ch <= '9') {
  1118                     digit = ch - '0';
  1119                 } else if (ch >= 'A' && ch <= 'F') {
  1120                     digit = ch - 'A' + 10;
  1121                 } else if (ch >= 'a' && ch <= 'f') {
  1122                     digit = ch - 'a' + 10;
  1123                 } else {
  1124                     goto Error;
  1126                 *outCharacter = (*outCharacter << 4) | digit;
  1128         } else {
  1129             goto Error;
  1131     } else if (ch >= 32 && ch <= 126 && ch != '\'') {
  1132         // ASCII literal character.
  1133         *outCharacter = ch;
  1134     } else {
  1135         goto Error;
  1138     ch = mTokenizer->nextChar();
  1139     if (ch != '\'') {
  1140         goto Error;
  1143     // Ensure that we consumed the entire token.
  1144     if (mTokenizer->nextToken(WHITESPACE).isEmpty()) {
  1145         return NO_ERROR;
  1148 Error:
  1149     ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().string());
  1150     return BAD_VALUE;
  1153 } // namespace android

mercurial