123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- /*
- * 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.
- */
- #define LOG_TAG "libvintf"
- #include <android-base/logging.h>
- #include "HalManifest.h"
- #include <dirent.h>
- #include <mutex>
- #include <set>
- #include <android-base/strings.h>
- #include "parse_string.h"
- #include "parse_xml.h"
- #include "utils.h"
- #include "CompatibilityMatrix.h"
- namespace android {
- namespace vintf {
- using details::Instances;
- using details::InstancesOfVersion;
- using details::mergeField;
- // Check <version> tag for all <hal> with the same name.
- bool HalManifest::shouldAdd(const ManifestHal& hal) const {
- if (!hal.isValid()) {
- return false;
- }
- if (hal.isOverride()) {
- return true;
- }
- auto existingHals = mHals.equal_range(hal.name);
- std::set<size_t> existingMajorVersions;
- for (auto it = existingHals.first; it != existingHals.second; ++it) {
- for (const auto& v : it->second.versions) {
- // Assume integrity on existingHals, so no check on emplace().second
- existingMajorVersions.insert(v.majorVer);
- }
- }
- for (const auto& v : hal.versions) {
- if (!existingMajorVersions.emplace(v.majorVer).second /* no insertion */) {
- return false;
- }
- }
- return true;
- }
- // Remove elements from "list" if p(element) returns true.
- template <typename List, typename Predicate>
- static void removeIf(List& list, Predicate predicate) {
- for (auto it = list.begin(); it != list.end();) {
- if (predicate(*it)) {
- it = list.erase(it);
- } else {
- ++it;
- }
- }
- }
- void HalManifest::removeHals(const std::string& name, size_t majorVer) {
- removeIf(mHals, [&name, majorVer](auto& existingHalPair) {
- auto& existingHal = existingHalPair.second;
- if (existingHal.name != name) {
- return false;
- }
- auto& existingVersions = existingHal.versions;
- removeIf(existingVersions, [majorVer](const auto& existingVersion) {
- return existingVersion.majorVer == majorVer;
- });
- return existingVersions.empty();
- });
- }
- bool HalManifest::add(ManifestHal&& halToAdd) {
- if (halToAdd.isOverride()) {
- if (halToAdd.isDisabledHal()) {
- // Special syntax when there are no instances at all. Remove all existing HALs
- // with the given name.
- mHals.erase(halToAdd.name);
- }
- // If there are <version> tags, remove all existing major versions that causes a conflict.
- for (const Version& versionToAdd : halToAdd.versions) {
- removeHals(halToAdd.name, versionToAdd.majorVer);
- }
- }
- return HalGroup::add(std::move(halToAdd));
- }
- bool HalManifest::shouldAddXmlFile(const ManifestXmlFile& xmlFile) const {
- auto existingXmlFiles = getXmlFiles(xmlFile.name());
- for (auto it = existingXmlFiles.first; it != existingXmlFiles.second; ++it) {
- if (xmlFile.version() == it->second.version()) {
- return false;
- }
- }
- return true;
- }
- std::set<std::string> HalManifest::getHalNames() const {
- std::set<std::string> names{};
- for (const auto &hal : mHals) {
- names.insert(hal.first);
- }
- return names;
- }
- std::set<std::string> HalManifest::getHalNamesAndVersions() const {
- std::set<std::string> names{};
- forEachInstance([&names](const ManifestInstance& e) {
- names.insert(toFQNameString(e.package(), e.version()));
- return true;
- });
- return names;
- }
- Transport HalManifest::getTransport(const std::string &package, const Version &v,
- const std::string &interfaceName, const std::string &instanceName) const {
- Transport transport{Transport::EMPTY};
- forEachInstanceOfInterface(package, v, interfaceName, [&](const auto& e) {
- if (e.instance() == instanceName) {
- transport = e.transport();
- }
- return transport == Transport::EMPTY; // if not found, continue
- });
- if (transport == Transport::EMPTY) {
- LOG(DEBUG) << "HalManifest::getTransport(" << mType << "): Cannot find "
- << toFQNameString(package, v, interfaceName, instanceName);
- }
- return transport;
- }
- bool HalManifest::forEachInstanceOfVersion(
- const std::string& package, const Version& expectVersion,
- const std::function<bool(const ManifestInstance&)>& func) const {
- for (const ManifestHal* hal : getHals(package)) {
- bool cont = hal->forEachInstance([&](const ManifestInstance& manifestInstance) {
- if (manifestInstance.version().minorAtLeast(expectVersion)) {
- return func(manifestInstance);
- }
- return true;
- });
- if (!cont) return false;
- }
- return true;
- }
- // indent = 2, {"foo"} => "foo"
- // indent = 2, {"foo", "bar"} => "\n foo\n bar";
- template <typename Container>
- void multilineIndent(std::ostream& os, size_t indent, const Container& lines) {
- if (lines.size() == 1) {
- os << *lines.begin();
- return;
- }
- for (const auto& line : lines) {
- os << "\n";
- for (size_t i = 0; i < indent; ++i) os << " ";
- os << line;
- }
- }
- // For each hal in mat, there must be a hal in manifest that supports this.
- std::vector<std::string> HalManifest::checkIncompatibleHals(const CompatibilityMatrix& mat) const {
- std::vector<std::string> ret;
- for (const MatrixHal &matrixHal : mat.getHals()) {
- if (matrixHal.optional) {
- continue;
- }
- std::set<FqInstance> manifestInstances;
- std::set<FqInstance> manifestInstancesNoPackage;
- std::set<Version> versions;
- for (const ManifestHal* manifestHal : getHals(matrixHal.name)) {
- manifestHal->forEachInstance([&](const auto& manifestInstance) {
- manifestInstances.insert(manifestInstance.getFqInstance());
- manifestInstancesNoPackage.insert(manifestInstance.getFqInstanceNoPackage());
- return true;
- });
- manifestHal->appendAllVersions(&versions);
- }
- if (!matrixHal.isCompatible(manifestInstances, versions)) {
- std::ostringstream oss;
- oss << matrixHal.name << ":\n required: ";
- multilineIndent(oss, 8, android::vintf::expandInstances(matrixHal));
- oss << "\n provided: ";
- if (manifestInstances.empty()) {
- multilineIndent(oss, 8, versions);
- } else {
- multilineIndent(oss, 8, manifestInstancesNoPackage);
- }
- ret.insert(ret.end(), oss.str());
- }
- }
- return ret;
- }
- std::set<std::string> HalManifest::checkUnusedHals(const CompatibilityMatrix& mat) const {
- std::set<std::string> ret;
- forEachInstance([&ret, &mat](const auto& manifestInstance) {
- const auto& fqInstance = manifestInstance.getFqInstance();
- if (!mat.matchInstance(fqInstance.getPackage(), fqInstance.getVersion(),
- fqInstance.getInterface(), fqInstance.getInstance())) {
- ret.insert(fqInstance.string());
- }
- return true;
- });
- return ret;
- }
- static bool checkVendorNdkCompatibility(const VendorNdk& matVendorNdk,
- const std::vector<VendorNdk>& manifestVendorNdk,
- std::string* error) {
- // For pre-P vendor images, device compatibility matrix does not specify <vendor-ndk>
- // tag. Ignore the check for these devices.
- if (matVendorNdk.version().empty()) {
- return true;
- }
- for (const auto& vndk : manifestVendorNdk) {
- if (vndk.version() != matVendorNdk.version()) {
- continue;
- }
- // version matches, check libraries
- std::vector<std::string> diff;
- std::set_difference(matVendorNdk.libraries().begin(), matVendorNdk.libraries().end(),
- vndk.libraries().begin(), vndk.libraries().end(),
- std::inserter(diff, diff.begin()));
- if (!diff.empty()) {
- if (error != nullptr) {
- *error = "Vndk libs incompatible for version " + matVendorNdk.version() +
- ". These libs are not in framework manifest:";
- for (const auto& name : diff) {
- *error += " " + name;
- }
- }
- return false;
- }
- return true;
- }
- // no match is found.
- if (error != nullptr) {
- *error = "Vndk version " + matVendorNdk.version() + " is not supported. " +
- "Supported versions in framework manifest are:";
- for (const auto& vndk : manifestVendorNdk) {
- *error += " " + vndk.version();
- }
- }
- return false;
- }
- static bool checkSystemSdkCompatibility(const SystemSdk& matSystemSdk,
- const SystemSdk& manifestSystemSdk, std::string* error) {
- SystemSdk notSupported = matSystemSdk.removeVersions(manifestSystemSdk);
- if (!notSupported.empty()) {
- if (error) {
- *error =
- "The following System SDK versions are required by device "
- "compatibility matrix but not supported by the framework manifest: [" +
- base::Join(notSupported.versions(), ", ") + "]. Supported versions are: [" +
- base::Join(manifestSystemSdk.versions(), ", ") + "].";
- }
- return false;
- }
- return true;
- }
- bool HalManifest::checkCompatibility(const CompatibilityMatrix &mat, std::string *error) const {
- if (mType == mat.mType) {
- if (error != nullptr) {
- *error = "Wrong type; checking " + to_string(mType) + " manifest against "
- + to_string(mat.mType) + " compatibility matrix";
- }
- return false;
- }
- auto incompatibleHals = checkIncompatibleHals(mat);
- if (!incompatibleHals.empty()) {
- if (error != nullptr) {
- *error = "HALs incompatible.";
- if (mat.level() != Level::UNSPECIFIED)
- *error += " Matrix level = " + to_string(mat.level()) + ".";
- if (level() != Level::UNSPECIFIED)
- *error += " Manifest level = " + to_string(level()) + ".";
- *error += " The following requirements are not met:\n";
- for (const auto& e : incompatibleHals) {
- *error += e + "\n";
- }
- }
- return false;
- }
- if (mType == SchemaType::FRAMEWORK) {
- if (!checkVendorNdkCompatibility(mat.device.mVendorNdk, framework.mVendorNdks, error)) {
- return false;
- }
- if (!checkSystemSdkCompatibility(mat.device.mSystemSdk, framework.mSystemSdk, error)) {
- return false;
- }
- } else if (mType == SchemaType::DEVICE) {
- bool sepolicyMatch = false;
- for (const auto &range : mat.framework.mSepolicy.sepolicyVersions()) {
- if (range.supportedBy(device.mSepolicyVersion)) {
- sepolicyMatch = true;
- break;
- }
- }
- if (!sepolicyMatch) {
- if (error != nullptr) {
- *error = "Sepolicy version " + to_string(device.mSepolicyVersion)
- + " doesn't satisify the requirements.";
- }
- return false;
- }
- if (!!kernel() && !kernel()->matchKernelRequirements(mat.framework.mKernels, error)) {
- return false;
- }
- }
- return true;
- }
- CompatibilityMatrix HalManifest::generateCompatibleMatrix() const {
- CompatibilityMatrix matrix;
- forEachInstance([&matrix](const ManifestInstance& e) {
- matrix.add(MatrixHal{
- .format = e.format(),
- .name = e.package(),
- .optional = true,
- .versionRanges = {VersionRange{e.version().majorVer, e.version().minorVer}},
- .interfaces = {{e.interface(), HalInterface{e.interface(), {e.instance()}}}}});
- return true;
- });
- if (mType == SchemaType::FRAMEWORK) {
- matrix.mType = SchemaType::DEVICE;
- // VNDK does not need to be added for compatibility
- } else if (mType == SchemaType::DEVICE) {
- matrix.mType = SchemaType::FRAMEWORK;
- matrix.framework.mSepolicy = Sepolicy(0u /* kernelSepolicyVersion */,
- {{device.mSepolicyVersion.majorVer, device.mSepolicyVersion.minorVer}});
- }
- return matrix;
- }
- status_t HalManifest::fetchAllInformation(const FileSystem* fileSystem, const std::string& path,
- std::string* error) {
- return details::fetchAllInformation(fileSystem, path, gHalManifestConverter, this, error);
- }
- SchemaType HalManifest::type() const {
- return mType;
- }
- void HalManifest::setType(SchemaType type) {
- mType = type;
- }
- Level HalManifest::level() const {
- return mLevel;
- }
- Version HalManifest::getMetaVersion() const {
- return mMetaVersion;
- }
- const Version &HalManifest::sepolicyVersion() const {
- CHECK(mType == SchemaType::DEVICE);
- return device.mSepolicyVersion;
- }
- const std::vector<VendorNdk>& HalManifest::vendorNdks() const {
- CHECK(mType == SchemaType::FRAMEWORK);
- return framework.mVendorNdks;
- }
- std::string HalManifest::getXmlFilePath(const std::string& xmlFileName,
- const Version& version) const {
- using std::literals::string_literals::operator""s;
- auto range = getXmlFiles(xmlFileName);
- for (auto it = range.first; it != range.second; ++it) {
- const ManifestXmlFile& manifestXmlFile = it->second;
- if (manifestXmlFile.version() == version) {
- if (!manifestXmlFile.overriddenPath().empty()) {
- return manifestXmlFile.overriddenPath();
- }
- return "/"s + (type() == SchemaType::DEVICE ? "vendor" : "system") + "/etc/" +
- xmlFileName + "_V" + std::to_string(version.majorVer) + "_" +
- std::to_string(version.minorVer) + ".xml";
- }
- }
- return "";
- }
- bool operator==(const HalManifest &lft, const HalManifest &rgt) {
- return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals &&
- lft.mXmlFiles == rgt.mXmlFiles &&
- (lft.mType != SchemaType::DEVICE ||
- (lft.device.mSepolicyVersion == rgt.device.mSepolicyVersion &&
- lft.device.mKernel == rgt.device.mKernel)) &&
- (lft.mType != SchemaType::FRAMEWORK ||
- (
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- lft.framework.mVndks == rgt.framework.mVndks &&
- #pragma clang diagnostic pop
- lft.framework.mVendorNdks == rgt.framework.mVendorNdks &&
- lft.framework.mSystemSdk == rgt.framework.mSystemSdk));
- }
- // Alternative to forEachInstance if you just need a set of instance names instead.
- std::set<std::string> HalManifest::getInstances(const std::string& halName, const Version& version,
- const std::string& interfaceName) const {
- std::set<std::string> ret;
- (void)forEachInstanceOfInterface(halName, version, interfaceName, [&ret](const auto& e) {
- ret.insert(e.instance());
- return true;
- });
- return ret;
- }
- // Return whether instance is in getInstances(...).
- bool HalManifest::hasInstance(const std::string& halName, const Version& version,
- const std::string& interfaceName, const std::string& instance) const {
- bool found = false;
- (void)forEachInstanceOfInterface(halName, version, interfaceName,
- [&found, &instance](const auto& e) {
- found |= (instance == e.instance());
- return !found; // if not found, continue
- });
- return found;
- }
- bool HalManifest::insertInstance(const FqInstance& fqInstance, Transport transport, Arch arch,
- HalFormat format, std::string* error) {
- for (ManifestHal& hal : getHals()) {
- if (hal.name == fqInstance.getPackage() && hal.format == format &&
- hal.transport() == transport && hal.arch() == arch) {
- return hal.insertInstance(fqInstance, error);
- }
- }
- ManifestHal hal;
- hal.name = fqInstance.getPackage();
- hal.format = format;
- hal.transportArch = TransportArch(transport, arch);
- if (!hal.insertInstance(fqInstance, error)) return false;
- return add(std::move(hal));
- }
- bool HalManifest::empty() const {
- HalManifest emptyManifest;
- emptyManifest.setType(type());
- return (*this) == emptyManifest;
- }
- const std::optional<KernelInfo>& HalManifest::kernel() const {
- return device.mKernel;
- }
- bool HalManifest::addAll(HalManifest* other, std::string* error) {
- if (other->mMetaVersion.majorVer != mMetaVersion.majorVer) {
- if (error) {
- *error = "Cannot merge manifest version " + to_string(mMetaVersion) + " and " +
- to_string(other->mMetaVersion);
- }
- return false;
- }
- mMetaVersion.minorVer = std::max(mMetaVersion.minorVer, other->mMetaVersion.minorVer);
- if (type() != other->type()) {
- if (error) {
- *error = "Cannot add a " + to_string(other->type()) + " manifest to a " +
- to_string(type()) + " manifest";
- }
- return false;
- }
- if (!addAllHals(other, error)) {
- return false;
- }
- if (!addAllXmlFiles(other, error)) {
- return false;
- }
- if (!mergeField(&mLevel, &other->mLevel, Level::UNSPECIFIED)) {
- if (error) {
- *error = "Conflicting target-level: " + to_string(level()) + " vs. " +
- to_string(other->level());
- }
- return false;
- }
- if (type() == SchemaType::DEVICE) {
- if (!mergeField(&device.mSepolicyVersion, &other->device.mSepolicyVersion)) {
- if (error) {
- *error = "Conflicting sepolicy version: " + to_string(sepolicyVersion()) + " vs. " +
- to_string(other->sepolicyVersion());
- }
- return false;
- }
- if (!mergeField(&device.mKernel, &other->device.mKernel)) {
- // If fails, both have values.
- if (error) {
- *error = "Conflicting kernel: " + to_string(device.mKernel->version()) + " vs. " +
- to_string(other->device.mKernel->version());
- }
- return false;
- }
- } else if (type() == SchemaType::FRAMEWORK) {
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- framework.mVndks.insert(framework.mVndks.end(), other->framework.mVndks.begin(),
- other->framework.mVndks.end());
- other->framework.mVndks.clear();
- #pragma clang diagnostic pop
- framework.mVendorNdks.insert(framework.mVendorNdks.end(),
- other->framework.mVendorNdks.begin(),
- other->framework.mVendorNdks.end());
- other->framework.mVendorNdks.clear();
- framework.mSystemSdk.addAll(&other->framework.mSystemSdk);
- } else {
- LOG(FATAL) << "unknown SchemaType: "
- << static_cast<std::underlying_type_t<SchemaType>>(type());
- }
- if (!other->empty()) {
- if (error) {
- *error =
- "Cannot add another manifest because it contains extraneous entries that "
- "are not recognized.";
- }
- return false;
- }
- return true;
- }
- } // namespace vintf
- } // namespace android
|