VintfObject::GetFwkMatrix combine matrices at runtime
... instead of reading /system/compatibility_matrix.xml,
it combines matrices under /system/etc/vintf at run-time. This ensure
GSI gets the correct framework requirements based on Shipping
FCM Version.
Test: `adb shell vintf` shows that fwk comp mat is correctly
read (even if /system/compatibility_matrix.xml is
deleted manually).
Test: vintf_object_tests on host/device.
Bug: 64720381
Bug: 70628538
Change-Id: Ie4607e2eb5f7ba97e21b309d369872e91ecb1d74
diff --git a/VintfObject.cpp b/VintfObject.cpp
index 8dd08ef..24694cd 100644
--- a/VintfObject.cpp
+++ b/VintfObject.cpp
@@ -20,6 +20,8 @@
#include "parse_xml.h"
#include "utils.h"
+#include <dirent.h>
+
#include <functional>
#include <memory>
#include <mutex>
@@ -30,6 +32,8 @@
#include <android-base/logging.h>
+#define FRAMEWORK_MATRIX_DIR "/system/etc/vintf/"
+
using std::placeholders::_1;
using std::placeholders::_2;
@@ -119,11 +123,130 @@
// static
std::shared_ptr<const CompatibilityMatrix> VintfObject::GetFrameworkCompatibilityMatrix(bool skipCache) {
static LockedSharedPtr<CompatibilityMatrix> gFrameworkMatrix;
+ static LockedSharedPtr<CompatibilityMatrix> gCombinedFrameworkMatrix;
+ static std::mutex gFrameworkCompatibilityMatrixMutex;
+
+ // To avoid deadlock, get device manifest before any locks.
+ auto deviceManifest = GetDeviceHalManifest();
+
+ std::unique_lock<std::mutex> _lock(gFrameworkCompatibilityMatrixMutex);
+
+ auto combined =
+ Get(&gCombinedFrameworkMatrix, skipCache,
+ std::bind(&VintfObject::GetCombinedFrameworkMatrix, deviceManifest, _1, _2));
+ if (combined != nullptr) {
+ return combined;
+ }
+
return Get(&gFrameworkMatrix, skipCache,
std::bind(&CompatibilityMatrix::fetchAllInformation, _1,
"/system/compatibility_matrix.xml", _2));
}
+status_t VintfObject::GetCombinedFrameworkMatrix(
+ const std::shared_ptr<const HalManifest>& deviceManifest, CompatibilityMatrix* out,
+ std::string* error) {
+ auto matrixFragments = GetAllFrameworkMatrixLevels(error);
+ if (matrixFragments.empty()) {
+ return NAME_NOT_FOUND;
+ }
+
+ Level deviceLevel = Level::UNSPECIFIED;
+
+ if (deviceManifest != nullptr) {
+ deviceLevel = deviceManifest->level();
+ }
+
+ // TODO(b/70628538): Do not infer from Shipping API level.
+#ifdef LIBVINTF_TARGET
+ if (deviceLevel == Level::UNSPECIFIED) {
+ auto shippingApi =
+ android::base::GetUintProperty<uint64_t>("ro.product.first_api_level", 0u);
+ if (shippingApi != 0u) {
+ deviceLevel = details::convertFromApiLevel(shippingApi);
+ }
+ }
+#endif
+
+ 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&& pair : matrixFragments) {
+ Level fragmentLevel = pair.object.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 " FRAMEWORK_MATRIX_DIR
+ " declare FCM version.";
+ }
+ return NAME_NOT_FOUND;
+ }
+
+ CompatibilityMatrix* combined =
+ CompatibilityMatrix::combine(deviceLevel, &matrixFragments, error);
+ if (combined == nullptr) {
+ return BAD_VALUE;
+ }
+ *out = std::move(*combined);
+ return OK;
+}
+
+std::vector<Named<CompatibilityMatrix>> VintfObject::GetAllFrameworkMatrixLevels(
+ std::string* error) {
+ std::vector<std::string> fileNames;
+ std::vector<Named<CompatibilityMatrix>> results;
+
+ if (details::gFetcher->listFiles(FRAMEWORK_MATRIX_DIR, &fileNames, error) != OK) {
+ return {};
+ }
+ for (const std::string& fileName : fileNames) {
+ std::string path = FRAMEWORK_MATRIX_DIR + fileName;
+
+ std::string content;
+ std::string fetchError;
+ status_t status = details::gFetcher->fetch(path, content, &fetchError);
+ if (status != OK) {
+ if (error) {
+ *error += "Ignore file " + path + ": " + fetchError + "\n";
+ }
+ continue;
+ }
+
+ auto it = results.emplace(results.end());
+ if (!gCompatibilityMatrixConverter(&it->object, content)) {
+ if (error) {
+ // TODO(b/71874788): do not use lastError() because it is not thread-safe.
+ *error +=
+ "Ignore file " + path + ": " + gCompatibilityMatrixConverter.lastError() + "\n";
+ }
+ results.erase(it);
+ continue;
+ }
+ }
+
+ if (results.empty()) {
+ if (error) {
+ *error = "No framework matrices under " FRAMEWORK_MATRIX_DIR
+ " can be fetched or parsed.\n" +
+ *error;
+ }
+ } else {
+ if (error && !error->empty()) {
+ LOG(WARNING) << *error;
+ *error = "";
+ }
+ }
+
+ return results;
+}
+
// static
std::shared_ptr<const RuntimeInfo> VintfObject::GetRuntimeInfo(bool skipCache,
RuntimeInfo::FetchFlags flags) {