check_vintf.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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. #include <getopt.h>
  17. #include <unistd.h>
  18. #include <iostream>
  19. #include <map>
  20. #include <android-base/parseint.h>
  21. #include <utils/Errors.h>
  22. #include <vintf/VintfObject.h>
  23. #include <vintf/parse_xml.h>
  24. #include "utils.h"
  25. namespace android {
  26. namespace vintf {
  27. namespace details {
  28. // fake sysprops
  29. using Properties = std::map<std::string, std::string>;
  30. enum Option : int {
  31. DUMP_FILE_LIST = 1,
  32. ROOTDIR,
  33. HELP,
  34. PROPERTY,
  35. CHECK_COMPAT,
  36. };
  37. // command line arguments
  38. using Args = std::multimap<Option, std::string>;
  39. class HostFileSystem : public FileSystemUnderPath {
  40. public:
  41. HostFileSystem(const std::string& rootdir) : FileSystemUnderPath(rootdir) {}
  42. status_t fetch(const std::string& path, std::string* fetched,
  43. std::string* error) const override {
  44. status_t status = FileSystemUnderPath::fetch(path, fetched, error);
  45. std::cerr << "Debug: Fetch '" << getRootDir() << path << "': " << toString(status)
  46. << std::endl;
  47. return status;
  48. }
  49. status_t listFiles(const std::string& path, std::vector<std::string>* out,
  50. std::string* error) const override {
  51. status_t status = FileSystemUnderPath::listFiles(path, out, error);
  52. std::cerr << "Debug: List '" << getRootDir() << path << "': " << toString(status)
  53. << std::endl;
  54. return status;
  55. }
  56. private:
  57. static std::string toString(status_t status) {
  58. return status == OK ? "SUCCESS" : strerror(-status);
  59. }
  60. };
  61. class PresetPropertyFetcher : public PropertyFetcher {
  62. public:
  63. std::string getProperty(const std::string& key,
  64. const std::string& defaultValue) const override {
  65. auto it = mProps.find(key);
  66. if (it == mProps.end()) {
  67. std::cerr << "Debug: Sysprop " << key << " is missing, default to '" << defaultValue
  68. << "'" << std::endl;
  69. return defaultValue;
  70. }
  71. std::cerr << "Debug: Sysprop " << key << "=" << it->second << std::endl;
  72. return it->second;
  73. }
  74. uint64_t getUintProperty(const std::string& key, uint64_t defaultValue,
  75. uint64_t max) const override {
  76. uint64_t result;
  77. std::string value = getProperty(key, "");
  78. if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
  79. return defaultValue;
  80. }
  81. bool getBoolProperty(const std::string& key, bool defaultValue) const override {
  82. std::string value = getProperty(key, "");
  83. if (value == "1" || value == "true") {
  84. return true;
  85. } else if (value == "0" || value == "false") {
  86. return false;
  87. }
  88. return defaultValue;
  89. }
  90. void setProperties(const Properties& props) { mProps.insert(props.begin(), props.end()); }
  91. private:
  92. std::map<std::string, std::string> mProps;
  93. };
  94. // helper functions
  95. template <typename T>
  96. std::unique_ptr<T> readObject(FileSystem* fileSystem, const std::string& path,
  97. const XmlConverter<T>& converter) {
  98. std::string xml;
  99. std::string error;
  100. status_t err = fileSystem->fetch(path, &xml, &error);
  101. if (err != OK) {
  102. std::cerr << "Error: Cannot read '" << path << "' (" << strerror(-err) << "): " << error
  103. << std::endl;
  104. return nullptr;
  105. }
  106. auto ret = std::make_unique<T>();
  107. if (!converter(ret.get(), xml, &error)) {
  108. std::cerr << "Error: Cannot parse '" << path << "': " << error << std::endl;
  109. return nullptr;
  110. }
  111. return ret;
  112. }
  113. int checkCompatibilityForFiles(const std::string& manifestPath, const std::string& matrixPath) {
  114. auto fileSystem = std::make_unique<FileSystemImpl>();
  115. auto manifest = readObject(fileSystem.get(), manifestPath, gHalManifestConverter);
  116. auto matrix = readObject(fileSystem.get(), matrixPath, gCompatibilityMatrixConverter);
  117. if (manifest == nullptr || matrix == nullptr) {
  118. return -1;
  119. }
  120. std::string error;
  121. if (!manifest->checkCompatibility(*matrix, &error)) {
  122. std::cerr << "Error: Incompatible: " << error << std::endl;
  123. std::cout << "false" << std::endl;
  124. return 1;
  125. }
  126. std::cout << "true" << std::endl;
  127. return 0;
  128. }
  129. Args parseArgs(int argc, char** argv) {
  130. int longOptFlag;
  131. int optionIndex;
  132. Args ret;
  133. std::vector<struct option> longopts{
  134. {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST},
  135. {"rootdir", required_argument, &longOptFlag, ROOTDIR},
  136. {"help", no_argument, &longOptFlag, HELP},
  137. {"property", required_argument, &longOptFlag, PROPERTY},
  138. {"check-compat", no_argument, &longOptFlag, CHECK_COMPAT},
  139. {0, 0, 0, 0}};
  140. std::map<int, Option> shortopts{
  141. {'h', HELP}, {'D', PROPERTY}, {'c', CHECK_COMPAT},
  142. };
  143. for (;;) {
  144. int c = getopt_long(argc, argv, "hcD:", longopts.data(), &optionIndex);
  145. if (c == -1) {
  146. break;
  147. }
  148. std::string argValue = optarg ? optarg : std::string{};
  149. if (c == 0) {
  150. ret.emplace(static_cast<Option>(longOptFlag), std::move(argValue));
  151. } else {
  152. ret.emplace(shortopts[c], std::move(argValue));
  153. }
  154. }
  155. if (optind < argc) {
  156. // see non option
  157. std::cerr << "unrecognized option `" << argv[optind] << "'" << std::endl;
  158. return {{HELP, ""}};
  159. }
  160. return ret;
  161. }
  162. template <typename T>
  163. Properties getProperties(const T& args) {
  164. Properties ret;
  165. for (const auto& arg : args) {
  166. auto pos = arg.find('=');
  167. auto key = arg.substr(0, pos);
  168. auto value = pos == std::string::npos ? std::string{} : arg.substr(pos + 1);
  169. ret[key] = value;
  170. }
  171. return ret;
  172. }
  173. int usage(const char* me) {
  174. std::cerr
  175. << me << ": check VINTF metadata." << std::endl
  176. << " Options:" << std::endl
  177. << " --dump-file-list: Dump a list of directories / files on device" << std::endl
  178. << " that is required to be used by --check-compat." << std::endl
  179. << " -c, --check-compat: check compatibility for files under the root" << std::endl
  180. << " directory specified by --root-dir." << std::endl
  181. << " --rootdir=<dir>: specify root directory for all metadata." << std::endl
  182. << " -D, --property <key>=<value>: specify sysprops." << std::endl
  183. << " --help: show this message." << std::endl
  184. << std::endl
  185. << " Example:" << std::endl
  186. << " # Get the list of required files." << std::endl
  187. << " " << me << " --dump-file-list > /tmp/files.txt" << std::endl
  188. << " # Pull from ADB, or use your own command to extract files from images"
  189. << std::endl
  190. << " ROOTDIR=/tmp/device/" << std::endl
  191. << " cat /tmp/files.txt | xargs -I{} bash -c \"mkdir -p $ROOTDIR`dirname {}` && adb "
  192. "pull {} $ROOTDIR{}\""
  193. << std::endl
  194. << " # Check compatibility." << std::endl
  195. << " " << me << " --check-compat --rootdir=$ROOTDIR \\" << std::endl
  196. << " --property ro.product.first_api_level=`adb shell getprop "
  197. "ro.product.first_api_level` \\"
  198. << std::endl
  199. << " --property ro.boot.product.hardware.sku=`adb shell getprop "
  200. "ro.boot.product.hardware.sku`"
  201. << std::endl;
  202. return 1;
  203. }
  204. int checkAllFiles(const std::string& rootdir, const Properties& props, std::string* error) {
  205. auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
  206. hostPropertyFetcher->setProperties(props);
  207. auto vintfObject = VintfObject::Builder()
  208. .setFileSystem(std::make_unique<HostFileSystem>(rootdir))
  209. .setPropertyFetcher(std::move(hostPropertyFetcher))
  210. .build();
  211. return vintfObject->checkCompatibility(error, CheckFlags::DISABLE_RUNTIME_INFO);
  212. }
  213. } // namespace details
  214. } // namespace vintf
  215. } // namespace android
  216. int main(int argc, char** argv) {
  217. using namespace android::vintf;
  218. using namespace android::vintf::details;
  219. // legacy usage: check_vintf <manifest.xml> <matrix.xml>
  220. if (argc == 3) {
  221. int ret = checkCompatibilityForFiles(argv[1], argv[2]);
  222. if (ret >= 0) return ret;
  223. }
  224. Args args = parseArgs(argc, argv);
  225. if (!iterateValues(args, HELP).empty()) {
  226. return usage(argv[0]);
  227. }
  228. if (!iterateValues(args, DUMP_FILE_LIST).empty()) {
  229. for (const auto& file : dumpFileList()) {
  230. std::cout << file << std::endl;
  231. }
  232. return 0;
  233. }
  234. auto rootdirs = iterateValues(args, ROOTDIR);
  235. auto properties = getProperties(iterateValues(args, PROPERTY));
  236. auto checkCompat = iterateValues(args, CHECK_COMPAT);
  237. if (!checkCompat.empty()) {
  238. if (rootdirs.empty()) {
  239. std::cerr << "Missing --rootdir option." << std::endl;
  240. return usage(argv[0]);
  241. }
  242. int ret = COMPATIBLE;
  243. for (const auto& rootdir : rootdirs) {
  244. std::cerr << "Debug: checking files under " << rootdir << "..." << std::endl;
  245. std::string error;
  246. int compat = checkAllFiles(rootdir, properties, &error);
  247. std::cerr << "Debug: files under " << rootdir
  248. << (compat == COMPATIBLE
  249. ? " is compatible"
  250. : compat == INCOMPATIBLE ? " are incompatible"
  251. : (" has encountered an error: " + error))
  252. << std::endl;
  253. }
  254. if (ret == COMPATIBLE) {
  255. std::cout << "true" << std::endl;
  256. }
  257. return ret;
  258. }
  259. return usage(argv[0]);
  260. }