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