blob: b11daf5d66c412a6224de8ccc968b0dda23bf474 [file] [log] [blame]
Yifan Honga72bde72017-09-28 13:36:48 -07001/*
2 * Copyright (C) 2017 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
Yifan Hong69c1b112018-02-27 17:06:00 -080017#include <getopt.h>
18#include <unistd.h>
Yifan Honga72bde72017-09-28 13:36:48 -070019
Yifan Hong69c1b112018-02-27 17:06:00 -080020#include <iostream>
21#include <map>
22
23#include <android-base/parseint.h>
24#include <utils/Errors.h>
25#include <vintf/VintfObject.h>
Yifan Honga72bde72017-09-28 13:36:48 -070026#include <vintf/parse_xml.h>
27#include "utils.h"
28
29namespace android {
30namespace vintf {
Yifan Hong69c1b112018-02-27 17:06:00 -080031namespace details {
Yifan Honga72bde72017-09-28 13:36:48 -070032
Yifan Hong69c1b112018-02-27 17:06:00 -080033// fake sysprops
34using Properties = std::map<std::string, std::string>;
35
36enum Option : int {
37 DUMP_FILE_LIST = 1,
38 ROOTDIR,
39 HELP,
40 PROPERTY,
41 CHECK_COMPAT,
42};
43// command line arguments
44using Args = std::multimap<Option, std::string>;
45
Yifan Hongdb308432018-07-13 16:06:25 -070046class HostFileSystem : public FileSystemUnderPath {
Yifan Hong69c1b112018-02-27 17:06:00 -080047 public:
Yifan Hongdb308432018-07-13 16:06:25 -070048 HostFileSystem(const std::string& rootdir) : FileSystemUnderPath(rootdir) {}
Yifan Hong10d86222018-04-06 15:41:05 -070049 status_t fetch(const std::string& path, std::string* fetched,
50 std::string* error) const override {
Yifan Hongdb308432018-07-13 16:06:25 -070051 status_t status = FileSystemUnderPath::fetch(path, fetched, error);
52 std::cerr << "Debug: Fetch '" << getRootDir() << path << "': " << toString(status)
53 << std::endl;
Yifan Hong10d86222018-04-06 15:41:05 -070054 return status;
Yifan Hong69c1b112018-02-27 17:06:00 -080055 }
Yifan Hong10d86222018-04-06 15:41:05 -070056 status_t listFiles(const std::string& path, std::vector<std::string>* out,
57 std::string* error) const override {
Yifan Hongdb308432018-07-13 16:06:25 -070058 status_t status = FileSystemUnderPath::listFiles(path, out, error);
59 std::cerr << "Debug: List '" << getRootDir() << path << "': " << toString(status)
60 << std::endl;
Yifan Hong69c1b112018-02-27 17:06:00 -080061 return status;
62 }
63
64 private:
Yifan Hong69c1b112018-02-27 17:06:00 -080065 static std::string toString(status_t status) {
66 return status == OK ? "SUCCESS" : strerror(-status);
67 }
Yifan Hong69c1b112018-02-27 17:06:00 -080068};
69
70class PresetPropertyFetcher : public PropertyFetcher {
71 public:
72 std::string getProperty(const std::string& key,
73 const std::string& defaultValue) const override {
74 auto it = mProps.find(key);
75 if (it == mProps.end()) {
76 std::cerr << "Debug: Sysprop " << key << " is missing, default to '" << defaultValue
77 << "'" << std::endl;
78 return defaultValue;
79 }
80 std::cerr << "Debug: Sysprop " << key << "=" << it->second << std::endl;
81 return it->second;
82 }
83 uint64_t getUintProperty(const std::string& key, uint64_t defaultValue,
84 uint64_t max) const override {
85 uint64_t result;
86 std::string value = getProperty(key, "");
87 if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
88 return defaultValue;
89 }
90 bool getBoolProperty(const std::string& key, bool defaultValue) const override {
91 std::string value = getProperty(key, "");
92 if (value == "1" || value == "true") {
93 return true;
94 } else if (value == "0" || value == "false") {
95 return false;
96 }
97 return defaultValue;
98 }
99 void setProperties(const Properties& props) { mProps.insert(props.begin(), props.end()); }
100
101 private:
102 std::map<std::string, std::string> mProps;
103};
104
Yifan Hong69c1b112018-02-27 17:06:00 -0800105// helper functions
Yifan Honga72bde72017-09-28 13:36:48 -0700106template <typename T>
Yifan Hong9f78c182018-07-12 14:45:52 -0700107std::unique_ptr<T> readObject(FileSystem* fileSystem, const std::string& path,
108 const XmlConverter<T>& converter) {
Yifan Honga72bde72017-09-28 13:36:48 -0700109 std::string xml;
Yifan Hong60217032018-01-08 16:19:42 -0800110 std::string error;
Yifan Hong9f78c182018-07-12 14:45:52 -0700111 status_t err = fileSystem->fetch(path, &xml, &error);
Yifan Honga72bde72017-09-28 13:36:48 -0700112 if (err != OK) {
Yifan Hong60217032018-01-08 16:19:42 -0800113 std::cerr << "Error: Cannot read '" << path << "' (" << strerror(-err) << "): " << error
114 << std::endl;
Yifan Honga72bde72017-09-28 13:36:48 -0700115 return nullptr;
116 }
117 auto ret = std::make_unique<T>();
Yifan Hong94757062018-02-09 16:36:31 -0800118 if (!converter(ret.get(), xml, &error)) {
119 std::cerr << "Error: Cannot parse '" << path << "': " << error << std::endl;
Yifan Honga72bde72017-09-28 13:36:48 -0700120 return nullptr;
121 }
122 return ret;
123}
124
Yifan Hong69c1b112018-02-27 17:06:00 -0800125int checkCompatibilityForFiles(const std::string& manifestPath, const std::string& matrixPath) {
Yifan Hong9f78c182018-07-12 14:45:52 -0700126 auto fileSystem = std::make_unique<FileSystemImpl>();
127 auto manifest = readObject(fileSystem.get(), manifestPath, gHalManifestConverter);
128 auto matrix = readObject(fileSystem.get(), matrixPath, gCompatibilityMatrixConverter);
Yifan Honga72bde72017-09-28 13:36:48 -0700129 if (manifest == nullptr || matrix == nullptr) {
130 return -1;
131 }
132
133 std::string error;
134 if (!manifest->checkCompatibility(*matrix, &error)) {
135 std::cerr << "Error: Incompatible: " << error << std::endl;
136 std::cout << "false" << std::endl;
137 return 1;
138 }
139
140 std::cout << "true" << std::endl;
141 return 0;
142}
Yifan Hong69c1b112018-02-27 17:06:00 -0800143
144Args parseArgs(int argc, char** argv) {
145 int longOptFlag;
146 int optionIndex;
147 Args ret;
148 std::vector<struct option> longopts{
149 {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST},
150 {"rootdir", required_argument, &longOptFlag, ROOTDIR},
151 {"help", no_argument, &longOptFlag, HELP},
152 {"property", required_argument, &longOptFlag, PROPERTY},
153 {"check-compat", no_argument, &longOptFlag, CHECK_COMPAT},
154 {0, 0, 0, 0}};
155 std::map<int, Option> shortopts{
156 {'h', HELP}, {'D', PROPERTY}, {'c', CHECK_COMPAT},
157 };
158 for (;;) {
159 int c = getopt_long(argc, argv, "hcD:", longopts.data(), &optionIndex);
160 if (c == -1) {
161 break;
162 }
163 std::string argValue = optarg ? optarg : std::string{};
164 if (c == 0) {
165 ret.emplace(static_cast<Option>(longOptFlag), std::move(argValue));
166 } else {
167 ret.emplace(shortopts[c], std::move(argValue));
168 }
169 }
170 if (optind < argc) {
171 // see non option
172 std::cerr << "unrecognized option `" << argv[optind] << "'" << std::endl;
173 return {{HELP, ""}};
174 }
175 return ret;
176}
177
178template <typename T>
179Properties getProperties(const T& args) {
180 Properties ret;
181 for (const auto& arg : args) {
182 auto pos = arg.find('=');
183 auto key = arg.substr(0, pos);
184 auto value = pos == std::string::npos ? std::string{} : arg.substr(pos + 1);
185 ret[key] = value;
186 }
187 return ret;
188}
189
190int usage(const char* me) {
191 std::cerr
192 << me << ": check VINTF metadata." << std::endl
193 << " Options:" << std::endl
194 << " --dump-file-list: Dump a list of directories / files on device" << std::endl
195 << " that is required to be used by --check-compat." << std::endl
196 << " -c, --check-compat: check compatibility for files under the root" << std::endl
197 << " directory specified by --root-dir." << std::endl
198 << " --rootdir=<dir>: specify root directory for all metadata." << std::endl
199 << " -D, --property <key>=<value>: specify sysprops." << std::endl
200 << " --help: show this message." << std::endl
201 << std::endl
202 << " Example:" << std::endl
203 << " # Get the list of required files." << std::endl
204 << " " << me << " --dump-file-list > /tmp/files.txt" << std::endl
205 << " # Pull from ADB, or use your own command to extract files from images"
206 << std::endl
207 << " ROOTDIR=/tmp/device/" << std::endl
208 << " cat /tmp/files.txt | xargs -I{} bash -c \"mkdir -p $ROOTDIR`dirname {}` && adb "
209 "pull {} $ROOTDIR{}\""
210 << std::endl
211 << " # Check compatibility." << std::endl
212 << " " << me << " --check-compat --rootdir=$ROOTDIR \\" << std::endl
213 << " --property ro.product.first_api_level=`adb shell getprop "
214 "ro.product.first_api_level` \\"
215 << std::endl
216 << " --property ro.boot.product.hardware.sku=`adb shell getprop "
217 "ro.boot.product.hardware.sku`"
218 << std::endl;
219 return 1;
220}
221
222int checkAllFiles(const std::string& rootdir, const Properties& props, std::string* error) {
Yifan Hong9f78c182018-07-12 14:45:52 -0700223 auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
224 hostPropertyFetcher->setProperties(props);
225 VintfObject vintfObject(std::make_unique<HostFileSystem>(rootdir),
226 nullptr /* partition mounter */, nullptr /* runtime info factory */,
227 std::move(hostPropertyFetcher));
228 return vintfObject.checkCompatibility({} /* packageInfo */, error, DISABLE_RUNTIME_INFO);
Yifan Hong69c1b112018-02-27 17:06:00 -0800229}
230
231} // namespace details
232} // namespace vintf
233} // namespace android
234
235int main(int argc, char** argv) {
236 using namespace android::vintf;
237 using namespace android::vintf::details;
238 // legacy usage: check_vintf <manifest.xml> <matrix.xml>
239 if (argc == 3) {
240 int ret = checkCompatibilityForFiles(argv[1], argv[2]);
241 if (ret >= 0) return ret;
242 }
243
244 Args args = parseArgs(argc, argv);
245
246 if (!iterateValues(args, HELP).empty()) {
247 return usage(argv[0]);
248 }
249
250 if (!iterateValues(args, DUMP_FILE_LIST).empty()) {
251 for (const auto& file : dumpFileList()) {
252 std::cout << file << std::endl;
253 }
254 return 0;
255 }
256
257 auto rootdirs = iterateValues(args, ROOTDIR);
258 auto properties = getProperties(iterateValues(args, PROPERTY));
259
260 auto checkCompat = iterateValues(args, CHECK_COMPAT);
261 if (!checkCompat.empty()) {
262 if (rootdirs.empty()) {
263 std::cerr << "Missing --rootdir option." << std::endl;
264 return usage(argv[0]);
265 }
266 int ret = COMPATIBLE;
267 for (const auto& rootdir : rootdirs) {
268 std::cerr << "Debug: checking files under " << rootdir << "..." << std::endl;
269 std::string error;
270 int compat = checkAllFiles(rootdir, properties, &error);
271 std::cerr << "Debug: files under " << rootdir
272 << (compat == COMPATIBLE
273 ? " is compatible"
274 : compat == INCOMPATIBLE ? " are incompatible"
275 : (" has encountered an error: " + error))
276 << std::endl;
277 }
278 if (ret == COMPATIBLE) {
279 std::cout << "true" << std::endl;
280 }
281 return ret;
282 }
283
284 return usage(argv[0]);
285}