blob: c85cbe3f1336b5b0fa4c17af258f814f523e5135 [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 Hong10d86222018-04-06 15:41:05 -070046class HostFileSystem : public FileSystem {
Yifan Hong69c1b112018-02-27 17:06:00 -080047 public:
Yifan Hong10d86222018-04-06 15:41:05 -070048 HostFileSystem(const std::string& rootdir) {
Yifan Hong69c1b112018-02-27 17:06:00 -080049 mRootDir = rootdir;
50 if (!mRootDir.empty() && mRootDir.back() != '/') {
51 mRootDir.push_back('/');
52 }
53 }
Yifan Hong10d86222018-04-06 15:41:05 -070054 status_t fetch(const std::string& path, std::string* fetched,
55 std::string* error) const override {
56 status_t status = mImpl.fetch(mRootDir + path, fetched, error);
57 std::cerr << "Debug: Fetch '" << mRootDir << path << "': " << toString(status) << std::endl;
58 return status;
Yifan Hong69c1b112018-02-27 17:06:00 -080059 }
Yifan Hong10d86222018-04-06 15:41:05 -070060 status_t listFiles(const std::string& path, std::vector<std::string>* out,
61 std::string* error) const override {
62 status_t status = mImpl.listFiles(mRootDir + path, out, error);
Yifan Hong69c1b112018-02-27 17:06:00 -080063 std::cerr << "Debug: List '" << mRootDir << path << "': " << toString(status) << std::endl;
64 return status;
65 }
66
67 private:
Yifan Hong69c1b112018-02-27 17:06:00 -080068 static std::string toString(status_t status) {
69 return status == OK ? "SUCCESS" : strerror(-status);
70 }
71 std::string mRootDir;
Yifan Hong10d86222018-04-06 15:41:05 -070072 FileSystemImpl mImpl;
Yifan Hong69c1b112018-02-27 17:06:00 -080073};
74
75class PresetPropertyFetcher : public PropertyFetcher {
76 public:
77 std::string getProperty(const std::string& key,
78 const std::string& defaultValue) const override {
79 auto it = mProps.find(key);
80 if (it == mProps.end()) {
81 std::cerr << "Debug: Sysprop " << key << " is missing, default to '" << defaultValue
82 << "'" << std::endl;
83 return defaultValue;
84 }
85 std::cerr << "Debug: Sysprop " << key << "=" << it->second << std::endl;
86 return it->second;
87 }
88 uint64_t getUintProperty(const std::string& key, uint64_t defaultValue,
89 uint64_t max) const override {
90 uint64_t result;
91 std::string value = getProperty(key, "");
92 if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
93 return defaultValue;
94 }
95 bool getBoolProperty(const std::string& key, bool defaultValue) const override {
96 std::string value = getProperty(key, "");
97 if (value == "1" || value == "true") {
98 return true;
99 } else if (value == "0" || value == "false") {
100 return false;
101 }
102 return defaultValue;
103 }
104 void setProperties(const Properties& props) { mProps.insert(props.begin(), props.end()); }
105
106 private:
107 std::map<std::string, std::string> mProps;
108};
109
110// globals
Yifan Hong69c1b112018-02-27 17:06:00 -0800111static PartitionMounter partitionMounter;
112PartitionMounter* gPartitionMounter = &partitionMounter;
113
114static ObjectFactory<RuntimeInfo> runtimeInfoFactory;
115ObjectFactory<RuntimeInfo>* gRuntimeInfoFactory = &runtimeInfoFactory;
116
117static PresetPropertyFetcher hostPropertyFetcher;
118const PropertyFetcher& getPropertyFetcher() {
119 return hostPropertyFetcher;
120}
121
122// helper functions
Yifan Honga72bde72017-09-28 13:36:48 -0700123template <typename T>
124std::unique_ptr<T> readObject(const std::string& path, const XmlConverter<T>& converter) {
125 std::string xml;
Yifan Hong60217032018-01-08 16:19:42 -0800126 std::string error;
Yifan Hong10d86222018-04-06 15:41:05 -0700127 status_t err = details::getFileSystem().fetch(path, &xml, &error);
Yifan Honga72bde72017-09-28 13:36:48 -0700128 if (err != OK) {
Yifan Hong60217032018-01-08 16:19:42 -0800129 std::cerr << "Error: Cannot read '" << path << "' (" << strerror(-err) << "): " << error
130 << std::endl;
Yifan Honga72bde72017-09-28 13:36:48 -0700131 return nullptr;
132 }
133 auto ret = std::make_unique<T>();
Yifan Hong94757062018-02-09 16:36:31 -0800134 if (!converter(ret.get(), xml, &error)) {
135 std::cerr << "Error: Cannot parse '" << path << "': " << error << std::endl;
Yifan Honga72bde72017-09-28 13:36:48 -0700136 return nullptr;
137 }
138 return ret;
139}
140
Yifan Hong69c1b112018-02-27 17:06:00 -0800141int checkCompatibilityForFiles(const std::string& manifestPath, const std::string& matrixPath) {
Yifan Hong10d86222018-04-06 15:41:05 -0700142 if (!VintfObject::InitFileSystem(std::make_unique<FileSystemImpl>())) {
143 std::cerr << "Cannot initialize FileSystem object." << std::endl;
144 return NO_INIT;
145 }
146
Yifan Hong69c1b112018-02-27 17:06:00 -0800147 auto manifest = readObject(manifestPath, gHalManifestConverter);
148 auto matrix = readObject(matrixPath, gCompatibilityMatrixConverter);
Yifan Honga72bde72017-09-28 13:36:48 -0700149 if (manifest == nullptr || matrix == nullptr) {
150 return -1;
151 }
152
153 std::string error;
154 if (!manifest->checkCompatibility(*matrix, &error)) {
155 std::cerr << "Error: Incompatible: " << error << std::endl;
156 std::cout << "false" << std::endl;
157 return 1;
158 }
159
160 std::cout << "true" << std::endl;
161 return 0;
162}
Yifan Hong69c1b112018-02-27 17:06:00 -0800163
164Args parseArgs(int argc, char** argv) {
165 int longOptFlag;
166 int optionIndex;
167 Args ret;
168 std::vector<struct option> longopts{
169 {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST},
170 {"rootdir", required_argument, &longOptFlag, ROOTDIR},
171 {"help", no_argument, &longOptFlag, HELP},
172 {"property", required_argument, &longOptFlag, PROPERTY},
173 {"check-compat", no_argument, &longOptFlag, CHECK_COMPAT},
174 {0, 0, 0, 0}};
175 std::map<int, Option> shortopts{
176 {'h', HELP}, {'D', PROPERTY}, {'c', CHECK_COMPAT},
177 };
178 for (;;) {
179 int c = getopt_long(argc, argv, "hcD:", longopts.data(), &optionIndex);
180 if (c == -1) {
181 break;
182 }
183 std::string argValue = optarg ? optarg : std::string{};
184 if (c == 0) {
185 ret.emplace(static_cast<Option>(longOptFlag), std::move(argValue));
186 } else {
187 ret.emplace(shortopts[c], std::move(argValue));
188 }
189 }
190 if (optind < argc) {
191 // see non option
192 std::cerr << "unrecognized option `" << argv[optind] << "'" << std::endl;
193 return {{HELP, ""}};
194 }
195 return ret;
196}
197
198template <typename T>
199Properties getProperties(const T& args) {
200 Properties ret;
201 for (const auto& arg : args) {
202 auto pos = arg.find('=');
203 auto key = arg.substr(0, pos);
204 auto value = pos == std::string::npos ? std::string{} : arg.substr(pos + 1);
205 ret[key] = value;
206 }
207 return ret;
208}
209
210int usage(const char* me) {
211 std::cerr
212 << me << ": check VINTF metadata." << std::endl
213 << " Options:" << std::endl
214 << " --dump-file-list: Dump a list of directories / files on device" << std::endl
215 << " that is required to be used by --check-compat." << std::endl
216 << " -c, --check-compat: check compatibility for files under the root" << std::endl
217 << " directory specified by --root-dir." << std::endl
218 << " --rootdir=<dir>: specify root directory for all metadata." << std::endl
219 << " -D, --property <key>=<value>: specify sysprops." << std::endl
220 << " --help: show this message." << std::endl
221 << std::endl
222 << " Example:" << std::endl
223 << " # Get the list of required files." << std::endl
224 << " " << me << " --dump-file-list > /tmp/files.txt" << std::endl
225 << " # Pull from ADB, or use your own command to extract files from images"
226 << std::endl
227 << " ROOTDIR=/tmp/device/" << std::endl
228 << " cat /tmp/files.txt | xargs -I{} bash -c \"mkdir -p $ROOTDIR`dirname {}` && adb "
229 "pull {} $ROOTDIR{}\""
230 << std::endl
231 << " # Check compatibility." << std::endl
232 << " " << me << " --check-compat --rootdir=$ROOTDIR \\" << std::endl
233 << " --property ro.product.first_api_level=`adb shell getprop "
234 "ro.product.first_api_level` \\"
235 << std::endl
236 << " --property ro.boot.product.hardware.sku=`adb shell getprop "
237 "ro.boot.product.hardware.sku`"
238 << std::endl;
239 return 1;
240}
241
242int checkAllFiles(const std::string& rootdir, const Properties& props, std::string* error) {
Yifan Hong10d86222018-04-06 15:41:05 -0700243 if (!VintfObject::InitFileSystem(std::make_unique<HostFileSystem>(rootdir))) {
244 std::cerr << "Cannot initialize FileSystem object." << std::endl;
245 return NO_INIT;
246 }
Yifan Hong69c1b112018-02-27 17:06:00 -0800247 hostPropertyFetcher.setProperties(props);
248
249 return VintfObject::CheckCompatibility({} /* packageInfo */, error, DISABLE_RUNTIME_INFO);
250}
251
252} // namespace details
253} // namespace vintf
254} // namespace android
255
256int main(int argc, char** argv) {
257 using namespace android::vintf;
258 using namespace android::vintf::details;
259 // legacy usage: check_vintf <manifest.xml> <matrix.xml>
260 if (argc == 3) {
261 int ret = checkCompatibilityForFiles(argv[1], argv[2]);
262 if (ret >= 0) return ret;
263 }
264
265 Args args = parseArgs(argc, argv);
266
267 if (!iterateValues(args, HELP).empty()) {
268 return usage(argv[0]);
269 }
270
271 if (!iterateValues(args, DUMP_FILE_LIST).empty()) {
272 for (const auto& file : dumpFileList()) {
273 std::cout << file << std::endl;
274 }
275 return 0;
276 }
277
278 auto rootdirs = iterateValues(args, ROOTDIR);
279 auto properties = getProperties(iterateValues(args, PROPERTY));
280
281 auto checkCompat = iterateValues(args, CHECK_COMPAT);
282 if (!checkCompat.empty()) {
283 if (rootdirs.empty()) {
284 std::cerr << "Missing --rootdir option." << std::endl;
285 return usage(argv[0]);
286 }
287 int ret = COMPATIBLE;
288 for (const auto& rootdir : rootdirs) {
289 std::cerr << "Debug: checking files under " << rootdir << "..." << std::endl;
290 std::string error;
291 int compat = checkAllFiles(rootdir, properties, &error);
292 std::cerr << "Debug: files under " << rootdir
293 << (compat == COMPATIBLE
294 ? " is compatible"
295 : compat == INCOMPATIBLE ? " are incompatible"
296 : (" has encountered an error: " + error))
297 << std::endl;
298 }
299 if (ret == COMPATIBLE) {
300 std::cout << "true" << std::endl;
301 }
302 return ret;
303 }
304
305 return usage(argv[0]);
306}