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.
313 lines
10 KiB
313 lines
10 KiB
/*
|
|
* Copyright (C) 2020 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 <algorithm>
|
|
#include <map>
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/logging.h>
|
|
#include <android-base/strings.h>
|
|
#include <vintf/Dirmap.h>
|
|
#include <vintf/HostFileSystem.h>
|
|
#include <vintf/VintfFm.h>
|
|
#include <vintf/VintfObject.h>
|
|
#include <vintf/parse_string.h>
|
|
#include <vintf/parse_xml.h>
|
|
|
|
#include "utils.h"
|
|
|
|
namespace android::vintf {
|
|
|
|
namespace {
|
|
|
|
int usage() {
|
|
LOG(ERROR) << R"(
|
|
vintffm: Utility to deprecate framework manifest.
|
|
usage:
|
|
vintffm <-c|--check> <--dirmap /system:system_dir> frozen_dir
|
|
Check framework manifest under system_root against frozen dir. root is the
|
|
root directory of the device, e.g. $ANDROID_PRODUCT_OUT.
|
|
vintffm <-u|--update> <--dirmap /system:system_dir> <-l|--level>=current_level output_frozen_dir
|
|
Update ${output_frozen_dir}/${current_level}.xml using framework manifest.
|
|
vintffm <-h|--help>
|
|
Print help message.
|
|
|
|
Example:
|
|
|
|
# Freeze a framework manifest for Android R.
|
|
m check-vintf-all # Build framework manifest.
|
|
vintffm --update --dirmap /system:$ANDROID_PRODUCT_OUT/system --level 5 \
|
|
system/libhidl/vintfdata/frozen
|
|
|
|
# Check that the framework manifest is aligned with the frozen data.
|
|
vintffm --check --dirmap /system:$ANDROID_PRODUCT_OUT/system \
|
|
system/libhidl/vintfdata/frozen
|
|
)";
|
|
return EX_USAGE;
|
|
}
|
|
|
|
class WritableFileSystemImpl : public WritableFileSystem {
|
|
public:
|
|
status_t fetch(const std::string& path, std::string* fetched,
|
|
std::string* error) const override {
|
|
return mRoFileSystem.fetch(path, fetched, error);
|
|
}
|
|
status_t listFiles(const std::string& path, std::vector<std::string>* out,
|
|
std::string* error) const override {
|
|
return mRoFileSystem.listFiles(path, out, error);
|
|
}
|
|
status_t write(const std::string& path, const std::string& content,
|
|
std::string* error) const override {
|
|
if (!android::base::WriteStringToFile(content, path)) {
|
|
int saved_errno = errno;
|
|
if (error) {
|
|
*error = "Can't write to " + path + ": " + strerror(saved_errno);
|
|
}
|
|
return saved_errno == 0 ? UNKNOWN_ERROR : -saved_errno;
|
|
}
|
|
return OK;
|
|
}
|
|
status_t deleteFile(const std::string& path, std::string* error) const override {
|
|
if (unlink(path.c_str()) == -1) {
|
|
int saved_errno = errno;
|
|
if (error) {
|
|
*error = "Can't unlink " + path + ": " + strerror(saved_errno);
|
|
}
|
|
return saved_errno == 0 ? UNKNOWN_ERROR : -saved_errno;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
private:
|
|
details::FileSystemImpl mRoFileSystem;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
namespace details {
|
|
|
|
// A VintfObject with a proper framework manifest and a fake device manifest with
|
|
// only targetFcmVersion.
|
|
class FmOnlyVintfObject : public VintfObject {
|
|
public:
|
|
FmOnlyVintfObject(std::unique_ptr<FileSystem>&& fs, Level targetFcmVersion)
|
|
: mFs(std::move(fs)) {
|
|
mDeviceManifest = std::make_shared<HalManifest>();
|
|
mDeviceManifest->mLevel = targetFcmVersion;
|
|
}
|
|
|
|
std::shared_ptr<const HalManifest> getDeviceHalManifest() override { return mDeviceManifest; }
|
|
std::shared_ptr<const CompatibilityMatrix> getFrameworkCompatibilityMatrix() override {
|
|
return nullptr;
|
|
}
|
|
std::shared_ptr<const CompatibilityMatrix> getDeviceCompatibilityMatrix() override {
|
|
return nullptr;
|
|
}
|
|
|
|
protected:
|
|
const std::unique_ptr<FileSystem>& getFileSystem() override { return mFs; }
|
|
// Set environment to empty to prevent accidentally reading other things.
|
|
const std::unique_ptr<PropertyFetcher>& getPropertyFetcher() override { return mNoOpProp; }
|
|
|
|
private:
|
|
std::unique_ptr<FileSystem> mFs;
|
|
std::shared_ptr<HalManifest> mDeviceManifest;
|
|
std::unique_ptr<PropertyFetcher> mNoOpProp = std::make_unique<details::PropertyFetcherNoOp>();
|
|
};
|
|
|
|
} // namespace details
|
|
|
|
VintfFm::VintfFm() : VintfFm(std::make_unique<WritableFileSystemImpl>()) {}
|
|
|
|
int VintfFm::main(int argc, char** argv) {
|
|
// clang-format off
|
|
const struct option longopts[] = {
|
|
{"check", no_argument, nullptr, 'c'},
|
|
{"dirmap", required_argument, nullptr, 'd'},
|
|
{"help", no_argument, nullptr, 'h'},
|
|
{"level", required_argument, nullptr, 'l'},
|
|
{"update", no_argument, nullptr, 'u'},
|
|
{0, 0, 0, 0}};
|
|
// clang-format on
|
|
|
|
bool checking = false;
|
|
bool updating = false;
|
|
Level current = Level::UNSPECIFIED;
|
|
std::vector<std::string> dirmapVec;
|
|
|
|
int res;
|
|
optind = 1;
|
|
while ((res = getopt_long(argc, argv, "cdhlu", longopts, nullptr)) >= 0) {
|
|
switch (res) {
|
|
case 'c': {
|
|
checking = true;
|
|
} break;
|
|
|
|
case 'd': {
|
|
dirmapVec.push_back(optarg);
|
|
} break;
|
|
|
|
case 'l': {
|
|
if (!parse(optarg, ¤t)) {
|
|
LOG(ERROR) << "Unable to parse '" << optarg << "' as level.";
|
|
return usage();
|
|
}
|
|
} break;
|
|
|
|
case 'u': {
|
|
updating = true;
|
|
} break;
|
|
|
|
case 'h':
|
|
default: {
|
|
return usage();
|
|
} break;
|
|
}
|
|
}
|
|
|
|
if ((checking + updating) != 1) {
|
|
LOG(ERROR) << "Exactly one of --check or --update must be set.";
|
|
return usage();
|
|
}
|
|
|
|
auto dirmap = details::getDirmap(dirmapVec);
|
|
auto vintfFsFactory = [&] {
|
|
return std::make_unique<details::HostFileSystem>(dirmap, NAME_NOT_FOUND, mFs.get());
|
|
};
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc != 1) {
|
|
LOG(ERROR) << "There must be exactly 1 positional arguments.";
|
|
return usage();
|
|
}
|
|
auto dir = argv[0];
|
|
|
|
if (updating) {
|
|
return update(vintfFsFactory, dir, current);
|
|
}
|
|
return check(vintfFsFactory, dir);
|
|
}
|
|
|
|
int VintfFm::update(const FsFactory& vintfFsFactory, const std::string& dir, Level level) {
|
|
if (level == Level::UNSPECIFIED) {
|
|
LOG(ERROR) << "Must specify last frozen level with --level for --update option.";
|
|
return usage();
|
|
}
|
|
|
|
auto manifest = getManifestForLevel(vintfFsFactory, level);
|
|
if (manifest == nullptr) {
|
|
LOG(ERROR) << "Unable to determine manifests for level " << level;
|
|
return EX_SOFTWARE;
|
|
}
|
|
|
|
if (!dumpMatrix(*manifest, dir, level)) {
|
|
return EX_SOFTWARE;
|
|
}
|
|
|
|
return EX_OK;
|
|
}
|
|
|
|
int VintfFm::check(const FsFactory& vintfFsFactory, const std::string& dir) {
|
|
auto frozenMatrices = loadMatrices(dir);
|
|
if (!frozenMatrices.has_value()) {
|
|
return EX_SOFTWARE;
|
|
}
|
|
for (const auto& [level, matrix] : *frozenMatrices) {
|
|
auto manifest = getManifestForLevel(vintfFsFactory, level);
|
|
if (manifest == nullptr) {
|
|
LOG(ERROR) << "Unable to determine manifests for level " << level;
|
|
return EX_SOFTWARE;
|
|
}
|
|
std::string error;
|
|
if (!manifest->checkCompatibility(matrix, &error)) {
|
|
LOG(ERROR) << "Framework manifest is incompatible with frozen matrix at level " << level
|
|
<< ": " << error;
|
|
return EX_SOFTWARE;
|
|
}
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
std::shared_ptr<const HalManifest> VintfFm::getManifestForLevel(const FsFactory& vintfFsFactory,
|
|
Level level) {
|
|
auto vintfObject = std::make_unique<details::FmOnlyVintfObject>(vintfFsFactory(), level);
|
|
auto frameworkManifest = vintfObject->getFrameworkHalManifest();
|
|
if (frameworkManifest == nullptr) {
|
|
LOG(ERROR) << "Unable to get framework HAL manifest for target FCM version " << level;
|
|
}
|
|
return frameworkManifest;
|
|
}
|
|
|
|
bool VintfFm::dumpMatrix(const HalManifest& frameworkManifest, const std::string& dir,
|
|
Level level) {
|
|
auto matrix = frameworkManifest.generateCompatibleMatrix(false /*optional*/);
|
|
std::string path = dir + "/" + to_string(level) + ".xml";
|
|
std::string error;
|
|
if (OK != mFs->write(path, toXml(matrix), &error)) {
|
|
LOG(ERROR) << "Unable to dump matrix to " << path << ": " << error;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::optional<VintfFm::FrozenMatrices> VintfFm::loadMatrices(const std::string& dir) {
|
|
std::string error;
|
|
std::vector<std::string> allFiles;
|
|
if (OK != mFs->listFiles(dir, &allFiles, &error)) {
|
|
LOG(ERROR) << "Unable to list files under " << dir << ": " << error;
|
|
return std::nullopt;
|
|
}
|
|
if (allFiles.empty()) {
|
|
LOG(ERROR) << "Unable to load frozen interfaces under " << dir << ": directory is empty.";
|
|
return std::nullopt;
|
|
}
|
|
auto ret = std::make_optional<FrozenMatrices>();
|
|
for (const auto& filename : allFiles) {
|
|
std::string path = dir + "/" + filename;
|
|
std::string xmlString;
|
|
if (OK != mFs->fetch(path, &xmlString, &error)) {
|
|
LOG(ERROR) << "Unable to read " << path << ": " << error;
|
|
return std::nullopt;
|
|
}
|
|
CompatibilityMatrix matrix;
|
|
if (!fromXml(&matrix, xmlString, &error)) {
|
|
LOG(ERROR) << "Unable to parse " << path << ": " << error;
|
|
return std::nullopt;
|
|
}
|
|
std::string_view filenameSv{filename};
|
|
(void)android::base::ConsumeSuffix(&filenameSv, ".xml");
|
|
std::string levelString{filenameSv};
|
|
Level matrixLevel;
|
|
if (!parse(levelString, &matrixLevel)) {
|
|
LOG(ERROR) << "Unable to parse " << path << ": " << levelString << " is not a level.";
|
|
return std::nullopt;
|
|
}
|
|
if (ret->find(matrixLevel) != ret->end()) {
|
|
LOG(ERROR) << "Duplicated level " << matrixLevel << ", second one is at " << path;
|
|
return std::nullopt;
|
|
}
|
|
ret->emplace(matrixLevel, std::move(matrix));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
} // namespace android::vintf
|