assemble_vintf: build and fuse compatibility matrix at build time.
The fused matrix:
(1) has all requirements in compatibilility_matrix.V.xml, where V
is the "level" attribute in device manifest (or is inferred if missing)
(2) has all other HALs as optional HALs in compatibility_matrix.U.xml
files, where U > V, including current.xml.
Bug: 69636193
Test: m (system_)compatibility_matrix/manifest.xml
Test: libvintf_test
Change-Id: I01d2f1ea779e270a9b3ca8de63a064747f36eb2d
Merged-In: I01d2f1ea779e270a9b3ca8de63a064747f36eb2d
diff --git a/CompatibilityMatrix.cpp b/CompatibilityMatrix.cpp
index f788569..521c44b 100644
--- a/CompatibilityMatrix.cpp
+++ b/CompatibilityMatrix.cpp
@@ -16,6 +16,8 @@
#include "CompatibilityMatrix.h"
+#include <utility>
+
#include "parse_string.h"
#include "utils.h"
@@ -71,6 +73,63 @@
return "";
}
+static VersionRange* findRangeWithMajorVersion(std::vector<VersionRange>& versionRanges,
+ size_t majorVer) {
+ for (VersionRange& vr : versionRanges) {
+ if (vr.majorVer == majorVer) {
+ return &vr;
+ }
+ }
+ return nullptr;
+}
+
+std::pair<MatrixHal*, VersionRange*> CompatibilityMatrix::getHalWithMajorVersion(
+ const std::string& name, size_t majorVer) {
+ for (MatrixHal* hal : getHals(name)) {
+ VersionRange* vr = findRangeWithMajorVersion(hal->versionRanges, majorVer);
+ if (vr != nullptr) {
+ return {hal, vr};
+ }
+ }
+ return {nullptr, nullptr};
+}
+
+bool CompatibilityMatrix::addAllHalsAsOptional(CompatibilityMatrix* other, std::string* error) {
+ if (other == nullptr || other->level() <= level()) {
+ return true;
+ }
+
+ for (auto& pair : other->mHals) {
+ const std::string& name = pair.first;
+ MatrixHal& halToAdd = pair.second;
+ for (const VersionRange& vr : halToAdd.versionRanges) {
+ MatrixHal* existingHal;
+ VersionRange* existingVr;
+ std::tie(existingHal, existingVr) = getHalWithMajorVersion(name, vr.majorVer);
+
+ if (existingHal == nullptr) {
+ halToAdd.optional = true;
+ add(std::move(halToAdd));
+ continue;
+ }
+
+ if (!existingHal->optional && !existingHal->containsInstances(halToAdd)) {
+ if (error != nullptr) {
+ *error = "HAL " + name + "@" + to_string(vr.minVer()) + " is a required " +
+ "HAL, but fully qualified instance names don't match (at FCM "
+ "Version " +
+ std::to_string(level()) + " and " + std::to_string(other->level()) +
+ ")";
+ }
+ return false;
+ }
+
+ existingVr->maxMinor = std::max(existingVr->maxMinor, vr.maxMinor);
+ }
+ }
+ return true;
+}
+
bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) {
return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals &&
lft.mXmlFiles == rgt.mXmlFiles &&
diff --git a/MatrixHal.cpp b/MatrixHal.cpp
index 50c5116..96dcf78 100644
--- a/MatrixHal.cpp
+++ b/MatrixHal.cpp
@@ -48,5 +48,23 @@
return ret;
}
+bool MatrixHal::containsInstances(const MatrixHal& other) const {
+ for (const auto& pair : other.interfaces) {
+ const std::string& interfaceName = pair.first;
+ auto thisIt = interfaces.find(interfaceName);
+ if (thisIt == interfaces.end()) {
+ return false;
+ }
+
+ const std::set<std::string>& thisInstances = thisIt->second.instances;
+ const std::set<std::string>& otherInstances = pair.second.instances;
+ if (!std::includes(thisInstances.begin(), thisInstances.end(), otherInstances.begin(),
+ otherInstances.end())) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace vintf
} // namespace android
diff --git a/assemble_vintf.cpp b/assemble_vintf.cpp
index d031d83..19294c6 100644
--- a/assemble_vintf.cpp
+++ b/assemble_vintf.cpp
@@ -25,6 +25,7 @@
#include <string>
#include <android-base/file.h>
+#include <android-base/parseint.h>
#include <vintf/KernelConfigParser.h>
#include <vintf/parse_string.h>
@@ -63,6 +64,24 @@
return true;
}
+ static bool getBooleanFlag(const char* key) {
+ const char* envValue = getenv(key);
+ return envValue != nullptr && strcmp(envValue, "true") == 0;
+ }
+
+ static size_t getIntegerFlag(const char* key, size_t defaultValue = 0) {
+ std::string envValue = getenv(key);
+ if (envValue.empty()) {
+ return defaultValue;
+ }
+ size_t value;
+ if (!base::ParseUint(envValue, &value)) {
+ std::cerr << "Error: " << key << " must be a number." << std::endl;
+ return defaultValue;
+ }
+ return value;
+ }
+
static std::string read(std::basic_istream<char>& is) {
std::stringstream ss;
ss << is.rdbuf();
@@ -73,6 +92,18 @@
return ::android::base::Basename(path) == gBaseConfig;
}
+ static Level convertFromApiLevel(size_t apiLevel) {
+ if (apiLevel < 26) {
+ return Level::LEGACY;
+ } else if (apiLevel == 26) {
+ return Level::O;
+ } else if (apiLevel == 27) {
+ return Level::O_MR1;
+ } else {
+ return Level::UNSPECIFIED;
+ }
+ }
+
// nullptr on any error, otherwise the condition.
static Condition generateCondition(const std::string& path) {
std::string fname = ::android::base::Basename(path);
@@ -173,17 +204,59 @@
return ret;
}
+ static std::string getFileNameFromPath(std::string path) {
+ auto idx = path.find_last_of("\\/");
+ if (idx != std::string::npos) {
+ path.erase(0, idx + 1);
+ }
+ return path;
+ }
+
std::basic_ostream<char>& out() const {
return mOutFileRef == nullptr ? std::cout : *mOutFileRef;
}
- bool assembleHalManifest(HalManifest* halManifest) {
+ template <typename S>
+ using Schemas = std::vector<std::pair<std::string, S>>;
+ using HalManifests = Schemas<HalManifest>;
+ using CompatibilityMatrices = Schemas<CompatibilityMatrix>;
+
+ bool assembleHalManifest(HalManifests* halManifests) {
std::string error;
+ HalManifest* halManifest = &halManifests->front().second;
+ for (auto it = halManifests->begin() + 1; it != halManifests->end(); ++it) {
+ const std::string& path = it->first;
+ HalManifest& halToAdd = it->second;
+
+ if (halToAdd.level() != Level::UNSPECIFIED) {
+ if (halManifest->level() == Level::UNSPECIFIED) {
+ halManifest->mLevel = halToAdd.level();
+ } else if (halManifest->level() != halToAdd.level()) {
+ std::cerr << "Inconsistent FCM Version in HAL manifests:" << std::endl
+ << " File '" << halManifests->front().first << "' has level "
+ << halManifest->level() << std::endl
+ << " File '" << path << "' has level " << halToAdd.level()
+ << std::endl;
+ return false;
+ }
+ }
+
+ if (!halManifest->addAll(std::move(halToAdd), &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;
+ }
+ }
if (halManifest->mType == SchemaType::DEVICE) {
if (!getFlag("BOARD_SEPOLICY_VERS", &halManifest->device.mSepolicyVersion)) {
return false;
}
+ if (!setDeviceFcmVersion(halManifest)) {
+ return false;
+ }
}
if (mOutputMatrix) {
@@ -248,12 +321,107 @@
return true;
}
- bool assembleCompatibilityMatrix(CompatibilityMatrix* matrix) {
- std::string error;
+ bool setDeviceFcmVersion(HalManifest* manifest) {
+ size_t shippingApiLevel = getIntegerFlag("PRODUCT_SHIPPING_API_LEVEL");
+ if (manifest->level() != Level::UNSPECIFIED) {
+ return true;
+ }
+ if (!getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST")) {
+ manifest->mLevel = Level::LEGACY;
+ return true;
+ }
+ // TODO(b/70628538): Do not infer from Shipping API level.
+ if (shippingApiLevel) {
+ std::cerr << "Warning: Shipping FCM Version is inferred from Shipping API level. "
+ << "Declare Shipping FCM Version in device manifest directly." << std::endl;
+ manifest->mLevel = convertFromApiLevel(shippingApiLevel);
+ if (manifest->mLevel == Level::UNSPECIFIED) {
+ std::cerr << "Error: Shipping FCM Version cannot be inferred from Shipping API "
+ << "level " << shippingApiLevel << "."
+ << "Declare Shipping FCM Version in device manifest directly."
+ << std::endl;
+ return false;
+ }
+ return true;
+ }
+ // TODO(b/69638851): should be an error if Shipping API level is not defined.
+ // For now, just leave it empty; when framework compatibility matrix is built,
+ // lowest FCM Version is assumed.
+ std::cerr << "Warning: Shipping FCM Version cannot be inferred, because:" << std::endl
+ << " (1) It is not explicitly declared in device manifest;" << std::endl
+ << " (2) PRODUCT_ENFORCE_VINTF_MANIFEST is set to true;" << std::endl
+ << " (3) PRODUCT_SHIPPING_API_LEVEL is undefined." << std::endl
+ << "Assuming 'unspecified' Shipping FCM Version. " << std::endl
+ << "To remove this warning, define 'level' attribute in device manifest."
+ << std::endl;
+ return true;
+ }
+
+ Level getLowestFcmVersion(const CompatibilityMatrices& matrices) {
+ Level ret = Level::UNSPECIFIED;
+ for (const auto& e : matrices) {
+ if (ret == Level::UNSPECIFIED || ret > e.second.level()) {
+ ret = e.second.level();
+ }
+ }
+ return ret;
+ }
+
+ bool assembleCompatibilityMatrix(CompatibilityMatrices* matrices) {
+ std::string error;
+ CompatibilityMatrix* matrix = nullptr;
KernelSepolicyVersion kernelSepolicyVers;
Version sepolicyVers;
- if (matrix->mType == SchemaType::FRAMEWORK) {
+ std::unique_ptr<HalManifest> checkManifest;
+ if (matrices->front().second.mType == SchemaType::DEVICE) {
+ matrix = &matrices->front().second;
+ }
+
+ if (matrices->front().second.mType == SchemaType::FRAMEWORK) {
+ Level deviceLevel = Level::UNSPECIFIED;
+ std::vector<std::string> fileList;
+ if (mCheckFile.is_open()) {
+ checkManifest = std::make_unique<HalManifest>();
+ if (!gHalManifestConverter(checkManifest.get(), read(mCheckFile))) {
+ std::cerr << "Cannot parse check file as a HAL manifest: "
+ << gHalManifestConverter.lastError() << std::endl;
+ return false;
+ }
+ deviceLevel = checkManifest->level();
+ }
+
+ if (deviceLevel == Level::UNSPECIFIED) {
+ // For GSI build, legacy devices that do not have a HAL manifest,
+ // and devices in development, merge all compatibility matrices.
+ deviceLevel = getLowestFcmVersion(*matrices);
+ }
+
+ for (auto& e : *matrices) {
+ if (e.second.level() == deviceLevel) {
+ fileList.push_back(e.first);
+ matrix = &e.second;
+ }
+ }
+ if (matrix == nullptr) {
+ std::cerr << "FATAL ERROR: cannot find matrix with level '" << deviceLevel << "'"
+ << std::endl;
+ return false;
+ }
+ for (auto& e : *matrices) {
+ if (e.second.level() <= deviceLevel) {
+ continue;
+ }
+ fileList.push_back(e.first);
+ if (!matrix->addAllHalsAsOptional(&e.second, &error)) {
+ std::cerr << "File \"" << e.first << "\" cannot be added: " << error
+ << ". See <hal> with the same name "
+ << "in previously parsed files or previously declared in this file."
+ << std::endl;
+ return false;
+ }
+ }
+
if (!getFlag("BOARD_SEPOLICY_VERS", &sepolicyVers)) {
return false;
}
@@ -273,21 +441,21 @@
return false;
}
matrix->framework.mAvbMetaVersion = avbMetaVersion;
+
+ out() << "<!--" << std::endl;
+ out() << " Input:" << std::endl;
+ for (const auto& path : fileList) {
+ out() << " " << getFileNameFromPath(path) << std::endl;
+ }
+ out() << "-->" << std::endl;
}
out() << gCompatibilityMatrixConverter(*matrix, mSerializeFlags);
out().flush();
- if (mCheckFile.is_open()) {
- HalManifest checkManifest;
- if (!gHalManifestConverter(&checkManifest, read(mCheckFile))) {
- std::cerr << "Cannot parse check file as a HAL manifest: "
- << gHalManifestConverter.lastError() << std::endl;
- return false;
- }
- if (!checkManifest.checkCompatibility(*matrix, &error)) {
- std::cerr << "Not compatible: " << error << std::endl;
- return false;
- }
+ if (checkManifest != nullptr && getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST") &&
+ !checkManifest->checkCompatibility(*matrix, &error)) {
+ std::cerr << "Not compatible: " << error << std::endl;
+ return false;
}
return true;
@@ -297,38 +465,33 @@
template <typename Schema, typename AssembleFunc>
AssembleStatus tryAssemble(const XmlConverter<Schema>& converter, const std::string& schemaName,
AssembleFunc assemble) {
+ Schemas<Schema> schemas;
Schema schema;
if (!converter(&schema, read(mInFiles.front()))) {
return TRY_NEXT;
}
auto firstType = schema.type();
+ schemas.emplace_back(mInFilePaths.front(), std::move(schema));
+
for (auto it = mInFiles.begin() + 1; it != mInFiles.end(); ++it) {
Schema additionalSchema;
+ const std::string fileName = mInFilePaths[std::distance(mInFiles.begin(), it)];
if (!converter(&additionalSchema, read(*it))) {
- std::cerr << "File \"" << mInFilePaths[std::distance(mInFiles.begin(), it)]
- << "\" is not a valid " << firstType << " " << schemaName
- << " (but the first file is a valid " << firstType << " " << schemaName
- << "). Error: " << converter.lastError() << std::endl;
+ std::cerr << "File \"" << fileName << "\" is not a valid " << firstType << " "
+ << schemaName << " (but the first file is a valid " << firstType << " "
+ << schemaName << "). Error: " << converter.lastError() << std::endl;
return FAIL_AND_EXIT;
}
if (additionalSchema.type() != firstType) {
- std::cerr << "File \"" << mInFilePaths[std::distance(mInFiles.begin(), it)]
- << "\" is a " << additionalSchema.type() << " " << schemaName
- << " (but a " << firstType << " " << schemaName << " is expected)."
- << std::endl;
+ std::cerr << "File \"" << fileName << "\" is a " << additionalSchema.type() << " "
+ << schemaName << " (but a " << firstType << " " << schemaName
+ << " is expected)." << std::endl;
return FAIL_AND_EXIT;
}
- std::string error;
- if (!schema.addAll(std::move(additionalSchema), &error)) {
- std::cerr << "File \"" << mInFilePaths[std::distance(mInFiles.begin(), it)]
- << "\" 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 FAIL_AND_EXIT;
- }
+
+ schemas.emplace_back(fileName, std::move(additionalSchema));
}
- return assemble(&schema) ? SUCCESS : FAIL_AND_EXIT;
+ return assemble(&schemas) ? SUCCESS : FAIL_AND_EXIT;
}
bool assemble() {
diff --git a/include/vintf/CompatibilityMatrix.h b/include/vintf/CompatibilityMatrix.h
index c9a8a9f..23cbe53 100644
--- a/include/vintf/CompatibilityMatrix.h
+++ b/include/vintf/CompatibilityMatrix.h
@@ -59,6 +59,16 @@
bool add(MatrixHal &&hal);
bool add(MatrixKernel &&kernel);
+ // Add all HALs as optional HALs from "other". This function moves MatrixHal objects
+ // from "other".
+ // Require other->level() > this->level(), otherwise do nothing.
+ bool addAllHalsAsOptional(CompatibilityMatrix* other, std::string* error);
+ // Return the MatrixHal object with the given name and major version. Since all major
+ // version are guaranteed distinct when add()-ed, there should be at most 1 match.
+ // Return nullptr if none is found.
+ std::pair<MatrixHal*, VersionRange*> getHalWithMajorVersion(const std::string& name,
+ size_t majorVer);
+
status_t fetchAllInformation(const std::string &path);
friend struct HalManifest;
diff --git a/include/vintf/MatrixHal.h b/include/vintf/MatrixHal.h
index 203aa57..47094fb 100644
--- a/include/vintf/MatrixHal.h
+++ b/include/vintf/MatrixHal.h
@@ -48,6 +48,9 @@
inline bool hasInterface(const std::string& interface_name) const {
return interfaces.find(interface_name) != interfaces.end();
}
+
+ // Return true if "this" contains all interface/instance instances in "other".
+ bool containsInstances(const MatrixHal& other) const;
};
} // namespace vintf
diff --git a/test/main.cpp b/test/main.cpp
index b8a25a2..09f2ad0 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -103,6 +103,9 @@
return mh.isValid();
}
std::vector<MatrixKernel>& getKernels(CompatibilityMatrix& cm) { return cm.framework.mKernels; }
+ bool addAllHalsAsOptional(CompatibilityMatrix* cm1, CompatibilityMatrix* cm2, std::string* e) {
+ return cm1->addAllHalsAsOptional(cm2, e);
+ }
std::map<std::string, HalInterface> testHalInterfaces() {
HalInterface intf;
@@ -2084,6 +2087,151 @@
EXPECT_EQ(1u, manifest.level());
}
+TEST_F(LibVintfTest, AddOptionalHal) {
+ CompatibilityMatrix cm1;
+ CompatibilityMatrix cm2;
+ std::string error;
+ std::string xml;
+
+ xml = "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"1\"/>";
+ EXPECT_TRUE(gCompatibilityMatrixConverter(&cm1, xml))
+ << gCompatibilityMatrixConverter.lastError();
+
+ xml =
+ "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"2\">\n"
+ " <hal format=\"hidl\" optional=\"false\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>1.0-1</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ "</compatibility-matrix>\n";
+ EXPECT_TRUE(gCompatibilityMatrixConverter(&cm2, xml))
+ << gCompatibilityMatrixConverter.lastError();
+
+ EXPECT_TRUE(addAllHalsAsOptional(&cm1, &cm2, &error)) << error;
+ xml = gCompatibilityMatrixConverter(cm1, SerializeFlag::HALS_ONLY);
+ EXPECT_EQ(xml,
+ "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"1\">\n"
+ " <hal format=\"hidl\" optional=\"true\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>1.0-1</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ "</compatibility-matrix>\n");
+}
+
+TEST_F(LibVintfTest, AddOptionalHalMinorVersion) {
+ CompatibilityMatrix cm1;
+ CompatibilityMatrix cm2;
+ std::string error;
+ std::string xml;
+
+ xml =
+ "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"1\">\n"
+ " <hal format=\"hidl\" optional=\"false\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>1.2-3</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ "</compatibility-matrix>\n";
+ EXPECT_TRUE(gCompatibilityMatrixConverter(&cm1, xml))
+ << gCompatibilityMatrixConverter.lastError();
+
+ xml =
+ "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"2\">\n"
+ " <hal format=\"hidl\" optional=\"false\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>1.0-4</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ "</compatibility-matrix>\n";
+ EXPECT_TRUE(gCompatibilityMatrixConverter(&cm2, xml))
+ << gCompatibilityMatrixConverter.lastError();
+
+ EXPECT_TRUE(addAllHalsAsOptional(&cm1, &cm2, &error)) << error;
+ xml = gCompatibilityMatrixConverter(cm1, SerializeFlag::HALS_ONLY);
+ EXPECT_EQ(xml,
+ "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"1\">\n"
+ " <hal format=\"hidl\" optional=\"false\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>1.2-4</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ "</compatibility-matrix>\n");
+}
+
+TEST_F(LibVintfTest, AddOptionalHalMajorVersion) {
+ CompatibilityMatrix cm1;
+ CompatibilityMatrix cm2;
+ std::string error;
+ std::string xml;
+
+ xml =
+ "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"1\">\n"
+ " <hal format=\"hidl\" optional=\"false\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>1.2-3</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ "</compatibility-matrix>\n";
+ EXPECT_TRUE(gCompatibilityMatrixConverter(&cm1, xml))
+ << gCompatibilityMatrixConverter.lastError();
+
+ xml =
+ "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"2\">\n"
+ " <hal format=\"hidl\" optional=\"false\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>2.0-4</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ "</compatibility-matrix>\n";
+ EXPECT_TRUE(gCompatibilityMatrixConverter(&cm2, xml))
+ << gCompatibilityMatrixConverter.lastError();
+
+ EXPECT_TRUE(addAllHalsAsOptional(&cm1, &cm2, &error)) << error;
+ xml = gCompatibilityMatrixConverter(cm1, SerializeFlag::HALS_ONLY);
+ EXPECT_EQ(xml,
+ "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"1\">\n"
+ " <hal format=\"hidl\" optional=\"false\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>1.2-3</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ " <hal format=\"hidl\" optional=\"true\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>2.0-4</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ "</compatibility-matrix>\n");
+}
+
} // namespace vintf
} // namespace android