You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
627 lines
23 KiB
627 lines
23 KiB
4 months ago
|
/*
|
||
|
* Copyright (C) 2017 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
#include <getopt.h>
|
||
|
#include <sysexits.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <functional>
|
||
|
#include <iostream>
|
||
|
#include <map>
|
||
|
#include <optional>
|
||
|
|
||
|
#include <aidl/metadata.h>
|
||
|
#include <android-base/file.h>
|
||
|
#include <android-base/logging.h>
|
||
|
#include <android-base/parseint.h>
|
||
|
#include <android-base/result.h>
|
||
|
#include <android-base/strings.h>
|
||
|
#include <hidl/metadata.h>
|
||
|
#include <kver/kernel_release.h>
|
||
|
#include <utils/Errors.h>
|
||
|
#include <vintf/Dirmap.h>
|
||
|
#include <vintf/HostFileSystem.h>
|
||
|
#include <vintf/KernelConfigParser.h>
|
||
|
#include <vintf/VintfObject.h>
|
||
|
#include <vintf/fcm_exclude.h>
|
||
|
#include <vintf/parse_string.h>
|
||
|
#include <vintf/parse_xml.h>
|
||
|
#include "utils.h"
|
||
|
|
||
|
using android::kver::KernelRelease;
|
||
|
|
||
|
namespace android {
|
||
|
namespace vintf {
|
||
|
namespace details {
|
||
|
|
||
|
// fake sysprops
|
||
|
using Properties = std::map<std::string, std::string>;
|
||
|
|
||
|
enum Option : int {
|
||
|
// Modes
|
||
|
HELP,
|
||
|
DUMP_FILE_LIST = 1,
|
||
|
CHECK_COMPAT,
|
||
|
CHECK_ONE,
|
||
|
|
||
|
// Options
|
||
|
ROOTDIR,
|
||
|
PROPERTY,
|
||
|
DIR_MAP,
|
||
|
KERNEL,
|
||
|
};
|
||
|
// command line arguments
|
||
|
using Args = std::multimap<Option, std::string>;
|
||
|
|
||
|
class PresetPropertyFetcher : public PropertyFetcher {
|
||
|
public:
|
||
|
std::string getProperty(const std::string& key,
|
||
|
const std::string& defaultValue) const override {
|
||
|
auto it = mProps.find(key);
|
||
|
if (it == mProps.end()) {
|
||
|
LOG(INFO) << "Sysprop " << key << " is missing, default to '" << defaultValue << "'";
|
||
|
return defaultValue;
|
||
|
}
|
||
|
LOG(INFO) << "Sysprop " << key << "=" << it->second;
|
||
|
return it->second;
|
||
|
}
|
||
|
uint64_t getUintProperty(const std::string& key, uint64_t defaultValue,
|
||
|
uint64_t max) const override {
|
||
|
uint64_t result;
|
||
|
std::string value = getProperty(key, "");
|
||
|
if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
|
||
|
return defaultValue;
|
||
|
}
|
||
|
bool getBoolProperty(const std::string& key, bool defaultValue) const override {
|
||
|
std::string value = getProperty(key, "");
|
||
|
if (value == "1" || value == "true") {
|
||
|
return true;
|
||
|
} else if (value == "0" || value == "false") {
|
||
|
return false;
|
||
|
}
|
||
|
return defaultValue;
|
||
|
}
|
||
|
void setProperties(const Properties& props) { mProps.insert(props.begin(), props.end()); }
|
||
|
|
||
|
private:
|
||
|
std::map<std::string, std::string> mProps;
|
||
|
};
|
||
|
|
||
|
struct StaticRuntimeInfo : public RuntimeInfo {
|
||
|
KernelVersion kernelVersion;
|
||
|
Level kernelLevel = Level::UNSPECIFIED;
|
||
|
std::string kernelConfigFile;
|
||
|
|
||
|
status_t fetchAllInformation(FetchFlags flags) override {
|
||
|
if (flags & RuntimeInfo::FetchFlag::CPU_VERSION) {
|
||
|
mKernel.mVersion = kernelVersion;
|
||
|
LOG(INFO) << "fetched kernel version " << kernelVersion;
|
||
|
}
|
||
|
if (flags & RuntimeInfo::FetchFlag::KERNEL_FCM) {
|
||
|
mKernel.mLevel = kernelLevel;
|
||
|
LOG(INFO) << "fetched kernel level from RuntimeInfo '" << kernelLevel << "'";
|
||
|
}
|
||
|
if (flags & RuntimeInfo::FetchFlag::CONFIG_GZ) {
|
||
|
std::string content;
|
||
|
if (!android::base::ReadFileToString(kernelConfigFile, &content)) {
|
||
|
LOG(ERROR) << "Cannot read " << kernelConfigFile;
|
||
|
return UNKNOWN_ERROR;
|
||
|
}
|
||
|
KernelConfigParser parser;
|
||
|
auto status = parser.processAndFinish(content);
|
||
|
if (status != OK) {
|
||
|
return status;
|
||
|
}
|
||
|
mKernel.mConfigs = std::move(parser.configs());
|
||
|
LOG(INFO) << "read kernel configs from " << kernelConfigFile;
|
||
|
}
|
||
|
if (flags & RuntimeInfo::FetchFlag::POLICYVERS) {
|
||
|
mKernelSepolicyVersion = SIZE_MAX;
|
||
|
}
|
||
|
return OK;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct StubRuntimeInfo : public RuntimeInfo {
|
||
|
status_t fetchAllInformation(FetchFlags) override { return UNKNOWN_ERROR; }
|
||
|
};
|
||
|
|
||
|
struct StaticRuntimeInfoFactory : public ObjectFactory<RuntimeInfo> {
|
||
|
std::shared_ptr<RuntimeInfo> info;
|
||
|
StaticRuntimeInfoFactory(std::shared_ptr<RuntimeInfo> i) : info(i) {}
|
||
|
std::shared_ptr<RuntimeInfo> make_shared() const override {
|
||
|
if (info) return info;
|
||
|
return std::make_shared<StubRuntimeInfo>();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// helper functions
|
||
|
template <typename T>
|
||
|
std::unique_ptr<T> readObject(FileSystem* fileSystem, const std::string& path) {
|
||
|
std::string xml;
|
||
|
std::string error;
|
||
|
status_t err = fileSystem->fetch(path, &xml, &error);
|
||
|
if (err != OK) {
|
||
|
LOG(ERROR) << "Cannot read '" << path << "' (" << strerror(-err) << "): " << error;
|
||
|
return nullptr;
|
||
|
}
|
||
|
auto ret = std::make_unique<T>();
|
||
|
ret->setFileName(path);
|
||
|
if (!fromXml(ret.get(), xml, &error)) {
|
||
|
LOG(ERROR) << "Cannot parse '" << path << "': " << error;
|
||
|
return nullptr;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int checkCompatibilityForFiles(const std::string& manifestPath, const std::string& matrixPath) {
|
||
|
auto fileSystem = std::make_unique<FileSystemImpl>();
|
||
|
auto manifest = readObject<HalManifest>(fileSystem.get(), manifestPath);
|
||
|
auto matrix = readObject<CompatibilityMatrix>(fileSystem.get(), matrixPath);
|
||
|
if (manifest == nullptr || matrix == nullptr) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
std::string error;
|
||
|
if (!manifest->checkCompatibility(*matrix, &error)) {
|
||
|
LOG(ERROR) << "Incompatible: " << error;
|
||
|
std::cout << "false" << std::endl;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
std::cout << "true" << std::endl;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Args parseArgs(int argc, char** argv) {
|
||
|
int longOptFlag;
|
||
|
int optionIndex;
|
||
|
Args ret;
|
||
|
std::vector<struct option> longopts{
|
||
|
// Modes
|
||
|
{"help", no_argument, &longOptFlag, HELP},
|
||
|
{"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST},
|
||
|
{"check-compat", no_argument, &longOptFlag, CHECK_COMPAT},
|
||
|
{"check-one", no_argument, &longOptFlag, CHECK_ONE},
|
||
|
// Options
|
||
|
{"rootdir", required_argument, &longOptFlag, ROOTDIR},
|
||
|
{"property", required_argument, &longOptFlag, PROPERTY},
|
||
|
{"dirmap", required_argument, &longOptFlag, DIR_MAP},
|
||
|
{"kernel", required_argument, &longOptFlag, KERNEL},
|
||
|
{0, 0, 0, 0}};
|
||
|
std::map<int, Option> shortopts{
|
||
|
{'h', HELP}, {'D', PROPERTY}, {'c', CHECK_COMPAT},
|
||
|
};
|
||
|
for (;;) {
|
||
|
int c = getopt_long(argc, argv, "hcD:", longopts.data(), &optionIndex);
|
||
|
if (c == -1) {
|
||
|
break;
|
||
|
}
|
||
|
std::string argValue = optarg ? optarg : std::string{};
|
||
|
if (c == 0) {
|
||
|
ret.emplace(static_cast<Option>(longOptFlag), std::move(argValue));
|
||
|
} else {
|
||
|
ret.emplace(shortopts[c], std::move(argValue));
|
||
|
}
|
||
|
}
|
||
|
if (optind < argc) {
|
||
|
// see non option
|
||
|
LOG(ERROR) << "unrecognized option `" << argv[optind] << "'";
|
||
|
return {{HELP, ""}};
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
Properties getProperties(const T& args) {
|
||
|
return splitArgs(args, '=');
|
||
|
}
|
||
|
|
||
|
// Parse a kernel version or a GKI kernel release.
|
||
|
bool parseKernelVersionOrRelease(const std::string& s, StaticRuntimeInfo* ret) {
|
||
|
// 5.4.42
|
||
|
if (parse(s, &ret->kernelVersion)) {
|
||
|
ret->kernelLevel = Level::UNSPECIFIED;
|
||
|
return true;
|
||
|
}
|
||
|
LOG(INFO) << "Cannot parse \"" << s << "\" as kernel version, parsing as GKI kernel release.";
|
||
|
|
||
|
// 5.4.42-android12-0-something
|
||
|
auto kernelRelease = KernelRelease::Parse(s, true /* allow suffix */);
|
||
|
if (kernelRelease.has_value()) {
|
||
|
ret->kernelVersion = KernelVersion{kernelRelease->version(), kernelRelease->patch_level(),
|
||
|
kernelRelease->sub_level()};
|
||
|
ret->kernelLevel = RuntimeInfo::gkiAndroidReleaseToLevel(kernelRelease->android_release());
|
||
|
return true;
|
||
|
}
|
||
|
LOG(INFO) << "Cannot parse \"" << s << "\" as GKI kernel release, parsing as kernel release";
|
||
|
|
||
|
// 5.4.42-something
|
||
|
auto pos = s.find_first_not_of("0123456789.");
|
||
|
// substr handles pos == npos case
|
||
|
if (parse(s.substr(0, pos), &ret->kernelVersion)) {
|
||
|
ret->kernelLevel = Level::UNSPECIFIED;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
LOG(INFO) << "Cannot parse \"" << s << "\" as kernel release";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Parse the first half of --kernel. |s| can either be a kernel version, a GKI kernel release,
|
||
|
// or a file that contains either of them.
|
||
|
bool parseKernelArgFirstHalf(const std::string& s, StaticRuntimeInfo* ret) {
|
||
|
if (parseKernelVersionOrRelease(s, ret)) {
|
||
|
LOG(INFO) << "Successfully parsed \"" << s << "\"";
|
||
|
return true;
|
||
|
}
|
||
|
std::string content;
|
||
|
if (!android::base::ReadFileToString(s, &content)) {
|
||
|
PLOG(INFO) << "Cannot read file " << s;
|
||
|
return false;
|
||
|
}
|
||
|
if (parseKernelVersionOrRelease(content, ret)) {
|
||
|
LOG(INFO) << "Successfully parsed content of " << s << ": " << content;
|
||
|
return true;
|
||
|
}
|
||
|
LOG(ERROR) << "Cannot parse content of " << s << ": " << content;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
std::shared_ptr<StaticRuntimeInfo> getRuntimeInfo(const T& args) {
|
||
|
auto ret = std::make_shared<StaticRuntimeInfo>();
|
||
|
if (std::distance(args.begin(), args.end()) > 1) {
|
||
|
LOG(ERROR) << "Can't have multiple --kernel options";
|
||
|
return nullptr;
|
||
|
}
|
||
|
const auto& arg = *args.begin();
|
||
|
auto colonPos = arg.rfind(":");
|
||
|
if (colonPos == std::string::npos) {
|
||
|
LOG(ERROR) << "Invalid --kernel";
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
if (!parseKernelArgFirstHalf(arg.substr(0, colonPos), ret.get())) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
ret->kernelConfigFile = arg.substr(colonPos + 1);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int usage(const char* me) {
|
||
|
LOG(ERROR)
|
||
|
<< me << ": check VINTF metadata." << std::endl
|
||
|
<< " Modes:" << std::endl
|
||
|
<< " --dump-file-list: Dump a list of directories / files on device" << std::endl
|
||
|
<< " that is required to be used by --check-compat." << std::endl
|
||
|
<< " -c, --check-compat: check compatibility for files under the root" << std::endl
|
||
|
<< " directory specified by --root-dir." << std::endl
|
||
|
<< " --check-one: check consistency of VINTF metadata for a single partition."
|
||
|
<< std::endl
|
||
|
<< std::endl
|
||
|
<< " Options:" << std::endl
|
||
|
<< " --rootdir=<dir>: specify root directory for all metadata. Same as " << std::endl
|
||
|
<< " --dirmap /:<dir>" << std::endl
|
||
|
<< " -D, --property <key>=<value>: specify sysprops." << std::endl
|
||
|
<< " --dirmap </system:/dir/to/system> [--dirmap </vendor:/dir/to/vendor>[...]]"
|
||
|
<< std::endl
|
||
|
<< " Map partitions to directories. Cannot be specified with --rootdir."
|
||
|
<< " --kernel <version:path/to/config>" << std::endl
|
||
|
<< " Use the given kernel version and config to check. If" << std::endl
|
||
|
<< " unspecified, kernel requirements are skipped." << std::endl
|
||
|
<< " The first half, version, can be just x.y.z, or a file " << std::endl
|
||
|
<< " containing the full kernel release string x.y.z-something." << std::endl
|
||
|
<< " --help: show this message." << std::endl
|
||
|
<< std::endl
|
||
|
<< " Example:" << std::endl
|
||
|
<< " # Get the list of required files." << std::endl
|
||
|
<< " " << me << " --dump-file-list > /tmp/files.txt" << std::endl
|
||
|
<< " # Pull from ADB, or use your own command to extract files from images"
|
||
|
<< std::endl
|
||
|
<< " ROOTDIR=/tmp/device/" << std::endl
|
||
|
<< " cat /tmp/files.txt | xargs -I{} bash -c \"mkdir -p $ROOTDIR`dirname {}` && adb "
|
||
|
"pull {} $ROOTDIR{}\""
|
||
|
<< std::endl
|
||
|
<< " # Check compatibility." << std::endl
|
||
|
<< " " << me << " --check-compat --rootdir=$ROOTDIR \\" << std::endl
|
||
|
<< " --property ro.product.first_api_level=`adb shell getprop "
|
||
|
"ro.product.first_api_level` \\"
|
||
|
<< std::endl
|
||
|
<< " --property ro.boot.product.hardware.sku=`adb shell getprop "
|
||
|
"ro.boot.product.hardware.sku`";
|
||
|
return EX_USAGE;
|
||
|
}
|
||
|
|
||
|
class CheckVintfUtils {
|
||
|
public:
|
||
|
// Print HALs in the device manifest that are not declared in FCMs <= target FCM version.
|
||
|
static void logHalsFromNewFcms(VintfObject* vintfObject,
|
||
|
const std::vector<HidlInterfaceMetadata>& hidlMetadata) {
|
||
|
auto deviceManifest = vintfObject->getDeviceHalManifest();
|
||
|
if (deviceManifest == nullptr) {
|
||
|
LOG(WARNING) << "Unable to print HALs from new FCMs: no device HAL manifest.";
|
||
|
return;
|
||
|
}
|
||
|
std::vector<CompatibilityMatrix> matrixFragments;
|
||
|
std::string error;
|
||
|
auto status = vintfObject->getAllFrameworkMatrixLevels(&matrixFragments, &error);
|
||
|
if (status != OK || matrixFragments.empty()) {
|
||
|
LOG(WARNING) << "Unable to print HALs from new FCMs: " << statusToString(status) << ": "
|
||
|
<< error;
|
||
|
return;
|
||
|
}
|
||
|
auto it = std::remove_if(matrixFragments.begin(), matrixFragments.end(),
|
||
|
[&](const CompatibilityMatrix& matrix) {
|
||
|
return matrix.level() != Level::UNSPECIFIED &&
|
||
|
matrix.level() > deviceManifest->level();
|
||
|
});
|
||
|
matrixFragments.erase(it, matrixFragments.end());
|
||
|
auto combined =
|
||
|
CompatibilityMatrix::combine(deviceManifest->level(), &matrixFragments, &error);
|
||
|
if (combined == nullptr) {
|
||
|
LOG(WARNING) << "Unable to print HALs from new FCMs: unable to combine matrix "
|
||
|
"fragments <= level "
|
||
|
<< deviceManifest->level() << ": " << error;
|
||
|
}
|
||
|
auto unused = deviceManifest->checkUnusedHals(*combined, hidlMetadata);
|
||
|
if (unused.empty()) {
|
||
|
LOG(INFO) << "All HALs in device manifest are declared in FCM <= level "
|
||
|
<< deviceManifest->level();
|
||
|
return;
|
||
|
}
|
||
|
LOG(INFO) << "The following HALs in device manifest are not declared in FCM <= level "
|
||
|
<< deviceManifest->level() << ": ";
|
||
|
for (const auto& hal : unused) {
|
||
|
LOG(INFO) << " " << hal;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// If |result| is already an error, don't do anything. Otherwise, set it to
|
||
|
// an error with |errorCode|. Return reference to Error object for appending
|
||
|
// additional error messages.
|
||
|
android::base::Error& SetErrorCode(std::optional<android::base::Error>* retError,
|
||
|
int errorCode = 0) {
|
||
|
if (!retError->has_value()) {
|
||
|
retError->emplace(errorCode);
|
||
|
} else {
|
||
|
// Use existing error code.
|
||
|
// There should already been an error message appended. Add a new line char for
|
||
|
// additional messages.
|
||
|
(**retError) << "\n";
|
||
|
}
|
||
|
return **retError;
|
||
|
}
|
||
|
|
||
|
// If |other| is an error, add it to |retError|.
|
||
|
template <typename T>
|
||
|
void AddResult(std::optional<android::base::Error>* retError, const android::base::Result<T>& other,
|
||
|
const char* additionalMessage = "") {
|
||
|
if (other.ok()) return;
|
||
|
SetErrorCode(retError, other.error().code()) << other.error() << additionalMessage;
|
||
|
}
|
||
|
|
||
|
static constexpr const char* gCheckMissingHalsSuggestion{
|
||
|
"\n- If this is a new package, add it to the latest framework compatibility matrix."
|
||
|
"\n- If no interface should be added to the framework compatibility matrix (e.g. "
|
||
|
"types-only package), add it to the exempt list in libvintf_fcm_exclude."};
|
||
|
|
||
|
android::base::Result<void> checkAllFiles(const Dirmap& dirmap, const Properties& props,
|
||
|
std::shared_ptr<StaticRuntimeInfo> runtimeInfo) {
|
||
|
auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
|
||
|
hostPropertyFetcher->setProperties(props);
|
||
|
|
||
|
CheckFlags::Type flags = CheckFlags::DEFAULT;
|
||
|
if (!runtimeInfo) flags = flags.disableRuntimeInfo();
|
||
|
|
||
|
auto vintfObject =
|
||
|
VintfObject::Builder()
|
||
|
.setFileSystem(std::make_unique<HostFileSystem>(dirmap, UNKNOWN_ERROR))
|
||
|
.setPropertyFetcher(std::move(hostPropertyFetcher))
|
||
|
.setRuntimeInfoFactory(std::make_unique<StaticRuntimeInfoFactory>(runtimeInfo))
|
||
|
.build();
|
||
|
|
||
|
std::optional<android::base::Error> retError = std::nullopt;
|
||
|
|
||
|
std::string compatibleError;
|
||
|
int compatibleResult = vintfObject->checkCompatibility(&compatibleError, flags);
|
||
|
if (compatibleResult == INCOMPATIBLE) {
|
||
|
SetErrorCode(&retError) << compatibleError;
|
||
|
} else if (compatibleResult != COMPATIBLE) {
|
||
|
SetErrorCode(&retError, -compatibleResult) << compatibleError;
|
||
|
}
|
||
|
|
||
|
auto hidlMetadata = HidlInterfaceMetadata::all();
|
||
|
|
||
|
std::string deprecateError;
|
||
|
int deprecateResult = vintfObject->checkDeprecation(hidlMetadata, &deprecateError);
|
||
|
if (deprecateResult == DEPRECATED) {
|
||
|
SetErrorCode(&retError) << deprecateError;
|
||
|
} else if (deprecateResult != NO_DEPRECATED_HALS) {
|
||
|
SetErrorCode(&retError, -deprecateResult) << deprecateError;
|
||
|
}
|
||
|
|
||
|
auto hasFcmExt = vintfObject->hasFrameworkCompatibilityMatrixExtensions();
|
||
|
AddResult(&retError, hasFcmExt);
|
||
|
|
||
|
auto deviceManifest = vintfObject->getDeviceHalManifest();
|
||
|
Level targetFcm = Level::UNSPECIFIED;
|
||
|
if (deviceManifest == nullptr) {
|
||
|
SetErrorCode(&retError, -NAME_NOT_FOUND) << "No device HAL manifest";
|
||
|
} else {
|
||
|
targetFcm = deviceManifest->level();
|
||
|
}
|
||
|
|
||
|
if (hasFcmExt.value_or(false) || (targetFcm != Level::UNSPECIFIED && targetFcm >= Level::R)) {
|
||
|
AddResult(&retError, vintfObject->checkUnusedHals(hidlMetadata));
|
||
|
} else {
|
||
|
LOG(INFO) << "Skip checking unused HALs.";
|
||
|
}
|
||
|
|
||
|
CheckVintfUtils::logHalsFromNewFcms(vintfObject.get(), hidlMetadata);
|
||
|
|
||
|
if (retError.has_value()) {
|
||
|
return *retError;
|
||
|
} else {
|
||
|
return {};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int checkDirmaps(const Dirmap& dirmap, const Properties& props) {
|
||
|
auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
|
||
|
hostPropertyFetcher->setProperties(props);
|
||
|
auto exitCode = EX_OK;
|
||
|
for (auto&& [prefix, mappedPath] : dirmap) {
|
||
|
auto vintfObject =
|
||
|
VintfObject::Builder()
|
||
|
.setFileSystem(std::make_unique<HostFileSystem>(dirmap, NAME_NOT_FOUND))
|
||
|
.setPropertyFetcher(std::move(hostPropertyFetcher))
|
||
|
.setRuntimeInfoFactory(std::make_unique<StaticRuntimeInfoFactory>(nullptr))
|
||
|
.build();
|
||
|
|
||
|
if (android::base::StartsWith(prefix, "/system")) {
|
||
|
LOG(INFO) << "Checking system manifest.";
|
||
|
auto manifest = vintfObject->getFrameworkHalManifest();
|
||
|
if (!manifest) {
|
||
|
LOG(ERROR) << "Cannot fetch system manifest.";
|
||
|
exitCode = EX_SOFTWARE;
|
||
|
}
|
||
|
LOG(INFO) << "Checking system matrix.";
|
||
|
auto matrix = vintfObject->getFrameworkCompatibilityMatrix();
|
||
|
if (!matrix) {
|
||
|
LOG(ERROR) << "Cannot fetch system matrix.";
|
||
|
exitCode = EX_SOFTWARE;
|
||
|
}
|
||
|
auto res = vintfObject->checkMissingHalsInMatrices(HidlInterfaceMetadata::all(),
|
||
|
AidlInterfaceMetadata::all(),
|
||
|
ShouldCheckMissingHalsInFcm);
|
||
|
if (!res.ok()) {
|
||
|
LOG(ERROR) << res.error() << gCheckMissingHalsSuggestion;
|
||
|
exitCode = EX_SOFTWARE;
|
||
|
}
|
||
|
|
||
|
res = vintfObject->checkMatrixHalsHasDefinition(HidlInterfaceMetadata::all(),
|
||
|
AidlInterfaceMetadata::all());
|
||
|
if (!res.ok()) {
|
||
|
LOG(ERROR) << res.error();
|
||
|
exitCode = EX_SOFTWARE;
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (android::base::StartsWith(prefix, "/vendor")) {
|
||
|
LOG(INFO) << "Checking vendor manifest.";
|
||
|
auto manifest = vintfObject->getDeviceHalManifest();
|
||
|
if (!manifest) {
|
||
|
LOG(ERROR) << "Cannot fetch vendor manifest.";
|
||
|
exitCode = EX_SOFTWARE;
|
||
|
}
|
||
|
LOG(INFO) << "Checking vendor matrix.";
|
||
|
auto matrix = vintfObject->getDeviceCompatibilityMatrix();
|
||
|
if (!matrix) {
|
||
|
LOG(ERROR) << "Cannot fetch vendor matrix.";
|
||
|
exitCode = EX_SOFTWARE;
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
LOG(ERROR) << "--check-one does not work with --dirmap " << prefix;
|
||
|
exitCode = EX_SOFTWARE;
|
||
|
}
|
||
|
return exitCode;
|
||
|
}
|
||
|
|
||
|
} // namespace details
|
||
|
} // namespace vintf
|
||
|
} // namespace android
|
||
|
|
||
|
int main(int argc, char** argv) {
|
||
|
android::base::SetLogger(android::base::StderrLogger);
|
||
|
|
||
|
using namespace android::vintf;
|
||
|
using namespace android::vintf::details;
|
||
|
// legacy usage: check_vintf <manifest.xml> <matrix.xml>
|
||
|
if (argc == 3 && *argv[1] != '-' && *argv[2] != '-') {
|
||
|
int ret = checkCompatibilityForFiles(argv[1], argv[2]);
|
||
|
if (ret >= 0) return ret;
|
||
|
}
|
||
|
|
||
|
Args args = parseArgs(argc, argv);
|
||
|
|
||
|
if (!iterateValues(args, HELP).empty()) {
|
||
|
return usage(argv[0]);
|
||
|
}
|
||
|
|
||
|
if (!iterateValues(args, DUMP_FILE_LIST).empty()) {
|
||
|
for (const auto& file : dumpFileList()) {
|
||
|
std::cout << file << std::endl;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
auto dirmap = getDirmap(iterateValues(args, DIR_MAP));
|
||
|
auto properties = getProperties(iterateValues(args, PROPERTY));
|
||
|
|
||
|
if (!iterateValues(args, CHECK_ONE).empty()) {
|
||
|
return checkDirmaps(dirmap, properties);
|
||
|
}
|
||
|
|
||
|
auto checkCompat = iterateValues(args, CHECK_COMPAT);
|
||
|
if (checkCompat.empty()) {
|
||
|
return usage(argv[0]);
|
||
|
}
|
||
|
|
||
|
auto rootdirs = iterateValues(args, ROOTDIR);
|
||
|
if (!rootdirs.empty()) {
|
||
|
if (std::distance(rootdirs.begin(), rootdirs.end()) > 1) {
|
||
|
LOG(ERROR) << "Can't have multiple --rootdir options";
|
||
|
return usage(argv[0]);
|
||
|
}
|
||
|
args.emplace(DIR_MAP, "/:" + *rootdirs.begin());
|
||
|
}
|
||
|
|
||
|
std::shared_ptr<StaticRuntimeInfo> runtimeInfo;
|
||
|
auto kernelArgs = iterateValues(args, KERNEL);
|
||
|
if (!kernelArgs.empty()) {
|
||
|
runtimeInfo = getRuntimeInfo(kernelArgs);
|
||
|
if (runtimeInfo == nullptr) {
|
||
|
return usage(argv[0]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (dirmap.empty()) {
|
||
|
LOG(ERROR) << "Missing --rootdir or --dirmap option.";
|
||
|
return usage(argv[0]);
|
||
|
}
|
||
|
|
||
|
auto compat = checkAllFiles(dirmap, properties, runtimeInfo);
|
||
|
|
||
|
if (compat.ok()) {
|
||
|
std::cout << "COMPATIBLE" << std::endl;
|
||
|
return EX_OK;
|
||
|
}
|
||
|
if (compat.error().code() == 0) {
|
||
|
LOG(ERROR) << "files are incompatible: " << compat.error();
|
||
|
std::cout << "INCOMPATIBLE" << std::endl;
|
||
|
return EX_DATAERR;
|
||
|
}
|
||
|
LOG(ERROR) << strerror(compat.error().code()) << ": " << compat.error();
|
||
|
return EX_SOFTWARE;
|
||
|
}
|