/* * 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 #include "HalManifest.h" #include #include #include #include "parse_string.h" #include "parse_xml.h" #include "utils.h" #include "CompatibilityMatrix.h" namespace android { namespace vintf { // Check tag for all with the same name. bool HalManifest::shouldAdd(const ManifestHal& hal) const { if (!hal.isValid()) { return false; } auto existingHals = mHals.equal_range(hal.name); std::set 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; } 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 HalManifest::getHalNames() const { std::set names{}; for (const auto &hal : mHals) { names.insert(hal.first); } return names; } std::set HalManifest::getHalNamesAndVersions() const { std::set names{}; for (const auto &hal : getHals()) { for (const auto &version : hal.versions) { names.insert(hal.name + "@" + to_string(version)); } } return names; } Transport HalManifest::getTransport(const std::string &package, const Version &v, const std::string &interfaceName, const std::string &instanceName) const { for (const ManifestHal *hal : getHals(package)) { bool found = false; for (auto& ver : hal->versions) { if (ver.majorVer == v.majorVer && ver.minorVer >= v.minorVer) { found = true; break; } } if (!found) { LOG(DEBUG) << "HalManifest::getTransport(" << to_string(mType) << "): Cannot find " << to_string(v) << " in supported versions of " << package; continue; } auto it = hal->interfaces.find(interfaceName); if (it == hal->interfaces.end()) { LOG(DEBUG) << "HalManifest::getTransport(" << to_string(mType) << "): Cannot find interface '" << interfaceName << "' in " << package << "@" << to_string(v); continue; } const auto &instances = it->second.instances; if (instances.find(instanceName) == instances.end()) { LOG(DEBUG) << "HalManifest::getTransport(" << to_string(mType) << "): Cannot find instance '" << instanceName << "' in " << package << "@" << to_string(v) << "::" << interfaceName; continue; } return hal->transportArch.transport; } LOG(DEBUG) << "HalManifest::getTransport(" << to_string(mType) << "): Cannot get transport for " << package << "@" << v << "::" << interfaceName << "/" << instanceName; return Transport::EMPTY; } std::set HalManifest::getSupportedVersions(const std::string &name) const { std::set ret; for (const ManifestHal *hal : getHals(name)) { ret.insert(hal->versions.begin(), hal->versions.end()); } return ret; } bool HalManifest::hasInstance(const std::string& halName, const Version& version, const std::string& interfaceName, const std::string& instanceName) const { const auto& instances = getInstances(halName, version, interfaceName); return instances.find(instanceName) != instances.end(); } using InstancesOfVersion = std::map>; using Instances = std::map; static bool satisfyVersion(const MatrixHal& matrixHal, const Version& manifestHalVersion) { for (const VersionRange &matrixVersionRange : matrixHal.versionRanges) { // If Compatibility Matrix says 2.5-2.7, the "2.7" is purely informational; // the framework can work with all 2.5-2.infinity. if (matrixVersionRange.supportedBy(manifestHalVersion)) { return true; } } return false; } // Check if matrixHal.interfaces is a subset of instancesOfVersion static bool satisfyAllInstances(const MatrixHal& matrixHal, const InstancesOfVersion &instancesOfVersion) { for (const auto& matrixHalInterfacePair : matrixHal.interfaces) { const std::string& interface = matrixHalInterfacePair.first; auto it = instancesOfVersion.find(interface); if (it == instancesOfVersion.end()) { return false; } const std::set& manifestInterfaceInstances = it->second; const std::set& matrixInterfaceInstances = matrixHalInterfacePair.second.instances; if (!std::includes(manifestInterfaceInstances.begin(), manifestInterfaceInstances.end(), matrixInterfaceInstances.begin(), matrixInterfaceInstances.end())) { return false; } } return true; } bool HalManifest::isCompatible(const MatrixHal& matrixHal) const { Instances instances; // Do the cross product version x interface x instance and sort them, // because interfaces / instances can span in multiple HALs. // This is efficient for small entries. for (const ManifestHal* manifestHal : getHals(matrixHal.name)) { for (const Version& manifestHalVersion : manifestHal->versions) { instances[manifestHalVersion] = {}; for (const auto& halInterfacePair : manifestHal->interfaces) { const std::string& interface = halInterfacePair.first; const auto& toAdd = halInterfacePair.second.instances; instances[manifestHalVersion][interface].insert(toAdd.begin(), toAdd.end()); } } } for (const auto& instanceMapPair : instances) { const Version& manifestHalVersion = instanceMapPair.first; const InstancesOfVersion& instancesOfVersion = instanceMapPair.second; if (!satisfyVersion(matrixHal, manifestHalVersion)) { continue; } if (!satisfyAllInstances(matrixHal, instancesOfVersion)) { continue; } return true; // match! } return false; } // For each hal in mat, there must be a hal in manifest that supports this. std::vector HalManifest::checkIncompatibility(const CompatibilityMatrix &mat, bool includeOptional) const { std::vector incompatible; for (const MatrixHal &matrixHal : mat.getHals()) { if (!includeOptional && matrixHal.optional) { continue; } // don't check optional; put it in the incompatibility list as well. if (!isCompatible(matrixHal)) { incompatible.push_back(matrixHal.name); } } return incompatible; } 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; } std::vector incompatibleHals = checkIncompatibility(mat, false /* includeOptional */); if (!incompatibleHals.empty()) { if (error != nullptr) { *error = "HALs incompatible."; for (const auto &name : incompatibleHals) { *error += " " + name; } } return false; } if (mType == SchemaType::FRAMEWORK) { // TODO(b/36400653) enable this. It is disabled since vndk is not yet defined. #ifdef VINTF_CHECK_VNDK bool match = false; const auto &matVndk = mat.device.mVndk; for (const auto &vndk : framework.mVndks) { if (!vndk.mVersionRange.in(matVndk.mVersionRange)) { continue; } // version matches, check libaries std::vector diff; std::set_difference(matVndk.mLibraries.begin(), matVndk.mLibraries.end(), vndk.mLibraries.begin(), vndk.mLibraries.end(), std::inserter(diff, diff.begin())) if (!diff.empty()) { if (error != nullptr) { *error = "Vndk libs incompatible."; for (const auto &name : diff) { *error += " " + name; } } return false; } match = true; break; } if (!match) { if (error != nullptr) { *error = "Vndk version " + to_string(matVndk.mVersionRange) + " is not supported."; } } #endif } else if (mType == SchemaType::DEVICE) { bool match = false; for (const auto &range : mat.framework.mSepolicy.sepolicyVersions()) { if (range.supportedBy(device.mSepolicyVersion)) { match = true; break; } } if (!match) { if (error != nullptr) { *error = "Sepolicy version " + to_string(device.mSepolicyVersion) + " doesn't satisify the requirements."; } return false; } } return true; } CompatibilityMatrix HalManifest::generateCompatibleMatrix() const { CompatibilityMatrix matrix; for (const ManifestHal &manifestHal : getHals()) { MatrixHal matrixHal{ .format = manifestHal.format, .name = manifestHal.name, .optional = true, .interfaces = manifestHal.interfaces }; for (const Version &manifestVersion : manifestHal.versions) { matrixHal.versionRanges.push_back({manifestVersion.majorVer, manifestVersion.minorVer}); } matrix.add(std::move(matrixHal)); } 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 std::string& path, std::string* error) { return details::fetchAllInformation(path, gHalManifestConverter, this, error); } SchemaType HalManifest::type() const { return mType; } 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 &HalManifest::vndks() const { CHECK(mType == SchemaType::FRAMEWORK); return framework.mVndks; } 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.mType != SchemaType::FRAMEWORK || (lft.framework.mVndks == rgt.framework.mVndks)); } } // namespace vintf } // namespace android