Yifan Hong | 676447a | 2016-11-15 12:57:23 -0800 | [diff] [blame] | 1 | /* |
| 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 Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 17 | #include <getopt.h> |
| 18 | |
| 19 | #include <android-base/strings.h> |
Yifan Hong | 3daec81 | 2017-02-27 18:49:11 -0800 | [diff] [blame] | 20 | #include <vintf/VintfObject.h> |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 21 | #include <vintf/parse_string.h> |
| 22 | #include <vintf/parse_xml.h> |
| 23 | #include <iomanip> |
| 24 | #include <iostream> |
| 25 | #include <string> |
| 26 | #include <vector> |
Yifan Hong | 676447a | 2016-11-15 12:57:23 -0800 | [diff] [blame] | 27 | |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 28 | using namespace ::android::vintf; |
| 29 | |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 30 | static const std::string kColumnSeperator = " "; |
| 31 | |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 32 | std::string existString(bool value) { |
| 33 | return value ? "GOOD" : "DOES NOT EXIST"; |
| 34 | } |
| 35 | |
| 36 | std::string compatibleString(int32_t value) { |
| 37 | switch (value) { |
| 38 | case COMPATIBLE: |
| 39 | return "GOOD"; |
| 40 | case INCOMPATIBLE: |
| 41 | return "INCOMPATIBLE"; |
| 42 | default: |
| 43 | return strerror(-value); |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | std::string boolCompatString(bool value) { |
| 48 | return compatibleString(value ? COMPATIBLE : INCOMPATIBLE); |
| 49 | } |
| 50 | |
| 51 | std::string deprecateString(int32_t value) { |
| 52 | switch (value) { |
| 53 | case NO_DEPRECATED_HALS: |
| 54 | return "GOOD"; |
| 55 | case DEPRECATED: |
| 56 | return "DEPRECATED"; |
| 57 | default: |
| 58 | return strerror(-value); |
| 59 | } |
| 60 | } |
| 61 | |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 62 | enum Status : int { |
| 63 | OK = 0, |
| 64 | USAGE, |
| 65 | }; |
| 66 | |
| 67 | struct ParsedOptions { |
| 68 | bool verbose = false; |
| 69 | }; |
| 70 | |
| 71 | struct Option { |
| 72 | char shortOption = '\0'; |
| 73 | std::string longOption; |
| 74 | std::string help; |
| 75 | std::function<Status(ParsedOptions*)> op; |
| 76 | }; |
| 77 | |
| 78 | std::string getShortOptions(const std::vector<Option>& options) { |
| 79 | std::stringstream ret; |
| 80 | for (const auto& e : options) |
| 81 | if (e.shortOption != '\0') ret << e.shortOption; |
| 82 | return ret.str(); |
| 83 | } |
| 84 | |
| 85 | std::unique_ptr<struct option[]> getLongOptions(const std::vector<Option>& options, |
| 86 | int* longOptFlag) { |
| 87 | std::unique_ptr<struct option[]> ret{new struct option[options.size() + 1]}; |
| 88 | int i = 0; |
| 89 | for (const auto& e : options) { |
| 90 | ret[i].name = e.longOption.c_str(); |
| 91 | ret[i].has_arg = no_argument; |
| 92 | ret[i].flag = longOptFlag; |
| 93 | ret[i].val = i; |
| 94 | |
| 95 | i++; |
| 96 | } |
| 97 | // getopt_long last option has all zeros |
| 98 | ret[i].name = NULL; |
| 99 | ret[i].has_arg = 0; |
| 100 | ret[i].flag = NULL; |
| 101 | ret[i].val = 0; |
| 102 | |
| 103 | return ret; |
| 104 | } |
| 105 | |
| 106 | Status parseOptions(int argc, char** argv, const std::vector<Option>& options, ParsedOptions* out) { |
| 107 | int longOptFlag; |
| 108 | std::unique_ptr<struct option[]> longOptions = getLongOptions(options, &longOptFlag); |
| 109 | std::string shortOptions = getShortOptions(options); |
| 110 | int optionIndex; |
| 111 | for (;;) { |
| 112 | int c = getopt_long(argc, argv, shortOptions.c_str(), longOptions.get(), &optionIndex); |
| 113 | if (c == -1) { |
| 114 | break; |
| 115 | } |
| 116 | const Option* found = nullptr; |
| 117 | for (size_t i = 0; i < options.size(); ++i) |
| 118 | if ((c == 0 && longOptFlag == static_cast<int>(i)) || |
| 119 | (c != 0 && c == options[i].shortOption)) |
| 120 | |
| 121 | found = &options[i]; |
| 122 | |
| 123 | if (found == nullptr) { |
| 124 | // see unrecognized options |
| 125 | std::cerr << "unrecognized option `" << argv[optind - 1] << "'" << std::endl; |
| 126 | return USAGE; |
| 127 | } |
| 128 | |
| 129 | Status status = found->op(out); |
| 130 | if (status != OK) return status; |
| 131 | } |
| 132 | if (optind < argc) { |
| 133 | // see non option |
| 134 | std::cerr << "unrecognized option `" << argv[optind] << "'" << std::endl; |
| 135 | return USAGE; |
| 136 | } |
| 137 | return OK; |
| 138 | } |
| 139 | |
| 140 | void usage(char* me, const std::vector<Option>& options) { |
| 141 | std::cerr << me << ": dump VINTF metadata via libvintf." << std::endl; |
| 142 | for (const auto& e : options) { |
| 143 | if (e.help.empty()) continue; |
| 144 | std::cerr << " "; |
| 145 | if (e.shortOption != '\0') std::cerr << "-" << e.shortOption; |
| 146 | if (e.shortOption != '\0' && !e.longOption.empty()) std::cerr << ", "; |
| 147 | if (!e.longOption.empty()) std::cerr << "--" << e.longOption; |
| 148 | std::cerr << ": " |
| 149 | << android::base::Join(android::base::Split(e.help, "\n"), "\n ") |
| 150 | << std::endl; |
| 151 | } |
| 152 | } |
| 153 | |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 154 | struct TableRow { |
| 155 | // Whether the HAL version is in device manifest, framework manifest, device compatibility |
| 156 | // matrix, framework compatibility matrix, respectively. |
| 157 | bool dm = false; |
| 158 | bool fm = false; |
| 159 | bool dcm = false; |
| 160 | bool fcm = false; |
| 161 | // If the HAL version is in device / framework compatibility matrix, whether it is required |
| 162 | // or not. |
| 163 | bool required = false; |
| 164 | |
| 165 | // Return true if: |
| 166 | // - not a required HAL version; OR |
| 167 | // - required in device matrix and framework manifest; |
| 168 | // - required in framework matrix and device manifest. |
| 169 | bool meetsReqeuirement() const { |
| 170 | if (!required) return true; |
| 171 | if (dcm && !fm) return false; |
| 172 | if (fcm && !dm) return false; |
| 173 | return true; |
| 174 | } |
| 175 | }; |
| 176 | |
| 177 | std::ostream& operator<<(std::ostream& out, const TableRow& row) { |
| 178 | return out << (row.required ? "R" : " ") << (row.meetsReqeuirement() ? " " : "!") |
| 179 | << kColumnSeperator << (row.dm ? "DM" : " ") << kColumnSeperator |
| 180 | << (row.fm ? "FM" : " ") << kColumnSeperator << (row.fcm ? "FCM" : " ") |
| 181 | << kColumnSeperator << (row.dcm ? "DCM" : " "); |
| 182 | } |
| 183 | |
| 184 | using RowMutator = std::function<void(TableRow*)>; |
| 185 | using Table = std::map<std::string, TableRow>; |
| 186 | |
| 187 | // Insert each fqInstanceName foo@x.y::IFoo/instance to the table by inserting the key |
| 188 | // if it does not exist and setting the corresponding indicator (as specified by "mutate"). |
| 189 | void insert(const HalManifest* manifest, Table* table, const RowMutator& mutate) { |
| 190 | if (manifest == nullptr) return; |
Yifan Hong | 2a90ffe | 2018-03-05 17:45:34 -0800 | [diff] [blame] | 191 | manifest->forEachInstance([&](const auto& manifestInstance) { |
Yifan Hong | 25e34dc | 2019-09-11 12:50:24 -0700 | [diff] [blame] | 192 | std::string key = manifestInstance.description(); |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 193 | mutate(&(*table)[key]); |
Yifan Hong | 2a90ffe | 2018-03-05 17:45:34 -0800 | [diff] [blame] | 194 | return true; |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 195 | }); |
| 196 | } |
| 197 | |
| 198 | void insert(const CompatibilityMatrix* matrix, Table* table, const RowMutator& mutate) { |
| 199 | if (matrix == nullptr) return; |
Yifan Hong | 2a90ffe | 2018-03-05 17:45:34 -0800 | [diff] [blame] | 200 | matrix->forEachInstance([&](const auto& matrixInstance) { |
Yifan Hong | 2a90ffe | 2018-03-05 17:45:34 -0800 | [diff] [blame] | 201 | for (auto minorVer = matrixInstance.versionRange().minMinor; |
Yifan Hong | 25e34dc | 2019-09-11 12:50:24 -0700 | [diff] [blame] | 202 | minorVer >= matrixInstance.versionRange().minMinor && |
| 203 | minorVer <= matrixInstance.versionRange().maxMinor; |
| 204 | ++minorVer) { |
| 205 | Version version{matrixInstance.versionRange().majorVer, minorVer}; |
| 206 | std::string key = matrixInstance.description(version); |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 207 | auto it = table->find(key); |
| 208 | if (it == table->end()) { |
Yifan Hong | 0ecc992 | 2018-03-06 16:09:35 -0800 | [diff] [blame] | 209 | mutate(&(*table)[key]); |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 210 | } else { |
| 211 | mutate(&it->second); |
Yifan Hong | 0ecc992 | 2018-03-06 16:09:35 -0800 | [diff] [blame] | 212 | if (minorVer == matrixInstance.versionRange().minMinor) { |
| 213 | it->second.required = !matrixInstance.optional(); |
| 214 | } |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 215 | } |
| 216 | } |
Yifan Hong | 2a90ffe | 2018-03-05 17:45:34 -0800 | [diff] [blame] | 217 | return true; |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 218 | }); |
| 219 | } |
| 220 | |
| 221 | Table generateHalSummary(const HalManifest* vm, const HalManifest* fm, |
| 222 | const CompatibilityMatrix* vcm, const CompatibilityMatrix* fcm) { |
| 223 | Table table; |
| 224 | insert(vm, &table, [](auto* row) { row->dm = true; }); |
| 225 | insert(fm, &table, [](auto* row) { row->fm = true; }); |
| 226 | insert(vcm, &table, [](auto* row) { row->dcm = true; }); |
| 227 | insert(fcm, &table, [](auto* row) { row->fcm = true; }); |
| 228 | |
| 229 | return table; |
| 230 | } |
| 231 | |
| 232 | static const std::vector<Option> gAvailableOptions{ |
| 233 | {'h', "help", "Print help message.", [](auto) { return USAGE; }}, |
| 234 | {'v', "verbose", "Dump detailed and raw content, including kernel configurations", [](auto o) { |
| 235 | o->verbose = true; |
| 236 | return OK; |
| 237 | }}}; |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 238 | // A convenience binary to dump information available through libvintf. |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 239 | int main(int argc, char** argv) { |
| 240 | ParsedOptions options; |
| 241 | Status status = parseOptions(argc, argv, gAvailableOptions, &options); |
| 242 | if (status == USAGE) usage(argv[0], gAvailableOptions); |
| 243 | if (status != OK) return status; |
Yifan Hong | 676447a | 2016-11-15 12:57:23 -0800 | [diff] [blame] | 244 | |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 245 | auto vm = VintfObject::GetDeviceHalManifest(); |
| 246 | auto fm = VintfObject::GetFrameworkHalManifest(); |
| 247 | auto vcm = VintfObject::GetDeviceCompatibilityMatrix(); |
| 248 | auto fcm = VintfObject::GetFrameworkCompatibilityMatrix(); |
| 249 | auto ki = VintfObject::GetRuntimeInfo(); |
| 250 | |
| 251 | if (!options.verbose) { |
| 252 | std::cout << "======== HALs =========" << std::endl |
| 253 | << "R: required. (empty): optional or missing from matrices. " |
| 254 | << "!: required and not in manifest." << std::endl |
| 255 | << "DM: device manifest. FM: framework manifest." << std::endl |
| 256 | << "FCM: framework compatibility matrix. DCM: device compatibility matrix." |
| 257 | << std::endl |
| 258 | << std::endl; |
| 259 | auto table = generateHalSummary(vm.get(), fm.get(), vcm.get(), fcm.get()); |
| 260 | |
| 261 | for (const auto& pair : table) |
| 262 | std::cout << pair.second << kColumnSeperator << pair.first << std::endl; |
| 263 | |
| 264 | std::cout << std::endl; |
| 265 | } |
| 266 | |
Yifan Hong | ba588bc | 2018-08-08 14:19:41 -0700 | [diff] [blame] | 267 | SerializeFlags::Type flags = SerializeFlags::EVERYTHING; |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 268 | if (!options.verbose) { |
Yifan Hong | a1b112d | 2018-08-07 17:44:10 -0700 | [diff] [blame] | 269 | flags = flags.disableHals().disableKernel(); |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 270 | } |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 271 | std::cout << "======== Device HAL Manifest =========" << std::endl; |
Yifan Hong | 25e2a77 | 2021-04-16 18:34:28 -0700 | [diff] [blame] | 272 | if (vm != nullptr) std::cout << toXml(*vm, flags); |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 273 | std::cout << "======== Framework HAL Manifest =========" << std::endl; |
Yifan Hong | 25e2a77 | 2021-04-16 18:34:28 -0700 | [diff] [blame] | 274 | if (fm != nullptr) std::cout << toXml(*fm, flags); |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 275 | std::cout << "======== Device Compatibility Matrix =========" << std::endl; |
Yifan Hong | 25e2a77 | 2021-04-16 18:34:28 -0700 | [diff] [blame] | 276 | if (vcm != nullptr) std::cout << toXml(*vcm, flags); |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 277 | std::cout << "======== Framework Compatibility Matrix =========" << std::endl; |
Yifan Hong | 25e2a77 | 2021-04-16 18:34:28 -0700 | [diff] [blame] | 278 | if (fcm != nullptr) std::cout << toXml(*fcm, flags); |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 279 | |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 280 | std::cout << "======== Runtime Info =========" << std::endl; |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 281 | if (ki != nullptr) std::cout << dump(*ki, options.verbose); |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 282 | |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 283 | std::cout << std::endl; |
| 284 | |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame] | 285 | std::cout << "======== Summary =========" << std::endl; |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 286 | std::cout << "Device Manifest? " << existString(vm != nullptr) << std::endl |
| 287 | << "Device Matrix? " << existString(vcm != nullptr) << std::endl |
| 288 | << "Framework Manifest? " << existString(fm != nullptr) << std::endl |
| 289 | << "Framework Matrix? " << existString(fcm != nullptr) << std::endl; |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 290 | std::string error; |
| 291 | if (vm && fcm) { |
| 292 | bool compatible = vm->checkCompatibility(*fcm, &error); |
| 293 | std::cout << "Device HAL Manifest <==> Framework Compatibility Matrix? " |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 294 | << boolCompatString(compatible); |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 295 | if (!compatible) |
| 296 | std::cout << ", " << error; |
| 297 | std::cout << std::endl; |
| 298 | } |
| 299 | if (fm && vcm) { |
| 300 | bool compatible = fm->checkCompatibility(*vcm, &error); |
| 301 | std::cout << "Framework HAL Manifest <==> Device Compatibility Matrix? " |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 302 | << boolCompatString(compatible); |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 303 | if (!compatible) |
| 304 | std::cout << ", " << error; |
| 305 | std::cout << std::endl; |
| 306 | } |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 307 | if (ki && fcm) { |
| 308 | bool compatible = ki->checkCompatibility(*fcm, &error); |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 309 | std::cout << "Runtime info <==> Framework Compatibility Matrix? " |
| 310 | << boolCompatString(compatible); |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 311 | if (!compatible) std::cout << ", " << error; |
| 312 | std::cout << std::endl; |
| 313 | } |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 314 | |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 315 | { |
Yifan Hong | 85e589e | 2019-12-18 13:12:29 -0800 | [diff] [blame] | 316 | auto compatible = VintfObject::GetInstance()->checkCompatibility(&error); |
| 317 | std::cout << "VintfObject::checkCompatibility? " |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 318 | << compatibleString(compatible); |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 319 | if (compatible != COMPATIBLE) std::cout << ", " << error; |
| 320 | std::cout << std::endl; |
| 321 | } |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 322 | |
| 323 | if (vm && fcm) { |
Yifan Hong | 03ea428 | 2020-03-17 17:58:35 -0700 | [diff] [blame] | 324 | // TODO(b/131717099): Use correct information from libhidlmetadata |
| 325 | auto deprecate = VintfObject::GetInstance()->checkDeprecation({}, &error); |
| 326 | std::cout << "VintfObject::CheckDeprecation (against device manifest) (w/o hidlmetadata)? " |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 327 | << deprecateString(deprecate); |
| 328 | if (deprecate != NO_DEPRECATED_HALS) std::cout << ", " << error; |
| 329 | std::cout << std::endl; |
| 330 | } |
Yifan Hong | 676447a | 2016-11-15 12:57:23 -0800 | [diff] [blame] | 331 | } |