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 #include <sysexits.h>
19 #include <unistd.h>
20 
21 #include <algorithm>
22 #include <functional>
23 #include <iostream>
24 #include <map>
25 #include <optional>
26 
27 #include <aidl/metadata.h>
28 #include <android-base/file.h>
29 #include <android-base/logging.h>
30 #include <android-base/parseint.h>
31 #include <android-base/result.h>
32 #include <android-base/strings.h>
33 #include <hidl/metadata.h>
34 #include <kver/kernel_release.h>
35 #include <utils/Errors.h>
36 #include <vintf/Dirmap.h>
37 #include <vintf/HostFileSystem.h>
38 #include <vintf/KernelConfigParser.h>
39 #include <vintf/VintfObject.h>
40 #include <vintf/fcm_exclude.h>
41 #include <vintf/parse_string.h>
42 #include <vintf/parse_xml.h>
43 #include "constants-private.h"
44 #include "utils.h"
45 
46 using android::kver::KernelRelease;
47 
48 namespace android {
49 namespace vintf {
50 namespace details {
51 
52 // fake sysprops
53 using Properties = std::map<std::string, std::string>;
54 
55 enum Option : int {
56     // Modes
57     HELP,
58     DUMP_FILE_LIST = 1,
59     CHECK_COMPAT,
60     CHECK_ONE,
61 
62     // Options
63     ROOTDIR,
64     PROPERTY,
65     DIR_MAP,
66     KERNEL,
67 };
68 // command line arguments
69 using Args = std::multimap<Option, std::string>;
70 
71 class PresetPropertyFetcher : public PropertyFetcher {
72    public:
getProperty(const std::string & key,const std::string & defaultValue) const73     std::string getProperty(const std::string& key,
74                             const std::string& defaultValue) const override {
75         auto it = mProps.find(key);
76         if (it == mProps.end()) {
77             LOG(INFO) << "Sysprop " << key << " is missing, default to '" << defaultValue << "'";
78             return defaultValue;
79         }
80         LOG(INFO) << "Sysprop " << key << "=" << it->second;
81         return it->second;
82     }
getUintProperty(const std::string & key,uint64_t defaultValue,uint64_t max) const83     uint64_t getUintProperty(const std::string& key, uint64_t defaultValue,
84                              uint64_t max) const override {
85         uint64_t result;
86         std::string value = getProperty(key, "");
87         if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
88         return defaultValue;
89     }
getBoolProperty(const std::string & key,bool defaultValue) const90     bool getBoolProperty(const std::string& key, bool defaultValue) const override {
91         std::string value = getProperty(key, "");
92         if (value == "1" || value == "true") {
93             return true;
94         } else if (value == "0" || value == "false") {
95             return false;
96         }
97         return defaultValue;
98     }
setProperties(const Properties & props)99     void setProperties(const Properties& props) { mProps.insert(props.begin(), props.end()); }
100 
101    private:
102     std::map<std::string, std::string> mProps;
103 };
104 
105 struct StaticRuntimeInfo : public RuntimeInfo {
106     KernelVersion kernelVersion;
107     Level kernelLevel = Level::UNSPECIFIED;
108     std::string kernelConfigFile;
109 
fetchAllInformationandroid::vintf::details::StaticRuntimeInfo110     status_t fetchAllInformation(FetchFlags flags) override {
111         if (flags & RuntimeInfo::FetchFlag::CPU_VERSION) {
112             mKernel.mVersion = kernelVersion;
113             LOG(INFO) << "fetched kernel version " << kernelVersion;
114         }
115         if (flags & RuntimeInfo::FetchFlag::KERNEL_FCM) {
116             mKernel.mLevel = kernelLevel;
117             LOG(INFO) << "fetched kernel level from RuntimeInfo '" << kernelLevel << "'";
118         }
119         if (flags & RuntimeInfo::FetchFlag::CONFIG_GZ) {
120             std::string content;
121             if (!android::base::ReadFileToString(kernelConfigFile, &content)) {
122                 LOG(ERROR) << "ERROR: Cannot read " << kernelConfigFile;
123                 return UNKNOWN_ERROR;
124             }
125             KernelConfigParser parser;
126             auto status = parser.processAndFinish(content);
127             if (status != OK) {
128                 return status;
129             }
130             mKernel.mConfigs = std::move(parser.configs());
131             LOG(INFO) << "read kernel configs from " << kernelConfigFile;
132         }
133         if (flags & RuntimeInfo::FetchFlag::POLICYVERS) {
134             mKernelSepolicyVersion = SIZE_MAX;
135         }
136         return OK;
137     }
138 
setIsMainlineandroid::vintf::details::StaticRuntimeInfo139     void setIsMainline(bool value) { mIsMainline = value; }
140 };
141 
142 struct StubRuntimeInfo : public RuntimeInfo {
fetchAllInformationandroid::vintf::details::StubRuntimeInfo143     status_t fetchAllInformation(FetchFlags) override { return UNKNOWN_ERROR; }
144 };
145 
146 struct StaticRuntimeInfoFactory : public ObjectFactory<RuntimeInfo> {
147     std::shared_ptr<RuntimeInfo> info;
StaticRuntimeInfoFactoryandroid::vintf::details::StaticRuntimeInfoFactory148     StaticRuntimeInfoFactory(std::shared_ptr<RuntimeInfo> i) : info(i) {}
make_sharedandroid::vintf::details::StaticRuntimeInfoFactory149     std::shared_ptr<RuntimeInfo> make_shared() const override {
150         if (info) return info;
151         return std::make_shared<StubRuntimeInfo>();
152     }
153 };
154 
155 // helper functions
156 template <typename T>
readObject(FileSystem * fileSystem,const std::string & path)157 std::unique_ptr<T> readObject(FileSystem* fileSystem, const std::string& path) {
158     std::string xml;
159     std::string error;
160     status_t err = fileSystem->fetch(path, &xml, &error);
161     if (err != OK) {
162         LOG(ERROR) << "ERROR: Cannot read '" << path << "' (" << strerror(-err) << "): " << error;
163         return nullptr;
164     }
165     auto ret = std::make_unique<T>();
166     ret->setFileName(path);
167     if (!fromXml(ret.get(), xml, &error)) {
168         LOG(ERROR) << "ERROR: Cannot parse '" << path << "': " << error;
169         return nullptr;
170     }
171     return ret;
172 }
173 
checkCompatibilityForFiles(const std::string & manifestPath,const std::string & matrixPath)174 int checkCompatibilityForFiles(const std::string& manifestPath, const std::string& matrixPath) {
175     auto fileSystem = std::make_unique<FileSystemImpl>();
176     auto manifest = readObject<HalManifest>(fileSystem.get(), manifestPath);
177     auto matrix = readObject<CompatibilityMatrix>(fileSystem.get(), matrixPath);
178     if (manifest == nullptr || matrix == nullptr) {
179         return -1;
180     }
181 
182     std::string error;
183     if (!manifest->checkCompatibility(*matrix, &error)) {
184         LOG(ERROR) << "ERROR: Incompatible: " << error;
185         std::cout << "false" << std::endl;
186         return 1;
187     }
188 
189     std::cout << "true" << std::endl;
190     return 0;
191 }
192 
parseArgs(int argc,char ** argv)193 Args parseArgs(int argc, char** argv) {
194     int longOptFlag;
195     int optionIndex;
196     Args ret;
197     std::vector<struct option> longopts{
198         // Modes
199         {"help", no_argument, &longOptFlag, HELP},
200         {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST},
201         {"check-compat", no_argument, &longOptFlag, CHECK_COMPAT},
202         {"check-one", no_argument, &longOptFlag, CHECK_ONE},
203         // Options
204         {"rootdir", required_argument, &longOptFlag, ROOTDIR},
205         {"property", required_argument, &longOptFlag, PROPERTY},
206         {"dirmap", required_argument, &longOptFlag, DIR_MAP},
207         {"kernel", required_argument, &longOptFlag, KERNEL},
208         {0, 0, 0, 0}};
209     std::map<int, Option> shortopts{
210         {'h', HELP}, {'D', PROPERTY}, {'c', CHECK_COMPAT},
211     };
212     for (;;) {
213         int c = getopt_long(argc, argv, "hcD:", longopts.data(), &optionIndex);
214         if (c == -1) {
215             break;
216         }
217         std::string argValue = optarg ? optarg : std::string{};
218         if (c == 0) {
219             ret.emplace(static_cast<Option>(longOptFlag), std::move(argValue));
220         } else {
221             ret.emplace(shortopts[c], std::move(argValue));
222         }
223     }
224     if (optind < argc) {
225         // see non option
226         LOG(ERROR) << "ERROR: unrecognized option `" << argv[optind] << "'";
227         return {{HELP, ""}};
228     }
229     return ret;
230 }
231 
232 template <typename T>
getProperties(const T & args)233 Properties getProperties(const T& args) {
234     return splitArgs(args, '=');
235 }
236 
237 // Parse a kernel version or a GKI kernel release.
parseKernelVersionOrRelease(const std::string & s,StaticRuntimeInfo * ret)238 bool parseKernelVersionOrRelease(const std::string& s, StaticRuntimeInfo* ret) {
239     // 5.4.42
240     if (parse(s, &ret->kernelVersion)) {
241         ret->kernelLevel = Level::UNSPECIFIED;
242         ret->setIsMainline(false);
243         LOG(INFO) << "\"" << s << "\" is not a mainline kernel.";
244         return true;
245     }
246     LOG(INFO) << "Cannot parse \"" << s << "\" as kernel version, parsing as GKI kernel release.";
247 
248     // 5.4.42-android12-0-something
249     auto kernelRelease = KernelRelease::Parse(s, true /* allow suffix */);
250     if (kernelRelease.has_value()) {
251         ret->kernelVersion = KernelVersion{kernelRelease->version(), kernelRelease->patch_level(),
252                                            kernelRelease->sub_level()};
253         ret->kernelLevel = RuntimeInfo::gkiAndroidReleaseToLevel(kernelRelease->android_release());
254         ret->setIsMainline(false);
255         LOG(INFO) << "\"" << s << "\" is not a mainline kernel.";
256         return true;
257     }
258     LOG(INFO) << "Cannot parse \"" << s << "\" as GKI kernel release, parsing as kernel release";
259 
260     // 5.4.42-something
261     auto pos = s.find_first_not_of("0123456789.");
262     // substr handles pos == npos case
263     if (parse(s.substr(0, pos), &ret->kernelVersion)) {
264         ret->kernelLevel = Level::UNSPECIFIED;
265 
266         bool isMainline = RuntimeInfo::kernelReleaseIsMainline(s);
267         ret->setIsMainline(isMainline);
268         LOG(INFO) << "\"" << s << "\" is" << (isMainline ? "" : " not") << " a mainline kernel.";
269 
270         return true;
271     }
272 
273     LOG(INFO) << "Cannot parse \"" << s << "\" as kernel release";
274     return false;
275 }
276 
277 // Parse the first half of --kernel. |s| can either be a kernel version, a GKI kernel release,
278 // or a file that contains either of them.
parseKernelArgFirstHalf(const std::string & s,StaticRuntimeInfo * ret)279 bool parseKernelArgFirstHalf(const std::string& s, StaticRuntimeInfo* ret) {
280     if (parseKernelVersionOrRelease(s, ret)) {
281         LOG(INFO) << "Successfully parsed \"" << s << "\"";
282         return true;
283     }
284     std::string content;
285     if (!android::base::ReadFileToString(s, &content)) {
286         PLOG(INFO) << "Cannot read file " << s;
287         return false;
288     }
289     if (parseKernelVersionOrRelease(content, ret)) {
290         LOG(INFO) << "Successfully parsed content of " << s << ": " << content;
291         return true;
292     }
293     LOG(ERROR) << "ERROR: Cannot parse content of " << s << ": " << content;
294     return false;
295 }
296 
297 template <typename T>
getRuntimeInfo(const T & args)298 std::shared_ptr<StaticRuntimeInfo> getRuntimeInfo(const T& args) {
299     auto ret = std::make_shared<StaticRuntimeInfo>();
300     if (std::distance(args.begin(), args.end()) > 1) {
301         LOG(ERROR) << "ERROR: Can't have multiple --kernel options";
302         return nullptr;
303     }
304     const auto& arg = *args.begin();
305     auto colonPos = arg.rfind(":");
306     if (colonPos == std::string::npos) {
307         LOG(ERROR) << "ERROR: Invalid --kernel";
308         return nullptr;
309     }
310 
311     if (!parseKernelArgFirstHalf(arg.substr(0, colonPos), ret.get())) {
312         return nullptr;
313     }
314 
315     ret->kernelConfigFile = arg.substr(colonPos + 1);
316     return ret;
317 }
318 
usage(const char * me)319 int usage(const char* me) {
320     LOG(ERROR)
321         << me << ": check VINTF metadata." << std::endl
322         << "    Modes:" << std::endl
323         << "        --dump-file-list: Dump a list of directories / files on device" << std::endl
324         << "                that is required to be used by --check-compat." << std::endl
325         << "        -c, --check-compat: check compatibility for files under the root" << std::endl
326         << "                directory specified by --root-dir." << std::endl
327         << "        --check-one: check consistency of VINTF metadata for a single partition."
328         << std::endl
329         << std::endl
330         << "    Options:" << std::endl
331         << "        --rootdir=<dir>: specify root directory for all metadata. Same as " << std::endl
332         << "                --dirmap /:<dir>" << std::endl
333         << "        -D, --property <key>=<value>: specify sysprops." << std::endl
334         << "        --dirmap </system:/dir/to/system> [--dirmap </vendor:/dir/to/vendor>[...]]"
335         << std::endl
336         << "                Map partitions to directories. Cannot be specified with --rootdir."
337         << "        --kernel <version:path/to/config>" << std::endl
338         << "                Use the given kernel version and config to check. If" << std::endl
339         << "                unspecified, kernel requirements are skipped." << std::endl
340         << "                The first half, version, can be just x.y.z, or a file " << std::endl
341         << "                containing the full kernel release string x.y.z-something." << std::endl
342         << "        --help: show this message." << std::endl
343         << std::endl
344         << "    Example:" << std::endl
345         << "        # Get the list of required files." << std::endl
346         << "        " << me << " --dump-file-list > /tmp/files.txt" << std::endl
347         << "        # Pull from ADB, or use your own command to extract files from images"
348         << std::endl
349         << "        ROOTDIR=/tmp/device/" << std::endl
350         << "        cat /tmp/files.txt | xargs -I{} bash -c \"mkdir -p $ROOTDIR`dirname {}` && adb "
351            "pull {} $ROOTDIR{}\""
352         << std::endl
353         << "        # Check compatibility." << std::endl
354         << "        " << me << " --check-compat --rootdir=$ROOTDIR \\" << std::endl
355         << "            --property ro.product.first_api_level=`adb shell getprop "
356            "ro.product.first_api_level` \\"
357         << std::endl
358         << "            --property ro.boot.product.hardware.sku=`adb shell getprop "
359            "ro.boot.product.hardware.sku`";
360     return EX_USAGE;
361 }
362 
363 class CheckVintfUtils {
364    public:
365     // Print HALs in the device manifest that are not declared in FCMs <= target FCM version.
logHalsFromNewFcms(VintfObject * vintfObject,const std::vector<HidlInterfaceMetadata> & hidlMetadata)366     static void logHalsFromNewFcms(VintfObject* vintfObject,
367                                    const std::vector<HidlInterfaceMetadata>& hidlMetadata) {
368         auto deviceManifest = vintfObject->getDeviceHalManifest();
369         if (deviceManifest == nullptr) {
370             LOG(WARNING) << "Unable to print HALs from new FCMs: no device HAL manifest.";
371             return;
372         }
373         std::string kernelLevelError;
374         auto kernelLevel = vintfObject->getKernelLevel(&kernelLevelError);
375         if (kernelLevel == Level::UNSPECIFIED) {
376             LOG(WARNING) << "getKernelLevel: " << kernelLevel;
377         }
378         std::vector<CompatibilityMatrix> matrixFragments;
379         std::string error;
380         auto status = vintfObject->getAllFrameworkMatrixLevels(&matrixFragments, &error);
381         if (status != OK || matrixFragments.empty()) {
382             LOG(WARNING) << "Unable to print HALs from new FCMs: " << statusToString(status) << ": "
383                          << error;
384             return;
385         }
386         auto it = std::remove_if(matrixFragments.begin(), matrixFragments.end(),
387                                  [&](const CompatibilityMatrix& matrix) {
388                                      return matrix.level() != Level::UNSPECIFIED &&
389                                             matrix.level() > deviceManifest->level();
390                                  });
391         matrixFragments.erase(it, matrixFragments.end());
392         auto combined = CompatibilityMatrix::combine(deviceManifest->level(), kernelLevel,
393                                                      &matrixFragments, &error);
394         if (combined == nullptr) {
395             LOG(WARNING) << "Unable to print HALs from new FCMs: unable to combine matrix "
396                             "fragments <= level "
397                          << deviceManifest->level() << ": " << error;
398         }
399         auto unused = deviceManifest->checkUnusedHals(*combined, hidlMetadata);
400         if (unused.empty()) {
401             LOG(INFO) << "All HALs in device manifest are declared in FCM <= level "
402                       << deviceManifest->level();
403             return;
404         }
405         LOG(INFO) << "The following HALs in device manifest are not declared in FCM <= level "
406                   << deviceManifest->level() << ": ";
407         for (const auto& hal : unused) {
408             LOG(INFO) << "  " << hal;
409         }
410     }
411 };
412 
413 // If |result| is already an error, don't do anything. Otherwise, set it to
414 // an error with |errorCode|. Return reference to Error object for appending
415 // additional error messages.
SetErrorCode(std::optional<android::base::Error<>> * retError,int errorCode=0)416 android::base::Error<>& SetErrorCode(std::optional<android::base::Error<>>* retError,
417                                      int errorCode = 0) {
418     if (!retError->has_value()) {
419         retError->emplace(errorCode);
420     } else {
421         // Use existing error code.
422         // There should already been an error message appended. Add a new line char for
423         // additional messages.
424         (**retError) << "\n";
425     }
426     return **retError;
427 }
428 
429 // If |other| is an error, add it to |retError|.
430 template <typename T>
AddResult(std::optional<android::base::Error<>> * retError,const android::base::Result<T> & other,const char * additionalMessage="")431 void AddResult(std::optional<android::base::Error<>>* retError,
432                const android::base::Result<T>& other, const char* additionalMessage = "") {
433     if (other.ok()) return;
434     SetErrorCode(retError, other.error().code()) << other.error() << additionalMessage;
435 }
436 
437 static constexpr const char* gCheckMissingHalsSuggestion{
438     "\n- If this is a new package, add it to the latest framework compatibility matrix."
439     "\n- If no interface should be added to the framework compatibility matrix (e.g. "
440     "types-only package), add it to the exempt list in libvintf_fcm_exclude."};
441 
checkAllFiles(const Dirmap & dirmap,const Properties & props,std::shared_ptr<StaticRuntimeInfo> runtimeInfo)442 android::base::Result<void> checkAllFiles(const Dirmap& dirmap, const Properties& props,
443                                           std::shared_ptr<StaticRuntimeInfo> runtimeInfo) {
444     auto hostFileSystem = std::make_unique<HostFileSystem>(dirmap, UNKNOWN_ERROR);
445     auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
446     hostPropertyFetcher->setProperties(props);
447 
448     CheckFlags::Type flags = CheckFlags::DEFAULT;
449     if (!runtimeInfo) flags = flags.disableRuntimeInfo();
450 
451     auto vintfObject =
452         VintfObject::Builder()
453             .setFileSystem(std::move(hostFileSystem))
454             .setPropertyFetcher(std::move(hostPropertyFetcher))
455             .setRuntimeInfoFactory(std::make_unique<StaticRuntimeInfoFactory>(runtimeInfo))
456             .build();
457 
458     std::optional<android::base::Error<>> retError = std::nullopt;
459 
460     std::string compatibleError;
461     int compatibleResult = vintfObject->checkCompatibility(&compatibleError, flags);
462     if (compatibleResult == INCOMPATIBLE) {
463         SetErrorCode(&retError) << compatibleError;
464     } else if (compatibleResult != COMPATIBLE) {
465         SetErrorCode(&retError, -compatibleResult) << compatibleError;
466     }
467 
468     auto hidlMetadata = HidlInterfaceMetadata::all();
469 
470     std::string deprecateError;
471     int deprecateResult = vintfObject->checkDeprecation(hidlMetadata, &deprecateError);
472     if (deprecateResult == DEPRECATED) {
473         SetErrorCode(&retError) << deprecateError;
474     } else if (deprecateResult != NO_DEPRECATED_HALS) {
475         SetErrorCode(&retError, -deprecateResult) << deprecateError;
476     }
477 
478     auto hasFcmExt = vintfObject->hasFrameworkCompatibilityMatrixExtensions();
479     AddResult(&retError, hasFcmExt);
480 
481     auto deviceManifest = vintfObject->getDeviceHalManifest();
482     Level targetFcm = Level::UNSPECIFIED;
483     if (deviceManifest == nullptr) {
484         SetErrorCode(&retError, -NAME_NOT_FOUND) << "No device HAL manifest";
485     } else {
486         targetFcm = deviceManifest->level();
487     }
488 
489     if (hasFcmExt.value_or(false) || (targetFcm != Level::UNSPECIFIED && targetFcm >= Level::R)) {
490         AddResult(&retError, vintfObject->checkUnusedHals(hidlMetadata));
491     } else {
492         LOG(INFO) << "Skip checking unused HALs.";
493     }
494 
495     CheckVintfUtils::logHalsFromNewFcms(vintfObject.get(), hidlMetadata);
496 
497     if (retError.has_value()) {
498         return *retError;
499     } else {
500         return {};
501     }
502 }
503 
504 // Checks consistency of VINTF metadata for a single partition.
505 // For now it supports either /system or /vendor.
checkOne(const Dirmap & dirmap,const Properties & props)506 int checkOne(const Dirmap& dirmap, const Properties& props) {
507     if (dirmap.count("/system") + dirmap.count("/vendor") != 1) {
508         LOG(ERROR) << "ERROR: --check-one requires either --dirmap /system or --dirmap /vendor";
509         return EX_SOFTWARE;
510     }
511 
512     auto hostFileSystem = std::make_unique<HostFileSystem>(dirmap, NAME_NOT_FOUND);
513     auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
514     hostPropertyFetcher->setProperties(props);
515 
516     auto vintfObject =
517         VintfObject::Builder()
518             .setFileSystem(std::move(hostFileSystem))
519             .setPropertyFetcher(std::move(hostPropertyFetcher))
520             .setRuntimeInfoFactory(std::make_unique<StaticRuntimeInfoFactory>(nullptr))
521             .build();
522 
523     if (dirmap.count("/system")) {
524         LOG(INFO) << "Checking system manifest.";
525         auto manifest = vintfObject->getFrameworkHalManifest();
526         if (!manifest) {
527             LOG(ERROR) << "ERROR: Cannot fetch system manifest.";
528             return EX_SOFTWARE;
529         }
530         LOG(INFO) << "Checking system matrix.";
531         auto matrix = vintfObject->getFrameworkCompatibilityMatrix();
532         if (!matrix) {
533             LOG(ERROR) << "ERROR: Cannot fetch system matrix.";
534             return EX_SOFTWARE;
535         }
536         auto res = vintfObject->checkMissingHalsInMatrices(
537             HidlInterfaceMetadata::all(), AidlInterfaceMetadata::all(),
538             ShouldCheckMissingHidlHalsInFcm, ShouldCheckMissingAidlHalsInFcm);
539         if (!res.ok()) {
540             LOG(ERROR) << "ERROR: " << res.error() << gCheckMissingHalsSuggestion;
541             return EX_SOFTWARE;
542         }
543 
544         res = vintfObject->checkMatrixHalsHasDefinition(HidlInterfaceMetadata::all(),
545                                                         AidlInterfaceMetadata::all());
546         if (!res.ok()) {
547             LOG(ERROR) << "ERROR: " << res.error();
548             return EX_SOFTWARE;
549         }
550         return EX_OK;
551     }
552 
553     if (dirmap.count("/vendor")) {
554         LOG(INFO) << "Checking vendor manifest.";
555         auto manifest = vintfObject->getDeviceHalManifest();
556         if (!manifest) {
557             LOG(ERROR) << "ERROR: Cannot fetch vendor manifest.";
558             return EX_SOFTWARE;
559         }
560         LOG(INFO) << "Checking vendor matrix.";
561         auto matrix = vintfObject->getDeviceCompatibilityMatrix();
562         if (!matrix) {
563             LOG(ERROR) << "ERROR: Cannot fetch vendor matrix.";
564             return EX_SOFTWARE;
565         }
566         return EX_OK;
567     }
568 
569     __builtin_unreachable();
570 }
571 
Logger(android::base::LogId,android::base::LogSeverity severity,const char *,const char *,unsigned int,const char * message)572 void Logger(android::base::LogId, android::base::LogSeverity severity, const char* /*tag*/,
573             const char* /*file*/, unsigned int /*line*/, const char* message) {
574     if (severity >= android::base::WARNING) {
575         fflush(stdout);
576         fprintf(stderr, "%s\n", message);
577     } else {
578         fprintf(stdout, "%s\n", message);
579     }
580 }
581 
582 }  // namespace details
583 }  // namespace vintf
584 }  // namespace android
585 
main(int argc,char ** argv)586 int main(int argc, char** argv) {
587     android::base::SetLogger(android::vintf::details::Logger);
588 
589     using namespace android::vintf;
590     using namespace android::vintf::details;
591     // legacy usage: check_vintf <manifest.xml> <matrix.xml>
592     if (argc == 3 && *argv[1] != '-' && *argv[2] != '-') {
593         int ret = checkCompatibilityForFiles(argv[1], argv[2]);
594         if (ret >= 0) return ret;
595     }
596 
597     Args args = parseArgs(argc, argv);
598 
599     if (!iterateValues(args, HELP).empty()) {
600         return usage(argv[0]);
601     }
602 
603     auto dirmap = getDirmap(iterateValues(args, DIR_MAP));
604     auto properties = getProperties(iterateValues(args, PROPERTY));
605     if (!iterateValues(args, DUMP_FILE_LIST).empty()) {
606         auto it = properties.find("ro.boot.product.hardware.sku");
607         const std::string sku = it == properties.end() ? "" : it->second;
608         for (const auto& file : dumpFileList(sku)) {
609             std::cout << file << std::endl;
610         }
611         return 0;
612     }
613 
614     if (!iterateValues(args, CHECK_ONE).empty()) {
615         return checkOne(dirmap, properties);
616     }
617 
618     auto checkCompat = iterateValues(args, CHECK_COMPAT);
619     if (checkCompat.empty()) {
620         return usage(argv[0]);
621     }
622 
623     auto rootdirs = iterateValues(args, ROOTDIR);
624     if (!rootdirs.empty()) {
625         if (std::distance(rootdirs.begin(), rootdirs.end()) > 1) {
626             LOG(ERROR) << "ERROR: Can't have multiple --rootdir options";
627             return usage(argv[0]);
628         }
629         args.emplace(DIR_MAP, "/:" + *rootdirs.begin());
630     }
631 
632     std::shared_ptr<StaticRuntimeInfo> runtimeInfo;
633     auto kernelArgs = iterateValues(args, KERNEL);
634     if (!kernelArgs.empty()) {
635         runtimeInfo = getRuntimeInfo(kernelArgs);
636         if (runtimeInfo == nullptr) {
637             return usage(argv[0]);
638         }
639     }
640 
641     if (dirmap.empty()) {
642         LOG(ERROR) << "ERROR: Missing --rootdir or --dirmap option.";
643         return usage(argv[0]);
644     }
645 
646     auto compat = checkAllFiles(dirmap, properties, runtimeInfo);
647 
648     if (compat.ok()) {
649         std::cout << "COMPATIBLE" << std::endl;
650         return EX_OK;
651     }
652     if (compat.error().code() == 0) {
653         LOG(ERROR) << "ERROR: files are incompatible: " << compat.error();
654         std::cout << "INCOMPATIBLE" << std::endl;
655         return EX_DATAERR;
656     }
657     LOG(ERROR) << "ERROR: " << strerror(compat.error().code()) << ": " << compat.error();
658     return EX_SOFTWARE;
659 }
660