| /* |
| * 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 <android-base/strings.h> |
| #include <vintf/VintfObject.h> |
| #include <vintf/parse_string.h> |
| #include <vintf/parse_xml.h> |
| #include <iomanip> |
| #include <iostream> |
| #include <string> |
| #include <vector> |
| |
| using namespace ::android::vintf; |
| |
| static const std::string kColumnSeperator = " "; |
| |
| std::string existString(bool value) { |
| return value ? "GOOD" : "DOES NOT EXIST"; |
| } |
| |
| std::string compatibleString(int32_t value) { |
| switch (value) { |
| case COMPATIBLE: |
| return "GOOD"; |
| case INCOMPATIBLE: |
| return "INCOMPATIBLE"; |
| default: |
| return strerror(-value); |
| } |
| } |
| |
| std::string boolCompatString(bool value) { |
| return compatibleString(value ? COMPATIBLE : INCOMPATIBLE); |
| } |
| |
| std::string deprecateString(int32_t value) { |
| switch (value) { |
| case NO_DEPRECATED_HALS: |
| return "GOOD"; |
| case DEPRECATED: |
| return "DEPRECATED"; |
| default: |
| return strerror(-value); |
| } |
| } |
| |
| enum Status : int { |
| OK = 0, |
| USAGE, |
| }; |
| |
| struct ParsedOptions { |
| bool verbose = false; |
| }; |
| |
| struct Option { |
| char shortOption = '\0'; |
| std::string longOption; |
| std::string help; |
| std::function<Status(ParsedOptions*)> op; |
| }; |
| |
| std::string getShortOptions(const std::vector<Option>& options) { |
| std::stringstream ret; |
| for (const auto& e : options) |
| if (e.shortOption != '\0') ret << e.shortOption; |
| return ret.str(); |
| } |
| |
| std::unique_ptr<struct option[]> getLongOptions(const std::vector<Option>& options, |
| int* longOptFlag) { |
| std::unique_ptr<struct option[]> ret{new struct option[options.size() + 1]}; |
| int i = 0; |
| for (const auto& e : options) { |
| ret[i].name = e.longOption.c_str(); |
| ret[i].has_arg = no_argument; |
| ret[i].flag = longOptFlag; |
| ret[i].val = i; |
| |
| i++; |
| } |
| // getopt_long last option has all zeros |
| ret[i].name = NULL; |
| ret[i].has_arg = 0; |
| ret[i].flag = NULL; |
| ret[i].val = 0; |
| |
| return ret; |
| } |
| |
| Status parseOptions(int argc, char** argv, const std::vector<Option>& options, ParsedOptions* out) { |
| int longOptFlag; |
| std::unique_ptr<struct option[]> longOptions = getLongOptions(options, &longOptFlag); |
| std::string shortOptions = getShortOptions(options); |
| int optionIndex; |
| for (;;) { |
| int c = getopt_long(argc, argv, shortOptions.c_str(), longOptions.get(), &optionIndex); |
| if (c == -1) { |
| break; |
| } |
| const Option* found = nullptr; |
| for (size_t i = 0; i < options.size(); ++i) |
| if ((c == 0 && longOptFlag == static_cast<int>(i)) || |
| (c != 0 && c == options[i].shortOption)) |
| |
| found = &options[i]; |
| |
| if (found == nullptr) { |
| // see unrecognized options |
| std::cerr << "unrecognized option `" << argv[optind - 1] << "'" << std::endl; |
| return USAGE; |
| } |
| |
| Status status = found->op(out); |
| if (status != OK) return status; |
| } |
| if (optind < argc) { |
| // see non option |
| std::cerr << "unrecognized option `" << argv[optind] << "'" << std::endl; |
| return USAGE; |
| } |
| return OK; |
| } |
| |
| void usage(char* me, const std::vector<Option>& options) { |
| std::cerr << me << ": dump VINTF metadata via libvintf." << std::endl; |
| for (const auto& e : options) { |
| if (e.help.empty()) continue; |
| std::cerr << " "; |
| if (e.shortOption != '\0') std::cerr << "-" << e.shortOption; |
| if (e.shortOption != '\0' && !e.longOption.empty()) std::cerr << ", "; |
| if (!e.longOption.empty()) std::cerr << "--" << e.longOption; |
| std::cerr << ": " |
| << android::base::Join(android::base::Split(e.help, "\n"), "\n ") |
| << std::endl; |
| } |
| } |
| |
| struct TableRow { |
| // Whether the HAL version is in device manifest, framework manifest, device compatibility |
| // matrix, framework compatibility matrix, respectively. |
| bool dm = false; |
| bool fm = false; |
| bool dcm = false; |
| bool fcm = false; |
| // If the HAL version is in device / framework compatibility matrix, whether it is required |
| // or not. |
| bool required = false; |
| |
| // Return true if: |
| // - not a required HAL version; OR |
| // - required in device matrix and framework manifest; |
| // - required in framework matrix and device manifest. |
| bool meetsReqeuirement() const { |
| if (!required) return true; |
| if (dcm && !fm) return false; |
| if (fcm && !dm) return false; |
| return true; |
| } |
| }; |
| |
| std::ostream& operator<<(std::ostream& out, const TableRow& row) { |
| return out << (row.required ? "R" : " ") << (row.meetsReqeuirement() ? " " : "!") |
| << kColumnSeperator << (row.dm ? "DM" : " ") << kColumnSeperator |
| << (row.fm ? "FM" : " ") << kColumnSeperator << (row.fcm ? "FCM" : " ") |
| << kColumnSeperator << (row.dcm ? "DCM" : " "); |
| } |
| |
| using RowMutator = std::function<void(TableRow*)>; |
| using Table = std::map<std::string, TableRow>; |
| |
| // Insert each fqInstanceName foo@x.y::IFoo/instance to the table by inserting the key |
| // if it does not exist and setting the corresponding indicator (as specified by "mutate"). |
| void insert(const HalManifest* manifest, Table* table, const RowMutator& mutate) { |
| if (manifest == nullptr) return; |
| manifest->forEachInstance([&](const auto& manifestInstance) { |
| std::string key = manifestInstance.description(); |
| mutate(&(*table)[key]); |
| return true; |
| }); |
| } |
| |
| void insert(const CompatibilityMatrix* matrix, Table* table, const RowMutator& mutate) { |
| if (matrix == nullptr) return; |
| matrix->forEachInstance([&](const auto& matrixInstance) { |
| for (auto minorVer = matrixInstance.versionRange().minMinor; |
| minorVer >= matrixInstance.versionRange().minMinor && |
| minorVer <= matrixInstance.versionRange().maxMinor; |
| ++minorVer) { |
| Version version{matrixInstance.versionRange().majorVer, minorVer}; |
| std::string key = matrixInstance.description(version); |
| auto it = table->find(key); |
| if (it == table->end()) { |
| mutate(&(*table)[key]); |
| } else { |
| mutate(&it->second); |
| if (minorVer == matrixInstance.versionRange().minMinor) { |
| it->second.required = !matrixInstance.optional(); |
| } |
| } |
| } |
| return true; |
| }); |
| } |
| |
| Table generateHalSummary(const HalManifest* vm, const HalManifest* fm, |
| const CompatibilityMatrix* vcm, const CompatibilityMatrix* fcm) { |
| Table table; |
| insert(vm, &table, [](auto* row) { row->dm = true; }); |
| insert(fm, &table, [](auto* row) { row->fm = true; }); |
| insert(vcm, &table, [](auto* row) { row->dcm = true; }); |
| insert(fcm, &table, [](auto* row) { row->fcm = true; }); |
| |
| return table; |
| } |
| |
| static const std::vector<Option> gAvailableOptions{ |
| {'h', "help", "Print help message.", [](auto) { return USAGE; }}, |
| {'v', "verbose", "Dump detailed and raw content, including kernel configurations", [](auto o) { |
| o->verbose = true; |
| return OK; |
| }}}; |
| // A convenience binary to dump information available through libvintf. |
| int main(int argc, char** argv) { |
| ParsedOptions options; |
| Status status = parseOptions(argc, argv, gAvailableOptions, &options); |
| if (status == USAGE) usage(argv[0], gAvailableOptions); |
| if (status != OK) return status; |
| |
| auto vm = VintfObject::GetDeviceHalManifest(); |
| auto fm = VintfObject::GetFrameworkHalManifest(); |
| auto vcm = VintfObject::GetDeviceCompatibilityMatrix(); |
| auto fcm = VintfObject::GetFrameworkCompatibilityMatrix(); |
| auto ki = VintfObject::GetRuntimeInfo(); |
| |
| if (!options.verbose) { |
| std::cout << "======== HALs =========" << std::endl |
| << "R: required. (empty): optional or missing from matrices. " |
| << "!: required and not in manifest." << std::endl |
| << "DM: device manifest. FM: framework manifest." << std::endl |
| << "FCM: framework compatibility matrix. DCM: device compatibility matrix." |
| << std::endl |
| << std::endl; |
| auto table = generateHalSummary(vm.get(), fm.get(), vcm.get(), fcm.get()); |
| |
| for (const auto& pair : table) |
| std::cout << pair.second << kColumnSeperator << pair.first << std::endl; |
| |
| std::cout << std::endl; |
| } |
| |
| SerializeFlags::Type flags = SerializeFlags::EVERYTHING; |
| if (!options.verbose) { |
| flags = flags.disableHals().disableKernel(); |
| } |
| std::cout << "======== Device HAL Manifest =========" << std::endl; |
| if (vm != nullptr) std::cout << toXml(*vm, flags); |
| std::cout << "======== Framework HAL Manifest =========" << std::endl; |
| if (fm != nullptr) std::cout << toXml(*fm, flags); |
| std::cout << "======== Device Compatibility Matrix =========" << std::endl; |
| if (vcm != nullptr) std::cout << toXml(*vcm, flags); |
| std::cout << "======== Framework Compatibility Matrix =========" << std::endl; |
| if (fcm != nullptr) std::cout << toXml(*fcm, flags); |
| |
| std::cout << "======== Runtime Info =========" << std::endl; |
| if (ki != nullptr) std::cout << dump(*ki, options.verbose); |
| |
| std::cout << std::endl; |
| |
| std::cout << "======== Summary =========" << std::endl; |
| std::cout << "Device Manifest? " << existString(vm != nullptr) << std::endl |
| << "Device Matrix? " << existString(vcm != nullptr) << std::endl |
| << "Framework Manifest? " << existString(fm != nullptr) << std::endl |
| << "Framework Matrix? " << existString(fcm != nullptr) << std::endl; |
| std::string error; |
| if (vm && fcm) { |
| bool compatible = vm->checkCompatibility(*fcm, &error); |
| std::cout << "Device HAL Manifest <==> Framework Compatibility Matrix? " |
| << boolCompatString(compatible); |
| if (!compatible) |
| std::cout << ", " << error; |
| std::cout << std::endl; |
| } |
| if (fm && vcm) { |
| bool compatible = fm->checkCompatibility(*vcm, &error); |
| std::cout << "Framework HAL Manifest <==> Device Compatibility Matrix? " |
| << boolCompatString(compatible); |
| if (!compatible) |
| std::cout << ", " << error; |
| std::cout << std::endl; |
| } |
| if (ki && fcm) { |
| bool compatible = ki->checkCompatibility(*fcm, &error); |
| std::cout << "Runtime info <==> Framework Compatibility Matrix? " |
| << boolCompatString(compatible); |
| if (!compatible) std::cout << ", " << error; |
| std::cout << std::endl; |
| } |
| |
| { |
| auto compatible = VintfObject::GetInstance()->checkCompatibility(&error); |
| std::cout << "VintfObject::checkCompatibility? " |
| << compatibleString(compatible); |
| if (compatible != COMPATIBLE) std::cout << ", " << error; |
| std::cout << std::endl; |
| } |
| |
| if (vm && fcm) { |
| // TODO(b/131717099): Use correct information from libhidlmetadata |
| auto deprecate = VintfObject::GetInstance()->checkDeprecation({}, &error); |
| std::cout << "VintfObject::CheckDeprecation (against device manifest) (w/o hidlmetadata)? " |
| << deprecateString(deprecate); |
| if (deprecate != NO_DEPRECATED_HALS) std::cout << ", " << error; |
| std::cout << std::endl; |
| } |
| } |