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.
403 lines
14 KiB
403 lines
14 KiB
/* |
|
* 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 "parse_string.h" |
|
#include "parse_xml.h" |
|
#include "utils.h" |
|
#include "CompatibilityMatrix.h" |
|
|
|
namespace android { |
|
namespace vintf { |
|
|
|
constexpr Version HalManifest::kVersion; |
|
|
|
// Check <version> tag for all <hal> 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<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; |
|
} |
|
|
|
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{}; |
|
for (const auto &hal : getHals()) { |
|
for (const auto &version : hal.versions) { |
|
names.insert(hal.name + "@" + to_string(version)); |
|
} |
|
} |
|
return names; |
|
} |
|
|
|
std::set<std::string> HalManifest::getInterfaceNames(const std::string &name) const { |
|
std::set<std::string> interfaceNames{}; |
|
for (const ManifestHal *hal : getHals(name)) { |
|
for (const auto &iface : hal->interfaces) { |
|
interfaceNames.insert(iface.first); |
|
} |
|
} |
|
return interfaceNames; |
|
} |
|
|
|
std::vector<const ManifestHal *> HalManifest::getHals(const std::string &name) const { |
|
std::vector<const ManifestHal *> ret; |
|
auto range = mHals.equal_range(name); |
|
for (auto it = range.first; it != range.second; ++it) { |
|
ret.push_back(&it->second); |
|
} |
|
return ret; |
|
} |
|
std::vector<ManifestHal *> HalManifest::getHals(const std::string &name) { |
|
std::vector<ManifestHal *> ret; |
|
auto range = mHals.equal_range(name); |
|
for (auto it = range.first; it != range.second; ++it) { |
|
ret.push_back(&it->second); |
|
} |
|
return ret; |
|
} |
|
|
|
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; |
|
|
|
} |
|
|
|
ConstMultiMapValueIterable<std::string, ManifestHal> HalManifest::getHals() const { |
|
return HalGroup<ManifestHal>::getHals(); |
|
} |
|
|
|
std::set<Version> HalManifest::getSupportedVersions(const std::string &name) const { |
|
std::set<Version> ret; |
|
for (const ManifestHal *hal : getHals(name)) { |
|
ret.insert(hal->versions.begin(), hal->versions.end()); |
|
} |
|
return ret; |
|
} |
|
|
|
|
|
std::set<std::string> HalManifest::getInstances( |
|
const std::string &halName, const std::string &interfaceName) const { |
|
std::set<std::string> ret; |
|
for (const ManifestHal *hal : getHals(halName)) { |
|
auto it = hal->interfaces.find(interfaceName); |
|
if (it != hal->interfaces.end()) { |
|
ret.insert(it->second.instances.begin(), it->second.instances.end()); |
|
} |
|
} |
|
return ret; |
|
} |
|
|
|
bool HalManifest::hasInstance(const std::string &halName, |
|
const std::string &interfaceName, const std::string &instanceName) const { |
|
const auto &instances = getInstances(halName, interfaceName); |
|
return instances.find(instanceName) != instances.end(); |
|
} |
|
|
|
using InstancesOfVersion = std::map<std::string /* interface */, |
|
std::set<std::string /* instance */>>; |
|
using Instances = std::map<Version, InstancesOfVersion>; |
|
|
|
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<std::string>& manifestInterfaceInstances = it->second; |
|
const std::set<std::string>& 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 <hal> 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<std::string> HalManifest::checkIncompatibility(const CompatibilityMatrix &mat, |
|
bool includeOptional) const { |
|
std::vector<std::string> 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<std::string> 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<std::string> 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) { |
|
return details::fetchAllInformation(path, gHalManifestConverter, this); |
|
} |
|
|
|
SchemaType HalManifest::type() const { |
|
return mType; |
|
} |
|
|
|
const Version &HalManifest::sepolicyVersion() const { |
|
CHECK(mType == SchemaType::DEVICE); |
|
return device.mSepolicyVersion; |
|
} |
|
|
|
const std::vector<Vndk> &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.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
|
|
|