HalManifests can be merged.
assemble_vintf and VintfObject can now merge manifests
correctly (previously, only <hal>'s are merged).
Test: libvintf_test
Test: vintf_object_test
Fixes: 78943004
Change-Id: I5e2987e9c97e0b60e976fe4e0bb8833edf043a53
diff --git a/AssembleVintf.cpp b/AssembleVintf.cpp
index bcefc73..e0a22d1 100644
--- a/AssembleVintf.cpp
+++ b/AssembleVintf.cpp
@@ -358,26 +358,8 @@
}
}
- // TODO(b/78943004): add everything
- if (!halManifest->addAllHals(&manifestToAdd, &error)) {
- std::cerr << "File \"" << path << "\" cannot be added: conflict on HAL \"" << error
- << "\" with an existing HAL. See <hal> with the same name "
- << "in previously parsed files or previously declared in this file."
- << std::endl;
- return false;
- }
-
- // Check that manifestToAdd is empty.
- if (!manifestToAdd.empty()) {
- std::cerr
- << "File \"" << path << "\" contains extraneous entries and attributes. "
- << "This is currently unsupported (b/78943004); it can only contain "
- << "<hal>s and attribute \"type\" and \"version\". Only the first input "
- << "file to assemble_vintf can contain other things. "
- << "Remaining entries and attributes are:" << std::endl
- << gHalManifestConverter(
- manifestToAdd,
- SerializeFlags::EVERYTHING.disableMetaVersion().disableSchemaType());
+ if (!halManifest->addAll(&manifestToAdd, &error)) {
+ std::cerr << "File \"" << path << "\" cannot be added: " << error << std::endl;
return false;
}
}
diff --git a/CompatibilityMatrix.cpp b/CompatibilityMatrix.cpp
index 290ebf9..5bb6d1e 100644
--- a/CompatibilityMatrix.cpp
+++ b/CompatibilityMatrix.cpp
@@ -28,6 +28,8 @@
namespace android {
namespace vintf {
+using details::mergeField;
+
bool CompatibilityMatrix::addKernel(MatrixKernel&& kernel, std::string* error) {
if (mType != SchemaType::FRAMEWORK) {
if (error) {
@@ -294,22 +296,6 @@
return true;
}
-template <typename T>
-static bool mergeField(T* dst, T* src) {
- static const T kEmpty{};
- if (*dst == *src) {
- return true; // no conflict
- }
- if (*src == kEmpty) {
- return true;
- }
- if (*dst == kEmpty) {
- *dst = std::move(*src);
- return true;
- }
- return false;
-}
-
bool CompatibilityMatrix::addSepolicy(CompatibilityMatrix* other, std::string* error) {
bool success = mergeField(&this->framework.mSepolicy, &other->framework.mSepolicy);
if (!success && error) *error = "<sepolicy> is already defined";
diff --git a/HalManifest.cpp b/HalManifest.cpp
index d844c4c..59a7c31 100644
--- a/HalManifest.cpp
+++ b/HalManifest.cpp
@@ -36,6 +36,7 @@
using details::Instances;
using details::InstancesOfVersion;
+using details::mergeField;
// Check <version> tag for all <hal> with the same name.
bool HalManifest::shouldAdd(const ManifestHal& hal) const {
@@ -479,5 +480,87 @@
return device.mKernel;
}
+bool HalManifest::addAll(HalManifest* other, std::string* error) {
+ if (other->mMetaVersion.majorVer != mMetaVersion.majorVer) {
+ if (error) {
+ *error = "Cannot merge manifest version " + to_string(mMetaVersion) + " and " +
+ to_string(other->mMetaVersion);
+ }
+ return false;
+ }
+ mMetaVersion.minorVer = std::max(mMetaVersion.minorVer, other->mMetaVersion.minorVer);
+
+ if (type() != other->type()) {
+ if (error) {
+ *error = "Cannot add a " + to_string(other->type()) + " manifest to a " +
+ to_string(type()) + " manifest";
+ }
+ return false;
+ }
+
+ if (!addAllHals(other, error)) {
+ return false;
+ }
+
+ if (!addAllXmlFiles(other, error)) {
+ return false;
+ }
+
+ if (!mergeField(&mLevel, &other->mLevel, Level::UNSPECIFIED)) {
+ if (error) {
+ *error = "Conflicting target-level: " + to_string(level()) + " vs. " +
+ to_string(other->level());
+ }
+ return false;
+ }
+
+ if (type() == SchemaType::DEVICE) {
+ if (!mergeField(&device.mSepolicyVersion, &other->device.mSepolicyVersion)) {
+ if (error) {
+ *error = "Conflicting sepolicy version: " + to_string(sepolicyVersion()) + " vs. " +
+ to_string(other->sepolicyVersion());
+ }
+ return false;
+ }
+
+ if (!mergeField(&device.mKernel, &other->device.mKernel)) {
+ // If fails, both have values.
+ if (error) {
+ *error = "Conflicting kernel: " + to_string(device.mKernel->version()) + " vs. " +
+ to_string(other->device.mKernel->version());
+ }
+ return false;
+ }
+ } else if (type() == SchemaType::FRAMEWORK) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ framework.mVndks.insert(framework.mVndks.end(), other->framework.mVndks.begin(),
+ other->framework.mVndks.end());
+ other->framework.mVndks.clear();
+#pragma clang diagnostic pop
+
+ framework.mVendorNdks.insert(framework.mVendorNdks.end(),
+ other->framework.mVendorNdks.begin(),
+ other->framework.mVendorNdks.end());
+ other->framework.mVendorNdks.clear();
+
+ framework.mSystemSdk.addAll(&other->framework.mSystemSdk);
+ } else {
+ LOG(FATAL) << "unknown SchemaType: "
+ << static_cast<std::underlying_type_t<SchemaType>>(type());
+ }
+
+ if (!other->empty()) {
+ if (error) {
+ *error =
+ "Cannot add another manifest because it contains extraneous entries that "
+ "are not recognized.";
+ }
+ return false;
+ }
+
+ return true;
+}
+
} // namespace vintf
} // namespace android
diff --git a/SystemSdk.cpp b/SystemSdk.cpp
index d2ee123..1dfd463 100644
--- a/SystemSdk.cpp
+++ b/SystemSdk.cpp
@@ -32,5 +32,10 @@
return versions() == other.versions();
}
+void SystemSdk::addAll(SystemSdk* other) {
+ mVersions.insert(other->mVersions.begin(), other->mVersions.end());
+ other->mVersions.clear();
+}
+
} // namespace vintf
} // namespace android
diff --git a/VintfObject.cpp b/VintfObject.cpp
index fbe4045..e66bcbe 100644
--- a/VintfObject.cpp
+++ b/VintfObject.cpp
@@ -215,7 +215,12 @@
err = fetchOneHalManifest(directory + file, &fragmentManifest, error);
if (err != OK) return err;
- manifest->addAllHals(&fragmentManifest);
+ if (!manifest->addAll(&fragmentManifest, error)) {
+ if (error) {
+ error->insert(0, "Cannot add manifest fragment " + directory + file + ":");
+ }
+ return UNKNOWN_ERROR;
+ }
}
return OK;
@@ -250,7 +255,12 @@
if (vendorStatus == OK) {
if (odmStatus == OK) {
- out->addAllHals(&odmManifest);
+ if (!out->addAll(&odmManifest, error)) {
+ if (error) {
+ error->insert(0, "Cannot add ODM manifest :");
+ }
+ return UNKNOWN_ERROR;
+ }
}
return addDirectoryManifests(kOdmManifestFragmentDir, out, error);
}
diff --git a/include/vintf/HalManifest.h b/include/vintf/HalManifest.h
index 1c90d17..6bcc8d6 100644
--- a/include/vintf/HalManifest.h
+++ b/include/vintf/HalManifest.h
@@ -131,6 +131,10 @@
// Get the <kernel> tag. Assumes type() == DEVICE.
const std::optional<KernelInfo>& kernel() const;
+ // Add everything from another manifest. If no errors (return true), it is guaranteed
+ // that other->empty() == true after execution.
+ [[nodiscard]] bool addAll(HalManifest* other, std::string* error = nullptr);
+
protected:
// Check before add()
bool shouldAdd(const ManifestHal& toAdd) const override;
diff --git a/include/vintf/SystemSdk.h b/include/vintf/SystemSdk.h
index 1828e4a..703c1e0 100644
--- a/include/vintf/SystemSdk.h
+++ b/include/vintf/SystemSdk.h
@@ -38,6 +38,9 @@
// return {v : v in this and not in other}
SystemSdk removeVersions(const SystemSdk& other) const;
+ // Move all from "other" to "this".
+ void addAll(SystemSdk* other);
+
private:
friend class AssembleVintfImpl;
friend struct SystemSdkConverter;
diff --git a/include/vintf/XmlFileGroup.h b/include/vintf/XmlFileGroup.h
index dd15647..b67de86 100644
--- a/include/vintf/XmlFileGroup.h
+++ b/include/vintf/XmlFileGroup.h
@@ -67,6 +67,7 @@
return false;
}
}
+ other->mXmlFiles.clear();
return true;
}
diff --git a/test/LibVintfTest.cpp b/test/LibVintfTest.cpp
index f5690aa..35804e7 100644
--- a/test/LibVintfTest.cpp
+++ b/test/LibVintfTest.cpp
@@ -3475,6 +3475,132 @@
gKernelInfoConverter(ki, SerializeFlags::NO_TAGS.enableKernelConfigs()));
}
+TEST_F(LibVintfTest, ManifestAddAllDeviceManifest) {
+ std::string xml1 = "<manifest version=\"1.0\" type=\"device\" />\n";
+ std::string xml2 =
+ "<manifest version=\"1.0\" type=\"device\" target-level=\"3\">\n"
+ " <hal format=\"hidl\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <transport>hwbinder</transport>\n"
+ " <fqname>@1.0::IFoo/default</fqname>\n"
+ " </hal>\n"
+ " <sepolicy>\n"
+ " <version>25.5</version>\n"
+ " </sepolicy>\n"
+ " <kernel version=\"3.18.31\">\n"
+ " <config>\n"
+ " <key>CONFIG_64BIT</key>\n"
+ " <value>y</value>\n"
+ " </config>\n"
+ " </kernel>\n"
+ " <xmlfile>\n"
+ " <name>media_profile</name>\n"
+ " <version>1.0</version>\n"
+ " </xmlfile>\n"
+ "</manifest>\n";
+
+ std::string error;
+ HalManifest manifest1;
+ ASSERT_TRUE(gHalManifestConverter(&manifest1, xml1, &error)) << error;
+ HalManifest manifest2;
+ ASSERT_TRUE(gHalManifestConverter(&manifest2, xml2, &error)) << error;
+
+ ASSERT_TRUE(manifest1.addAll(&manifest2, &error)) << error;
+
+ EXPECT_EQ(xml2, gHalManifestConverter(manifest1));
+}
+
+TEST_F(LibVintfTest, ManifestAddAllFrameworkManifest) {
+ std::string xml1 = "<manifest version=\"1.0\" type=\"framework\" />\n";
+ std::string xml2 =
+ "<manifest version=\"1.0\" type=\"framework\">\n"
+ " <hal format=\"hidl\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <transport>hwbinder</transport>\n"
+ " <fqname>@1.0::IFoo/default</fqname>\n"
+ " </hal>\n"
+ " <vendor-ndk>\n"
+ " <version>P</version>\n"
+ " <library>libbase.so</library>\n"
+ " </vendor-ndk>\n"
+ " <system-sdk>\n"
+ " <version>1</version>\n"
+ " </system-sdk>\n"
+ " <xmlfile>\n"
+ " <name>media_profile</name>\n"
+ " <version>1.0</version>\n"
+ " </xmlfile>\n"
+ "</manifest>\n";
+
+ std::string error;
+ HalManifest manifest1;
+ ASSERT_TRUE(gHalManifestConverter(&manifest1, xml1, &error)) << error;
+ HalManifest manifest2;
+ ASSERT_TRUE(gHalManifestConverter(&manifest2, xml2, &error)) << error;
+
+ ASSERT_TRUE(manifest1.addAll(&manifest2, &error)) << error;
+
+ EXPECT_EQ(xml2, gHalManifestConverter(manifest1));
+}
+
+TEST_F(LibVintfTest, ManifestAddAllConflictLevel) {
+ std::string xml1 = "<manifest version=\"1.0\" type=\"device\" target-level=\"2\" />\n";
+ std::string xml2 = "<manifest version=\"1.0\" type=\"device\" target-level=\"3\" />\n";
+
+ std::string error;
+ HalManifest manifest1;
+ ASSERT_TRUE(gHalManifestConverter(&manifest1, xml1, &error)) << error;
+ HalManifest manifest2;
+ ASSERT_TRUE(gHalManifestConverter(&manifest2, xml2, &error)) << error;
+
+ ASSERT_FALSE(manifest1.addAll(&manifest2, &error));
+ EXPECT_IN("Conflicting target-level", error);
+}
+
+TEST_F(LibVintfTest, ManifestAddAllConflictSepolicy) {
+ std::string xml1 =
+ "<manifest version=\"1.0\" type=\"device\">\n"
+ " <sepolicy>\n"
+ " <version>25.5</version>\n"
+ " </sepolicy>\n"
+ "</manifest>\n";
+ std::string xml2 =
+ "<manifest version=\"1.0\" type=\"device\">\n"
+ " <sepolicy>\n"
+ " <version>30.0</version>\n"
+ " </sepolicy>\n"
+ "</manifest>\n";
+
+ std::string error;
+ HalManifest manifest1;
+ ASSERT_TRUE(gHalManifestConverter(&manifest1, xml1, &error)) << error;
+ HalManifest manifest2;
+ ASSERT_TRUE(gHalManifestConverter(&manifest2, xml2, &error)) << error;
+
+ ASSERT_FALSE(manifest1.addAll(&manifest2, &error));
+ EXPECT_IN("Conflicting sepolicy version", error);
+}
+
+TEST_F(LibVintfTest, ManifestAddAllConflictKernel) {
+ std::string xml1 =
+ "<manifest version=\"1.0\" type=\"device\">\n"
+ " <kernel version=\"3.18.0\" />\n"
+ "</manifest>\n";
+ std::string xml2 =
+ "<manifest version=\"1.0\" type=\"device\">\n"
+ " <kernel version=\"3.18.1\" />\n"
+ "</manifest>\n";
+
+ std::string error;
+ HalManifest manifest1;
+ ASSERT_TRUE(gHalManifestConverter(&manifest1, xml1, &error)) << error;
+ HalManifest manifest2;
+ ASSERT_TRUE(gHalManifestConverter(&manifest2, xml2, &error)) << error;
+
+ ASSERT_FALSE(manifest1.addAll(&manifest2, &error));
+ EXPECT_IN("Conflicting kernel", error);
+}
+
} // namespace vintf
} // namespace android
diff --git a/utils.h b/utils.h
index dccc811..5378b67 100644
--- a/utils.h
+++ b/utils.h
@@ -81,6 +81,25 @@
virtual bool getBoolProperty(const std::string& key, bool defaultValue) const override;
};
+// Merge src into dst.
+// postcondition (if successful): *src == empty.
+template <typename T>
+static bool mergeField(T* dst, T* src, const T& empty = T{}) {
+ if (*dst == *src) {
+ *src = empty;
+ return true; // no conflict
+ }
+ if (*src == empty) {
+ return true;
+ }
+ if (*dst == empty) {
+ *dst = std::move(*src);
+ *src = empty;
+ return true;
+ }
+ return false;
+}
+
} // namespace details
} // namespace vintf
} // namespace android