| /* |
| * 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 <aidl/metadata.h> |
| #include <android-base/logging.h> |
| #include <android-base/result.h> |
| #include <android-base/strings.h> |
| #include <hidl/metadata.h> |
| |
| #include "CompatibilityMatrix.h" |
| #include "constants-private.h" |
| #include "parse_string.h" |
| #include "parse_xml.h" |
| #include "utils.h" |
| |
| using std::placeholders::_1; |
| using std::placeholders::_2; |
| using std::string_literals::operator""s; |
| |
| 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, |
| const F& fetchAllInformation) { |
| std::unique_lock<std::mutex> _lock(ptr->mutex); |
| if (!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() { |
| return GetInstance()->getDeviceHalManifest(); |
| } |
| |
| std::shared_ptr<const HalManifest> VintfObject::getDeviceHalManifest() { |
| return Get(__func__, &mDeviceManifest, |
| std::bind(&VintfObject::fetchDeviceHalManifest, this, _1, _2)); |
| } |
| |
| std::shared_ptr<const HalManifest> VintfObject::GetFrameworkHalManifest() { |
| return GetInstance()->getFrameworkHalManifest(); |
| } |
| |
| std::shared_ptr<const HalManifest> VintfObject::getFrameworkHalManifest() { |
| return Get(__func__, &mFrameworkManifest, |
| std::bind(&VintfObject::fetchFrameworkHalManifest, this, _1, _2)); |
| } |
| |
| std::shared_ptr<const CompatibilityMatrix> VintfObject::GetDeviceCompatibilityMatrix() { |
| return GetInstance()->getDeviceCompatibilityMatrix(); |
| } |
| |
| std::shared_ptr<const CompatibilityMatrix> VintfObject::getDeviceCompatibilityMatrix() { |
| return Get(__func__, &mDeviceMatrix, std::bind(&VintfObject::fetchDeviceMatrix, this, _1, _2)); |
| } |
| |
| std::shared_ptr<const CompatibilityMatrix> VintfObject::GetFrameworkCompatibilityMatrix() { |
| return GetInstance()->getFrameworkCompatibilityMatrix(); |
| } |
| |
| std::shared_ptr<const CompatibilityMatrix> VintfObject::getFrameworkCompatibilityMatrix() { |
| // To avoid deadlock, get device manifest before any locks. |
| auto deviceManifest = getDeviceHalManifest(); |
| |
| std::unique_lock<std::mutex> _lock(mFrameworkCompatibilityMatrixMutex); |
| |
| auto combined = |
| Get(__func__, &mCombinedFrameworkMatrix, |
| std::bind(&VintfObject::getCombinedFrameworkMatrix, this, deviceManifest, _1, _2)); |
| if (combined != nullptr) { |
| return combined; |
| } |
| |
| return Get(__func__, &mFrameworkMatrix, |
| 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 "s + 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_"s + 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_"s + 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_"s + 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::fetchUnfilteredFrameworkHalManifest(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 char*, const char*>> 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 "s + 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); |
| } |
| |
| status_t VintfObject::fetchFrameworkHalManifest(HalManifest* out, std::string* error) { |
| status_t status = fetchUnfilteredFrameworkHalManifest(out, error); |
| if (status != OK) { |
| return status; |
| } |
| filterHalsByDeviceManifestLevel(out); |
| return OK; |
| } |
| |
| void VintfObject::filterHalsByDeviceManifestLevel(HalManifest* out) { |
| auto deviceManifest = getDeviceHalManifest(); |
| if (deviceManifest == nullptr) { |
| LOG(WARNING) << "Cannot fetch device manifest to determine target FCM version to " |
| "filter framework manifest HALs."; |
| return; |
| } |
| Level deviceManifestLevel = deviceManifest->level(); |
| if (deviceManifestLevel == Level::UNSPECIFIED) { |
| LOG(WARNING) |
| << "Not filtering framework manifest HALs because target FCM version is unspecified."; |
| return; |
| } |
| out->removeHalsIf([deviceManifestLevel](const ManifestHal& hal) { |
| if (hal.getMaxLevel() == Level::UNSPECIFIED) { |
| return false; |
| } |
| return hal.getMaxLevel() < deviceManifestLevel; |
| }); |
| } |
| |
| 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 (!fromXml(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(RuntimeInfo::FetchFlags flags) { |
| return GetInstance()->getRuntimeInfo(flags); |
| } |
| std::shared_ptr<const RuntimeInfo> VintfObject::getRuntimeInfo(RuntimeInfo::FetchFlags flags) { |
| std::unique_lock<std::mutex> _lock(mDeviceRuntimeInfo.mutex); |
| |
| // Skip fetching information that has already been fetched previously. |
| flags &= (~mDeviceRuntimeInfo.fetchedFlags); |
| |
| if (mDeviceRuntimeInfo.object == nullptr) { |
| mDeviceRuntimeInfo.object = getRuntimeInfoFactory()->make_shared(); |
| } |
| |
| status_t status = mDeviceRuntimeInfo.object->fetchAllInformation(flags); |
| if (status != OK) { |
| // If only kernel FCM is needed, ignore errors when fetching RuntimeInfo because RuntimeInfo |
| // is not available on host. On host, the kernel level can still be inferred from device |
| // manifest. |
| // If other information is needed, flag the error by returning nullptr. |
| auto allExceptKernelFcm = RuntimeInfo::FetchFlag::ALL & ~RuntimeInfo::FetchFlag::KERNEL_FCM; |
| bool needDeviceRuntimeInfo = flags & allExceptKernelFcm; |
| if (needDeviceRuntimeInfo) { |
| mDeviceRuntimeInfo.fetchedFlags &= (~flags); // mark the fields as "not fetched" |
| return nullptr; |
| } |
| } |
| |
| // To support devices without GKI, RuntimeInfo::fetchAllInformation does not report errors |
| // if kernel level cannot be retrieved. If so, fetch kernel FCM version from device HAL |
| // manifest and store it in RuntimeInfo too. |
| if (flags & RuntimeInfo::FetchFlag::KERNEL_FCM) { |
| Level deviceManifestKernelLevel = Level::UNSPECIFIED; |
| auto manifest = getDeviceHalManifest(); |
| if (manifest) { |
| deviceManifestKernelLevel = manifest->inferredKernelLevel(); |
| } |
| if (deviceManifestKernelLevel != Level::UNSPECIFIED) { |
| Level kernelLevel = mDeviceRuntimeInfo.object->kernelLevel(); |
| if (kernelLevel == Level::UNSPECIFIED) { |
| mDeviceRuntimeInfo.object->setKernelLevel(deviceManifestKernelLevel); |
| } else if (kernelLevel != deviceManifestKernelLevel) { |
| LOG(WARNING) << "uname() reports kernel level " << kernelLevel |
| << " but device manifest sets kernel level " |
| << deviceManifestKernelLevel << ". Using kernel level " << kernelLevel; |
| } |
| } |
| } |
| |
| 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 { |
| |
| 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; |
| } |
| |
| std::vector<CompatibilityMatrix> targetMatrices; |
| // Partition matrixFragments into two groups, where the second group |
| // contains all matrices whose level == deviceLevel. |
| auto targetMatricesPartition = std::partition( |
| matrixFragments.begin(), matrixFragments.end(), |
| [&](const CompatibilityMatrix& matrix) { return matrix.level() != deviceLevel; }); |
| // Move these matrices into the targetMatrices vector... |
| std::move(targetMatricesPartition, matrixFragments.end(), std::back_inserter(targetMatrices)); |
| if (targetMatrices.empty()) { |
| if (error) |
| *error = "Cannot find framework matrix at FCM version " + to_string(deviceLevel) + "."; |
| return NAME_NOT_FOUND; |
| } |
| // so that they can be combined into one matrix for deprecation checking. |
| auto targetMatrix = CompatibilityMatrix::combine(deviceLevel, &targetMatrices, error); |
| if (targetMatrix == nullptr) { |
| return BAD_VALUE; |
| } |
| |
| 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 (auto it = matrixFragments.begin(); it < targetMatricesPartition; ++it) { |
| const auto& namedMatrix = *it; |
| 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 runtimeInfo = getRuntimeInfo(RuntimeInfo::FetchFlag::KERNEL_FCM); |
| if (!runtimeInfo) { |
| if (error) *error = "Cannot retrieve runtime info with kernel level."; |
| return Level::UNSPECIFIED; |
| } |
| if (runtimeInfo->kernelLevel() != Level::UNSPECIFIED) { |
| return runtimeInfo->kernelLevel(); |
| } |
| if (error) { |
| *error = "Both device manifest and kernel release do 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 {}; |
| } |
| |
| namespace { |
| |
| // Insert |name| into |ret| if shouldCheck(name). |
| void InsertIf(const std::string& name, const std::function<bool(const std::string&)>& shouldCheck, |
| std::set<std::string>* ret) { |
| if (shouldCheck(name)) ret->insert(name); |
| } |
| |
| std::string StripHidlInterface(const std::string& fqNameString) { |
| FQName fqName; |
| if (!fqName.setTo(fqNameString)) { |
| return ""; |
| } |
| return fqName.getPackageAndVersion().string(); |
| } |
| |
| std::string StripAidlType(const std::string& type) { |
| auto items = android::base::Split(type, "."); |
| if (items.empty()) { |
| return ""; |
| } |
| items.erase(items.end() - 1); |
| return android::base::Join(items, "."); |
| } |
| |
| // android.hardware.foo@1.0 |
| std::set<std::string> HidlMetadataToPackagesAndVersions( |
| const std::vector<HidlInterfaceMetadata>& hidlMetadata, |
| const std::function<bool(const std::string&)>& shouldCheck) { |
| std::set<std::string> ret; |
| for (const auto& item : hidlMetadata) { |
| InsertIf(StripHidlInterface(item.name), shouldCheck, &ret); |
| } |
| return ret; |
| } |
| |
| // android.hardware.foo |
| std::set<std::string> AidlMetadataToPackages( |
| const std::vector<AidlInterfaceMetadata>& aidlMetadata, |
| const std::function<bool(const std::string&)>& shouldCheck) { |
| std::set<std::string> ret; |
| for (const auto& item : aidlMetadata) { |
| for (const auto& type : item.types) { |
| InsertIf(StripAidlType(type), shouldCheck, &ret); |
| } |
| } |
| return ret; |
| } |
| |
| // android.hardware.foo@1.0::IFoo. |
| // Note that UDTs are not filtered out, so there might be non-interface types. |
| std::set<std::string> HidlMetadataToNames(const std::vector<HidlInterfaceMetadata>& hidlMetadata) { |
| std::set<std::string> ret; |
| for (const auto& item : hidlMetadata) { |
| ret.insert(item.name); |
| } |
| return ret; |
| } |
| |
| // android.hardware.foo.IFoo |
| // Note that UDTs are not filtered out, so there might be non-interface types. |
| std::set<std::string> AidlMetadataToNames(const std::vector<AidlInterfaceMetadata>& aidlMetadata) { |
| std::set<std::string> ret; |
| for (const auto& item : aidlMetadata) { |
| for (const auto& type : item.types) { |
| ret.insert(type); |
| } |
| } |
| return ret; |
| } |
| |
| } // anonymous namespace |
| |
| android::base::Result<std::vector<CompatibilityMatrix>> VintfObject::getAllFrameworkMatrixLevels() { |
| // Get all framework matrix fragments instead of the combined framework compatibility matrix |
| // because the latter may omit interfaces from the latest FCM if device target-level is not |
| // the latest. |
| std::vector<CompatibilityMatrix> matrixFragments; |
| std::string error; |
| auto matrixFragmentsStatus = getAllFrameworkMatrixLevels(&matrixFragments, &error); |
| if (matrixFragmentsStatus != OK) { |
| return android::base::Error(-matrixFragmentsStatus) |
| << "Unable to get all framework matrix fragments: " << error; |
| } |
| if (matrixFragments.empty()) { |
| if (error.empty()) { |
| error = "Cannot get framework matrix for each FCM version for unknown error."; |
| } |
| return android::base::Error(-NAME_NOT_FOUND) << error; |
| } |
| return matrixFragments; |
| } |
| |
| android::base::Result<void> VintfObject::checkMissingHalsInMatrices( |
| const std::vector<HidlInterfaceMetadata>& hidlMetadata, |
| const std::vector<AidlInterfaceMetadata>& aidlMetadata, |
| std::function<bool(const std::string&)> shouldCheck) { |
| if (!shouldCheck) { |
| shouldCheck = [](const auto&) { return true; }; |
| } |
| |
| auto matrixFragments = getAllFrameworkMatrixLevels(); |
| if (!matrixFragments.ok()) return matrixFragments.error(); |
| |
| // Filter aidlMetadata and hidlMetadata with shouldCheck. |
| auto allAidlPackages = AidlMetadataToPackages(aidlMetadata, shouldCheck); |
| auto allHidlPackagesAndVersions = HidlMetadataToPackagesAndVersions(hidlMetadata, shouldCheck); |
| |
| // Filter out instances in allAidlMetadata and allHidlMetadata that are in the matrices. |
| std::vector<std::string> errors; |
| for (const auto& matrix : matrixFragments.value()) { |
| matrix.forEachInstance([&](const MatrixInstance& matrixInstance) { |
| switch (matrixInstance.format()) { |
| case HalFormat::AIDL: { |
| allAidlPackages.erase(matrixInstance.package()); |
| return true; // continue to next instance |
| } |
| case HalFormat::HIDL: { |
| for (Version v = matrixInstance.versionRange().minVer(); |
| v <= matrixInstance.versionRange().maxVer(); ++v.minorVer) { |
| allHidlPackagesAndVersions.erase( |
| toFQNameString(matrixInstance.package(), v)); |
| } |
| return true; // continue to next instance |
| } |
| default: { |
| if (shouldCheck(matrixInstance.package())) { |
| errors.push_back("HAL package " + matrixInstance.package() + |
| " is not allowed to have format " + |
| to_string(matrixInstance.format()) + "."); |
| } |
| return true; // continue to next instance |
| } |
| } |
| }); |
| } |
| |
| if (!allHidlPackagesAndVersions.empty()) { |
| errors.push_back( |
| "The following HIDL packages are not found in any compatibility matrix fragments:\t\n" + |
| android::base::Join(allHidlPackagesAndVersions, "\t\n")); |
| } |
| if (!allAidlPackages.empty()) { |
| errors.push_back( |
| "The following AIDL packages are not found in any compatibility matrix fragments:\t\n" + |
| android::base::Join(allAidlPackages, "\t\n")); |
| } |
| |
| if (!errors.empty()) { |
| return android::base::Error() << android::base::Join(errors, "\n"); |
| } |
| |
| return {}; |
| } |
| |
| android::base::Result<void> VintfObject::checkMatrixHalsHasDefinition( |
| const std::vector<HidlInterfaceMetadata>& hidlMetadata, |
| const std::vector<AidlInterfaceMetadata>& aidlMetadata) { |
| auto matrixFragments = getAllFrameworkMatrixLevels(); |
| if (!matrixFragments.ok()) return matrixFragments.error(); |
| |
| auto allAidlNames = AidlMetadataToNames(aidlMetadata); |
| auto allHidlNames = HidlMetadataToNames(hidlMetadata); |
| std::set<std::string> badAidlInterfaces; |
| std::set<std::string> badHidlInterfaces; |
| |
| std::vector<std::string> errors; |
| for (const auto& matrix : matrixFragments.value()) { |
| if (matrix.level() == Level::UNSPECIFIED) { |
| LOG(INFO) << "Skip checkMatrixHalsHasDefinition() on " << matrix.fileName() |
| << " with no level."; |
| continue; |
| } |
| |
| matrix.forEachInstance([&](const MatrixInstance& matrixInstance) { |
| switch (matrixInstance.format()) { |
| case HalFormat::AIDL: { |
| auto matrixInterface = |
| toAidlFqnameString(matrixInstance.package(), matrixInstance.interface()); |
| if (allAidlNames.find(matrixInterface) == allAidlNames.end()) { |
| errors.push_back( |
| "AIDL interface " + matrixInterface + " is referenced in " + |
| matrix.fileName() + |
| ", but there is no corresponding .aidl definition associated with an " |
| "aidl_interface module in this build. Typo?"); |
| } |
| return true; // continue to next instance |
| } |
| case HalFormat::HIDL: { |
| for (Version v = matrixInstance.versionRange().minVer(); |
| v <= matrixInstance.versionRange().maxVer(); ++v.minorVer) { |
| auto matrixInterface = matrixInstance.interfaceDescription(v); |
| if (allHidlNames.find(matrixInterface) == allHidlNames.end()) { |
| errors.push_back( |
| "HIDL interface " + matrixInterface + " is referenced in " + |
| matrix.fileName() + |
| ", but there is no corresponding .hal definition associated with " |
| "a hidl_interface module in this build. Typo?"); |
| } |
| } |
| return true; // continue to next instance |
| } |
| default: { |
| // We do not have data for native HALs. |
| return true; // continue to next instance |
| } |
| } |
| }); |
| } |
| |
| if (!errors.empty()) { |
| return android::base::Error() << android::base::Join(errors, "\n"); |
| } |
| |
| 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 |