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 | |
| 154 | // android.hardware.foo@1.0-1::IFoo/default. |
| 155 | // Note that the format is extended to support a range of versions. |
| 156 | std::string fqInstanceName(const std::string& package, const VersionRange& range, |
| 157 | const std::string& interface, const std::string& instance) { |
| 158 | std::stringstream ss; |
| 159 | ss << package << "@" << range << "::" << interface << "/" << instance; |
| 160 | return ss.str(); |
| 161 | } |
| 162 | |
| 163 | struct TableRow { |
| 164 | // Whether the HAL version is in device manifest, framework manifest, device compatibility |
| 165 | // matrix, framework compatibility matrix, respectively. |
| 166 | bool dm = false; |
| 167 | bool fm = false; |
| 168 | bool dcm = false; |
| 169 | bool fcm = false; |
| 170 | // If the HAL version is in device / framework compatibility matrix, whether it is required |
| 171 | // or not. |
| 172 | bool required = false; |
| 173 | |
| 174 | // Return true if: |
| 175 | // - not a required HAL version; OR |
| 176 | // - required in device matrix and framework manifest; |
| 177 | // - required in framework matrix and device manifest. |
| 178 | bool meetsReqeuirement() const { |
| 179 | if (!required) return true; |
| 180 | if (dcm && !fm) return false; |
| 181 | if (fcm && !dm) return false; |
| 182 | return true; |
| 183 | } |
| 184 | }; |
| 185 | |
| 186 | std::ostream& operator<<(std::ostream& out, const TableRow& row) { |
| 187 | return out << (row.required ? "R" : " ") << (row.meetsReqeuirement() ? " " : "!") |
| 188 | << kColumnSeperator << (row.dm ? "DM" : " ") << kColumnSeperator |
| 189 | << (row.fm ? "FM" : " ") << kColumnSeperator << (row.fcm ? "FCM" : " ") |
| 190 | << kColumnSeperator << (row.dcm ? "DCM" : " "); |
| 191 | } |
| 192 | |
| 193 | using RowMutator = std::function<void(TableRow*)>; |
| 194 | using Table = std::map<std::string, TableRow>; |
| 195 | |
| 196 | // Insert each fqInstanceName foo@x.y::IFoo/instance to the table by inserting the key |
| 197 | // if it does not exist and setting the corresponding indicator (as specified by "mutate"). |
| 198 | void insert(const HalManifest* manifest, Table* table, const RowMutator& mutate) { |
| 199 | if (manifest == nullptr) return; |
| 200 | manifest->forEachInstance([&](const auto& package, const auto& version, const auto& interface, |
| 201 | const auto& instance, bool* /* stop */) { |
| 202 | std::string key = fqInstanceName(package, VersionRange{version.majorVer, version.minorVer}, |
| 203 | interface, instance); |
| 204 | mutate(&(*table)[key]); |
| 205 | }); |
| 206 | } |
| 207 | |
| 208 | void insert(const CompatibilityMatrix* matrix, Table* table, const RowMutator& mutate) { |
| 209 | if (matrix == nullptr) return; |
| 210 | matrix->forEachInstance([&](const auto& package, const auto& range, const auto& interface, |
| 211 | const auto& instance, bool optional, bool* /* stop */) { |
| 212 | bool missed = false; |
| 213 | for (auto minorVer = range.minMinor; minorVer <= range.maxMinor; ++minorVer) { |
| 214 | std::string key = fqInstanceName(package, VersionRange{range.majorVer, minorVer}, |
| 215 | interface, instance); |
| 216 | auto it = table->find(key); |
| 217 | if (it == table->end()) { |
| 218 | missed = true; |
| 219 | } else { |
| 220 | mutate(&it->second); |
| 221 | it->second.required = !optional; |
| 222 | } |
| 223 | } |
| 224 | if (missed) { |
| 225 | std::string key = fqInstanceName(package, range, interface, instance); |
| 226 | mutate(&(*table)[key]); |
| 227 | } |
| 228 | }); |
| 229 | } |
| 230 | |
| 231 | Table generateHalSummary(const HalManifest* vm, const HalManifest* fm, |
| 232 | const CompatibilityMatrix* vcm, const CompatibilityMatrix* fcm) { |
| 233 | Table table; |
| 234 | insert(vm, &table, [](auto* row) { row->dm = true; }); |
| 235 | insert(fm, &table, [](auto* row) { row->fm = true; }); |
| 236 | insert(vcm, &table, [](auto* row) { row->dcm = true; }); |
| 237 | insert(fcm, &table, [](auto* row) { row->fcm = true; }); |
| 238 | |
| 239 | return table; |
| 240 | } |
| 241 | |
| 242 | static const std::vector<Option> gAvailableOptions{ |
| 243 | {'h', "help", "Print help message.", [](auto) { return USAGE; }}, |
| 244 | {'v', "verbose", "Dump detailed and raw content, including kernel configurations", [](auto o) { |
| 245 | o->verbose = true; |
| 246 | return OK; |
| 247 | }}}; |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 248 | // A convenience binary to dump information available through libvintf. |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame^] | 249 | int main(int argc, char** argv) { |
| 250 | ParsedOptions options; |
| 251 | Status status = parseOptions(argc, argv, gAvailableOptions, &options); |
| 252 | if (status == USAGE) usage(argv[0], gAvailableOptions); |
| 253 | if (status != OK) return status; |
Yifan Hong | 676447a | 2016-11-15 12:57:23 -0800 | [diff] [blame] | 254 | |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame^] | 255 | auto vm = VintfObject::GetDeviceHalManifest(); |
| 256 | auto fm = VintfObject::GetFrameworkHalManifest(); |
| 257 | auto vcm = VintfObject::GetDeviceCompatibilityMatrix(); |
| 258 | auto fcm = VintfObject::GetFrameworkCompatibilityMatrix(); |
| 259 | auto ki = VintfObject::GetRuntimeInfo(); |
| 260 | |
| 261 | if (!options.verbose) { |
| 262 | std::cout << "======== HALs =========" << std::endl |
| 263 | << "R: required. (empty): optional or missing from matrices. " |
| 264 | << "!: required and not in manifest." << std::endl |
| 265 | << "DM: device manifest. FM: framework manifest." << std::endl |
| 266 | << "FCM: framework compatibility matrix. DCM: device compatibility matrix." |
| 267 | << std::endl |
| 268 | << std::endl; |
| 269 | auto table = generateHalSummary(vm.get(), fm.get(), vcm.get(), fcm.get()); |
| 270 | |
| 271 | for (const auto& pair : table) |
| 272 | std::cout << pair.second << kColumnSeperator << pair.first << std::endl; |
| 273 | |
| 274 | std::cout << std::endl; |
| 275 | } |
| 276 | |
| 277 | SerializeFlags flags = SerializeFlag::EVERYTHING; |
| 278 | if (!options.verbose) { |
| 279 | flags |= SerializeFlag::NO_HALS; |
| 280 | flags |= SerializeFlag::NO_KERNEL; |
| 281 | } |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 282 | std::cout << "======== Device HAL Manifest =========" << std::endl; |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame^] | 283 | if (vm != nullptr) std::cout << gHalManifestConverter(*vm, flags); |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 284 | std::cout << "======== Framework HAL Manifest =========" << std::endl; |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame^] | 285 | if (fm != nullptr) std::cout << gHalManifestConverter(*fm, flags); |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 286 | std::cout << "======== Device Compatibility Matrix =========" << std::endl; |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame^] | 287 | if (vcm != nullptr) std::cout << gCompatibilityMatrixConverter(*vcm, flags); |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 288 | std::cout << "======== Framework Compatibility Matrix =========" << std::endl; |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame^] | 289 | if (fcm != nullptr) std::cout << gCompatibilityMatrixConverter(*fcm, flags); |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 290 | |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 291 | std::cout << "======== Runtime Info =========" << std::endl; |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame^] | 292 | if (ki != nullptr) std::cout << dump(*ki, options.verbose); |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 293 | |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 294 | std::cout << std::endl; |
| 295 | |
Yifan Hong | e3a9234 | 2018-01-25 17:00:16 -0800 | [diff] [blame^] | 296 | std::cout << "======== Summary =========" << std::endl; |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 297 | std::cout << "Device Manifest? " << existString(vm != nullptr) << std::endl |
| 298 | << "Device Matrix? " << existString(vcm != nullptr) << std::endl |
| 299 | << "Framework Manifest? " << existString(fm != nullptr) << std::endl |
| 300 | << "Framework Matrix? " << existString(fcm != nullptr) << std::endl; |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 301 | std::string error; |
| 302 | if (vm && fcm) { |
| 303 | bool compatible = vm->checkCompatibility(*fcm, &error); |
| 304 | std::cout << "Device HAL Manifest <==> Framework Compatibility Matrix? " |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 305 | << boolCompatString(compatible); |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 306 | if (!compatible) |
| 307 | std::cout << ", " << error; |
| 308 | std::cout << std::endl; |
| 309 | } |
| 310 | if (fm && vcm) { |
| 311 | bool compatible = fm->checkCompatibility(*vcm, &error); |
| 312 | std::cout << "Framework HAL Manifest <==> Device Compatibility Matrix? " |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 313 | << boolCompatString(compatible); |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 314 | if (!compatible) |
| 315 | std::cout << ", " << error; |
| 316 | std::cout << std::endl; |
| 317 | } |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 318 | if (ki && fcm) { |
| 319 | bool compatible = ki->checkCompatibility(*fcm, &error); |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 320 | std::cout << "Runtime info <==> Framework Compatibility Matrix? " |
| 321 | << boolCompatString(compatible); |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 322 | if (!compatible) std::cout << ", " << error; |
| 323 | std::cout << std::endl; |
| 324 | } |
Yifan Hong | 1e5a054 | 2017-04-28 14:37:56 -0700 | [diff] [blame] | 325 | |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 326 | { |
| 327 | auto compatible = VintfObject::CheckCompatibility({}, &error); |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 328 | std::cout << "VintfObject::CheckCompatibility? " |
| 329 | << compatibleString(compatible); |
Yifan Hong | 3f835d6 | 2017-05-16 13:10:11 -0700 | [diff] [blame] | 330 | if (compatible != COMPATIBLE) std::cout << ", " << error; |
| 331 | std::cout << std::endl; |
| 332 | } |
Yifan Hong | 3eed7e1 | 2018-01-25 11:22:52 -0800 | [diff] [blame] | 333 | |
| 334 | if (vm && fcm) { |
| 335 | auto deprecate = VintfObject::CheckDeprecation(&error); |
| 336 | std::cout << "VintfObject::CheckDeprecation (against device manifest)? " |
| 337 | << deprecateString(deprecate); |
| 338 | if (deprecate != NO_DEPRECATED_HALS) std::cout << ", " << error; |
| 339 | std::cout << std::endl; |
| 340 | } |
Yifan Hong | 676447a | 2016-11-15 12:57:23 -0800 | [diff] [blame] | 341 | } |