16b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown/*
26b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * Copyright (C) 2008 The Android Open Source Project
36b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown *
46b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
56b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * you may not use this file except in compliance with the License.
66b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * You may obtain a copy of the License at
76b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown *
86b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
96b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown *
106b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * Unless required by applicable law or agreed to in writing, software
116b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
126b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * See the License for the specific language governing permissions and
146b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown * limitations under the License.
156b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown */
166b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#define LOG_TAG "KeyLayoutMap"
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include <stdlib.h>
206b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#include <android/keycodes.h>
21b93a03f841d93498bfea6cc92a22faa34bce1337Mathias Agopian#include <androidfw/Keyboard.h>
22b93a03f841d93498bfea6cc92a22faa34bce1337Mathias Agopian#include <androidfw/KeyLayoutMap.h>
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include <utils/Log.h>
246b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#include <utils/Errors.h>
256b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#include <utils/Tokenizer.h>
266b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#include <utils/Timers.h>
276b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
286b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown// Enables debug output for the parser.
296b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#define DEBUG_PARSER 0
306b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
316b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown// Enables debug output for parser performance.
326b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#define DEBUG_PARSER_PERFORMANCE 0
336b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
346b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown// Enables debug output for mapping.
356b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#define DEBUG_MAPPING 0
366b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectnamespace android {
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
406b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brownstatic const char* WHITESPACE = " \t\r";
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
426b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown// --- KeyLayoutMap ---
436b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
446b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff BrownKeyLayoutMap::KeyLayoutMap() {
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
476b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff BrownKeyLayoutMap::~KeyLayoutMap() {
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
509f25b7fdf216c9ef0bd2322cd223eeaf0d60f77fJeff Brownstatus_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) {
519f25b7fdf216c9ef0bd2322cd223eeaf0d60f77fJeff Brown    outMap->clear();
526b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
536b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    Tokenizer* tokenizer;
546b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    status_t status = Tokenizer::open(filename, &tokenizer);
556b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    if (status) {
563762c311729fe9f3af085c14c5c1fb471d994c03Steve Block        ALOGE("Error %d opening key layout map file %s.", status, filename.string());
576b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    } else {
589f25b7fdf216c9ef0bd2322cd223eeaf0d60f77fJeff Brown        sp<KeyLayoutMap> map = new KeyLayoutMap();
599f25b7fdf216c9ef0bd2322cd223eeaf0d60f77fJeff Brown        if (!map.get()) {
603762c311729fe9f3af085c14c5c1fb471d994c03Steve Block            ALOGE("Error allocating key layout map.");
616b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown            status = NO_MEMORY;
626b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        } else {
636b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#if DEBUG_PARSER_PERFORMANCE
646b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown            nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
656b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#endif
669f25b7fdf216c9ef0bd2322cd223eeaf0d60f77fJeff Brown            Parser parser(map.get(), tokenizer);
676b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown            status = parser.parse();
686b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#if DEBUG_PARSER_PERFORMANCE
696b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown            nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
705baa3a62a97544669fba6d65a11c07f252e654ddSteve Block            ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
716b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                    tokenizer->getFilename().string(), tokenizer->getLineNumber(),
726b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                    elapsedTime / 1000000.0);
736b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#endif
749f25b7fdf216c9ef0bd2322cd223eeaf0d60f77fJeff Brown            if (!status) {
756b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                *outMap = map;
766b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown            }
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
786b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        delete tokenizer;
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
806b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    return status;
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8349ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brownstatus_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode,
8449ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown        int32_t* outKeyCode, uint32_t* outFlags) const {
8549ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown    const Key* key = getKey(scanCode, usageCode);
8649ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown    if (!key) {
876b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#if DEBUG_MAPPING
8849ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown        ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode);
896b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#endif
9049ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown        *outKeyCode = AKEYCODE_UNKNOWN;
9149ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown        *outFlags = 0;
926b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        return NAME_NOT_FOUND;
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9549ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown    *outKeyCode = key->keyCode;
9649ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown    *outFlags = key->flags;
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
986b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#if DEBUG_MAPPING
9949ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown    ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.",
10049ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown            scanCode, usageCode, *outKeyCode, *outFlags);
1016b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#endif
1026b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    return NO_ERROR;
1036b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown}
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10549ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brownconst KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const {
10649ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown    if (usageCode) {
10749ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown        ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
10849ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown        if (index >= 0) {
10949ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown            return &mKeysByUsageCode.valueAt(index);
11049ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown        }
11149ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown    }
112cc1169831921d9295b2fc01c1eaf7e9b00836f53Jeff Brown    if (scanCode) {
113cc1169831921d9295b2fc01c1eaf7e9b00836f53Jeff Brown        ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
114cc1169831921d9295b2fc01c1eaf7e9b00836f53Jeff Brown        if (index >= 0) {
115cc1169831921d9295b2fc01c1eaf7e9b00836f53Jeff Brown            return &mKeysByScanCode.valueAt(index);
116cc1169831921d9295b2fc01c1eaf7e9b00836f53Jeff Brown        }
117cc1169831921d9295b2fc01c1eaf7e9b00836f53Jeff Brown    }
11849ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown    return NULL;
11949ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown}
12049ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown
1216f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brownstatus_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
12249ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown    const size_t N = mKeysByScanCode.size();
1236b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    for (size_t i=0; i<N; i++) {
12449ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown        if (mKeysByScanCode.valueAt(i).keyCode == keyCode) {
12549ccac530b5a798e3c4a79b66b51b8546a0deed1Jeff Brown            outScanCodes->add(mKeysByScanCode.keyAt(i));
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1286b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    return NO_ERROR;
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1313a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brownstatus_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const {
1326f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    ssize_t index = mAxes.indexOfKey(scanCode);
1336f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    if (index < 0) {
1346f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown#if DEBUG_MAPPING
1355baa3a62a97544669fba6d65a11c07f252e654ddSteve Block        ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode);
1366f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown#endif
1376f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown        return NAME_NOT_FOUND;
1386f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    }
1396f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown
1403a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown    *outAxisInfo = mAxes.valueAt(index);
1416f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown
1426f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown#if DEBUG_MAPPING
1435baa3a62a97544669fba6d65a11c07f252e654ddSteve Block    ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, "
1443a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            "splitValue=%d, flatOverride=%d.",
1453a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            scanCode,
1463a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis,
1473a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            outAxisInfo->splitValue, outAxisInfo->flatOverride);
1486f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown#endif
1496f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    return NO_ERROR;
1506f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown}
1516f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown
1526f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown
1536b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown// --- KeyLayoutMap::Parser ---
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1556b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff BrownKeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) :
1566b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        mMap(map), mTokenizer(tokenizer) {
1576b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown}
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1596b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff BrownKeyLayoutMap::Parser::~Parser() {
1606b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown}
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1626b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brownstatus_t KeyLayoutMap::Parser::parse() {
1636b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    while (!mTokenizer->isEof()) {
1646b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#if DEBUG_PARSER
1655baa3a62a97544669fba6d65a11c07f252e654ddSteve Block        ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
1666b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                mTokenizer->peekRemainderOfLine().string());
1676b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#endif
1686b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
1696b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        mTokenizer->skipDelimiters(WHITESPACE);
1706b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
1716b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
1726b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown            String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
1736b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown            if (keywordToken == "key") {
1746b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                mTokenizer->skipDelimiters(WHITESPACE);
1756b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                status_t status = parseKey();
1766b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                if (status) return status;
1776f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown            } else if (keywordToken == "axis") {
1786f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                mTokenizer->skipDelimiters(WHITESPACE);
1796f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                status_t status = parseAxis();
1806f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                if (status) return status;
1816b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown            } else {
1823762c311729fe9f3af085c14c5c1fb471d994c03Steve Block                ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
1836b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                        keywordToken.string());
1846b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                return BAD_VALUE;
1856b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown            }
1866b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
1876b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown            mTokenizer->skipDelimiters(WHITESPACE);
1888bca94ad83f82682bc15df29dc25377fe43da17fJeff Brown            if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
1898bca94ad83f82682bc15df29dc25377fe43da17fJeff Brown                ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
1906b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                        mTokenizer->getLocation().string(),
1916b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                        mTokenizer->peekRemainderOfLine().string());
1926b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                return BAD_VALUE;
1936b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown            }
1946b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        }
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1966b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        mTokenizer->nextLine();
1976b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    }
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    return NO_ERROR;
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2016b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brownstatus_t KeyLayoutMap::Parser::parseKey() {
2024a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown    String8 codeToken = mTokenizer->nextToken(WHITESPACE);
2034a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown    bool mapUsage = false;
2044a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown    if (codeToken == "usage") {
2054a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown        mapUsage = true;
2064a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown        mTokenizer->skipDelimiters(WHITESPACE);
2074a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown        codeToken = mTokenizer->nextToken(WHITESPACE);
2084a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown    }
2094a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown
2106b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    char* end;
2114a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown    int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
2126b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    if (*end) {
2134a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown        ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
2144a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown                mapUsage ? "usage" : "scan code", codeToken.string());
2156b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        return BAD_VALUE;
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2174a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown    KeyedVector<int32_t, Key>& map =
2184a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown            mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
2194a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown    if (map.indexOfKey(code) >= 0) {
2204a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown        ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
2214a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown                mapUsage ? "usage" : "scan code", codeToken.string());
2226b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        return BAD_VALUE;
2236b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    }
2246b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
2256b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    mTokenizer->skipDelimiters(WHITESPACE);
2266b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
2276b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
2286b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    if (!keyCode) {
2293762c311729fe9f3af085c14c5c1fb471d994c03Steve Block        ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
2306b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                keyCodeToken.string());
2316b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        return BAD_VALUE;
2326b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    }
2336b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
2346b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    uint32_t flags = 0;
2356b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    for (;;) {
2366b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        mTokenizer->skipDelimiters(WHITESPACE);
2378bca94ad83f82682bc15df29dc25377fe43da17fJeff Brown        if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break;
2386b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
2396b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        String8 flagToken = mTokenizer->nextToken(WHITESPACE);
2406b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        uint32_t flag = getKeyFlagByLabel(flagToken.string());
2416b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        if (!flag) {
2423762c311729fe9f3af085c14c5c1fb471d994c03Steve Block            ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
2436b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                    flagToken.string());
2446b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown            return BAD_VALUE;
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2466b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        if (flags & flag) {
2473762c311729fe9f3af085c14c5c1fb471d994c03Steve Block            ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(),
2486b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown                    flagToken.string());
2496b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown            return BAD_VALUE;
2506b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        }
2516b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown        flags |= flag;
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2536b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown
2546b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#if DEBUG_PARSER
2554a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown    ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.",
2564a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown            mapUsage ? "usage" : "scan code", code, keyCode, flags);
2576b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown#endif
2586b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    Key key;
2596b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    key.keyCode = keyCode;
2606b53e8daa69cba1a2a5a7c95a01e37ce9c53226cJeff Brown    key.flags = flags;
2614a3862f6b0a8971b6ab5b1c46735b28e7604d0dbJeff Brown    map.add(code, key);
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    return NO_ERROR;
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2656f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brownstatus_t KeyLayoutMap::Parser::parseAxis() {
2666f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
2676f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    char* end;
2686f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
2696f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    if (*end) {
2703762c311729fe9f3af085c14c5c1fb471d994c03Steve Block        ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(),
2716f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                scanCodeToken.string());
2726f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown        return BAD_VALUE;
2736f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    }
2746f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    if (mMap->mAxes.indexOfKey(scanCode) >= 0) {
2753762c311729fe9f3af085c14c5c1fb471d994c03Steve Block        ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
2766f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown                scanCodeToken.string());
2776f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown        return BAD_VALUE;
2786f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    }
2796f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown
2803a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown    AxisInfo axisInfo;
2813a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown
2826f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    mTokenizer->skipDelimiters(WHITESPACE);
2833a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown    String8 token = mTokenizer->nextToken(WHITESPACE);
2843a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown    if (token == "invert") {
2853a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        axisInfo.mode = AxisInfo::MODE_INVERT;
2863a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown
2873a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        mTokenizer->skipDelimiters(WHITESPACE);
2883a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        String8 axisToken = mTokenizer->nextToken(WHITESPACE);
2893a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        axisInfo.axis = getAxisByLabel(axisToken.string());
2903a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        if (axisInfo.axis < 0) {
2913762c311729fe9f3af085c14c5c1fb471d994c03Steve Block            ALOGE("%s: Expected inverted axis label, got '%s'.",
2923a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown                    mTokenizer->getLocation().string(), axisToken.string());
2933a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            return BAD_VALUE;
2943a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        }
2953a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown    } else if (token == "split") {
2963a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        axisInfo.mode = AxisInfo::MODE_SPLIT;
2973a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown
2983a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        mTokenizer->skipDelimiters(WHITESPACE);
2993a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        String8 splitToken = mTokenizer->nextToken(WHITESPACE);
3003a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0));
3013a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        if (*end) {
3023762c311729fe9f3af085c14c5c1fb471d994c03Steve Block            ALOGE("%s: Expected split value, got '%s'.",
3033a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown                    mTokenizer->getLocation().string(), splitToken.string());
3043a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            return BAD_VALUE;
3053a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        }
3063a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown
3073a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        mTokenizer->skipDelimiters(WHITESPACE);
3083a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE);
3093a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        axisInfo.axis = getAxisByLabel(lowAxisToken.string());
3103a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        if (axisInfo.axis < 0) {
3113762c311729fe9f3af085c14c5c1fb471d994c03Steve Block            ALOGE("%s: Expected low axis label, got '%s'.",
3123a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown                    mTokenizer->getLocation().string(), lowAxisToken.string());
3133a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            return BAD_VALUE;
3143a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        }
3153a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown
3163a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        mTokenizer->skipDelimiters(WHITESPACE);
3173a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        String8 highAxisToken = mTokenizer->nextToken(WHITESPACE);
3183a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        axisInfo.highAxis = getAxisByLabel(highAxisToken.string());
3193a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        if (axisInfo.highAxis < 0) {
3203762c311729fe9f3af085c14c5c1fb471d994c03Steve Block            ALOGE("%s: Expected high axis label, got '%s'.",
3213a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown                    mTokenizer->getLocation().string(), highAxisToken.string());
3223a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            return BAD_VALUE;
3233a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        }
3243a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown    } else {
3253a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        axisInfo.axis = getAxisByLabel(token.string());
3263a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        if (axisInfo.axis < 0) {
3273762c311729fe9f3af085c14c5c1fb471d994c03Steve Block            ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.",
3283a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown                    mTokenizer->getLocation().string(), token.string());
3293a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            return BAD_VALUE;
3303a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        }
3313a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown    }
3323a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown
3333a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown    for (;;) {
3343a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        mTokenizer->skipDelimiters(WHITESPACE);
3358bca94ad83f82682bc15df29dc25377fe43da17fJeff Brown        if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') {
3363a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            break;
3373a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        }
3383a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
3393a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        if (keywordToken == "flat") {
3403a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            mTokenizer->skipDelimiters(WHITESPACE);
3413a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            String8 flatToken = mTokenizer->nextToken(WHITESPACE);
3423a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0));
3433a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            if (*end) {
3443762c311729fe9f3af085c14c5c1fb471d994c03Steve Block                ALOGE("%s: Expected flat value, got '%s'.",
3453a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown                        mTokenizer->getLocation().string(), flatToken.string());
3463a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown                return BAD_VALUE;
3473a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            }
3483a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        } else {
3493762c311729fe9f3af085c14c5c1fb471d994c03Steve Block            ALOGE("%s: Expected keyword 'flat', got '%s'.",
3503a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown                    mTokenizer->getLocation().string(), keywordToken.string());
3513a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            return BAD_VALUE;
3523a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown        }
3536f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    }
3546f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown
3556f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown#if DEBUG_PARSER
3565baa3a62a97544669fba6d65a11c07f252e654ddSteve Block    ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, "
3573a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            "splitValue=%d, flatOverride=%d.",
3583a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            scanCode,
3593a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            axisInfo.mode, axisInfo.axis, axisInfo.highAxis,
3603a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown            axisInfo.splitValue, axisInfo.flatOverride);
3616f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown#endif
3623a22fa057091cd6614f58ebc57a7ce4fe29462bbJeff Brown    mMap->mAxes.add(scanCode, axisInfo);
3636f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown    return NO_ERROR;
3646f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown}
3656f2fba428ca5e77a26d991ad728e346cc47609eeJeff Brown
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project};
367