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