Merge changes from topic "fcm_version"
* changes:
Add AssembleVintfTest
assemble_vintf: warn for missing env var for fwk_cm.empty.xml
Add Named<T> in place of std::pair<std::string, T>
assemble_vintf: move logic to CompatibilityMatrix::combine
Use base::Split to replace tokenize() function.
assemble_vintf: refactor to prepare for test
diff --git a/Android.bp b/Android.bp
index 2fd9eef..fea4d38 100644
--- a/Android.bp
+++ b/Android.bp
@@ -105,6 +105,20 @@
],
}
+cc_library_static {
+ name: "libassemblevintf",
+ host_supported: true,
+ defaults: ["libvintf-defaults"],
+ shared_libs: [
+ "libvintf",
+ "libbase",
+ ],
+ srcs: [
+ "AssembleVintf.cpp",
+ ],
+ export_include_dirs: ["include-test"],
+}
+
cc_binary_host {
name: "assemble_vintf",
defaults: ["libvintf-defaults"],
@@ -112,8 +126,11 @@
"libvintf",
"libbase",
],
+ static_libs: [
+ "libassemblevintf",
+ ],
srcs: [
- "assemble_vintf.cpp"
+ "assemble_vintf_main.cpp"
],
}
diff --git a/assemble_vintf.cpp b/AssembleVintf.cpp
similarity index 61%
rename from assemble_vintf.cpp
rename to AssembleVintf.cpp
index 0aa79fd..d2a0b8f 100644
--- a/assemble_vintf.cpp
+++ b/AssembleVintf.cpp
@@ -14,22 +14,24 @@
* limitations under the License.
*/
-#include <getopt.h>
#include <stdlib.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
-#include <unordered_map>
#include <sstream>
#include <string>
+#include <unordered_map>
#include <android-base/file.h>
#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <vintf/AssembleVintf.h>
#include <vintf/KernelConfigParser.h>
#include <vintf/parse_string.h>
#include <vintf/parse_xml.h>
+#include "utils.h"
#define BUFFER_SIZE sysconf(_SC_PAGESIZE)
@@ -58,16 +60,27 @@
/**
* Slurps the device manifest file and add build time flag to it.
*/
-class AssembleVintf {
+class AssembleVintfImpl : public AssembleVintf {
using Condition = std::unique_ptr<KernelConfig>;
using ConditionedConfig = std::pair<Condition, std::vector<KernelConfig> /* configs */>;
public:
- template<typename T>
- static bool getFlag(const std::string& key, T* value) {
- const char *envValue = getenv(key.c_str());
- if (envValue == NULL) {
- std::cerr << "Warning: " << key << " is missing, defaulted to " << (*value)
+ void setFakeEnv(const std::string& key, const std::string& value) { mFakeEnv[key] = value; }
+
+ std::string getEnv(const std::string& key) const {
+ auto it = mFakeEnv.find(key);
+ if (it != mFakeEnv.end()) {
+ return it->second;
+ }
+ const char* envValue = getenv(key.c_str());
+ return envValue != nullptr ? std::string(envValue) : std::string();
+ }
+
+ template <typename T>
+ bool getFlag(const std::string& key, T* value) const {
+ std::string envValue = getEnv(key);
+ if (envValue.empty()) {
+ std::cerr << "Warning: " << key << " is missing, defaulted to " << (*value) << "."
<< std::endl;
return true;
}
@@ -83,15 +96,15 @@
* Set *out to environment variable if *out is not a dummy value (i.e. default constructed).
*/
template <typename T>
- bool getFlagIfUnset(const std::string& envKey, T* out) {
+ bool getFlagIfUnset(const std::string& envKey, T* out) const {
bool hasExistingValue = !(*out == T{});
bool hasEnvValue = false;
T envValue;
- const char* envCValue = getenv(envKey.c_str());
- if (envCValue != nullptr) {
- if (!parse(envCValue, &envValue)) {
- std::cerr << "Cannot parse " << envCValue << "." << std::endl;
+ std::string envStrValue = getEnv(envKey);
+ if (!envStrValue.empty()) {
+ if (!parse(envStrValue, &envValue)) {
+ std::cerr << "Cannot parse " << envValue << "." << std::endl;
return false;
}
hasEnvValue = true;
@@ -113,13 +126,10 @@
return true;
}
- static bool getBooleanFlag(const char* key) {
- const char* envValue = getenv(key);
- return envValue != nullptr && strcmp(envValue, "true") == 0;
- }
+ bool getBooleanFlag(const std::string& key) const { return getEnv(key) == std::string("true"); }
- static size_t getIntegerFlag(const char* key, size_t defaultValue = 0) {
- std::string envValue = getenv(key);
+ size_t getIntegerFlag(const std::string& key, size_t defaultValue = 0) const {
+ std::string envValue = getEnv(key);
if (envValue.empty()) {
return defaultValue;
}
@@ -185,14 +195,10 @@
return std::make_unique<KernelConfig>(std::move(sub), Tristate::YES);
}
- static bool parseFileForKernelConfigs(const std::string& path, std::vector<KernelConfig>* out) {
- std::ifstream ifs{path};
- if (!ifs.is_open()) {
- std::cerr << "File '" << path << "' does not exist or cannot be read." << std::endl;
- return false;
- }
+ static bool parseFileForKernelConfigs(std::basic_istream<char>& stream,
+ std::vector<KernelConfig>* out) {
KernelConfigParser parser(true /* processComments */, true /* relaxedFormat */);
- std::string content = read(ifs);
+ std::string content = read(stream);
status_t err = parser.process(content.c_str(), content.size());
if (err != OK) {
std::cerr << parser.error();
@@ -217,35 +223,32 @@
return true;
}
- static bool parseFilesForKernelConfigs(const std::string& path,
+ static bool parseFilesForKernelConfigs(std::vector<NamedIstream>* streams,
std::vector<ConditionedConfig>* out) {
out->clear();
ConditionedConfig commonConfig;
bool foundCommonConfig = false;
bool ret = true;
- char *pathIter;
- char *modPath = new char[path.length() + 1];
- strcpy(modPath, path.c_str());
- pathIter = strtok(modPath, ":");
- while (ret && pathIter != NULL) {
- if (isCommonConfig(pathIter)) {
- ret &= parseFileForKernelConfigs(pathIter, &commonConfig.second);
+
+ for (auto& namedStream : *streams) {
+ if (isCommonConfig(namedStream.name())) {
+ ret &= parseFileForKernelConfigs(namedStream.stream(), &commonConfig.second);
foundCommonConfig = true;
} else {
- Condition condition = generateCondition(pathIter);
+ Condition condition = generateCondition(namedStream.name());
ret &= (condition != nullptr);
std::vector<KernelConfig> kernelConfigs;
- if ((ret &= parseFileForKernelConfigs(pathIter, &kernelConfigs)))
+ if ((ret &= parseFileForKernelConfigs(namedStream.stream(), &kernelConfigs)))
out->emplace_back(std::move(condition), std::move(kernelConfigs));
}
- pathIter = strtok(NULL, ":");
}
- delete[] modPath;
if (!foundCommonConfig) {
- std::cerr << "No android-base.cfg is found in these paths: '" << path << "'"
- << std::endl;
+ std::cerr << "No android-base.cfg is found in these paths:" << std::endl;
+ for (auto& namedStream : *streams) {
+ std::cerr << " " << namedStream.name() << std::endl;
+ }
}
ret &= foundCommonConfig;
// first element is always common configs (no conditions).
@@ -261,28 +264,26 @@
return path;
}
- std::basic_ostream<char>& out() const {
- return mOutFileRef == nullptr ? std::cout : *mOutFileRef;
- }
+ std::basic_ostream<char>& out() const { return mOutRef == nullptr ? std::cout : *mOutRef; }
template <typename S>
- using Schemas = std::vector<std::pair<std::string, S>>;
+ using Schemas = std::vector<Named<S>>;
using HalManifests = Schemas<HalManifest>;
using CompatibilityMatrices = Schemas<CompatibilityMatrix>;
bool assembleHalManifest(HalManifests* halManifests) {
std::string error;
- HalManifest* halManifest = &halManifests->front().second;
+ HalManifest* halManifest = &halManifests->front().object;
for (auto it = halManifests->begin() + 1; it != halManifests->end(); ++it) {
- const std::string& path = it->first;
- HalManifest& halToAdd = it->second;
+ const std::string& path = it->name;
+ HalManifest& halToAdd = it->object;
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 "
+ << " File '" << halManifests->front().name << "' has level "
<< halManifest->level() << std::endl
<< " File '" << path << "' has level " << halToAdd.level()
<< std::endl;
@@ -327,9 +328,9 @@
}
out().flush();
- if (mCheckFile.is_open()) {
+ if (mCheckFile != nullptr) {
CompatibilityMatrix checkMatrix;
- if (!gCompatibilityMatrixConverter(&checkMatrix, read(mCheckFile))) {
+ if (!gCompatibilityMatrixConverter(&checkMatrix, read(*mCheckFile))) {
std::cerr << "Cannot parse check file as a compatibility matrix: "
<< gCompatibilityMatrixConverter.lastError() << std::endl;
return false;
@@ -344,9 +345,9 @@
}
bool assembleFrameworkCompatibilityMatrixKernels(CompatibilityMatrix* matrix) {
- for (const auto& pair : mKernels) {
+ for (auto& pair : mKernels) {
std::vector<ConditionedConfig> conditionedConfigs;
- if (!parseFilesForKernelConfigs(pair.second, &conditionedConfigs)) {
+ if (!parseFilesForKernelConfigs(&pair.second, &conditionedConfigs)) {
return false;
}
for (ConditionedConfig& conditionedConfig : conditionedConfigs) {
@@ -400,8 +401,8 @@
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();
+ if (ret == Level::UNSPECIFIED || ret > e.object.level()) {
+ ret = e.object.level();
}
}
return ret;
@@ -410,19 +411,16 @@
bool assembleCompatibilityMatrix(CompatibilityMatrices* matrices) {
std::string error;
CompatibilityMatrix* matrix = nullptr;
- KernelSepolicyVersion kernelSepolicyVers;
- Version sepolicyVers;
std::unique_ptr<HalManifest> checkManifest;
- if (matrices->front().second.mType == SchemaType::DEVICE) {
- matrix = &matrices->front().second;
+ if (matrices->front().object.mType == SchemaType::DEVICE) {
+ matrix = &matrices->front().object;
}
- if (matrices->front().second.mType == SchemaType::FRAMEWORK) {
+ if (matrices->front().object.mType == SchemaType::FRAMEWORK) {
Level deviceLevel = Level::UNSPECIFIED;
- std::vector<std::string> fileList;
- if (mCheckFile.is_open()) {
+ if (mCheckFile != nullptr) {
checkManifest = std::make_unique<HalManifest>();
- if (!gHalManifestConverter(checkManifest.get(), read(mCheckFile))) {
+ if (!gHalManifestConverter(checkManifest.get(), read(*mCheckFile))) {
std::cerr << "Cannot parse check file as a HAL manifest: "
<< gHalManifestConverter.lastError() << std::endl;
return false;
@@ -436,55 +434,39 @@
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;
+ if (deviceLevel == Level::UNSPECIFIED) {
+ // building empty.xml
+ matrix = &matrices->front().object;
+ } else {
+ matrix = CompatibilityMatrix::combine(deviceLevel, matrices, &error);
+ if (matrix == nullptr) {
+ std::cerr << error << std::endl;
return false;
}
}
- if (!getFlag("BOARD_SEPOLICY_VERS", &sepolicyVers)) {
- return false;
- }
- if (!getFlag("POLICYVERS", &kernelSepolicyVers)) {
- return false;
- }
-
if (!assembleFrameworkCompatibilityMatrixKernels(matrix)) {
return false;
}
- matrix->framework.mSepolicy =
- Sepolicy(kernelSepolicyVers, {{sepolicyVers.majorVer, sepolicyVers.minorVer}});
-
- Version avbMetaVersion;
- if (!getFlag("FRAMEWORK_VBMETA_VERSION", &avbMetaVersion)) {
- return false;
+ // set sepolicy.sepolicy-version to BOARD_SEPOLICY_VERS when none is specified.
+ std::vector<VersionRange>* sepolicyVrs =
+ &matrix->framework.mSepolicy.mSepolicyVersionRanges;
+ VersionRange sepolicyVr;
+ if (!sepolicyVrs->empty()) sepolicyVr = sepolicyVrs->front();
+ if (getFlagIfUnset("BOARD_SEPOLICY_VERS", &sepolicyVr)) {
+ *sepolicyVrs = {{sepolicyVr}};
}
- matrix->framework.mAvbMetaVersion = avbMetaVersion;
+
+ getFlagIfUnset("POLICYVERS", &matrix->framework.mSepolicy.mKernelSepolicyVersion);
+ getFlagIfUnset("FRAMEWORK_VBMETA_VERSION", &matrix->framework.mAvbMetaVersion);
out() << "<!--" << std::endl;
out() << " Input:" << std::endl;
- for (const auto& path : fileList) {
- out() << " " << getFileNameFromPath(path) << std::endl;
+ for (const auto& e : *matrices) {
+ if (!e.name.empty()) {
+ out() << " " << getFileNameFromPath(e.name) << std::endl;
+ }
}
out() << "-->" << std::endl;
}
@@ -533,7 +515,7 @@
return assemble(&schemas) ? SUCCESS : FAIL_AND_EXIT;
}
- bool assemble() {
+ bool assemble() override {
using std::placeholders::_1;
if (mInFiles.empty()) {
std::cerr << "Missing input file." << std::endl;
@@ -541,14 +523,14 @@
}
auto status = tryAssemble(gHalManifestConverter, "manifest",
- std::bind(&AssembleVintf::assembleHalManifest, this, _1));
+ std::bind(&AssembleVintfImpl::assembleHalManifest, this, _1));
if (status == SUCCESS) return true;
if (status == FAIL_AND_EXIT) return false;
resetInFiles();
status = tryAssemble(gCompatibilityMatrixConverter, "compatibility matrix",
- std::bind(&AssembleVintf::assembleCompatibilityMatrix, this, _1));
+ std::bind(&AssembleVintfImpl::assembleCompatibilityMatrix, this, _1));
if (status == SUCCESS) return true;
if (status == FAIL_AND_EXIT) return false;
@@ -560,22 +542,30 @@
return false;
}
- bool openOutFile(const char* path) {
- mOutFileRef = std::make_unique<std::ofstream>();
- mOutFileRef->open(path);
- return mOutFileRef->is_open();
+ std::ostream& setOutputStream(Ostream&& out) override {
+ mOutRef = std::move(out);
+ return *mOutRef;
}
- bool openInFile(const char* path) {
- auto s = std::make_unique<std::ifstream>(path);
- if (!s->is_open()) return false;
- mInFiles.emplace(mInFiles.end(), std::string{path}, std::move(s));
- return true;
+ std::istream& addInputStream(const std::string& name, Istream&& in) override {
+ auto it = mInFiles.emplace(mInFiles.end(), name, std::move(in));
+ return it->stream();
}
- bool openCheckFile(const char* path) {
- mCheckFile.open(path);
- return mCheckFile.is_open();
+ std::istream& setCheckInputStream(Istream&& in) override {
+ mCheckFile = std::move(in);
+ return *mCheckFile;
+ }
+
+ bool hasKernelVersion(const Version& kernelVer) const override {
+ return mKernels.find(kernelVer) != mKernels.end();
+ }
+
+ std::istream& addKernelConfigInputStream(const Version& kernelVer, const std::string& name,
+ Istream&& in) override {
+ auto&& kernel = mKernels[kernelVer];
+ auto it = kernel.emplace(kernel.end(), name, std::move(in));
+ return it->stream();
}
void resetInFiles() {
@@ -585,169 +575,76 @@
}
}
- void setOutputMatrix() { mOutputMatrix = true; }
+ void setOutputMatrix() override { mOutputMatrix = true; }
- bool setHalsOnly() {
+ bool setHalsOnly() override {
if (mSerializeFlags) return false;
mSerializeFlags |= SerializeFlag::HALS_ONLY;
return true;
}
- bool setNoHals() {
+ bool setNoHals() override {
if (mSerializeFlags) return false;
mSerializeFlags |= SerializeFlag::NO_HALS;
return true;
}
- bool addKernel(const std::string& kernelArg) {
- auto ind = kernelArg.find(':');
- if (ind == std::string::npos) {
- std::cerr << "Unrecognized --kernel option '" << kernelArg << "'" << std::endl;
- return false;
- }
- std::string kernelVerStr{kernelArg.begin(), kernelArg.begin() + ind};
- std::string kernelConfigPath{kernelArg.begin() + ind + 1, kernelArg.end()};
- Version kernelVer;
- if (!parse(kernelVerStr, &kernelVer)) {
- std::cerr << "Unrecognized kernel version '" << kernelVerStr << "'" << std::endl;
- return false;
- }
- if (mKernels.find(kernelVer) != mKernels.end()) {
- std::cerr << "Multiple --kernel for " << kernelVer << " is specified." << std::endl;
- return false;
- }
- mKernels[kernelVer] = kernelConfigPath;
- return true;
- }
-
private:
std::vector<NamedIstream> mInFiles;
- std::unique_ptr<std::ofstream> mOutFileRef;
- std::ifstream mCheckFile;
+ Ostream mOutRef;
+ Istream mCheckFile;
bool mOutputMatrix = false;
SerializeFlags mSerializeFlags = SerializeFlag::EVERYTHING;
- std::map<Version, std::string> mKernels;
+ std::map<Version, std::vector<NamedIstream>> mKernels;
+ std::map<std::string, std::string> mFakeEnv;
};
+bool AssembleVintf::openOutFile(const std::string& path) {
+ return static_cast<std::ofstream&>(setOutputStream(std::make_unique<std::ofstream>(path)))
+ .is_open();
+}
+
+bool AssembleVintf::openInFile(const std::string& path) {
+ return static_cast<std::ifstream&>(addInputStream(path, std::make_unique<std::ifstream>(path)))
+ .is_open();
+}
+
+bool AssembleVintf::openCheckFile(const std::string& path) {
+ return static_cast<std::ifstream&>(setCheckInputStream(std::make_unique<std::ifstream>(path)))
+ .is_open();
+}
+
+bool AssembleVintf::addKernel(const std::string& kernelArg) {
+ auto tokens = base::Split(kernelArg, ":");
+ if (tokens.size() <= 1) {
+ std::cerr << "Unrecognized --kernel option '" << kernelArg << "'" << std::endl;
+ return false;
+ }
+ Version kernelVer;
+ if (!parse(tokens.front(), &kernelVer)) {
+ std::cerr << "Unrecognized kernel version '" << tokens.front() << "'" << std::endl;
+ return false;
+ }
+ if (hasKernelVersion(kernelVer)) {
+ std::cerr << "Multiple --kernel for " << kernelVer << " is specified." << std::endl;
+ return false;
+ }
+ for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) {
+ bool opened =
+ static_cast<std::ifstream&>(
+ addKernelConfigInputStream(kernelVer, *it, std::make_unique<std::ifstream>(*it)))
+ .is_open();
+ if (!opened) {
+ std::cerr << "Cannot open file '" << *it << "'." << std::endl;
+ return false;
+ }
+ }
+ return true;
+}
+
+std::unique_ptr<AssembleVintf> AssembleVintf::newInstance() {
+ return std::make_unique<AssembleVintfImpl>();
+}
+
} // namespace vintf
} // namespace android
-
-void help() {
- std::cerr << "assemble_vintf: Checks if a given manifest / matrix file is valid and \n"
- " fill in build-time flags into the given file.\n"
- "assemble_vintf -h\n"
- " Display this help text.\n"
- "assemble_vintf -i <input file>[:<input file>[...]] [-o <output file>] [-m]\n"
- " [-c [<check file>]]\n"
- " Fill in build-time flags into the given file.\n"
- " -i <input file>[:<input file>[...]]\n"
- " A list of input files. Format is automatically detected for the\n"
- " first file, and the remaining files must have the same format.\n"
- " Files other than the first file should only have <hal> defined;\n"
- " other entries are ignored.\n"
- " -o <output file>\n"
- " Optional output file. If not specified, write to stdout.\n"
- " -m\n"
- " a compatible compatibility matrix is\n"
- " generated instead; for example, given a device manifest,\n"
- " a framework compatibility matrix is generated. This flag\n"
- " is ignored when input is a compatibility matrix.\n"
- " -c [<check file>]\n"
- " After writing the output file, check compatibility between\n"
- " output file and check file.\n"
- " If -c is set but the check file is not specified, a warning\n"
- " message is written to stderr. Return 0.\n"
- " If the check file is specified but is not compatible, an error\n"
- " message is written to stderr. Return 1.\n"
- " --kernel=<version>:<android-base.cfg>[:<android-base-arch.cfg>[...]]\n"
- " Add a kernel entry to framework compatibility matrix.\n"
- " Ignored for other input format.\n"
- " <version> has format: 3.18\n"
- " <android-base.cfg> is the location of android-base.cfg\n"
- " <android-base-arch.cfg> is the location of an optional\n"
- " arch-specific config fragment, more than one may be specified\n"
- " -l, --hals-only\n"
- " Output has only <hal> entries. Cannot be used with -n.\n"
- " -n, --no-hals\n"
- " Output has no <hal> entries (but all other entries).\n"
- " Cannot be used with -l.\n";
-}
-
-int main(int argc, char **argv) {
- const struct option longopts[] = {{"kernel", required_argument, NULL, 'k'},
- {"hals-only", no_argument, NULL, 'l'},
- {"no-hals", no_argument, NULL, 'n'},
- {0, 0, 0, 0}};
-
- std::string outFilePath;
- ::android::vintf::AssembleVintf assembleVintf;
- int res;
- int optind;
- while ((res = getopt_long(argc, argv, "hi:o:mc:nl", longopts, &optind)) >= 0) {
- switch (res) {
- case 'i': {
- char* inFilePath = strtok(optarg, ":");
- while (inFilePath != NULL) {
- if (!assembleVintf.openInFile(inFilePath)) {
- std::cerr << "Failed to open " << optarg << std::endl;
- return 1;
- }
- inFilePath = strtok(NULL, ":");
- }
- } break;
-
- case 'o': {
- outFilePath = optarg;
- if (!assembleVintf.openOutFile(optarg)) {
- std::cerr << "Failed to open " << optarg << std::endl;
- return 1;
- }
- } break;
-
- case 'm': {
- assembleVintf.setOutputMatrix();
- } break;
-
- case 'c': {
- if (strlen(optarg) != 0) {
- if (!assembleVintf.openCheckFile(optarg)) {
- std::cerr << "Failed to open " << optarg << std::endl;
- return 1;
- }
- } else {
- std::cerr << "WARNING: no compatibility check is done on "
- << (outFilePath.empty() ? "output" : outFilePath) << std::endl;
- }
- } break;
-
- case 'k': {
- if (!assembleVintf.addKernel(optarg)) {
- std::cerr << "ERROR: Unrecognized --kernel argument." << std::endl;
- return 1;
- }
- } break;
-
- case 'l': {
- if (!assembleVintf.setHalsOnly()) {
- return 1;
- }
- } break;
-
- case 'n': {
- if (!assembleVintf.setNoHals()) {
- return 1;
- }
- } break;
-
- case 'h':
- default: {
- help();
- return 1;
- } break;
- }
- }
-
- bool success = assembleVintf.assemble();
-
- return success ? 0 : 1;
-}
diff --git a/CompatibilityMatrix.cpp b/CompatibilityMatrix.cpp
index 1e7d542..076055c 100644
--- a/CompatibilityMatrix.cpp
+++ b/CompatibilityMatrix.cpp
@@ -21,6 +21,9 @@
#include "parse_string.h"
#include "utils.h"
+#include <iostream>
+#include "parse_xml.h"
+
namespace android {
namespace vintf {
@@ -166,5 +169,122 @@
lft.framework.mAvbMetaVersion == rgt.framework.mAvbMetaVersion));
}
+// Find compatibility_matrix.empty.xml (which has unspecified level) and use it
+// as a base matrix.
+CompatibilityMatrix* CompatibilityMatrix::findOrInsertBaseMatrix(
+ std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error) {
+ bool multipleFound = false;
+ CompatibilityMatrix* matrix = nullptr;
+ for (auto& e : *matrices) {
+ if (e.object.level() == Level::UNSPECIFIED) {
+ if (!e.object.mHals.empty()) {
+ if (error) {
+ *error = "Error: File \"" + e.name + "\" should not contain " + "HAL elements.";
+ }
+ return nullptr;
+ }
+
+ if (!e.object.mXmlFiles.empty()) {
+ if (error) {
+ *error =
+ "Error: File \"" + e.name + "\" should not contain " + "XML File elements.";
+ }
+ return nullptr;
+ }
+
+ if (matrix != nullptr) {
+ multipleFound = true;
+ }
+
+ matrix = &e.object;
+ // continue to detect multiple files with "unspecified" levels
+ }
+ }
+
+ if (multipleFound) {
+ if (error) {
+ *error =
+ "Error: multiple framework compatibility matrix files have "
+ "unspecified level; there should only be one such file.\n";
+ for (auto& e : *matrices) {
+ if (e.object.level() == Level::UNSPECIFIED) {
+ *error += " " + e.name + "\n";
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ if (matrix == nullptr) {
+ matrix = &matrices->emplace(matrices->end())->object;
+ matrix->mType = SchemaType::FRAMEWORK;
+ matrix->mLevel = Level::UNSPECIFIED;
+ }
+
+ return matrix;
+}
+
+CompatibilityMatrix* CompatibilityMatrix::combine(Level deviceLevel,
+ std::vector<Named<CompatibilityMatrix>>* matrices,
+ std::string* error) {
+ if (deviceLevel == Level::UNSPECIFIED) {
+ if (error) {
+ *error = "Error: device level is unspecified.";
+ }
+ return nullptr;
+ }
+
+ CompatibilityMatrix* matrix = findOrInsertBaseMatrix(matrices, error);
+ if (matrix == nullptr) {
+ return nullptr;
+ }
+
+ matrix->mLevel = deviceLevel;
+
+ for (auto& e : *matrices) {
+ if (&e.object != matrix && e.object.level() == deviceLevel) {
+ if (!matrix->addAllHals(&e.object, error)) {
+ if (error) {
+ *error = "File \"" + e.name + "\" cannot be added: HAL " + *error +
+ " has a conflict.";
+ }
+ return nullptr;
+ }
+
+ if (!matrix->addAllXmlFiles(&e.object, error)) {
+ if (error) {
+ *error = "File \"" + e.name + "\" cannot be added: XML File entry " + *error +
+ " has a conflict.";
+ }
+ return nullptr;
+ }
+ }
+ }
+
+ for (auto& e : *matrices) {
+ if (&e.object != matrix && e.object.level() != Level::UNSPECIFIED &&
+ e.object.level() > deviceLevel) {
+ if (!matrix->addAllHalsAsOptional(&e.object, error)) {
+ if (error) {
+ *error = "File \"" + e.name + "\" cannot be added: " + *error +
+ ". See <hal> with the same name " +
+ "in previously parsed files or previously declared in this file.";
+ }
+ return nullptr;
+ }
+
+ if (!matrix->addAllXmlFilesAsOptional(&e.object, error)) {
+ if (error) {
+ *error = "File \"" + e.name + "\" cannot be added: XML File entry " + *error +
+ " has a conflict.";
+ }
+ return nullptr;
+ }
+ }
+ }
+
+ return matrix;
+}
+
} // namespace vintf
} // namespace android
diff --git a/assemble_vintf_main.cpp b/assemble_vintf_main.cpp
new file mode 100644
index 0000000..45e6dc2
--- /dev/null
+++ b/assemble_vintf_main.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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 <getopt.h>
+
+#include <iostream>
+
+#include <android-base/strings.h>
+#include <vintf/AssembleVintf.h>
+#include "utils.h"
+
+void help() {
+ std::cerr << "assemble_vintf: Checks if a given manifest / matrix file is valid and \n"
+ " fill in build-time flags into the given file.\n"
+ "assemble_vintf -h\n"
+ " Display this help text.\n"
+ "assemble_vintf -i <input file>[:<input file>[...]] [-o <output file>] [-m]\n"
+ " [-c [<check file>]]\n"
+ " Fill in build-time flags into the given file.\n"
+ " -i <input file>[:<input file>[...]]\n"
+ " A list of input files. Format is automatically detected for the\n"
+ " first file, and the remaining files must have the same format.\n"
+ " Files other than the first file should only have <hal> defined;\n"
+ " other entries are ignored.\n"
+ " -o <output file>\n"
+ " Optional output file. If not specified, write to stdout.\n"
+ " -m\n"
+ " a compatible compatibility matrix is\n"
+ " generated instead; for example, given a device manifest,\n"
+ " a framework compatibility matrix is generated. This flag\n"
+ " is ignored when input is a compatibility matrix.\n"
+ " -c [<check file>]\n"
+ " After writing the output file, check compatibility between\n"
+ " output file and check file.\n"
+ " If -c is set but the check file is not specified, a warning\n"
+ " message is written to stderr. Return 0.\n"
+ " If the check file is specified but is not compatible, an error\n"
+ " message is written to stderr. Return 1.\n"
+ " --kernel=<version>:<android-base.cfg>[:<android-base-arch.cfg>[...]]\n"
+ " Add a kernel entry to framework compatibility matrix.\n"
+ " Ignored for other input format.\n"
+ " <version> has format: 3.18\n"
+ " <android-base.cfg> is the location of android-base.cfg\n"
+ " <android-base-arch.cfg> is the location of an optional\n"
+ " arch-specific config fragment, more than one may be specified\n"
+ " -l, --hals-only\n"
+ " Output has only <hal> entries. Cannot be used with -n.\n"
+ " -n, --no-hals\n"
+ " Output has no <hal> entries (but all other entries).\n"
+ " Cannot be used with -l.\n";
+}
+
+int main(int argc, char** argv) {
+ using namespace ::android::vintf;
+ const struct option longopts[] = {{"kernel", required_argument, NULL, 'k'},
+ {"hals-only", no_argument, NULL, 'l'},
+ {"no-hals", no_argument, NULL, 'n'},
+ {0, 0, 0, 0}};
+
+ std::string outFilePath;
+ auto assembleVintf = AssembleVintf::newInstance();
+ int res;
+ int optind;
+ while ((res = getopt_long(argc, argv, "hi:o:mc:nl", longopts, &optind)) >= 0) {
+ switch (res) {
+ case 'i': {
+ for (const auto& inFilePath : ::android::base::Split(optarg, ":")) {
+ if (!assembleVintf->openInFile(inFilePath.c_str())) {
+ std::cerr << "Failed to open " << inFilePath << std::endl;
+ return 1;
+ }
+ }
+ } break;
+
+ case 'o': {
+ outFilePath = optarg;
+ if (!assembleVintf->openOutFile(optarg)) {
+ std::cerr << "Failed to open " << optarg << std::endl;
+ return 1;
+ }
+ } break;
+
+ case 'm': {
+ assembleVintf->setOutputMatrix();
+ } break;
+
+ case 'c': {
+ if (!assembleVintf->openCheckFile(optarg)) {
+ std::cerr << "Failed to open " << optarg << std::endl;
+ return 1;
+ }
+ } break;
+
+ case 'k': {
+ if (!assembleVintf->addKernel(optarg)) {
+ std::cerr << "ERROR: Unrecognized --kernel argument." << std::endl;
+ return 1;
+ }
+ } break;
+
+ case 'l': {
+ if (!assembleVintf->setHalsOnly()) {
+ return 1;
+ }
+ } break;
+
+ case 'n': {
+ if (!assembleVintf->setNoHals()) {
+ return 1;
+ }
+ } break;
+
+ case 'h':
+ default: {
+ help();
+ return 1;
+ } break;
+ }
+ }
+
+ bool success = assembleVintf->assemble();
+
+ return success ? 0 : 1;
+}
diff --git a/include-test/vintf/AssembleVintf.h b/include-test/vintf/AssembleVintf.h
new file mode 100644
index 0000000..1b457eb
--- /dev/null
+++ b/include-test/vintf/AssembleVintf.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_VINTF_ASSEMBLE_VINTF_H
+#define ANDROID_VINTF_ASSEMBLE_VINTF_H
+
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <vintf/Version.h>
+
+namespace android {
+namespace vintf {
+
+class AssembleVintf {
+ public:
+ using Ostream = std::unique_ptr<std::ostream>;
+ using Istream = std::unique_ptr<std::istream>;
+
+ static std::unique_ptr<AssembleVintf> newInstance();
+
+ virtual ~AssembleVintf() = default;
+ virtual bool setHalsOnly() = 0;
+ virtual bool setNoHals() = 0;
+ virtual void setOutputMatrix() = 0;
+ virtual bool assemble() = 0;
+
+ bool openOutFile(const std::string& path);
+ bool openInFile(const std::string& path);
+ bool openCheckFile(const std::string& path);
+ bool addKernel(const std::string& kernelArg);
+
+ virtual std::ostream& setOutputStream(Ostream&&) = 0;
+ virtual std::istream& addInputStream(const std::string& name, Istream&&) = 0;
+ virtual std::istream& setCheckInputStream(Istream&&) = 0;
+ virtual std::istream& addKernelConfigInputStream(const Version& kernelVer,
+ const std::string& name, Istream&& in) = 0;
+ virtual void setFakeEnv(const std::string& key, const std::string& value) = 0;
+
+ protected:
+ virtual bool hasKernelVersion(const Version&) const = 0;
+ virtual std::string getEnv(const std::string& key) const = 0;
+};
+
+} // namespace vintf
+} // namespace android
+#endif // ANDROID_VINTF_ASSEMBLE_VINTF_H
diff --git a/include/vintf/CompatibilityMatrix.h b/include/vintf/CompatibilityMatrix.h
index bfb4dbf..62b505a 100644
--- a/include/vintf/CompatibilityMatrix.h
+++ b/include/vintf/CompatibilityMatrix.h
@@ -27,6 +27,7 @@
#include "MapValueIterator.h"
#include "MatrixHal.h"
#include "MatrixKernel.h"
+#include "Named.h"
#include "SchemaType.h"
#include "Sepolicy.h"
#include "Vndk.h"
@@ -73,12 +74,25 @@
status_t fetchAllInformation(const std::string& path, std::string* error = nullptr);
+ // Combine a subset of "matrices". For each CompatibilityMatrix in matrices,
+ // - If level() == UNSPECIFIED, use it as the base matrix (for non-HAL, non-XML-file
+ // requirements).
+ // - If level() < deviceLevel, ignore
+ // - If level() == deviceLevel, all HAL versions and XML files are added as is
+ // (optionality is kept)
+ // - If level() > deviceLevel, all HAL versions and XML files are added as optional.
+ static CompatibilityMatrix* combine(Level deviceLevel,
+ std::vector<Named<CompatibilityMatrix>>* matrices,
+ std::string* error);
+ static CompatibilityMatrix* findOrInsertBaseMatrix(
+ std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error);
+
friend struct HalManifest;
friend struct RuntimeInfo;
friend struct CompatibilityMatrixConverter;
friend struct LibVintfTest;
friend class VintfObject;
- friend class AssembleVintf;
+ friend class AssembleVintfImpl;
friend bool operator==(const CompatibilityMatrix &, const CompatibilityMatrix &);
SchemaType mType;
diff --git a/include/vintf/HalManifest.h b/include/vintf/HalManifest.h
index cc64201..b9cd504 100644
--- a/include/vintf/HalManifest.h
+++ b/include/vintf/HalManifest.h
@@ -122,7 +122,7 @@
private:
friend struct HalManifestConverter;
friend class VintfObject;
- friend class AssembleVintf;
+ friend class AssembleVintfImpl;
friend struct LibVintfTest;
friend std::string dump(const HalManifest &vm);
friend bool operator==(const HalManifest &lft, const HalManifest &rgt);
diff --git a/include/vintf/MatrixKernel.h b/include/vintf/MatrixKernel.h
index f0990a8..cee7e32 100644
--- a/include/vintf/MatrixKernel.h
+++ b/include/vintf/MatrixKernel.h
@@ -58,7 +58,7 @@
private:
friend struct MatrixKernelConverter;
friend struct MatrixKernelConditionsConverter;
- friend class AssembleVintf;
+ friend class AssembleVintfImpl;
KernelVersion mMinLts;
std::vector<KernelConfig> mConfigs;
diff --git a/include/vintf/Named.h b/include/vintf/Named.h
new file mode 100644
index 0000000..eacb70b
--- /dev/null
+++ b/include/vintf/Named.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_VINTF_NAMED_H
+#define ANDROID_VINTF_NAMED_H
+
+#include <string>
+
+template <typename T>
+struct Named {
+ std::string name;
+ T object;
+
+ Named() = default;
+ Named(const std::string& n, const T& o) : name(n), object(o) {}
+ Named(std::string&& n, T&& o) : name(std::move(n)), object(std::move(o)) {}
+};
+
+#endif // ANDROID_VINTF_NAMED_H
diff --git a/include/vintf/Sepolicy.h b/include/vintf/Sepolicy.h
index 698cab0..09b9c97 100644
--- a/include/vintf/Sepolicy.h
+++ b/include/vintf/Sepolicy.h
@@ -43,7 +43,9 @@
inline const std::vector<VersionRange> &sepolicyVersions() const {
return mSepolicyVersionRanges;
}
-private:
+
+ private:
+ friend class AssembleVintfImpl;
friend struct SepolicyConverter;
KernelSepolicyVersion mKernelSepolicyVersion;
std::vector<VersionRange> mSepolicyVersionRanges;
diff --git a/test/Android.bp b/test/Android.bp
index 5c9fc43..40d8fbf 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -18,6 +18,7 @@
host_supported: true,
gtest: false,
srcs: [
+ "AssembleVintfTest.cpp",
"LibVintfTest.cpp",
],
@@ -27,7 +28,10 @@
"liblog",
"libvintf",
],
- static_libs: ["libgtest"],
+ static_libs: [
+ "libgtest",
+ "libassemblevintf",
+ ],
cflags: [
"-O0",
diff --git a/test/AssembleVintfTest.cpp b/test/AssembleVintfTest.cpp
new file mode 100644
index 0000000..b1d146c
--- /dev/null
+++ b/test/AssembleVintfTest.cpp
@@ -0,0 +1,290 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AssembleVintfTest"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <vintf/AssembleVintf.h>
+#include <vintf/parse_string.h>
+
+namespace android {
+namespace vintf {
+
+static bool In(const std::string& sub, const std::string& str) {
+ return str.find(sub) != std::string::npos;
+}
+#define EXPECT_IN(sub, str) EXPECT_TRUE(In((sub), (str))) << (str);
+
+class AssembleVintfTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ mInstance = AssembleVintf::newInstance();
+ auto s = std::make_unique<std::stringstream>();
+ mOutputStream = s.get();
+ mInstance->setOutputStream(std::move(s));
+ }
+ virtual void TearDown() override { mInstance = nullptr; }
+
+ const std::unique_ptr<AssembleVintf>& getInstance() { return mInstance; }
+
+ std::string getOutput() { return mOutputStream->str(); }
+
+ void resetOutput() { mOutputStream->str(""); }
+
+ void setFakeEnvs(const std::map<std::string, std::string>& envs) {
+ for (const auto& pair : envs) {
+ getInstance()->setFakeEnv(pair.first, pair.second);
+ }
+ }
+
+ void addInput(const std::string& name, const std::string& s) {
+ getInstance()->addInputStream(name, std::make_unique<std::stringstream>(s));
+ }
+
+ std::unique_ptr<AssembleVintf> mInstance;
+ // do not own this object.
+ std::stringstream* mOutputStream;
+};
+
+TEST_F(AssembleVintfTest, FrameworkMatrixEmpty) {
+ std::string xmlEmpty = "<compatibility-matrix version=\"1.0\" type=\"framework\" />";
+ std::string kernel318 = "CONFIG_FOO=y\n";
+ std::string kernel318_64 = "CONFIG_BAR=y\n";
+ std::string kernel44 = "# CONFIG_FOO is not set\n";
+ std::string kernel44_64 = "CONFIG_BAR=y\n";
+
+ addInput("compatibility_matrix.empty.xml", xmlEmpty);
+ setFakeEnvs({
+ {"POLICYVERS", "30"},
+ {"BOARD_SEPOLICY_VERS", "10000.0"},
+ {"FRAMEWORK_VBMETA_VERSION", "1.0"},
+ });
+ getInstance()->addKernelConfigInputStream({3, 18}, "android-base.cfg",
+ std::make_unique<std::stringstream>(kernel318));
+ getInstance()->addKernelConfigInputStream({3, 18}, "android-base-arm64.cfg",
+ std::make_unique<std::stringstream>(kernel318_64));
+ getInstance()->addKernelConfigInputStream({4, 4}, "android-base.cfg",
+ std::make_unique<std::stringstream>(kernel44));
+ getInstance()->addKernelConfigInputStream({4, 4}, "android-base-arm64.cfg",
+ std::make_unique<std::stringstream>(kernel44_64));
+
+ EXPECT_TRUE(getInstance()->assemble());
+
+ EXPECT_IN(
+ "<compatibility-matrix version=\"1.0\" type=\"framework\">\n"
+ " <kernel version=\"3.18.0\">\n"
+ " <config>\n"
+ " <key>CONFIG_FOO</key>\n"
+ " <value type=\"tristate\">y</value>\n"
+ " </config>\n"
+ " </kernel>\n"
+ " <kernel version=\"3.18.0\">\n"
+ " <conditions>\n"
+ " <config>\n"
+ " <key>CONFIG_ARM64</key>\n"
+ " <value type=\"tristate\">y</value>\n"
+ " </config>\n"
+ " </conditions>\n"
+ " <config>\n"
+ " <key>CONFIG_BAR</key>\n"
+ " <value type=\"tristate\">y</value>\n"
+ " </config>\n"
+ " </kernel>\n"
+ " <kernel version=\"4.4.0\">\n"
+ " <config>\n"
+ " <key>CONFIG_FOO</key>\n"
+ " <value type=\"tristate\">n</value>\n"
+ " </config>\n"
+ " </kernel>\n"
+ " <kernel version=\"4.4.0\">\n"
+ " <conditions>\n"
+ " <config>\n"
+ " <key>CONFIG_ARM64</key>\n"
+ " <value type=\"tristate\">y</value>\n"
+ " </config>\n"
+ " </conditions>\n"
+ " <config>\n"
+ " <key>CONFIG_BAR</key>\n"
+ " <value type=\"tristate\">y</value>\n"
+ " </config>\n"
+ " </kernel>\n"
+ " <sepolicy>\n"
+ " <kernel-sepolicy-version>30</kernel-sepolicy-version>\n"
+ " <sepolicy-version>10000.0</sepolicy-version>\n"
+ " </sepolicy>\n"
+ " <avb>\n"
+ " <vbmeta-version>1.0</vbmeta-version>\n"
+ " </avb>\n"
+ "</compatibility-matrix>\n",
+ getOutput());
+}
+
+TEST_F(AssembleVintfTest, FrameworkMatrix) {
+ std::string tail =
+ " <kernel version=\"3.18.0\">\n"
+ " <config>\n"
+ " <key>CONFIG_FOO</key>\n"
+ " <value type=\"tristate\">y</value>\n"
+ " </config>\n"
+ " </kernel>\n"
+ " <sepolicy>\n"
+ " <kernel-sepolicy-version>30</kernel-sepolicy-version>\n"
+ " <sepolicy-version>10000.0</sepolicy-version>\n"
+ " </sepolicy>\n"
+ " <avb>\n"
+ " <vbmeta-version>1.0</vbmeta-version>\n"
+ " </avb>\n"
+ "</compatibility-matrix>\n";
+
+ std::string xmlEmpty = "<compatibility-matrix version=\"1.0\" type=\"framework\">\n" + tail;
+
+ std::string xml1 =
+ "<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</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ "</compatibility-matrix>\n";
+
+ std::string xml2 =
+ "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"2\">\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";
+
+ std::string xml3 =
+ "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"3\">\n"
+ " <hal format=\"hidl\" optional=\"false\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>2.0</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ "</compatibility-matrix>\n";
+
+ auto manifest = [](size_t level) {
+ return "<manifest version=\"1.0\" type=\"device\" target-level=\"" + std::to_string(level) +
+ "\">\n" +
+ " <hal format=\"hidl\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>1.1</version>\n"
+ " <transport>hwbinder</transport>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ " <hal format=\"hidl\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>2.0</version>\n"
+ " <transport>hwbinder</transport>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ " <sepolicy>\n"
+ " <version>10000.0</version>\n"
+ " </sepolicy>\n"
+ "</manifest>\n";
+ };
+
+ addInput("compatibility_matrix.1.xml", xml1);
+ addInput("compatibility_matrix.2.xml", xml2);
+ addInput("compatibility_matrix.3.xml", xml3);
+ addInput("compatibility_matrix.empty.xml", xmlEmpty);
+ getInstance()->setFakeEnv("PRODUCT_ENFORCE_VINTF_MANIFEST", "true");
+
+ resetOutput();
+ getInstance()->setCheckInputStream(std::make_unique<std::stringstream>(manifest(1)));
+ getInstance()->assemble();
+ EXPECT_IN(
+ "<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"
+ " <hal format=\"hidl\" optional=\"true\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>2.0</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n" +
+ tail,
+ getOutput());
+
+ resetOutput();
+ getInstance()->setCheckInputStream(std::make_unique<std::stringstream>(manifest(2)));
+ getInstance()->assemble();
+ EXPECT_IN(
+ "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"2\">\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"
+ " <hal format=\"hidl\" optional=\"true\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>2.0</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n" +
+ tail,
+ getOutput());
+
+ resetOutput();
+ getInstance()->setCheckInputStream(std::make_unique<std::stringstream>(manifest(3)));
+ getInstance()->assemble();
+ EXPECT_IN(
+ "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"3\">\n"
+ " <hal format=\"hidl\" optional=\"false\">\n"
+ " <name>android.hardware.foo</name>\n"
+ " <version>2.0</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>default</instance>\n"
+ " </interface>\n"
+ " </hal>\n" +
+ tail,
+ getOutput());
+}
+
+} // namespace vintf
+} // namespace android