• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 <input/KeyCharacterMap.h>
18 #include <input/KeyLayoutMap.h>
19 #include <input/PropertyMap.h>
20 #include <input/VirtualKeyMap.h>
21 
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 using namespace android;
28 
29 static const char* PROG_NAME = "validatekeymaps";
30 static bool gQuiet = false;
31 
32 /**
33  * Return true if 'str' contains 'substr', ignoring case.
34  */
containsSubstringCaseInsensitive(std::string_view str,std::string_view substr)35 static bool containsSubstringCaseInsensitive(std::string_view str, std::string_view substr) {
36     auto it = std::search(str.begin(), str.end(), substr.begin(), substr.end(),
37                           [](char left, char right) {
38                               return std::tolower(left) == std::tolower(right);
39                           });
40     return it != str.end();
41 }
42 
43 enum class FileType {
44     UNKNOWN,
45     KEY_LAYOUT,
46     KEY_CHARACTER_MAP,
47     VIRTUAL_KEY_DEFINITION,
48     INPUT_DEVICE_CONFIGURATION,
49 };
50 
log(const char * fmt,...)51 static void log(const char* fmt, ...) {
52     if (gQuiet) {
53         return;
54     }
55     va_list args;
56     va_start(args, fmt);
57     vfprintf(stdout, fmt, args);
58     va_end(args);
59 }
60 
error(const char * fmt,...)61 static void error(const char* fmt,  ...) {
62     va_list args;
63     va_start(args, fmt);
64     vfprintf(stderr, fmt, args);
65     va_end(args);
66 }
67 
usage()68 static void usage() {
69     error("Keymap Validation Tool\n\n");
70     error("Usage:\n");
71     error(" %s [-q] [*.kl] [*.kcm] [*.idc] [virtualkeys.*] [...]\n"
72           "   Validates the specified key layouts, key character maps, \n"
73           "   input device configurations, or virtual key definitions.\n\n"
74           "   -q Quiet; do not write anything to standard out.\n",
75           PROG_NAME);
76 }
77 
getFileType(const char * filename)78 static FileType getFileType(const char* filename) {
79     const char *extension = strrchr(filename, '.');
80     if (extension) {
81         if (strcmp(extension, ".kl") == 0) {
82             return FileType::KEY_LAYOUT;
83         }
84         if (strcmp(extension, ".kcm") == 0) {
85             return FileType::KEY_CHARACTER_MAP;
86         }
87         if (strcmp(extension, ".idc") == 0) {
88             return FileType::INPUT_DEVICE_CONFIGURATION;
89         }
90     }
91 
92     if (strstr(filename, "virtualkeys.")) {
93         return FileType::VIRTUAL_KEY_DEFINITION;
94     }
95 
96     return FileType::UNKNOWN;
97 }
98 
99 /**
100  * Return true if the filename is allowed, false otherwise.
101  */
validateKeyLayoutFileName(const std::string & filename)102 static bool validateKeyLayoutFileName(const std::string& filename) {
103     static const std::string kMicrosoftReason =
104             "Microsoft's controllers are designed to work with Generic.kl. Please check with "
105             "Microsoft prior to adding these layouts. See b/194334400";
106     static const std::vector<std::pair<std::string, std::string>> kBannedDevices{
107             std::make_pair("Vendor_0a5c_Product_8502",
108                            "This vendorId/productId combination conflicts with 'SnakeByte "
109                            "iDroid:con', 'BT23BK keyboard', and other keyboards. Instead, consider "
110                            "matching these specific devices by name. See b/36976285, b/191720859"),
111             std::make_pair("Vendor_045e_Product_0b05", kMicrosoftReason),
112             std::make_pair("Vendor_045e_Product_0b20", kMicrosoftReason),
113             std::make_pair("Vendor_045e_Product_0b21", kMicrosoftReason),
114             std::make_pair("Vendor_045e_Product_0b22", kMicrosoftReason),
115     };
116 
117     for (const auto& [filenameSubstr, reason] : kBannedDevices) {
118         if (containsSubstringCaseInsensitive(filename, filenameSubstr)) {
119             error("You are trying to add a key layout %s, which matches %s. ", filename.c_str(),
120                   filenameSubstr.c_str());
121             error("This would cause some devices to function incorrectly. ");
122             error("%s. ", reason.c_str());
123             return false;
124         }
125     }
126     return true;
127 }
128 
validateFile(const char * filename)129 static bool validateFile(const char* filename) {
130     log("Validating file '%s'...\n", filename);
131 
132     FileType fileType = getFileType(filename);
133     switch (fileType) {
134         case FileType::UNKNOWN:
135             error("Supported file types: *.kl, *.kcm, virtualkeys.*\n\n");
136             return false;
137 
138         case FileType::KEY_LAYOUT: {
139             if (!validateKeyLayoutFileName(filename)) {
140                 return false;
141             }
142             base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(filename);
143             if (!ret.ok()) {
144                 if (ret.error().message() == "Missing kernel config") {
145                     // It means the layout is valid, but won't be loaded on this device because
146                     // this layout requires a certain kernel config.
147                     return true;
148                 }
149                 error("Error %s parsing key layout file.\n\n", ret.error().message().c_str());
150                 return false;
151             }
152             break;
153         }
154 
155         case FileType::KEY_CHARACTER_MAP: {
156             base::Result<std::shared_ptr<KeyCharacterMap>> ret =
157                     KeyCharacterMap::load(filename, KeyCharacterMap::Format::ANY);
158             if (!ret.ok()) {
159                 error("Error %s parsing key character map file.\n\n",
160                       ret.error().message().c_str());
161                 return false;
162             }
163             break;
164         }
165 
166         case FileType::INPUT_DEVICE_CONFIGURATION: {
167             android::base::Result<std::unique_ptr<PropertyMap>> propertyMap =
168                     PropertyMap::load(String8(filename));
169             if (!propertyMap.ok()) {
170                 error("Error parsing input device configuration file: %s.\n\n",
171                       propertyMap.error().message().c_str());
172                 return false;
173             }
174             break;
175         }
176 
177         case FileType::VIRTUAL_KEY_DEFINITION: {
178             std::unique_ptr<VirtualKeyMap> map = VirtualKeyMap::load(filename);
179             if (!map) {
180                 error("Error while parsing virtual key definition file.\n\n");
181                 return false;
182             }
183             break;
184         }
185     }
186 
187     return true;
188 }
189 
main(int argc,const char ** argv)190 int main(int argc, const char** argv) {
191     if (argc < 2) {
192         usage();
193         return 1;
194     }
195 
196     int result = 0;
197     for (int i = 1; i < argc; i++) {
198         if (i == 1 && !strcmp(argv[1], "-q")) {
199             gQuiet = true;
200             continue;
201         }
202         if (!validateFile(argv[i])) {
203             result = 1;
204         }
205     }
206 
207     if (result) {
208         error("Failed!\n");
209     } else {
210         log("Success.\n");
211     }
212     return result;
213 }
214