123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286 |
- /*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <getopt.h>
- #include <unistd.h>
- #include <iostream>
- #include <map>
- #include <android-base/parseint.h>
- #include <utils/Errors.h>
- #include <vintf/VintfObject.h>
- #include <vintf/parse_xml.h>
- #include "utils.h"
- namespace android {
- namespace vintf {
- namespace details {
- // fake sysprops
- using Properties = std::map<std::string, std::string>;
- enum Option : int {
- DUMP_FILE_LIST = 1,
- ROOTDIR,
- HELP,
- PROPERTY,
- CHECK_COMPAT,
- };
- // command line arguments
- using Args = std::multimap<Option, std::string>;
- class HostFileSystem : public FileSystemUnderPath {
- public:
- HostFileSystem(const std::string& rootdir) : FileSystemUnderPath(rootdir) {}
- status_t fetch(const std::string& path, std::string* fetched,
- std::string* error) const override {
- status_t status = FileSystemUnderPath::fetch(path, fetched, error);
- std::cerr << "Debug: Fetch '" << getRootDir() << path << "': " << toString(status)
- << std::endl;
- return status;
- }
- status_t listFiles(const std::string& path, std::vector<std::string>* out,
- std::string* error) const override {
- status_t status = FileSystemUnderPath::listFiles(path, out, error);
- std::cerr << "Debug: List '" << getRootDir() << path << "': " << toString(status)
- << std::endl;
- return status;
- }
- private:
- static std::string toString(status_t status) {
- return status == OK ? "SUCCESS" : strerror(-status);
- }
- };
- class PresetPropertyFetcher : public PropertyFetcher {
- public:
- std::string getProperty(const std::string& key,
- const std::string& defaultValue) const override {
- auto it = mProps.find(key);
- if (it == mProps.end()) {
- std::cerr << "Debug: Sysprop " << key << " is missing, default to '" << defaultValue
- << "'" << std::endl;
- return defaultValue;
- }
- std::cerr << "Debug: Sysprop " << key << "=" << it->second << std::endl;
- return it->second;
- }
- uint64_t getUintProperty(const std::string& key, uint64_t defaultValue,
- uint64_t max) const override {
- uint64_t result;
- std::string value = getProperty(key, "");
- if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
- return defaultValue;
- }
- bool getBoolProperty(const std::string& key, bool defaultValue) const override {
- std::string value = getProperty(key, "");
- if (value == "1" || value == "true") {
- return true;
- } else if (value == "0" || value == "false") {
- return false;
- }
- return defaultValue;
- }
- void setProperties(const Properties& props) { mProps.insert(props.begin(), props.end()); }
- private:
- std::map<std::string, std::string> mProps;
- };
- // helper functions
- template <typename T>
- std::unique_ptr<T> readObject(FileSystem* fileSystem, const std::string& path,
- const XmlConverter<T>& converter) {
- std::string xml;
- std::string error;
- status_t err = fileSystem->fetch(path, &xml, &error);
- if (err != OK) {
- std::cerr << "Error: Cannot read '" << path << "' (" << strerror(-err) << "): " << error
- << std::endl;
- return nullptr;
- }
- auto ret = std::make_unique<T>();
- if (!converter(ret.get(), xml, &error)) {
- std::cerr << "Error: Cannot parse '" << path << "': " << error << std::endl;
- return nullptr;
- }
- return ret;
- }
- int checkCompatibilityForFiles(const std::string& manifestPath, const std::string& matrixPath) {
- auto fileSystem = std::make_unique<FileSystemImpl>();
- auto manifest = readObject(fileSystem.get(), manifestPath, gHalManifestConverter);
- auto matrix = readObject(fileSystem.get(), matrixPath, gCompatibilityMatrixConverter);
- if (manifest == nullptr || matrix == nullptr) {
- return -1;
- }
- std::string error;
- if (!manifest->checkCompatibility(*matrix, &error)) {
- std::cerr << "Error: Incompatible: " << error << std::endl;
- std::cout << "false" << std::endl;
- return 1;
- }
- std::cout << "true" << std::endl;
- return 0;
- }
- Args parseArgs(int argc, char** argv) {
- int longOptFlag;
- int optionIndex;
- Args ret;
- std::vector<struct option> longopts{
- {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST},
- {"rootdir", required_argument, &longOptFlag, ROOTDIR},
- {"help", no_argument, &longOptFlag, HELP},
- {"property", required_argument, &longOptFlag, PROPERTY},
- {"check-compat", no_argument, &longOptFlag, CHECK_COMPAT},
- {0, 0, 0, 0}};
- std::map<int, Option> shortopts{
- {'h', HELP}, {'D', PROPERTY}, {'c', CHECK_COMPAT},
- };
- for (;;) {
- int c = getopt_long(argc, argv, "hcD:", longopts.data(), &optionIndex);
- if (c == -1) {
- break;
- }
- std::string argValue = optarg ? optarg : std::string{};
- if (c == 0) {
- ret.emplace(static_cast<Option>(longOptFlag), std::move(argValue));
- } else {
- ret.emplace(shortopts[c], std::move(argValue));
- }
- }
- if (optind < argc) {
- // see non option
- std::cerr << "unrecognized option `" << argv[optind] << "'" << std::endl;
- return {{HELP, ""}};
- }
- return ret;
- }
- template <typename T>
- Properties getProperties(const T& args) {
- Properties ret;
- for (const auto& arg : args) {
- auto pos = arg.find('=');
- auto key = arg.substr(0, pos);
- auto value = pos == std::string::npos ? std::string{} : arg.substr(pos + 1);
- ret[key] = value;
- }
- return ret;
- }
- int usage(const char* me) {
- std::cerr
- << me << ": check VINTF metadata." << std::endl
- << " Options:" << std::endl
- << " --dump-file-list: Dump a list of directories / files on device" << std::endl
- << " that is required to be used by --check-compat." << std::endl
- << " -c, --check-compat: check compatibility for files under the root" << std::endl
- << " directory specified by --root-dir." << std::endl
- << " --rootdir=<dir>: specify root directory for all metadata." << std::endl
- << " -D, --property <key>=<value>: specify sysprops." << std::endl
- << " --help: show this message." << std::endl
- << std::endl
- << " Example:" << std::endl
- << " # Get the list of required files." << std::endl
- << " " << me << " --dump-file-list > /tmp/files.txt" << std::endl
- << " # Pull from ADB, or use your own command to extract files from images"
- << std::endl
- << " ROOTDIR=/tmp/device/" << std::endl
- << " cat /tmp/files.txt | xargs -I{} bash -c \"mkdir -p $ROOTDIR`dirname {}` && adb "
- "pull {} $ROOTDIR{}\""
- << std::endl
- << " # Check compatibility." << std::endl
- << " " << me << " --check-compat --rootdir=$ROOTDIR \\" << std::endl
- << " --property ro.product.first_api_level=`adb shell getprop "
- "ro.product.first_api_level` \\"
- << std::endl
- << " --property ro.boot.product.hardware.sku=`adb shell getprop "
- "ro.boot.product.hardware.sku`"
- << std::endl;
- return 1;
- }
- int checkAllFiles(const std::string& rootdir, const Properties& props, std::string* error) {
- auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
- hostPropertyFetcher->setProperties(props);
- auto vintfObject = VintfObject::Builder()
- .setFileSystem(std::make_unique<HostFileSystem>(rootdir))
- .setPropertyFetcher(std::move(hostPropertyFetcher))
- .build();
- return vintfObject->checkCompatibility(error, CheckFlags::DISABLE_RUNTIME_INFO);
- }
- } // namespace details
- } // namespace vintf
- } // namespace android
- int main(int argc, char** argv) {
- using namespace android::vintf;
- using namespace android::vintf::details;
- // legacy usage: check_vintf <manifest.xml> <matrix.xml>
- if (argc == 3) {
- int ret = checkCompatibilityForFiles(argv[1], argv[2]);
- if (ret >= 0) return ret;
- }
- Args args = parseArgs(argc, argv);
- if (!iterateValues(args, HELP).empty()) {
- return usage(argv[0]);
- }
- if (!iterateValues(args, DUMP_FILE_LIST).empty()) {
- for (const auto& file : dumpFileList()) {
- std::cout << file << std::endl;
- }
- return 0;
- }
- auto rootdirs = iterateValues(args, ROOTDIR);
- auto properties = getProperties(iterateValues(args, PROPERTY));
- auto checkCompat = iterateValues(args, CHECK_COMPAT);
- if (!checkCompat.empty()) {
- if (rootdirs.empty()) {
- std::cerr << "Missing --rootdir option." << std::endl;
- return usage(argv[0]);
- }
- int ret = COMPATIBLE;
- for (const auto& rootdir : rootdirs) {
- std::cerr << "Debug: checking files under " << rootdir << "..." << std::endl;
- std::string error;
- int compat = checkAllFiles(rootdir, properties, &error);
- std::cerr << "Debug: files under " << rootdir
- << (compat == COMPATIBLE
- ? " is compatible"
- : compat == INCOMPATIBLE ? " are incompatible"
- : (" has encountered an error: " + error))
- << std::endl;
- }
- if (ret == COMPATIBLE) {
- std::cout << "true" << std::endl;
- }
- return ret;
- }
- return usage(argv[0]);
- }
|