1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <binder/Binder.h>
18 #include <binder/Parcel.h>
19 #include <gtest/gtest.h>
20 #include <input/InputDevice.h>
21 #include <input/KeyLayoutMap.h>
22 #include <input/Keyboard.h>
23 #include <linux/uinput.h>
24 #include "android-base/file.h"
25 
26 namespace android {
27 
28 // --- InputDeviceIdentifierTest ---
29 
TEST(InputDeviceIdentifierTest,getCanonicalName)30 TEST(InputDeviceIdentifierTest, getCanonicalName) {
31     InputDeviceIdentifier identifier;
32     identifier.name = "test device";
33     ASSERT_EQ(std::string("test_device"), identifier.getCanonicalName());
34 
35     identifier.name = "deviceName-123 version_C!";
36     ASSERT_EQ(std::string("deviceName-123_version_C_"), identifier.getCanonicalName());
37 }
38 
39 class InputDeviceKeyMapTest : public testing::Test {
40 protected:
loadKeyLayout(const char * name)41     void loadKeyLayout(const char* name) {
42         std::string path =
43                 getInputDeviceConfigurationFilePathByName(name,
44                                                           InputDeviceConfigurationFileType::
45                                                                   KEY_LAYOUT);
46         ASSERT_FALSE(path.empty());
47         base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path);
48         ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << path;
49         mKeyMap.keyLayoutMap = std::move(*ret);
50         mKeyMap.keyLayoutFile = path;
51     }
52 
loadKeyCharacterMap(const char * name)53     void loadKeyCharacterMap(const char* name) {
54         InputDeviceIdentifier identifier;
55         identifier.name = name;
56         std::string path =
57                 getInputDeviceConfigurationFilePathByName(identifier.getCanonicalName(),
58                                                           InputDeviceConfigurationFileType::
59                                                                   KEY_CHARACTER_MAP);
60         ASSERT_FALSE(path.empty()) << "KeyCharacterMap for " << name << " not found";
61         base::Result<std::shared_ptr<KeyCharacterMap>> ret =
62                 KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
63         ASSERT_TRUE(ret.ok()) << "Cannot load KeyCharacterMap at " << path;
64         mKeyMap.keyCharacterMap = *ret;
65         mKeyMap.keyCharacterMapFile = path;
66     }
67 
SetUp()68     void SetUp() override {
69 #if !defined(__ANDROID__)
70         GTEST_SKIP() << "b/253299089 Generic files are currently read directly from device.";
71 #endif
72         loadKeyLayout("Generic");
73         loadKeyCharacterMap("Generic");
74     }
75 
76     KeyMap mKeyMap;
77 };
78 
TEST_F(InputDeviceKeyMapTest,keyCharacterMapParcelingTest)79 TEST_F(InputDeviceKeyMapTest, keyCharacterMapParcelingTest) {
80     Parcel parcel;
81     mKeyMap.keyCharacterMap->writeToParcel(&parcel);
82     parcel.setDataPosition(0);
83     std::shared_ptr<KeyCharacterMap> map = KeyCharacterMap::readFromParcel(&parcel);
84     // Verify the key character map is the same as original
85     ASSERT_EQ(*map, *mKeyMap.keyCharacterMap);
86 }
87 
TEST_F(InputDeviceKeyMapTest,keyCharacterMapWithOverlayParcelingTest)88 TEST_F(InputDeviceKeyMapTest, keyCharacterMapWithOverlayParcelingTest) {
89     Parcel parcel;
90     std::string overlayPath = base::GetExecutableDirectory() + "/data/german.kcm";
91     base::Result<std::shared_ptr<KeyCharacterMap>> overlay =
92             KeyCharacterMap::load(overlayPath, KeyCharacterMap::Format::OVERLAY);
93     ASSERT_TRUE(overlay.ok()) << "Cannot load KeyCharacterMap at " << overlayPath;
94     mKeyMap.keyCharacterMap->combine(*overlay->get());
95     mKeyMap.keyCharacterMap->writeToParcel(&parcel);
96     parcel.setDataPosition(0);
97     std::shared_ptr<KeyCharacterMap> map = KeyCharacterMap::readFromParcel(&parcel);
98     ASSERT_EQ(*map, *mKeyMap.keyCharacterMap);
99 }
100 
TEST_F(InputDeviceKeyMapTest,keyCharacterMapApplyMultipleOverlaysTest)101 TEST_F(InputDeviceKeyMapTest, keyCharacterMapApplyMultipleOverlaysTest) {
102     std::string frenchOverlayPath = base::GetExecutableDirectory() + "/data/french.kcm";
103     std::string englishOverlayPath = base::GetExecutableDirectory() + "/data/english_us.kcm";
104     std::string germanOverlayPath = base::GetExecutableDirectory() + "/data/german.kcm";
105     base::Result<std::shared_ptr<KeyCharacterMap>> frenchOverlay =
106             KeyCharacterMap::load(frenchOverlayPath, KeyCharacterMap::Format::OVERLAY);
107     ASSERT_TRUE(frenchOverlay.ok()) << "Cannot load KeyCharacterMap at " << frenchOverlayPath;
108     base::Result<std::shared_ptr<KeyCharacterMap>> englishOverlay =
109             KeyCharacterMap::load(englishOverlayPath, KeyCharacterMap::Format::OVERLAY);
110     ASSERT_TRUE(englishOverlay.ok()) << "Cannot load KeyCharacterMap at " << englishOverlayPath;
111     base::Result<std::shared_ptr<KeyCharacterMap>> germanOverlay =
112             KeyCharacterMap::load(germanOverlayPath, KeyCharacterMap::Format::OVERLAY);
113     ASSERT_TRUE(germanOverlay.ok()) << "Cannot load KeyCharacterMap at " << germanOverlayPath;
114 
115     // Apply the French overlay
116     mKeyMap.keyCharacterMap->combine(*frenchOverlay->get());
117     // Copy the result for later
118     std::shared_ptr<KeyCharacterMap> frenchOverlaidKeyCharacterMap =
119             std::make_shared<KeyCharacterMap>(*mKeyMap.keyCharacterMap);
120 
121     // Apply the English overlay
122     mKeyMap.keyCharacterMap->combine(*englishOverlay->get());
123     // Verify that the result is different from the French overlay result
124     ASSERT_NE(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
125 
126     // Apply the German overlay
127     mKeyMap.keyCharacterMap->combine(*germanOverlay->get());
128     // Verify that the result is different from the French overlay result
129     ASSERT_NE(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
130 
131     // Apply the French overlay
132     mKeyMap.keyCharacterMap->combine(*frenchOverlay->get());
133     // Verify that the result is the same like after applying it initially
134     ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
135 }
136 
TEST_F(InputDeviceKeyMapTest,keyCharacterMapApplyOverlayTest)137 TEST_F(InputDeviceKeyMapTest, keyCharacterMapApplyOverlayTest) {
138     std::string frenchOverlayPath = base::GetExecutableDirectory() + "/data/french.kcm";
139     base::Result<std::shared_ptr<KeyCharacterMap>> frenchOverlay =
140             KeyCharacterMap::load(frenchOverlayPath, KeyCharacterMap::Format::OVERLAY);
141     ASSERT_TRUE(frenchOverlay.ok()) << "Cannot load KeyCharacterMap at " << frenchOverlayPath;
142 
143     // Apply the French overlay
144     mKeyMap.keyCharacterMap->combine(*frenchOverlay->get());
145 
146     // Check if mapping for key_Q is correct
147     int32_t outKeyCode;
148     status_t mapKeyResult = mKeyMap.keyCharacterMap->mapKey(KEY_Q, /*usageCode=*/0, &outKeyCode);
149     ASSERT_EQ(mapKeyResult, OK) << "No mapping for KEY_Q for " << frenchOverlayPath;
150     ASSERT_EQ(outKeyCode, AKEYCODE_A);
151 
152     mapKeyResult = mKeyMap.keyCharacterMap->mapKey(KEY_E, /*usageCode=*/0, &outKeyCode);
153     ASSERT_NE(mapKeyResult, OK) << "Mapping exists for KEY_E for " << frenchOverlayPath;
154 }
155 
TEST_F(InputDeviceKeyMapTest,keyCharacterMapBadAxisLabel)156 TEST_F(InputDeviceKeyMapTest, keyCharacterMapBadAxisLabel) {
157     std::string klPath = base::GetExecutableDirectory() + "/data/bad_axis_label.kl";
158 
159     base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
160     ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
161 }
162 
TEST_F(InputDeviceKeyMapTest,keyCharacterMapBadLedLabel)163 TEST_F(InputDeviceKeyMapTest, keyCharacterMapBadLedLabel) {
164     std::string klPath = base::GetExecutableDirectory() + "/data/bad_led_label.kl";
165 
166     base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
167     ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
168 }
169 
TEST(InputDeviceKeyLayoutTest,HidUsageCodesFallbackMapping)170 TEST(InputDeviceKeyLayoutTest, HidUsageCodesFallbackMapping) {
171     std::string klPath = base::GetExecutableDirectory() + "/data/hid_fallback_mapping.kl";
172     base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
173     ASSERT_TRUE(ret.ok()) << "Unable to load KeyLayout at " << klPath;
174     const std::shared_ptr<KeyLayoutMap>& keyLayoutMap = *ret;
175 
176     static constexpr std::array<int32_t, 5> hidUsageCodesWithoutFallback = {0x0c0067, 0x0c0070,
177                                                                             0x0c006F, 0x0c0079,
178                                                                             0x0c007A};
179     for (int32_t hidUsageCode : hidUsageCodesWithoutFallback) {
180         int32_t outKeyCode;
181         uint32_t outFlags;
182         keyLayoutMap->mapKey(0, hidUsageCode, &outKeyCode, &outFlags);
183         ASSERT_FALSE(outFlags & POLICY_FLAG_FALLBACK_USAGE_MAPPING)
184                 << "HID usage code should not be marked as fallback";
185         std::vector<int32_t> usageCodes = keyLayoutMap->findUsageCodesForKey(outKeyCode);
186         ASSERT_NE(std::find(usageCodes.begin(), usageCodes.end(), hidUsageCode), usageCodes.end())
187                 << "Fallback usage code should be mapped to key";
188     }
189 
190     static constexpr std::array<int32_t, 6> hidUsageCodesWithFallback = {0x0c007C, 0x0c0173,
191                                                                          0x0c019C, 0x0c01A2,
192                                                                          0x0d0044, 0x0d005a};
193     for (int32_t hidUsageCode : hidUsageCodesWithFallback) {
194         int32_t outKeyCode;
195         uint32_t outFlags;
196         keyLayoutMap->mapKey(0, hidUsageCode, &outKeyCode, &outFlags);
197         ASSERT_TRUE(outFlags & POLICY_FLAG_FALLBACK_USAGE_MAPPING)
198                 << "HID usage code should be marked as fallback";
199         std::vector<int32_t> usageCodes = keyLayoutMap->findUsageCodesForKey(outKeyCode);
200         ASSERT_EQ(std::find(usageCodes.begin(), usageCodes.end(), hidUsageCode), usageCodes.end())
201                 << "Fallback usage code should not be mapped to key";
202     }
203 }
204 
TEST(InputDeviceKeyLayoutTest,DoesNotLoadWhenRequiredKernelConfigIsMissing)205 TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) {
206 #if !defined(__ANDROID__)
207     GTEST_SKIP() << "Can't check kernel configs on host";
208 #endif
209     std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_fake_config.kl";
210     base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
211     ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
212     // We assert error message here because it's used by 'validatekeymaps' tool
213     ASSERT_EQ("Missing kernel config", ret.error().message());
214 }
215 
TEST(InputDeviceKeyLayoutTest,LoadsWhenRequiredKernelConfigIsPresent)216 TEST(InputDeviceKeyLayoutTest, LoadsWhenRequiredKernelConfigIsPresent) {
217 #if !defined(__ANDROID__)
218     GTEST_SKIP() << "Can't check kernel configs on host";
219 #endif
220     std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_real_config.kl";
221     base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
222     ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << klPath;
223     const std::shared_ptr<KeyLayoutMap>& map = *ret;
224     ASSERT_NE(nullptr, map) << "Map should be valid because CONFIG_UHID should always be present";
225 }
226 
227 } // namespace android
228