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 17 #include <getopt.h> 18 19 #include <android-base/strings.h> 20 #include <vintf/VintfObject.h> 21 #include <vintf/parse_string.h> 22 #include <vintf/parse_xml.h> 23 #include <iomanip> 24 #include <iostream> 25 #include <string> 26 #include <vector> 27 28 using namespace ::android::vintf; 29 30 static const std::string kColumnSeperator = " "; 31 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 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 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; 191 manifest->forEachInstance([&](const auto& manifestInstance) { 192 std::string key = manifestInstance.description(); 193 mutate(&(*table)[key]); 194 return true; 195 }); 196 } 197 198 void insert(const CompatibilityMatrix* matrix, Table* table, const RowMutator& mutate) { 199 if (matrix == nullptr) return; 200 matrix->forEachInstance([&](const auto& matrixInstance) { 201 for (auto minorVer = matrixInstance.versionRange().minMinor; 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); 207 auto it = table->find(key); 208 if (it == table->end()) { 209 mutate(&(*table)[key]); 210 } else { 211 mutate(&it->second); 212 if (minorVer == matrixInstance.versionRange().minMinor) { 213 it->second.required = !matrixInstance.optional(); 214 } 215 } 216 } 217 return true; 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 }}}; 238 // A convenience binary to dump information available through libvintf. 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; 244 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 267 SerializeFlags::Type flags = SerializeFlags::EVERYTHING; 268 if (!options.verbose) { 269 flags = flags.disableHals().disableKernel(); 270 } 271 std::cout << "======== Device HAL Manifest =========" << std::endl; 272 if (vm != nullptr) std::cout << toXml(*vm, flags); 273 std::cout << "======== Framework HAL Manifest =========" << std::endl; 274 if (fm != nullptr) std::cout << toXml(*fm, flags); 275 std::cout << "======== Device Compatibility Matrix =========" << std::endl; 276 if (vcm != nullptr) std::cout << toXml(*vcm, flags); 277 std::cout << "======== Framework Compatibility Matrix =========" << std::endl; 278 if (fcm != nullptr) std::cout << toXml(*fcm, flags); 279 280 std::cout << "======== Runtime Info =========" << std::endl; 281 if (ki != nullptr) std::cout << dump(*ki, options.verbose); 282 283 std::cout << std::endl; 284 285 std::cout << "======== Summary =========" << std::endl; 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; 290 std::string error; 291 if (vm && fcm) { 292 bool compatible = vm->checkCompatibility(*fcm, &error); 293 std::cout << "Device HAL Manifest <==> Framework Compatibility Matrix? " 294 << boolCompatString(compatible); 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? " 302 << boolCompatString(compatible); 303 if (!compatible) 304 std::cout << ", " << error; 305 std::cout << std::endl; 306 } 307 if (ki && fcm) { 308 bool compatible = ki->checkCompatibility(*fcm, &error); 309 std::cout << "Runtime info <==> Framework Compatibility Matrix? " 310 << boolCompatString(compatible); 311 if (!compatible) std::cout << ", " << error; 312 std::cout << std::endl; 313 } 314 315 { 316 auto compatible = VintfObject::GetInstance()->checkCompatibility(&error); 317 std::cout << "VintfObject::checkCompatibility? " 318 << compatibleString(compatible); 319 if (compatible != COMPATIBLE) std::cout << ", " << error; 320 std::cout << std::endl; 321 } 322 323 if (vm && fcm) { 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)? " 327 << deprecateString(deprecate); 328 if (deprecate != NO_DEPRECATED_HALS) std::cout << ", " << error; 329 std::cout << std::endl; 330 } 331 } 332