| /* |
| * 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 "VintfObject.h" |
| |
| #include <dirent.h> |
| |
| #include <algorithm> |
| #include <functional> |
| #include <memory> |
| #include <mutex> |
| |
| #include <android-base/logging.h> |
| #include <android-base/result.h> |
| #include <android-base/strings.h> |
| #include <hidl/metadata.h> |
| |
| #include "CompatibilityMatrix.h" |
| #include "parse_string.h" |
| #include "parse_xml.h" |
| #include "utils.h" |
| |
| using std::placeholders::_1; |
| using std::placeholders::_2; |
| |
| namespace android { |
| namespace vintf { |
| |
| using namespace details; |
| |
| #ifdef LIBVINTF_TARGET |
| static constexpr bool kIsTarget = true; |
| #else |
| static constexpr bool kIsTarget = false; |
| #endif |
| |
| template <typename T, typename F> |
| static std::shared_ptr<const T> Get(const char* id, LockedSharedPtr<T>* ptr, bool skipCache, |
| const F& fetchAllInformation) { |
| std::unique_lock<std::mutex> _lock(ptr->mutex); |
| if (skipCache || !ptr->fetchedOnce) { |
| LOG(INFO) << id << ": Reading VINTF information."; |
| ptr->object = std::make_unique<T>(); |
| std::string error; |
| status_t status = fetchAllInformation(ptr->object.get(), &error); |
| if (status == OK) { |
| ptr->fetchedOnce = true; |
| LOG(INFO) << id << ": Successfully processed VINTF information"; |
| } else { |
| // Doubled because a malformed error std::string might cause us to |
| // lose the status. |
| LOG(ERROR) << id << ": status from fetching VINTF information: " << status; |
| LOG(ERROR) << id << ": " << status << " VINTF parse error: " << error; |
| ptr->object = nullptr; // frees the old object |
| } |
| } |
| return ptr->object; |
| } |
| |
| static std::unique_ptr<FileSystem> createDefaultFileSystem() { |
| std::unique_ptr<FileSystem> fileSystem; |
| if (kIsTarget) { |
| fileSystem = std::make_unique<details::FileSystemImpl>(); |
| } else { |
| fileSystem = std::make_unique<details::FileSystemNoOp>(); |
| } |
| return fileSystem; |
| } |
| |
| static std::unique_ptr<PropertyFetcher> createDefaultPropertyFetcher() { |
| std::unique_ptr<PropertyFetcher> propertyFetcher; |
| if (kIsTarget) { |
| propertyFetcher = std::make_unique<details::PropertyFetcherImpl>(); |
| } else { |
| propertyFetcher = std::make_unique<details::PropertyFetcherNoOp>(); |
| } |
| return propertyFetcher; |
| } |
| |
| std::shared_ptr<VintfObject> VintfObject::GetInstance() { |
| static details::LockedSharedPtr<VintfObject> sInstance{}; |
| std::unique_lock<std::mutex> lock(sInstance.mutex); |
| if (sInstance.object == nullptr) { |
| sInstance.object = std::shared_ptr<VintfObject>(VintfObject::Builder().build().release()); |
| } |
| return sInstance.object; |
| } |
| |
| std::shared_ptr<const HalManifest> VintfObject::GetDeviceHalManifest(bool skipCache) { |
| return GetInstance()->getDeviceHalManifest(skipCache); |
| } |
| |
| std::shared_ptr<const HalManifest> VintfObject::getDeviceHalManifest(bool skipCache) { |
| return Get(__func__, &mDeviceManifest, skipCache, |
| std::bind(&VintfObject::fetchDeviceHalManifest, this, _1, _2)); |
| } |
| |
| std::shared_ptr<const HalManifest> VintfObject::GetFrameworkHalManifest(bool skipCache) { |
| return GetInstance()->getFrameworkHalManifest(skipCache); |
| } |
| |
| std::shared_ptr<const HalManifest> VintfObject::getFrameworkHalManifest(bool skipCache) { |
| return Get(__func__, &mFrameworkManifest, skipCache, |
| std::bind(&VintfObject::fetchFrameworkHalManifest, this, _1, _2)); |
| } |
| |
| std::shared_ptr<const CompatibilityMatrix> VintfObject::GetDeviceCompatibilityMatrix(bool skipCache) { |
| return GetInstance()->getDeviceCompatibilityMatrix(skipCache); |
| } |
| |
| std::shared_ptr<const CompatibilityMatrix> VintfObject::getDeviceCompatibilityMatrix( |
| bool skipCache) { |
| return Get(__func__, &mDeviceMatrix, skipCache, |
| std::bind(&VintfObject::fetchDeviceMatrix, this, _1, _2)); |
| } |
| |
| std::shared_ptr<const CompatibilityMatrix> VintfObject::GetFrameworkCompatibilityMatrix(bool skipCache) { |
| return GetInstance()->getFrameworkCompatibilityMatrix(skipCache); |
| } |
| |
| std::shared_ptr<const CompatibilityMatrix> VintfObject::getFrameworkCompatibilityMatrix( |
| bool skipCache) { |
| // To avoid deadlock, get device manifest before any locks. |
| auto deviceManifest = getDeviceHalManifest(); |
| |
| std::unique_lock<std::mutex> _lock(mFrameworkCompatibilityMatrixMutex); |
| |
| auto combined = |
| Get(__func__, &mCombinedFrameworkMatrix, skipCache, |
| std::bind(&VintfObject::getCombinedFrameworkMatrix, this, deviceManifest, _1, _2)); |
| if (combined != nullptr) { |
| return combined; |
| } |
| |
| return Get(__func__, &mFrameworkMatrix, skipCache, |
| std::bind(&CompatibilityMatrix::fetchAllInformation, _1, getFileSystem().get(), |
| kSystemLegacyMatrix, _2)); |
| } |
| |
| status_t VintfObject::getCombinedFrameworkMatrix( |
| const std::shared_ptr<const HalManifest>& deviceManifest, CompatibilityMatrix* out, |
| std::string* error) { |
| std::vector<CompatibilityMatrix> matrixFragments; |
| auto matrixFragmentsStatus = getAllFrameworkMatrixLevels(&matrixFragments, error); |
| if (matrixFragmentsStatus != OK) { |
| return matrixFragmentsStatus; |
| } |
| if (matrixFragments.empty()) { |
| if (error && error->empty()) { |
| *error = "Cannot get framework matrix for each FCM version for unknown error."; |
| } |
| return NAME_NOT_FOUND; |
| } |
| |
| Level deviceLevel = Level::UNSPECIFIED; |
| |
| if (deviceManifest != nullptr) { |
| deviceLevel = deviceManifest->level(); |
| } |
| |
| // TODO(b/70628538): Do not infer from Shipping API level. |
| if (deviceLevel == Level::UNSPECIFIED) { |
| auto shippingApi = getPropertyFetcher()->getUintProperty("ro.product.first_api_level", 0u); |
| if (shippingApi != 0u) { |
| deviceLevel = details::convertFromApiLevel(shippingApi); |
| } |
| } |
| |
| if (deviceLevel == Level::UNSPECIFIED) { |
| // Cannot infer FCM version. Combine all matrices by assuming |
| // Shipping FCM Version == min(all supported FCM Versions in the framework) |
| for (auto&& fragment : matrixFragments) { |
| Level fragmentLevel = fragment.level(); |
| if (fragmentLevel != Level::UNSPECIFIED && deviceLevel > fragmentLevel) { |
| deviceLevel = fragmentLevel; |
| } |
| } |
| } |
| |
| if (deviceLevel == Level::UNSPECIFIED) { |
| // None of the fragments specify any FCM version. Should never happen except |
| // for inconsistent builds. |
| if (error) { |
| *error = "No framework compatibility matrix files under " + kSystemVintfDir + |
| " declare FCM version."; |
| } |
| return NAME_NOT_FOUND; |
| } |
| |
| auto combined = CompatibilityMatrix::combine(deviceLevel, &matrixFragments, error); |
| if (combined == nullptr) { |
| return BAD_VALUE; |
| } |
| *out = std::move(*combined); |
| return OK; |
| } |
| |
| // Load and combine all of the manifests in a directory |
| status_t VintfObject::addDirectoryManifests(const std::string& directory, HalManifest* manifest, |
| std::string* error) { |
| std::vector<std::string> fileNames; |
| status_t err = getFileSystem()->listFiles(directory, &fileNames, error); |
| // if the directory isn't there, that's okay |
| if (err == NAME_NOT_FOUND) return OK; |
| if (err != OK) return err; |
| |
| for (const std::string& file : fileNames) { |
| // Only adds HALs because all other things are added by libvintf |
| // itself for now. |
| HalManifest fragmentManifest; |
| err = fetchOneHalManifest(directory + file, &fragmentManifest, error); |
| if (err != OK) return err; |
| |
| if (!manifest->addAll(&fragmentManifest, error)) { |
| if (error) { |
| error->insert(0, "Cannot add manifest fragment " + directory + file + ": "); |
| } |
| return UNKNOWN_ERROR; |
| } |
| } |
| |
| return OK; |
| } |
| |
| // Priority for loading vendor manifest: |
| // 1. Vendor manifest + device fragments + ODM manifest (optional) + odm fragments |
| // 2. Vendor manifest + device fragments |
| // 3. ODM manifest (optional) + odm fragments |
| // 4. /vendor/manifest.xml (legacy, no fragments) |
| // where: |
| // A + B means unioning <hal> tags from A and B. If B declares an override, then this takes priority |
| // over A. |
| status_t VintfObject::fetchDeviceHalManifest(HalManifest* out, std::string* error) { |
| HalManifest vendorManifest; |
| status_t vendorStatus = fetchVendorHalManifest(&vendorManifest, error); |
| if (vendorStatus != OK && vendorStatus != NAME_NOT_FOUND) { |
| return vendorStatus; |
| } |
| |
| if (vendorStatus == OK) { |
| *out = std::move(vendorManifest); |
| status_t fragmentStatus = addDirectoryManifests(kVendorManifestFragmentDir, out, error); |
| if (fragmentStatus != OK) { |
| return fragmentStatus; |
| } |
| } |
| |
| HalManifest odmManifest; |
| status_t odmStatus = fetchOdmHalManifest(&odmManifest, error); |
| if (odmStatus != OK && odmStatus != NAME_NOT_FOUND) { |
| return odmStatus; |
| } |
| |
| if (vendorStatus == OK) { |
| if (odmStatus == OK) { |
| if (!out->addAll(&odmManifest, error)) { |
| if (error) { |
| error->insert(0, "Cannot add ODM manifest :"); |
| } |
| return UNKNOWN_ERROR; |
| } |
| } |
| return addDirectoryManifests(kOdmManifestFragmentDir, out, error); |
| } |
| |
| // vendorStatus != OK, "out" is not changed. |
| if (odmStatus == OK) { |
| *out = std::move(odmManifest); |
| return addDirectoryManifests(kOdmManifestFragmentDir, out, error); |
| } |
| |
| // Use legacy /vendor/manifest.xml |
| return out->fetchAllInformation(getFileSystem().get(), kVendorLegacyManifest, error); |
| } |
| |
| // Priority: |
| // 1. if {vendorSku} is defined, /vendor/etc/vintf/manifest_{vendorSku}.xml |
| // 2. /vendor/etc/vintf/manifest.xml |
| // where: |
| // {vendorSku} is the value of ro.boot.product.vendor.sku |
| status_t VintfObject::fetchVendorHalManifest(HalManifest* out, std::string* error) { |
| status_t status; |
| |
| std::string vendorSku; |
| vendorSku = getPropertyFetcher()->getProperty("ro.boot.product.vendor.sku", ""); |
| |
| if (!vendorSku.empty()) { |
| status = |
| fetchOneHalManifest(kVendorVintfDir + "manifest_" + vendorSku + ".xml", out, error); |
| if (status == OK || status != NAME_NOT_FOUND) { |
| return status; |
| } |
| } |
| |
| status = fetchOneHalManifest(kVendorManifest, out, error); |
| if (status == OK || status != NAME_NOT_FOUND) { |
| return status; |
| } |
| |
| return NAME_NOT_FOUND; |
| } |
| |
| // "out" is written to iff return status is OK. |
| // Priority: |
| // 1. if {sku} is defined, /odm/etc/vintf/manifest_{sku}.xml |
| // 2. /odm/etc/vintf/manifest.xml |
| // 3. if {sku} is defined, /odm/etc/manifest_{sku}.xml |
| // 4. /odm/etc/manifest.xml |
| // where: |
| // {sku} is the value of ro.boot.product.hardware.sku |
| status_t VintfObject::fetchOdmHalManifest(HalManifest* out, std::string* error) { |
| status_t status; |
| |
| std::string productModel; |
| productModel = getPropertyFetcher()->getProperty("ro.boot.product.hardware.sku", ""); |
| |
| if (!productModel.empty()) { |
| status = |
| fetchOneHalManifest(kOdmVintfDir + "manifest_" + productModel + ".xml", out, error); |
| if (status == OK || status != NAME_NOT_FOUND) { |
| return status; |
| } |
| } |
| |
| status = fetchOneHalManifest(kOdmManifest, out, error); |
| if (status == OK || status != NAME_NOT_FOUND) { |
| return status; |
| } |
| |
| if (!productModel.empty()) { |
| status = fetchOneHalManifest(kOdmLegacyVintfDir + "manifest_" + productModel + ".xml", out, |
| error); |
| if (status == OK || status != NAME_NOT_FOUND) { |
| return status; |
| } |
| } |
| |
| status = fetchOneHalManifest(kOdmLegacyManifest, out, error); |
| if (status == OK || status != NAME_NOT_FOUND) { |
| return status; |
| } |
| |
| return NAME_NOT_FOUND; |
| } |
| |
| // Fetch one manifest.xml file. "out" is written to iff return status is OK. |
| // Returns NAME_NOT_FOUND if file is missing. |
| status_t VintfObject::fetchOneHalManifest(const std::string& path, HalManifest* out, |
| std::string* error) { |
| HalManifest ret; |
| status_t status = ret.fetchAllInformation(getFileSystem().get(), path, error); |
| if (status == OK) { |
| *out = std::move(ret); |
| } |
| return status; |
| } |
| |
| status_t VintfObject::fetchDeviceMatrix(CompatibilityMatrix* out, std::string* error) { |
| CompatibilityMatrix etcMatrix; |
| if (etcMatrix.fetchAllInformation(getFileSystem().get(), kVendorMatrix, error) == OK) { |
| *out = std::move(etcMatrix); |
| return OK; |
| } |
| return out->fetchAllInformation(getFileSystem().get(), kVendorLegacyMatrix, error); |
| } |
| |
| // Priority: |
| // 1. /system/etc/vintf/manifest.xml |
| // + /system/etc/vintf/manifest/*.xml if they exist |
| // + /product/etc/vintf/manifest.xml if it exists |
| // + /product/etc/vintf/manifest/*.xml if they exist |
| // 2. (deprecated) /system/manifest.xml |
| status_t VintfObject::fetchFrameworkHalManifest(HalManifest* out, std::string* error) { |
| auto systemEtcStatus = fetchOneHalManifest(kSystemManifest, out, error); |
| if (systemEtcStatus == OK) { |
| auto dirStatus = addDirectoryManifests(kSystemManifestFragmentDir, out, error); |
| if (dirStatus != OK) { |
| return dirStatus; |
| } |
| |
| std::vector<std::pair<const std::string&, const std::string&>> extensions{ |
| {kProductManifest, kProductManifestFragmentDir}, |
| {kSystemExtManifest, kSystemExtManifestFragmentDir}, |
| }; |
| for (auto&& [manifestPath, frags] : extensions) { |
| HalManifest halManifest; |
| auto status = fetchOneHalManifest(manifestPath, &halManifest, error); |
| if (status != OK && status != NAME_NOT_FOUND) { |
| return status; |
| } |
| if (status == OK) { |
| if (!out->addAll(&halManifest, error)) { |
| if (error) { |
| error->insert(0, "Cannot add " + manifestPath + ":"); |
| } |
| return UNKNOWN_ERROR; |
| } |
| } |
| |
| auto fragmentStatus = addDirectoryManifests(frags, out, error); |
| if (fragmentStatus != OK) { |
| return fragmentStatus; |
| } |
| } |
| return OK; |
| } else { |
| LOG(WARNING) << "Cannot fetch " << kSystemManifest << ": " |
| << (error ? *error : strerror(-systemEtcStatus)); |
| } |
| |
| return out->fetchAllInformation(getFileSystem().get(), kSystemLegacyManifest, error); |
| } |
| |
| static void appendLine(std::string* error, const std::string& message) { |
| if (error != nullptr) { |
| if (!error->empty()) *error += "\n"; |
| *error += message; |
| } |
| } |
| |
| status_t VintfObject::getOneMatrix(const std::string& path, CompatibilityMatrix* out, |
| std::string* error) { |
| std::string content; |
| status_t status = getFileSystem()->fetch(path, &content, error); |
| if (status != OK) { |
| return status; |
| } |
| if (!gCompatibilityMatrixConverter(out, content, error)) { |
| if (error) { |
| error->insert(0, "Cannot parse " + path + ": "); |
| } |
| return BAD_VALUE; |
| } |
| out->setFileName(path); |
| return OK; |
| } |
| |
| status_t VintfObject::getAllFrameworkMatrixLevels(std::vector<CompatibilityMatrix>* results, |
| std::string* error) { |
| std::vector<std::string> dirs = { |
| kSystemVintfDir, |
| kSystemExtVintfDir, |
| kProductVintfDir, |
| }; |
| for (const auto& dir : dirs) { |
| std::vector<std::string> fileNames; |
| status_t listStatus = getFileSystem()->listFiles(dir, &fileNames, error); |
| if (listStatus == NAME_NOT_FOUND) { |
| continue; |
| } |
| if (listStatus != OK) { |
| return listStatus; |
| } |
| for (const std::string& fileName : fileNames) { |
| std::string path = dir + fileName; |
| CompatibilityMatrix namedMatrix; |
| std::string matrixError; |
| status_t matrixStatus = getOneMatrix(path, &namedMatrix, &matrixError); |
| if (matrixStatus != OK) { |
| // Manifests and matrices share the same dir. Client may not have enough |
| // permissions to read system manifests, or may not be able to parse it. |
| auto logLevel = matrixStatus == BAD_VALUE ? base::DEBUG : base::ERROR; |
| LOG(logLevel) << "Framework Matrix: Ignore file " << path << ": " << matrixError; |
| continue; |
| } |
| results->emplace_back(std::move(namedMatrix)); |
| } |
| |
| if (dir == kSystemVintfDir && results->empty()) { |
| if (error) { |
| *error = "No framework matrices under " + dir + " can be fetched or parsed.\n"; |
| } |
| return NAME_NOT_FOUND; |
| } |
| } |
| |
| if (results->empty()) { |
| if (error) { |
| *error = |
| "No framework matrices can be fetched or parsed. " |
| "The following directories are searched:\n " + |
| android::base::Join(dirs, "\n "); |
| } |
| return NAME_NOT_FOUND; |
| } |
| return OK; |
| } |
| |
| std::shared_ptr<const RuntimeInfo> VintfObject::GetRuntimeInfo(bool skipCache, |
| RuntimeInfo::FetchFlags flags) { |
| return GetInstance()->getRuntimeInfo(skipCache, flags); |
| } |
| std::shared_ptr<const RuntimeInfo> VintfObject::getRuntimeInfo(bool skipCache, |
| RuntimeInfo::FetchFlags flags) { |
| std::unique_lock<std::mutex> _lock(mDeviceRuntimeInfo.mutex); |
| |
| if (!skipCache) { |
| flags &= (~mDeviceRuntimeInfo.fetchedFlags); |
| } |
| |
| if (mDeviceRuntimeInfo.object == nullptr) { |
| mDeviceRuntimeInfo.object = getRuntimeInfoFactory()->make_shared(); |
| } |
| |
| // Fetch kernel FCM version from device HAL manifest and store it in RuntimeInfo too. |
| if ((flags & RuntimeInfo::FetchFlag::KERNEL_FCM) != 0) { |
| auto manifest = getDeviceHalManifest(); |
| if (!manifest) { |
| mDeviceRuntimeInfo.fetchedFlags &= ~RuntimeInfo::FetchFlag::KERNEL_FCM; |
| return nullptr; |
| } |
| Level level = Level::UNSPECIFIED; |
| if (manifest->kernel().has_value()) { |
| level = manifest->kernel()->level(); |
| } |
| mDeviceRuntimeInfo.object->setKernelLevel(level); |
| flags &= ~RuntimeInfo::FetchFlag::KERNEL_FCM; |
| } |
| |
| status_t status = mDeviceRuntimeInfo.object->fetchAllInformation(flags); |
| if (status != OK) { |
| mDeviceRuntimeInfo.fetchedFlags &= (~flags); // mark the fields as "not fetched" |
| return nullptr; |
| } |
| |
| mDeviceRuntimeInfo.fetchedFlags |= flags; |
| return mDeviceRuntimeInfo.object; |
| } |
| |
| int32_t VintfObject::checkCompatibility(std::string* error, CheckFlags::Type flags) { |
| status_t status = OK; |
| // null checks for files and runtime info |
| if (getFrameworkHalManifest() == nullptr) { |
| appendLine(error, "No framework manifest file from device or from update package"); |
| status = NO_INIT; |
| } |
| if (getDeviceHalManifest() == nullptr) { |
| appendLine(error, "No device manifest file from device or from update package"); |
| status = NO_INIT; |
| } |
| if (getFrameworkCompatibilityMatrix() == nullptr) { |
| appendLine(error, "No framework matrix file from device or from update package"); |
| status = NO_INIT; |
| } |
| if (getDeviceCompatibilityMatrix() == nullptr) { |
| appendLine(error, "No device matrix file from device or from update package"); |
| status = NO_INIT; |
| } |
| |
| if (flags.isRuntimeInfoEnabled()) { |
| if (getRuntimeInfo() == nullptr) { |
| appendLine(error, "No runtime info from device"); |
| status = NO_INIT; |
| } |
| } |
| if (status != OK) return status; |
| |
| // compatiblity check. |
| if (!getDeviceHalManifest()->checkCompatibility(*getFrameworkCompatibilityMatrix(), error)) { |
| if (error) { |
| error->insert(0, |
| "Device manifest and framework compatibility matrix are incompatible: "); |
| } |
| return INCOMPATIBLE; |
| } |
| if (!getFrameworkHalManifest()->checkCompatibility(*getDeviceCompatibilityMatrix(), error)) { |
| if (error) { |
| error->insert(0, |
| "Framework manifest and device compatibility matrix are incompatible: "); |
| } |
| return INCOMPATIBLE; |
| } |
| |
| if (flags.isRuntimeInfoEnabled()) { |
| if (!getRuntimeInfo()->checkCompatibility(*getFrameworkCompatibilityMatrix(), error, |
| flags)) { |
| if (error) { |
| error->insert(0, |
| "Runtime info and framework compatibility matrix are incompatible: "); |
| } |
| return INCOMPATIBLE; |
| } |
| } |
| |
| return COMPATIBLE; |
| } |
| |
| namespace details { |
| |
| const std::string kSystemVintfDir = "/system/etc/vintf/"; |
| const std::string kVendorVintfDir = "/vendor/etc/vintf/"; |
| const std::string kOdmVintfDir = "/odm/etc/vintf/"; |
| const std::string kProductVintfDir = "/product/etc/vintf/"; |
| const std::string kSystemExtVintfDir = "/system_ext/etc/vintf/"; |
| |
| const std::string kVendorManifest = kVendorVintfDir + "manifest.xml"; |
| const std::string kSystemManifest = kSystemVintfDir + "manifest.xml"; |
| const std::string kVendorMatrix = kVendorVintfDir + "compatibility_matrix.xml"; |
| const std::string kOdmManifest = kOdmVintfDir + "manifest.xml"; |
| const std::string kProductMatrix = kProductVintfDir + "compatibility_matrix.xml"; |
| const std::string kProductManifest = kProductVintfDir + "manifest.xml"; |
| const std::string kSystemExtManifest = kSystemExtVintfDir + "manifest.xml"; |
| |
| const std::string kVendorManifestFragmentDir = kVendorVintfDir + "manifest/"; |
| const std::string kSystemManifestFragmentDir = kSystemVintfDir + "manifest/"; |
| const std::string kOdmManifestFragmentDir = kOdmVintfDir + "manifest/"; |
| const std::string kProductManifestFragmentDir = kProductVintfDir + "manifest/"; |
| const std::string kSystemExtManifestFragmentDir = kSystemExtVintfDir + "manifest/"; |
| |
| const std::string kVendorLegacyManifest = "/vendor/manifest.xml"; |
| const std::string kVendorLegacyMatrix = "/vendor/compatibility_matrix.xml"; |
| const std::string kSystemLegacyManifest = "/system/manifest.xml"; |
| const std::string kSystemLegacyMatrix = "/system/compatibility_matrix.xml"; |
| const std::string kOdmLegacyVintfDir = "/odm/etc/"; |
| const std::string kOdmLegacyManifest = kOdmLegacyVintfDir + "manifest.xml"; |
| |
| std::vector<std::string> dumpFileList() { |
| return { |
| // clang-format off |
| kSystemVintfDir, |
| kVendorVintfDir, |
| kOdmVintfDir, |
| kProductVintfDir, |
| kSystemExtVintfDir, |
| kOdmLegacyVintfDir, |
| kVendorLegacyManifest, |
| kVendorLegacyMatrix, |
| kSystemLegacyManifest, |
| kSystemLegacyMatrix, |
| // clang-format on |
| }; |
| } |
| |
| } // namespace details |
| |
| bool VintfObject::IsHalDeprecated(const MatrixHal& oldMatrixHal, |
| const CompatibilityMatrix& targetMatrix, |
| const ListInstances& listInstances, |
| const ChildrenMap& childrenMap, std::string* appendedError) { |
| bool isDeprecated = false; |
| oldMatrixHal.forEachInstance([&](const MatrixInstance& oldMatrixInstance) { |
| if (IsInstanceDeprecated(oldMatrixInstance, targetMatrix, listInstances, childrenMap, |
| appendedError)) { |
| isDeprecated = true; |
| } |
| return true; // continue to check next instance |
| }); |
| return isDeprecated; |
| } |
| |
| // Let oldMatrixInstance = package@x.y-w::interface/instancePattern. |
| // If any "@servedVersion::interface/servedInstance" in listInstances(package@x.y::interface) |
| // matches instancePattern, return true iff for all child interfaces (from |
| // GetListedInstanceInheritance), IsFqInstanceDeprecated returns false. |
| bool VintfObject::IsInstanceDeprecated(const MatrixInstance& oldMatrixInstance, |
| const CompatibilityMatrix& targetMatrix, |
| const ListInstances& listInstances, |
| const ChildrenMap& childrenMap, std::string* appendedError) { |
| const std::string& package = oldMatrixInstance.package(); |
| const Version& version = oldMatrixInstance.versionRange().minVer(); |
| const std::string& interface = oldMatrixInstance.interface(); |
| |
| std::vector<std::string> instanceHint; |
| if (!oldMatrixInstance.isRegex()) { |
| instanceHint.push_back(oldMatrixInstance.exactInstance()); |
| } |
| |
| std::vector<std::string> accumulatedErrors; |
| auto list = listInstances(package, version, interface, instanceHint); |
| |
| for (const auto& pair : list) { |
| const std::string& servedInstance = pair.first; |
| Version servedVersion = pair.second; |
| std::string servedFqInstanceString = |
| toFQNameString(package, servedVersion, interface, servedInstance); |
| if (!oldMatrixInstance.matchInstance(servedInstance)) { |
| // ignore unrelated instance |
| continue; |
| } |
| |
| auto inheritance = GetListedInstanceInheritance(package, servedVersion, interface, |
| servedInstance, listInstances, childrenMap); |
| if (!inheritance.has_value()) { |
| accumulatedErrors.push_back(inheritance.error().message()); |
| continue; |
| } |
| |
| std::vector<std::string> errors; |
| for (const auto& fqInstance : *inheritance) { |
| auto result = IsFqInstanceDeprecated(targetMatrix, oldMatrixInstance.format(), |
| fqInstance, listInstances); |
| if (result.ok()) { |
| errors.clear(); |
| break; |
| } |
| errors.push_back(result.error().message()); |
| } |
| |
| if (errors.empty()) { |
| continue; |
| } |
| accumulatedErrors.insert(accumulatedErrors.end(), errors.begin(), errors.end()); |
| } |
| |
| if (accumulatedErrors.empty()) { |
| return false; |
| } |
| appendLine(appendedError, android::base::Join(accumulatedErrors, "\n")); |
| return true; |
| } |
| |
| // Check if fqInstance is listed in |listInstances|. |
| bool VintfObject::IsInstanceListed(const ListInstances& listInstances, |
| const FqInstance& fqInstance) { |
| auto list = |
| listInstances(fqInstance.getPackage(), fqInstance.getVersion(), fqInstance.getInterface(), |
| {fqInstance.getInstance()} /* instanceHint*/); |
| return std::any_of(list.begin(), list.end(), |
| [&](const auto& pair) { return pair.first == fqInstance.getInstance(); }); |
| } |
| |
| // Return a list of FqInstance, where each element: |
| // - is listed in |listInstances|; AND |
| // - is, or inherits from, package@version::interface/instance (as specified by |childrenMap|) |
| android::base::Result<std::vector<FqInstance>> VintfObject::GetListedInstanceInheritance( |
| const std::string& package, const Version& version, const std::string& interface, |
| const std::string& instance, const ListInstances& listInstances, |
| const ChildrenMap& childrenMap) { |
| FqInstance fqInstance; |
| if (!fqInstance.setTo(package, version.majorVer, version.minorVer, interface, instance)) { |
| return android::base::Error() << toFQNameString(package, version, interface, instance) |
| << " is not a valid FqInstance"; |
| } |
| |
| if (!IsInstanceListed(listInstances, fqInstance)) { |
| return {}; |
| } |
| |
| const FQName& fqName = fqInstance.getFqName(); |
| |
| std::vector<FqInstance> ret; |
| ret.push_back(fqInstance); |
| |
| auto childRange = childrenMap.equal_range(fqName.string()); |
| for (auto it = childRange.first; it != childRange.second; ++it) { |
| const auto& childFqNameString = it->second; |
| FQName childFqName; |
| if (!childFqName.setTo(childFqNameString)) { |
| return android::base::Error() << "Cannot parse " << childFqNameString << " as FQName"; |
| } |
| FqInstance childFqInstance; |
| if (!childFqInstance.setTo(childFqName, fqInstance.getInstance())) { |
| return android::base::Error() << "Cannot merge " << childFqName.string() << "/" |
| << fqInstance.getInstance() << " as FqInstance"; |
| continue; |
| } |
| if (!IsInstanceListed(listInstances, childFqInstance)) { |
| continue; |
| } |
| ret.push_back(childFqInstance); |
| } |
| return ret; |
| } |
| |
| // Check if |fqInstance| is in |targetMatrix|; essentially equal to |
| // targetMatrix.matchInstance(fqInstance), but provides richer error message. In details: |
| // 1. package@x.?::interface/servedInstance is not in targetMatrix; OR |
| // 2. package@x.z::interface/servedInstance is in targetMatrix but |
| // servedInstance is not in listInstances(package@x.z::interface) |
| android::base::Result<void> VintfObject::IsFqInstanceDeprecated( |
| const CompatibilityMatrix& targetMatrix, HalFormat format, const FqInstance& fqInstance, |
| const ListInstances& listInstances) { |
| // Find minimum package@x.? in target matrix, and check if instance is in target matrix. |
| bool foundInstance = false; |
| Version targetMatrixMinVer{SIZE_MAX, SIZE_MAX}; |
| targetMatrix.forEachInstanceOfPackage( |
| format, fqInstance.getPackage(), [&](const auto& targetMatrixInstance) { |
| if (targetMatrixInstance.versionRange().majorVer == fqInstance.getMajorVersion() && |
| targetMatrixInstance.interface() == fqInstance.getInterface() && |
| targetMatrixInstance.matchInstance(fqInstance.getInstance())) { |
| targetMatrixMinVer = |
| std::min(targetMatrixMinVer, targetMatrixInstance.versionRange().minVer()); |
| foundInstance = true; |
| } |
| return true; |
| }); |
| if (!foundInstance) { |
| return android::base::Error() |
| << fqInstance.string() << " is deprecated in compatibility matrix at FCM Version " |
| << targetMatrix.level() << "; it should not be served."; |
| } |
| |
| // Assuming that targetMatrix requires @x.u-v, require that at least @x.u is served. |
| bool targetVersionServed = false; |
| for (const auto& newPair : |
| listInstances(fqInstance.getPackage(), targetMatrixMinVer, fqInstance.getInterface(), |
| {fqInstance.getInstance()} /* instanceHint */)) { |
| if (newPair.first == fqInstance.getInstance()) { |
| targetVersionServed = true; |
| break; |
| } |
| } |
| |
| if (!targetVersionServed) { |
| return android::base::Error() |
| << fqInstance.string() << " is deprecated; requires at least " << targetMatrixMinVer; |
| } |
| return {}; |
| } |
| |
| int32_t VintfObject::checkDeprecation(const ListInstances& listInstances, |
| const std::vector<HidlInterfaceMetadata>& hidlMetadata, |
| std::string* error) { |
| std::vector<CompatibilityMatrix> matrixFragments; |
| auto matrixFragmentsStatus = getAllFrameworkMatrixLevels(&matrixFragments, error); |
| if (matrixFragmentsStatus != OK) { |
| return matrixFragmentsStatus; |
| } |
| if (matrixFragments.empty()) { |
| if (error && error->empty()) { |
| *error = "Cannot get framework matrix for each FCM version for unknown error."; |
| } |
| return NAME_NOT_FOUND; |
| } |
| auto deviceManifest = getDeviceHalManifest(); |
| if (deviceManifest == nullptr) { |
| if (error) *error = "No device manifest."; |
| return NAME_NOT_FOUND; |
| } |
| Level deviceLevel = deviceManifest->level(); |
| if (deviceLevel == Level::UNSPECIFIED) { |
| if (error) *error = "Device manifest does not specify Shipping FCM Version."; |
| return BAD_VALUE; |
| } |
| |
| const CompatibilityMatrix* targetMatrix = nullptr; |
| for (const auto& namedMatrix : matrixFragments) { |
| if (namedMatrix.level() == deviceLevel) { |
| targetMatrix = &namedMatrix; |
| } |
| } |
| if (targetMatrix == nullptr) { |
| if (error) |
| *error = "Cannot find framework matrix at FCM version " + to_string(deviceLevel) + "."; |
| return NAME_NOT_FOUND; |
| } |
| |
| std::multimap<std::string, std::string> childrenMap; |
| for (const auto& child : hidlMetadata) { |
| for (const auto& parent : child.inherited) { |
| childrenMap.emplace(parent, child.name); |
| } |
| } |
| |
| // Find a list of possibly deprecated HALs by comparing |listInstances| with older matrices. |
| // Matrices with unspecified level are considered "current". |
| bool isDeprecated = false; |
| for (const auto& namedMatrix : matrixFragments) { |
| if (namedMatrix.level() == Level::UNSPECIFIED) continue; |
| if (namedMatrix.level() >= deviceLevel) continue; |
| |
| for (const MatrixHal& hal : namedMatrix.getHals()) { |
| if (IsHalDeprecated(hal, *targetMatrix, listInstances, childrenMap, error)) { |
| isDeprecated = true; |
| } |
| } |
| } |
| |
| return isDeprecated ? DEPRECATED : NO_DEPRECATED_HALS; |
| } |
| |
| int32_t VintfObject::checkDeprecation(const std::vector<HidlInterfaceMetadata>& hidlMetadata, |
| std::string* error) { |
| using namespace std::placeholders; |
| auto deviceManifest = getDeviceHalManifest(); |
| ListInstances inManifest = |
| [&deviceManifest](const std::string& package, Version version, const std::string& interface, |
| const std::vector<std::string>& /* hintInstances */) { |
| std::vector<std::pair<std::string, Version>> ret; |
| deviceManifest->forEachInstanceOfInterface( |
| HalFormat::HIDL, package, version, interface, |
| [&ret](const ManifestInstance& manifestInstance) { |
| ret.push_back( |
| std::make_pair(manifestInstance.instance(), manifestInstance.version())); |
| return true; |
| }); |
| return ret; |
| }; |
| return checkDeprecation(inManifest, hidlMetadata, error); |
| } |
| |
| Level VintfObject::getKernelLevel(std::string* error) { |
| auto manifest = getDeviceHalManifest(); |
| if (!manifest) { |
| if (error) *error = "Cannot retrieve device manifest."; |
| return Level::UNSPECIFIED; |
| } |
| if (manifest->kernel().has_value() && manifest->kernel()->level() != Level::UNSPECIFIED) { |
| return manifest->kernel()->level(); |
| } |
| if (error) *error = "Device manifest does not specify kernel FCM version."; |
| return Level::UNSPECIFIED; |
| } |
| |
| const std::unique_ptr<FileSystem>& VintfObject::getFileSystem() { |
| return mFileSystem; |
| } |
| |
| const std::unique_ptr<PropertyFetcher>& VintfObject::getPropertyFetcher() { |
| return mPropertyFetcher; |
| } |
| |
| const std::unique_ptr<ObjectFactory<RuntimeInfo>>& VintfObject::getRuntimeInfoFactory() { |
| return mRuntimeInfoFactory; |
| } |
| |
| android::base::Result<bool> VintfObject::hasFrameworkCompatibilityMatrixExtensions() { |
| std::vector<CompatibilityMatrix> matrixFragments; |
| std::string error; |
| status_t status = getAllFrameworkMatrixLevels(&matrixFragments, &error); |
| if (status != OK) { |
| return android::base::Error(-status) |
| << "Cannot get all framework matrix fragments: " << error; |
| } |
| for (const auto& namedMatrix : matrixFragments) { |
| // Returns true if product matrix exists. |
| if (android::base::StartsWith(namedMatrix.fileName(), kProductVintfDir)) { |
| return true; |
| } |
| // Returns true if system_ext matrix exists. |
| if (android::base::StartsWith(namedMatrix.fileName(), kSystemExtVintfDir)) { |
| return true; |
| } |
| // Returns true if device system matrix exists. |
| if (android::base::StartsWith(namedMatrix.fileName(), kSystemVintfDir) && |
| namedMatrix.level() == Level::UNSPECIFIED && !namedMatrix.getHals().empty()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| android::base::Result<void> VintfObject::checkUnusedHals( |
| const std::vector<HidlInterfaceMetadata>& hidlMetadata) { |
| auto matrix = getFrameworkCompatibilityMatrix(); |
| if (matrix == nullptr) { |
| return android::base::Error(-NAME_NOT_FOUND) << "Missing framework matrix."; |
| } |
| auto manifest = getDeviceHalManifest(); |
| if (manifest == nullptr) { |
| return android::base::Error(-NAME_NOT_FOUND) << "Missing device manifest."; |
| } |
| auto unused = manifest->checkUnusedHals(*matrix, hidlMetadata); |
| if (!unused.empty()) { |
| return android::base::Error() |
| << "The following instances are in the device manifest but " |
| << "not specified in framework compatibility matrix: \n" |
| << " " << android::base::Join(unused, "\n ") << "\n" |
| << "Suggested fix:\n" |
| << "1. Update deprecated HALs to the latest version.\n" |
| << "2. Check for any typos in device manifest or framework compatibility " |
| << "matrices with FCM version >= " << matrix->level() << ".\n" |
| << "3. For new platform HALs, add them to any framework compatibility matrix " |
| << "with FCM version >= " << matrix->level() << " where applicable.\n" |
| << "4. For device-specific HALs, add to DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE " |
| << "or DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE."; |
| } |
| return {}; |
| } |
| |
| // make_unique does not work because VintfObject constructor is private. |
| VintfObject::Builder::Builder() : mObject(std::unique_ptr<VintfObject>(new VintfObject())) {} |
| |
| VintfObject::Builder& VintfObject::Builder::setFileSystem(std::unique_ptr<FileSystem>&& e) { |
| mObject->mFileSystem = std::move(e); |
| return *this; |
| } |
| |
| VintfObject::Builder& VintfObject::Builder::setRuntimeInfoFactory( |
| std::unique_ptr<ObjectFactory<RuntimeInfo>>&& e) { |
| mObject->mRuntimeInfoFactory = std::move(e); |
| return *this; |
| } |
| |
| VintfObject::Builder& VintfObject::Builder::setPropertyFetcher( |
| std::unique_ptr<PropertyFetcher>&& e) { |
| mObject->mPropertyFetcher = std::move(e); |
| return *this; |
| } |
| |
| std::unique_ptr<VintfObject> VintfObject::Builder::build() { |
| if (!mObject->mFileSystem) mObject->mFileSystem = createDefaultFileSystem(); |
| if (!mObject->mRuntimeInfoFactory) |
| mObject->mRuntimeInfoFactory = std::make_unique<ObjectFactory<RuntimeInfo>>(); |
| if (!mObject->mPropertyFetcher) mObject->mPropertyFetcher = createDefaultPropertyFetcher(); |
| return std::move(mObject); |
| } |
| |
| } // namespace vintf |
| } // namespace android |