Fix KeyCharacterMap overlays by reloading base overlay before applying another overlay.

Add all fields to parcel (mLoadFileName, mLayoutOverlayApplied, mKeysByScanCode, mKeysByUsageCode).
Add all fields to equality operator.
Add tests to libinput_tests.

Bug: 210090260
Test: Manually verified that layout changes correctly.
Test: atest libinput_tests
Change-Id: I1777b87e5c2ce8a0dbd42ad48748ea9e669c9139
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 3baeb55..2039fa6 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -86,10 +86,13 @@
 
 // --- KeyCharacterMap ---
 
-KeyCharacterMap::KeyCharacterMap() : mType(KeyboardType::UNKNOWN) {}
+KeyCharacterMap::KeyCharacterMap(const std::string& filename)
+      : mType(KeyboardType::UNKNOWN), mLoadFileName(filename) {}
 
 KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other)
       : mType(other.mType),
+        mLoadFileName(other.mLoadFileName),
+        mLayoutOverlayApplied(other.mLayoutOverlayApplied),
         mKeysByScanCode(other.mKeysByScanCode),
         mKeysByUsageCode(other.mKeysByUsageCode) {
     for (size_t i = 0; i < other.mKeys.size(); i++) {
@@ -98,16 +101,19 @@
 }
 
 KeyCharacterMap::~KeyCharacterMap() {
-    for (size_t i = 0; i < mKeys.size(); i++) {
-        Key* key = mKeys.editValueAt(i);
-        delete key;
-    }
+    clear();
 }
 
 bool KeyCharacterMap::operator==(const KeyCharacterMap& other) const {
     if (mType != other.mType) {
         return false;
     }
+    if (mLoadFileName != other.mLoadFileName) {
+        return false;
+    }
+    if (mLayoutOverlayApplied != other.mLayoutOverlayApplied) {
+        return false;
+    }
     if (mKeys.size() != other.mKeys.size() ||
         mKeysByScanCode.size() != other.mKeysByScanCode.size() ||
         mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) {
@@ -146,6 +152,10 @@
     return true;
 }
 
+bool KeyCharacterMap::operator!=(const KeyCharacterMap& other) const {
+    return !(*this == other);
+}
+
 base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename,
                                                                      Format format) {
     Tokenizer* tokenizer;
@@ -153,12 +163,18 @@
     if (status) {
         return Errorf("Error {} opening key character map file {}.", status, filename.c_str());
     }
-    std::unique_ptr<Tokenizer> t(tokenizer);
-    auto ret = load(t.get(), format);
-    if (ret.ok()) {
-        (*ret)->mLoadFileName = filename;
+    std::shared_ptr<KeyCharacterMap> map =
+            std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap(filename));
+    if (!map.get()) {
+        ALOGE("Error allocating key character map.");
+        return Errorf("Error allocating key character map.");
     }
-    return ret;
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    status = map->load(t.get(), format);
+    if (status == OK) {
+        return map;
+    }
+    return Errorf("Load KeyCharacterMap failed {}.", status);
 }
 
 base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::loadContents(
@@ -169,40 +185,67 @@
         ALOGE("Error %d opening key character map.", status);
         return Errorf("Error {} opening key character map.", status);
     }
-    std::unique_ptr<Tokenizer> t(tokenizer);
-    auto ret = load(t.get(), format);
-    if (ret.ok()) {
-        (*ret)->mLoadFileName = filename;
-    }
-    return ret;
-}
-
-base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(Tokenizer* tokenizer,
-                                                                     Format format) {
-    status_t status = OK;
-    std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
+    std::shared_ptr<KeyCharacterMap> map =
+            std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap(filename));
     if (!map.get()) {
         ALOGE("Error allocating key character map.");
         return Errorf("Error allocating key character map.");
     }
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    status = map->load(t.get(), format);
+    if (status == OK) {
+        return map;
+    }
+    return Errorf("Load KeyCharacterMap failed {}.", status);
+}
+
+status_t KeyCharacterMap::load(Tokenizer* tokenizer, Format format) {
+    status_t status = OK;
 #if DEBUG_PARSER_PERFORMANCE
     nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
 #endif
-    Parser parser(map.get(), tokenizer, format);
+    Parser parser(this, tokenizer, format);
     status = parser.parse();
 #if DEBUG_PARSER_PERFORMANCE
     nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
     ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
           tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0);
 #endif
-    if (status == OK) {
-        return map;
+    if (status != OK) {
+        ALOGE("Loading KeyCharacterMap failed with status %s", statusToString(status).c_str());
     }
+    return status;
+}
 
-    return Errorf("Load KeyCharacterMap failed {}.", status);
+void KeyCharacterMap::clear() {
+    mKeysByScanCode.clear();
+    mKeysByUsageCode.clear();
+    for (size_t i = 0; i < mKeys.size(); i++) {
+        Key* key = mKeys.editValueAt(i);
+        delete key;
+    }
+    mKeys.clear();
+    mLayoutOverlayApplied = false;
+    mType = KeyboardType::UNKNOWN;
+}
+
+status_t KeyCharacterMap::reloadBaseFromFile() {
+    clear();
+    Tokenizer* tokenizer;
+    status_t status = Tokenizer::open(String8(mLoadFileName.c_str()), &tokenizer);
+    if (status) {
+        ALOGE("Error %s opening key character map file %s.", statusToString(status).c_str(),
+              mLoadFileName.c_str());
+        return status;
+    }
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    return load(t.get(), KeyCharacterMap::Format::BASE);
 }
 
 void KeyCharacterMap::combine(const KeyCharacterMap& overlay) {
+    if (mLayoutOverlayApplied) {
+        reloadBaseFromFile();
+    }
     for (size_t i = 0; i < overlay.mKeys.size(); i++) {
         int32_t keyCode = overlay.mKeys.keyAt(i);
         Key* key = overlay.mKeys.valueAt(i);
@@ -224,7 +267,7 @@
         mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i),
                                          overlay.mKeysByUsageCode.valueAt(i));
     }
-    mLoadFileName = overlay.mLoadFileName;
+    mLayoutOverlayApplied = true;
 }
 
 KeyCharacterMap::KeyboardType KeyCharacterMap::getKeyboardType() const {
@@ -636,8 +679,11 @@
         ALOGE("%s: Null parcel", __func__);
         return nullptr;
     }
-    std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
+    std::string loadFileName = parcel->readCString();
+    std::shared_ptr<KeyCharacterMap> map =
+            std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap(loadFileName));
     map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32());
+    map->mLayoutOverlayApplied = parcel->readBool();
     size_t numKeys = parcel->readInt32();
     if (parcel->errorCheck()) {
         return nullptr;
@@ -687,6 +733,30 @@
             return nullptr;
         }
     }
+    size_t numKeysByScanCode = parcel->readInt32();
+    if (parcel->errorCheck()) {
+        return nullptr;
+    }
+    for (size_t i = 0; i < numKeysByScanCode; i++) {
+        int32_t key = parcel->readInt32();
+        int32_t value = parcel->readInt32();
+        map->mKeysByScanCode.add(key, value);
+        if (parcel->errorCheck()) {
+            return nullptr;
+        }
+    }
+    size_t numKeysByUsageCode = parcel->readInt32();
+    if (parcel->errorCheck()) {
+        return nullptr;
+    }
+    for (size_t i = 0; i < numKeysByUsageCode; i++) {
+        int32_t key = parcel->readInt32();
+        int32_t value = parcel->readInt32();
+        map->mKeysByUsageCode.add(key, value);
+        if (parcel->errorCheck()) {
+            return nullptr;
+        }
+    }
     return map;
 }
 
@@ -695,7 +765,9 @@
         ALOGE("%s: Null parcel", __func__);
         return;
     }
+    parcel->writeCString(mLoadFileName.c_str());
     parcel->writeInt32(static_cast<int32_t>(mType));
+    parcel->writeBool(mLayoutOverlayApplied);
 
     size_t numKeys = mKeys.size();
     parcel->writeInt32(numKeys);
@@ -715,6 +787,18 @@
         }
         parcel->writeInt32(0);
     }
+    size_t numKeysByScanCode = mKeysByScanCode.size();
+    parcel->writeInt32(numKeysByScanCode);
+    for (size_t i = 0; i < numKeysByScanCode; i++) {
+        parcel->writeInt32(mKeysByScanCode.keyAt(i));
+        parcel->writeInt32(mKeysByScanCode.valueAt(i));
+    }
+    size_t numKeysByUsageCode = mKeysByUsageCode.size();
+    parcel->writeInt32(numKeysByUsageCode);
+    for (size_t i = 0; i < numKeysByUsageCode; i++) {
+        parcel->writeInt32(mKeysByUsageCode.keyAt(i));
+        parcel->writeInt32(mKeysByUsageCode.valueAt(i));
+    }
 }
 #endif // __linux__
 
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 18ab1cb..6ffe851 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -37,6 +37,7 @@
         "libui",
         "libutils",
     ],
+    data: ["data/*.kcm"],
     test_suites: ["device-tests"],
 }
 
@@ -59,5 +60,5 @@
         "libbinder",
         "libui",
         "libbase",
-    ]
+    ],
 }
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index f8f2f4e..61e88df 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -20,6 +20,7 @@
 #include <input/InputDevice.h>
 #include <input/KeyLayoutMap.h>
 #include <input/Keyboard.h>
+#include "android-base/file.h"
 
 namespace android {
 
@@ -82,4 +83,53 @@
     ASSERT_EQ(*map, *mKeyMap.keyCharacterMap);
 }
 
+TEST_F(InputDeviceKeyMapTest, keyCharacterMapWithOverlayParcelingTest) {
+    Parcel parcel;
+    std::string overlayPath = base::GetExecutableDirectory() + "/data/german.kcm";
+    base::Result<std::shared_ptr<KeyCharacterMap>> overlay =
+            KeyCharacterMap::load(overlayPath, KeyCharacterMap::Format::OVERLAY);
+    ASSERT_TRUE(overlay.ok()) << "Cannot load KeyCharacterMap at " << overlayPath;
+    mKeyMap.keyCharacterMap->combine(*overlay->get());
+    mKeyMap.keyCharacterMap->writeToParcel(&parcel);
+    parcel.setDataPosition(0);
+    std::shared_ptr<KeyCharacterMap> map = KeyCharacterMap::readFromParcel(&parcel);
+    ASSERT_EQ(*map, *mKeyMap.keyCharacterMap);
+}
+
+TEST_F(InputDeviceKeyMapTest, keyCharacteMapApplyMultipleOverlaysTest) {
+    std::string frenchOverlayPath = base::GetExecutableDirectory() + "/data/french.kcm";
+    std::string englishOverlayPath = base::GetExecutableDirectory() + "/data/english_us.kcm";
+    std::string germanOverlayPath = base::GetExecutableDirectory() + "/data/german.kcm";
+    base::Result<std::shared_ptr<KeyCharacterMap>> frenchOverlay =
+            KeyCharacterMap::load(frenchOverlayPath, KeyCharacterMap::Format::OVERLAY);
+    ASSERT_TRUE(frenchOverlay.ok()) << "Cannot load KeyCharacterMap at " << frenchOverlayPath;
+    base::Result<std::shared_ptr<KeyCharacterMap>> englishOverlay =
+            KeyCharacterMap::load(englishOverlayPath, KeyCharacterMap::Format::OVERLAY);
+    ASSERT_TRUE(englishOverlay.ok()) << "Cannot load KeyCharacterMap at " << englishOverlayPath;
+    base::Result<std::shared_ptr<KeyCharacterMap>> germanOverlay =
+            KeyCharacterMap::load(germanOverlayPath, KeyCharacterMap::Format::OVERLAY);
+    ASSERT_TRUE(germanOverlay.ok()) << "Cannot load KeyCharacterMap at " << germanOverlayPath;
+
+    // Apply the French overlay
+    mKeyMap.keyCharacterMap->combine(*frenchOverlay->get());
+    // Copy the result for later
+    std::shared_ptr<KeyCharacterMap> frenchOverlaidKeyCharacterMap =
+            std::make_shared<KeyCharacterMap>(*mKeyMap.keyCharacterMap);
+
+    // Apply the English overlay
+    mKeyMap.keyCharacterMap->combine(*englishOverlay->get());
+    // Verify that the result is different from the French overlay result
+    ASSERT_NE(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
+
+    // Apply the German overlay
+    mKeyMap.keyCharacterMap->combine(*germanOverlay->get());
+    // Verify that the result is different from the French overlay result
+    ASSERT_NE(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
+
+    // Apply the French overlay
+    mKeyMap.keyCharacterMap->combine(*frenchOverlay->get());
+    // Verify that the result is the same like after applying it initially
+    ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
+}
+
 } // namespace android
diff --git a/libs/input/tests/data/english_us.kcm b/libs/input/tests/data/english_us.kcm
new file mode 100644
index 0000000..d0ef027
--- /dev/null
+++ b/libs/input/tests/data/english_us.kcm
@@ -0,0 +1,311 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# English (US) keyboard layout.
+# Unlike the default (generic) keyboard layout, English (US) does not contain any
+# special ALT characters.
+#
+
+type OVERLAY
+
+### ROW 1
+
+key GRAVE {
+    label:                              '`'
+    base:                               '`'
+    shift:                              '~'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '1'
+    shift:                              '!'
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '2'
+    shift:                              '@'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '3'
+    shift:                              '#'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '4'
+    shift:                              '$'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '5'
+    shift:                              '%'
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '6'
+    shift:                              '^'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '7'
+    shift:                              '&'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '8'
+    shift:                              '*'
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '9'
+    shift:                              '('
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '0'
+    shift:                              ')'
+}
+
+key MINUS {
+    label:                              '-'
+    base:                               '-'
+    shift:                              '_'
+}
+
+key EQUALS {
+    label:                              '='
+    base:                               '='
+    shift:                              '+'
+}
+
+### ROW 2
+
+key Q {
+    label:                              'Q'
+    base:                               'q'
+    shift, capslock:                    'Q'
+}
+
+key W {
+    label:                              'W'
+    base:                               'w'
+    shift, capslock:                    'W'
+}
+
+key E {
+    label:                              'E'
+    base:                               'e'
+    shift, capslock:                    'E'
+}
+
+key R {
+    label:                              'R'
+    base:                               'r'
+    shift, capslock:                    'R'
+}
+
+key T {
+    label:                              'T'
+    base:                               't'
+    shift, capslock:                    'T'
+}
+
+key Y {
+    label:                              'Y'
+    base:                               'y'
+    shift, capslock:                    'Y'
+}
+
+key U {
+    label:                              'U'
+    base:                               'u'
+    shift, capslock:                    'U'
+}
+
+key I {
+    label:                              'I'
+    base:                               'i'
+    shift, capslock:                    'I'
+}
+
+key O {
+    label:                              'O'
+    base:                               'o'
+    shift, capslock:                    'O'
+}
+
+key P {
+    label:                              'P'
+    base:                               'p'
+    shift, capslock:                    'P'
+}
+
+key LEFT_BRACKET {
+    label:                              '['
+    base:                               '['
+    shift:                              '{'
+}
+
+key RIGHT_BRACKET {
+    label:                              ']'
+    base:                               ']'
+    shift:                              '}'
+}
+
+key BACKSLASH {
+    label:                              '\\'
+    base:                               '\\'
+    shift:                              '|'
+}
+
+### ROW 3
+
+key A {
+    label:                              'A'
+    base:                               'a'
+    shift, capslock:                    'A'
+}
+
+key S {
+    label:                              'S'
+    base:                               's'
+    shift, capslock:                    'S'
+}
+
+key D {
+    label:                              'D'
+    base:                               'd'
+    shift, capslock:                    'D'
+}
+
+key F {
+    label:                              'F'
+    base:                               'f'
+    shift, capslock:                    'F'
+}
+
+key G {
+    label:                              'G'
+    base:                               'g'
+    shift, capslock:                    'G'
+}
+
+key H {
+    label:                              'H'
+    base:                               'h'
+    shift, capslock:                    'H'
+}
+
+key J {
+    label:                              'J'
+    base:                               'j'
+    shift, capslock:                    'J'
+}
+
+key K {
+    label:                              'K'
+    base:                               'k'
+    shift, capslock:                    'K'
+}
+
+key L {
+    label:                              'L'
+    base:                               'l'
+    shift, capslock:                    'L'
+}
+
+key SEMICOLON {
+    label:                              ';'
+    base:                               ';'
+    shift:                              ':'
+}
+
+key APOSTROPHE {
+    label:                              '\''
+    base:                               '\''
+    shift:                              '"'
+}
+
+### ROW 4
+
+key Z {
+    label:                              'Z'
+    base:                               'z'
+    shift, capslock:                    'Z'
+}
+
+key X {
+    label:                              'X'
+    base:                               'x'
+    shift, capslock:                    'X'
+}
+
+key C {
+    label:                              'C'
+    base:                               'c'
+    shift, capslock:                    'C'
+}
+
+key V {
+    label:                              'V'
+    base:                               'v'
+    shift, capslock:                    'V'
+}
+
+key B {
+    label:                              'B'
+    base:                               'b'
+    shift, capslock:                    'B'
+}
+
+key N {
+    label:                              'N'
+    base:                               'n'
+    shift, capslock:                    'N'
+}
+
+key M {
+    label:                              'M'
+    base:                               'm'
+    shift, capslock:                    'M'
+}
+
+key COMMA {
+    label:                              ','
+    base:                               ','
+    shift:                              '<'
+}
+
+key PERIOD {
+    label:                              '.'
+    base:                               '.'
+    shift:                              '>'
+}
+
+key SLASH {
+    label:                              '/'
+    base:                               '/'
+    shift:                              '?'
+}
\ No newline at end of file
diff --git a/libs/input/tests/data/french.kcm b/libs/input/tests/data/french.kcm
new file mode 100644
index 0000000..db69ea0
--- /dev/null
+++ b/libs/input/tests/data/french.kcm
@@ -0,0 +1,336 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# French keyboard layout, AZERTY style.
+#
+
+type OVERLAY
+
+map key 16 A
+map key 17 Z
+map key 30 Q
+map key 39 M
+map key 44 W
+map key 50 COMMA
+map key 51 SEMICOLON
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+    label:                              '\u00b2'
+    base:                               '\u00b2'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '&'
+    shift:                              '1'
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '\u00e9'
+    shift:                              '2'
+    ralt:                               '~'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '"'
+    shift:                              '3'
+    ralt:                               '#'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '\''
+    shift:                              '4'
+    ralt:                               '{'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '('
+    shift:                              '5'
+    ralt:                               '['
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '-'
+    shift:                              '6'
+    ralt:                               '|'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '\u00e8'
+    shift:                              '7'
+    ralt:                               '`'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '_'
+    shift:                              '8'
+    ralt:                               '\\'
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '\u00e7'
+    shift:                              '9'
+    ralt:                               '^'
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '\u00e0'
+    shift:                              '0'
+    ralt:                               '@'
+}
+
+key MINUS {
+    label:                              ')'
+    base:                               ')'
+    shift:                              '\u00b0'
+    ralt:                               ']'
+}
+
+key EQUALS {
+    label:                              '='
+    base:                               '='
+    shift:                              '+'
+    ralt:                               '}'
+}
+
+### ROW 2
+
+key A {
+    label:                              'A'
+    base:                               'a'
+    shift, capslock:                    'A'
+}
+
+key Z {
+    label:                              'Z'
+    base:                               'z'
+    shift, capslock:                    'Z'
+}
+
+key E {
+    label:                              'E'
+    base:                               'e'
+    shift, capslock:                    'E'
+    ralt:                               '\u20ac'
+}
+
+key R {
+    label:                              'R'
+    base:                               'r'
+    shift, capslock:                    'R'
+}
+
+key T {
+    label:                              'T'
+    base:                               't'
+    shift, capslock:                    'T'
+}
+
+key Y {
+    label:                              'Y'
+    base:                               'y'
+    shift, capslock:                    'Y'
+}
+
+key U {
+    label:                              'U'
+    base:                               'u'
+    shift, capslock:                    'U'
+}
+
+key I {
+    label:                              'I'
+    base:                               'i'
+    shift, capslock:                    'I'
+}
+
+key O {
+    label:                              'O'
+    base:                               'o'
+    shift, capslock:                    'O'
+}
+
+key P {
+    label:                              'P'
+    base:                               'p'
+    shift, capslock:                    'P'
+}
+
+key LEFT_BRACKET {
+    label:                              '\u02c6'
+    base:                               '\u0302'
+    shift:                              '\u0308'
+}
+
+key RIGHT_BRACKET {
+    label:                              '$'
+    base:                               '$'
+    shift:                              '\u00a3'
+    ralt:                               '\u00a4'
+}
+
+### ROW 3
+
+key Q {
+    label:                              'Q'
+    base:                               'q'
+    shift, capslock:                    'Q'
+}
+
+key S {
+    label:                              'S'
+    base:                               's'
+    shift, capslock:                    'S'
+}
+
+key D {
+    label:                              'D'
+    base:                               'd'
+    shift, capslock:                    'D'
+}
+
+key F {
+    label:                              'F'
+    base:                               'f'
+    shift, capslock:                    'F'
+}
+
+key G {
+    label:                              'G'
+    base:                               'g'
+    shift, capslock:                    'G'
+}
+
+key H {
+    label:                              'H'
+    base:                               'h'
+    shift, capslock:                    'H'
+}
+
+key J {
+    label:                              'J'
+    base:                               'j'
+    shift, capslock:                    'J'
+}
+
+key K {
+    label:                              'K'
+    base:                               'k'
+    shift, capslock:                    'K'
+}
+
+key L {
+    label:                              'L'
+    base:                               'l'
+    shift, capslock:                    'L'
+}
+
+key M {
+    label:                              'M'
+    base:                               'm'
+    shift, capslock:                    'M'
+}
+
+key APOSTROPHE {
+    label:                              '\u00f9'
+    base:                               '\u00f9'
+    shift:                              '%'
+}
+
+key BACKSLASH {
+    label:                              '*'
+    base:                               '*'
+    shift:                              '\u00b5'
+}
+
+### ROW 4
+
+key PLUS {
+    label:                              '<'
+    base:                               '<'
+    shift:                              '>'
+}
+
+key W {
+    label:                              'W'
+    base:                               'w'
+    shift, capslock:                    'W'
+}
+
+key X {
+    label:                              'X'
+    base:                               'x'
+    shift, capslock:                    'X'
+}
+
+key C {
+    label:                              'C'
+    base:                               'c'
+    shift, capslock:                    'C'
+}
+
+key V {
+    label:                              'V'
+    base:                               'v'
+    shift, capslock:                    'V'
+}
+
+key B {
+    label:                              'B'
+    base:                               'b'
+    shift, capslock:                    'B'
+}
+
+key N {
+    label:                              'N'
+    base:                               'n'
+    shift, capslock:                    'N'
+}
+
+key COMMA {
+    label:                              ','
+    base:                               ','
+    shift:                              '?'
+}
+
+key SEMICOLON {
+    label:                              ';'
+    base:                               ';'
+    shift:                              '.'
+}
+
+key PERIOD {
+    label:                              ':'
+    base:                               ':'
+    shift:                              '/'
+}
+
+key SLASH {
+    label:                              '!'
+    base:                               '!'
+    shift:                              '\u00a7'
+}
\ No newline at end of file
diff --git a/libs/input/tests/data/german.kcm b/libs/input/tests/data/german.kcm
new file mode 100644
index 0000000..2fbc5e5
--- /dev/null
+++ b/libs/input/tests/data/german.kcm
@@ -0,0 +1,336 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# German keyboard layout, QWERTZ style.
+#
+
+type OVERLAY
+
+map key 12 SLASH            # � ? \
+map key 21 Z
+map key 44 Y
+map key 53 MINUS            # - _
+map key 86 PLUS             # < > |
+
+map key usage 32 A              # for testing purposes only
+map key usage 67 B              # for testing purposes only
+
+### ROW 1
+
+key GRAVE {
+    label:                              '^'
+    base:                               '^'
+    shift:                              '\u00b0'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '1'
+    shift:                              '!'
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '2'
+    shift:                              '"'
+    ralt:                               '\u00b2'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '3'
+    shift:                              '\u00a7'
+    ralt:                               '\u00b3'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '4'
+    shift:                              '$'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '5'
+    shift:                              '%'
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '6'
+    shift:                              '&'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '7'
+    shift:                              '/'
+    ralt:                               '{'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '8'
+    shift:                              '('
+    ralt:                               '['
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '9'
+    shift:                              ')'
+    ralt:                               ']'
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '0'
+    shift:                              '='
+    ralt:                               '}'
+}
+
+key SLASH {
+    label:                              '\u00df'
+    base:                               '\u00df'
+    shift:                              '?'
+    ralt:                               '\\'
+}
+
+key EQUALS {
+    label:                              '\u00b4'
+    base:                               '\u0301'
+    shift:                              '\u0300'
+}
+
+### ROW 2
+
+key Q {
+    label:                              'Q'
+    base:                               'q'
+    shift, capslock:                    'Q'
+    ralt:                               '@'
+}
+
+key W {
+    label:                              'W'
+    base:                               'w'
+    shift, capslock:                    'W'
+}
+
+key E {
+    label:                              'E'
+    base:                               'e'
+    shift, capslock:                    'E'
+    ralt:                               '\u20ac'
+}
+
+key R {
+    label:                              'R'
+    base:                               'r'
+    shift, capslock:                    'R'
+}
+
+key T {
+    label:                              'T'
+    base:                               't'
+    shift, capslock:                    'T'
+}
+
+key Z {
+    label:                              'Z'
+    base:                               'z'
+    shift, capslock:                    'Z'
+}
+
+key U {
+    label:                              'U'
+    base:                               'u'
+    shift, capslock:                    'U'
+}
+
+key I {
+    label:                              'I'
+    base:                               'i'
+    shift, capslock:                    'I'
+}
+
+key O {
+    label:                              'O'
+    base:                               'o'
+    shift, capslock:                    'O'
+}
+
+key P {
+    label:                              'P'
+    base:                               'p'
+    shift, capslock:                    'P'
+}
+
+key LEFT_BRACKET {
+    label:                              '\u00dc'
+    base:                               '\u00fc'
+    shift, capslock:                    '\u00dc'
+}
+
+key RIGHT_BRACKET {
+    label:                              '+'
+    base:                               '+'
+    shift:                              '*'
+    ralt:                               '~'
+}
+
+### ROW 3
+
+key A {
+    label:                              'A'
+    base:                               'a'
+    shift, capslock:                    'A'
+}
+
+key S {
+    label:                              'S'
+    base:                               's'
+    shift, capslock:                    'S'
+}
+
+key D {
+    label:                              'D'
+    base:                               'd'
+    shift, capslock:                    'D'
+}
+
+key F {
+    label:                              'F'
+    base:                               'f'
+    shift, capslock:                    'F'
+}
+
+key G {
+    label:                              'G'
+    base:                               'g'
+    shift, capslock:                    'G'
+}
+
+key H {
+    label:                              'H'
+    base:                               'h'
+    shift, capslock:                    'H'
+}
+
+key J {
+    label:                              'J'
+    base:                               'j'
+    shift, capslock:                    'J'
+}
+
+key K {
+    label:                              'K'
+    base:                               'k'
+    shift, capslock:                    'K'
+}
+
+key L {
+    label:                              'L'
+    base:                               'l'
+    shift, capslock:                    'L'
+}
+
+key SEMICOLON {
+    label:                              '\u00d6'
+    base:                               '\u00f6'
+    shift, capslock:                    '\u00d6'
+}
+
+key APOSTROPHE {
+    label:                              '\u00c4'
+    base:                               '\u00e4'
+    shift, capslock:                    '\u00c4'
+}
+
+key BACKSLASH {
+    label:                              '#'
+    base:                               '#'
+    shift:                              '\''
+}
+
+### ROW 4
+
+key PLUS {
+    label:                              '<'
+    base:                               '<'
+    shift:                              '>'
+    ralt:                               '|'
+}
+
+key Y {
+    label:                              'Y'
+    base:                               'y'
+    shift, capslock:                    'Y'
+}
+
+key X {
+    label:                              'X'
+    base:                               'x'
+    shift, capslock:                    'X'
+}
+
+key C {
+    label:                              'C'
+    base:                               'c'
+    shift, capslock:                    'C'
+}
+
+key V {
+    label:                              'V'
+    base:                               'v'
+    shift, capslock:                    'V'
+}
+
+key B {
+    label:                              'B'
+    base:                               'b'
+    shift, capslock:                    'B'
+}
+
+key N {
+    label:                              'N'
+    base:                               'n'
+    shift, capslock:                    'N'
+}
+
+key M {
+    label:                              'M'
+    base:                               'm'
+    shift, capslock:                    'M'
+    ralt:                               '\u00b5'
+}
+
+key COMMA {
+    label:                              ','
+    base:                               ','
+    shift:                              ';'
+}
+
+key PERIOD {
+    label:                              '.'
+    base:                               '.'
+    shift:                              ':'
+}
+
+key MINUS {
+    label:                              '-'
+    base:                               '-'
+    shift:                              '_'
+}