diff options
author | Yifan Hong | 2018-10-11 12:38:57 -0500 |
---|---|---|
committer | Yifan Hong | 2018-10-15 15:41:04 -0500 |
commit | e7837b164cf04b95ddc1e365bda89d0422b925ad (patch) | |
tree | 60ae6c3f1da9d7e2903d295649ddd6f77159bede | |
parent | b40e32c92c3d60f59385001a53e7a057fdef37da (diff) | |
download | platform-system-libvintf-e7837b164cf04b95ddc1e365bda89d0422b925ad.tar.gz platform-system-libvintf-e7837b164cf04b95ddc1e365bda89d0422b925ad.tar.xz platform-system-libvintf-e7837b164cf04b95ddc1e365bda89d0422b925ad.zip |
CompatibilityMatrix::combine() combines everything correctly
* Combines <kernel> correctly in order to make assemble_vintf
add <kernel> in any order. That is, we now allow:
assemble_vintf -i base_matrix.xml -i kernel_config_base.xml
-i kernel_config_x86.xml ...
and <kernel>'s will be added in the correct order even when
the order of kernel_config_base.xml and kernel_config_x86.xml
is swapped.
* Combine <sepolicy> and <avb> correctly. They can only
be defined once per level.
Test: libvintf_test
Test: vintf_object_test
Test: build framework compatibility matrix and inspect output manually
Bug: 78943004
Change-Id: I6c90849b8bef0b690fb292bcc5fa8c514f323d96
-rw-r--r-- | AssembleVintf.cpp | 52 | ||||
-rw-r--r-- | CompatibilityMatrix.cpp | 312 | ||||
-rw-r--r-- | VintfObject.cpp | 3 | ||||
-rw-r--r-- | include/vintf/CompatibilityMatrix.h | 48 | ||||
-rw-r--r-- | include/vintf/HalGroup.h | 2 | ||||
-rw-r--r-- | include/vintf/MatrixKernel.h | 1 | ||||
-rw-r--r-- | include/vintf/XmlFileGroup.h | 2 | ||||
-rw-r--r-- | test/LibVintfTest.cpp | 5 |
8 files changed, 233 insertions, 192 deletions
diff --git a/AssembleVintf.cpp b/AssembleVintf.cpp index 272301e..cc8db31 100644 --- a/AssembleVintf.cpp +++ b/AssembleVintf.cpp | |||
@@ -412,6 +412,7 @@ class AssembleVintfImpl : public AssembleVintf { | |||
412 | return true; | 412 | return true; |
413 | } | 413 | } |
414 | 414 | ||
415 | // Parse --kernel arguments and write to output matrix. | ||
415 | bool assembleFrameworkCompatibilityMatrixKernels(CompatibilityMatrix* matrix) { | 416 | bool assembleFrameworkCompatibilityMatrixKernels(CompatibilityMatrix* matrix) { |
416 | for (auto& pair : mKernels) { | 417 | for (auto& pair : mKernels) { |
417 | std::vector<ConditionedConfig> conditionedConfigs; | 418 | std::vector<ConditionedConfig> conditionedConfigs; |
@@ -422,7 +423,11 @@ class AssembleVintfImpl : public AssembleVintf { | |||
422 | MatrixKernel kernel(KernelVersion{pair.first}, std::move(conditionedConfig.second)); | 423 | MatrixKernel kernel(KernelVersion{pair.first}, std::move(conditionedConfig.second)); |
423 | if (conditionedConfig.first != nullptr) | 424 | if (conditionedConfig.first != nullptr) |
424 | kernel.mConditions.push_back(std::move(*conditionedConfig.first)); | 425 | kernel.mConditions.push_back(std::move(*conditionedConfig.first)); |
425 | matrix->framework.mKernels.push_back(std::move(kernel)); | 426 | std::string error; |
427 | if (!matrix->addKernel(std::move(kernel), &error)) { | ||
428 | std::cerr << "Error:" << error << std::endl; | ||
429 | return false; | ||
430 | }; | ||
426 | } | 431 | } |
427 | } | 432 | } |
428 | return true; | 433 | return true; |
@@ -484,7 +489,24 @@ class AssembleVintfImpl : public AssembleVintf { | |||
484 | std::string error; | 489 | std::string error; |
485 | CompatibilityMatrix* matrix = nullptr; | 490 | CompatibilityMatrix* matrix = nullptr; |
486 | std::unique_ptr<HalManifest> checkManifest; | 491 | std::unique_ptr<HalManifest> checkManifest; |
492 | std::unique_ptr<CompatibilityMatrix> builtMatrix; | ||
493 | |||
494 | if (mCheckFile != nullptr) { | ||
495 | checkManifest = std::make_unique<HalManifest>(); | ||
496 | if (!gHalManifestConverter(checkManifest.get(), read(*mCheckFile), &error)) { | ||
497 | std::cerr << "Cannot parse check file as a HAL manifest: " << error << std::endl; | ||
498 | return false; | ||
499 | } | ||
500 | } | ||
501 | |||
487 | if (matrices->front().object.mType == SchemaType::DEVICE) { | 502 | if (matrices->front().object.mType == SchemaType::DEVICE) { |
503 | if (matrices->size() > 1) { | ||
504 | std::cerr | ||
505 | << "assemble_vintf does not support merging device compatibilility matrices." | ||
506 | << std::endl; | ||
507 | return false; | ||
508 | } | ||
509 | |||
488 | matrix = &matrices->front().object; | 510 | matrix = &matrices->front().object; |
489 | 511 | ||
490 | auto vndkVersion = base::Trim(getEnv("REQUIRED_VNDK_VERSION")); | 512 | auto vndkVersion = base::Trim(getEnv("REQUIRED_VNDK_VERSION")); |
@@ -506,24 +528,20 @@ class AssembleVintfImpl : public AssembleVintf { | |||
506 | } | 528 | } |
507 | 529 | ||
508 | if (matrices->front().object.mType == SchemaType::FRAMEWORK) { | 530 | if (matrices->front().object.mType == SchemaType::FRAMEWORK) { |
509 | Level deviceLevel = Level::UNSPECIFIED; | 531 | Level deviceLevel = |
510 | if (mCheckFile != nullptr) { | 532 | checkManifest != nullptr ? checkManifest->level() : Level::UNSPECIFIED; |
511 | checkManifest = std::make_unique<HalManifest>(); | ||
512 | if (!gHalManifestConverter(checkManifest.get(), read(*mCheckFile), &error)) { | ||
513 | std::cerr << "Cannot parse check file as a HAL manifest: " << error | ||
514 | << std::endl; | ||
515 | return false; | ||
516 | } | ||
517 | deviceLevel = checkManifest->level(); | ||
518 | } | ||
519 | |||
520 | if (deviceLevel == Level::UNSPECIFIED) { | 533 | if (deviceLevel == Level::UNSPECIFIED) { |
521 | // For GSI build, legacy devices that do not have a HAL manifest, | ||
522 | // and devices in development, merge all compatibility matrices. | ||
523 | deviceLevel = getLowestFcmVersion(*matrices); | 534 | deviceLevel = getLowestFcmVersion(*matrices); |
535 | if (checkManifest != nullptr && deviceLevel != Level::UNSPECIFIED) { | ||
536 | std::cerr << "Warning: No Target FCM Version for device. Assuming \"" | ||
537 | << to_string(deviceLevel) | ||
538 | << "\" when building final framework compatibility matrix." | ||
539 | << std::endl; | ||
540 | } | ||
524 | } | 541 | } |
542 | builtMatrix = CompatibilityMatrix::combine(deviceLevel, matrices, &error); | ||
543 | matrix = builtMatrix.get(); | ||
525 | 544 | ||
526 | matrix = CompatibilityMatrix::combine(deviceLevel, matrices, &error); | ||
527 | if (matrix == nullptr) { | 545 | if (matrix == nullptr) { |
528 | std::cerr << error << std::endl; | 546 | std::cerr << error << std::endl; |
529 | return false; | 547 | return false; |
@@ -558,9 +576,9 @@ class AssembleVintfImpl : public AssembleVintf { | |||
558 | } | 576 | } |
559 | 577 | ||
560 | getFlagIfUnset("POLICYVERS", &matrix->framework.mSepolicy.mKernelSepolicyVersion, | 578 | getFlagIfUnset("POLICYVERS", &matrix->framework.mSepolicy.mKernelSepolicyVersion, |
561 | deviceLevel == Level::UNSPECIFIED /* log */); | 579 | false /* log */); |
562 | getFlagIfUnset("FRAMEWORK_VBMETA_VERSION", &matrix->framework.mAvbMetaVersion, | 580 | getFlagIfUnset("FRAMEWORK_VBMETA_VERSION", &matrix->framework.mAvbMetaVersion, |
563 | deviceLevel == Level::UNSPECIFIED /* log */); | 581 | false /* log */); |
564 | // Hard-override existing AVB version | 582 | // Hard-override existing AVB version |
565 | getFlag("FRAMEWORK_VBMETA_VERSION_OVERRIDE", &matrix->framework.mAvbMetaVersion, | 583 | getFlag("FRAMEWORK_VBMETA_VERSION_OVERRIDE", &matrix->framework.mAvbMetaVersion, |
566 | false /* log */); | 584 | false /* log */); |
diff --git a/CompatibilityMatrix.cpp b/CompatibilityMatrix.cpp index 6a5645c..290ebf9 100644 --- a/CompatibilityMatrix.cpp +++ b/CompatibilityMatrix.cpp | |||
@@ -19,6 +19,8 @@ | |||
19 | #include <iostream> | 19 | #include <iostream> |
20 | #include <utility> | 20 | #include <utility> |
21 | 21 | ||
22 | #include <android-base/strings.h> | ||
23 | |||
22 | #include "parse_string.h" | 24 | #include "parse_string.h" |
23 | #include "parse_xml.h" | 25 | #include "parse_xml.h" |
24 | #include "utils.h" | 26 | #include "utils.h" |
@@ -26,14 +28,59 @@ | |||
26 | namespace android { | 28 | namespace android { |
27 | namespace vintf { | 29 | namespace vintf { |
28 | 30 | ||
29 | bool CompatibilityMatrix::add(MatrixHal &&hal) { | 31 | bool CompatibilityMatrix::addKernel(MatrixKernel&& kernel, std::string* error) { |
30 | return HalGroup<MatrixHal>::add(std::move(hal)); | ||
31 | } | ||
32 | |||
33 | bool CompatibilityMatrix::add(MatrixKernel &&kernel) { | ||
34 | if (mType != SchemaType::FRAMEWORK) { | 32 | if (mType != SchemaType::FRAMEWORK) { |
33 | if (error) { | ||
34 | *error = "Cannot add <kernel> to a " + to_string(mType) + " compatibility matrix."; | ||
35 | } | ||
35 | return false; | 36 | return false; |
36 | } | 37 | } |
38 | |||
39 | auto it = framework.mKernels.begin(); | ||
40 | for (; it != framework.mKernels.end(); ++it) { | ||
41 | if (it->minLts() == kernel.minLts()) { | ||
42 | break; | ||
43 | } | ||
44 | if (it->minLts().version == kernel.minLts().version && | ||
45 | it->minLts().majorRev == kernel.minLts().majorRev) { | ||
46 | if (error) { | ||
47 | *error = "Kernel version mismatch; cannot add " + to_string(kernel.minLts()) + | ||
48 | " because " + to_string(it->minLts()) + " was added."; | ||
49 | } | ||
50 | return false; | ||
51 | } | ||
52 | } | ||
53 | |||
54 | bool seenVersion = it != framework.mKernels.end(); | ||
55 | |||
56 | if (seenVersion) { | ||
57 | // If no conditions, must be the first among the same minLts | ||
58 | // because O libvintf only checks the first <kernel> tag that version matches. | ||
59 | if (kernel.conditions().empty()) { | ||
60 | // Found first <kernel> with the same minLts. | ||
61 | // Append config if it does not have <condition>s, else error. | ||
62 | if (it->conditions().empty()) { | ||
63 | const auto& configs = kernel.configs(); | ||
64 | it->mConfigs.insert(it->mConfigs.end(), configs.begin(), configs.end()); | ||
65 | } else { | ||
66 | if (error) { | ||
67 | *error = | ||
68 | "Base compatibility matrix has <condition> for the first <kernel> " | ||
69 | "with minlts " + | ||
70 | to_string(kernel.minLts()) + " for unknown reason."; | ||
71 | } | ||
72 | return false; | ||
73 | } | ||
74 | return true; | ||
75 | } | ||
76 | } else { | ||
77 | // First <kernel> of a minLts must not have <condition>'s for backwards compatibility | ||
78 | // with O libvintf. | ||
79 | if (!kernel.conditions().empty()) { | ||
80 | framework.mKernels.push_back(MatrixKernel(KernelVersion{kernel.minLts()}, {})); | ||
81 | } | ||
82 | } | ||
83 | |||
37 | framework.mKernels.push_back(std::move(kernel)); | 84 | framework.mKernels.push_back(std::move(kernel)); |
38 | return true; | 85 | return true; |
39 | } | 86 | } |
@@ -196,6 +243,27 @@ bool CompatibilityMatrix::addAllXmlFilesAsOptional(CompatibilityMatrix* other, s | |||
196 | return true; | 243 | return true; |
197 | } | 244 | } |
198 | 245 | ||
246 | // Merge Kernel. | ||
247 | // Add <kernel> from exact "level", then optionally add <kernel> from high levels to low levels. | ||
248 | // For example, (each letter is a kernel version x.y.z) | ||
249 | // 1.xml: A1, B1 | ||
250 | // 2.xml: B2, C2, D2 | ||
251 | // 3.xml: D3, E3 | ||
252 | // Then the combined 1.xml should have | ||
253 | // A1, B1 (from 1.xml, required), C2, D2, E3 (optional, use earliest possible). | ||
254 | bool CompatibilityMatrix::addAllKernels(CompatibilityMatrix* other, std::string* error) { | ||
255 | for (MatrixKernel& kernel : other->framework.mKernels) { | ||
256 | KernelVersion ver = kernel.minLts(); | ||
257 | if (!addKernel(std::move(kernel), error)) { | ||
258 | if (error) { | ||
259 | *error = "Cannot add kernel version " + to_string(ver) + ": " + *error; | ||
260 | } | ||
261 | return false; | ||
262 | } | ||
263 | } | ||
264 | return true; | ||
265 | } | ||
266 | |||
199 | bool CompatibilityMatrix::addAllKernelsAsOptional(CompatibilityMatrix* other, std::string* error) { | 267 | bool CompatibilityMatrix::addAllKernelsAsOptional(CompatibilityMatrix* other, std::string* error) { |
200 | if (other == nullptr || other->level() <= level()) { | 268 | if (other == nullptr || other->level() <= level()) { |
201 | return true; | 269 | return true; |
@@ -216,9 +284,9 @@ bool CompatibilityMatrix::addAllKernelsAsOptional(CompatibilityMatrix* other, st | |||
216 | } | 284 | } |
217 | 285 | ||
218 | KernelVersion minLts = kernelToAdd.minLts(); | 286 | KernelVersion minLts = kernelToAdd.minLts(); |
219 | if (!add(std::move(kernelToAdd))) { | 287 | if (!addKernel(std::move(kernelToAdd), error)) { |
220 | if (error) { | 288 | if (error) { |
221 | *error = "Cannot add " + to_string(minLts) + " for unknown reason."; | 289 | *error = "Cannot add " + to_string(minLts) + ": " + *error; |
222 | } | 290 | } |
223 | return false; | 291 | return false; |
224 | } | 292 | } |
@@ -226,6 +294,34 @@ bool CompatibilityMatrix::addAllKernelsAsOptional(CompatibilityMatrix* other, st | |||
226 | return true; | 294 | return true; |
227 | } | 295 | } |
228 | 296 | ||
297 | template <typename T> | ||
298 | static bool mergeField(T* dst, T* src) { | ||
299 | static const T kEmpty{}; | ||
300 | if (*dst == *src) { | ||
301 | return true; // no conflict | ||
302 | } | ||
303 | if (*src == kEmpty) { | ||
304 | return true; | ||
305 | } | ||
306 | if (*dst == kEmpty) { | ||
307 | *dst = std::move(*src); | ||
308 | return true; | ||
309 | } | ||
310 | return false; | ||
311 | } | ||
312 | |||
313 | bool CompatibilityMatrix::addSepolicy(CompatibilityMatrix* other, std::string* error) { | ||
314 | bool success = mergeField(&this->framework.mSepolicy, &other->framework.mSepolicy); | ||
315 | if (!success && error) *error = "<sepolicy> is already defined"; | ||
316 | return success; | ||
317 | } | ||
318 | |||
319 | bool CompatibilityMatrix::addAvbMetaVersion(CompatibilityMatrix* other, std::string* error) { | ||
320 | bool success = mergeField(&this->framework.mAvbMetaVersion, &other->framework.mAvbMetaVersion); | ||
321 | if (!success && error) *error = "<avb><vbmeta-version> is already defined"; | ||
322 | return success; | ||
323 | } | ||
324 | |||
229 | bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) { | 325 | bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) { |
230 | return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals && | 326 | return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals && |
231 | lft.mXmlFiles == rgt.mXmlFiles && | 327 | lft.mXmlFiles == rgt.mXmlFiles && |
@@ -243,175 +339,83 @@ bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) | |||
243 | lft.framework.mAvbMetaVersion == rgt.framework.mAvbMetaVersion)); | 339 | lft.framework.mAvbMetaVersion == rgt.framework.mAvbMetaVersion)); |
244 | } | 340 | } |
245 | 341 | ||
246 | // Find compatibility_matrix.empty.xml (which has unspecified level) and use it | 342 | std::unique_ptr<CompatibilityMatrix> CompatibilityMatrix::combine( |
247 | // as a base matrix. | 343 | Level deviceLevel, std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error) { |
248 | CompatibilityMatrix* CompatibilityMatrix::findOrInsertBaseMatrix( | 344 | // Check type. |
249 | std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error) { | 345 | for (const auto& e : *matrices) { |
250 | std::vector<CompatibilityMatrix*> matricesUnspecified; | 346 | if (e.object.type() != SchemaType::FRAMEWORK) { |
251 | std::vector<CompatibilityMatrix*> matricesEmpty; | ||
252 | for (auto& e : *matrices) { | ||
253 | if (e.object.level() == Level::UNSPECIFIED) { | ||
254 | matricesUnspecified.push_back(&e.object); | ||
255 | |||
256 | if (!e.object.mHals.empty()) { | ||
257 | continue; | ||
258 | } | ||
259 | |||
260 | if (!e.object.mXmlFiles.empty()) { | ||
261 | continue; | ||
262 | } | ||
263 | |||
264 | matricesEmpty.push_back(&e.object); | ||
265 | } | ||
266 | } | ||
267 | |||
268 | if (matricesEmpty.size() > 1) { | ||
269 | if (error) { | ||
270 | *error = | ||
271 | "Error: multiple framework compatibility matrix files have " | ||
272 | "unspecified level; there should only be one such file.\n"; | ||
273 | for (auto& e : *matrices) { | ||
274 | if (e.object.level() == Level::UNSPECIFIED) { | ||
275 | *error += " " + e.name + "\n"; | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | return nullptr; | ||
280 | } | ||
281 | if (matricesEmpty.size() == 1) { | ||
282 | return matricesEmpty.front(); | ||
283 | } | ||
284 | if (!matricesUnspecified.empty()) { | ||
285 | return matricesUnspecified.front(); | ||
286 | } | ||
287 | auto matrix = &matrices->emplace(matrices->end())->object; | ||
288 | matrix->mType = SchemaType::FRAMEWORK; | ||
289 | matrix->mLevel = Level::UNSPECIFIED; | ||
290 | return matrix; | ||
291 | } | ||
292 | |||
293 | // Check if there are two files declaring level="1", for example, because | ||
294 | // combine() use this assumption to simplify a lot of logic. | ||
295 | static bool checkDuplicateLevels(const std::vector<Named<CompatibilityMatrix>>& matrices, | ||
296 | std::string* error) { | ||
297 | std::map<Level, const std::string*> existingLevels; | ||
298 | for (const auto& e : matrices) { | ||
299 | if (e.object.level() != Level::UNSPECIFIED && | ||
300 | existingLevels.find(e.object.level()) != existingLevels.end()) { | ||
301 | if (error) { | 347 | if (error) { |
302 | *error = "Conflict of levels: file \"" + | 348 | *error = "File \"" + e.name + "\" is not a framework compatibility matrix."; |
303 | *existingLevels.find(e.object.level())->second + "\" and \"" + e.name + | 349 | return nullptr; |
304 | " both have level " + to_string(e.object.level()); | ||
305 | } | 350 | } |
306 | return false; | ||
307 | } | 351 | } |
308 | existingLevels.emplace(e.object.level(), &e.name); | ||
309 | } | 352 | } |
310 | return true; | ||
311 | } | ||
312 | 353 | ||
313 | CompatibilityMatrix* CompatibilityMatrix::combine(Level deviceLevel, | 354 | // Matrices with unspecified (empty) level are auto-filled with deviceLevel. |
314 | std::vector<Named<CompatibilityMatrix>>* matrices, | 355 | for (auto& e : *matrices) { |
315 | std::string* error) { | 356 | if (e.object.level() == Level::UNSPECIFIED) { |
316 | if (!checkDuplicateLevels(*matrices, error)) { | 357 | e.object.mLevel = deviceLevel; |
317 | return nullptr; | 358 | } |
318 | } | 359 | } |
319 | 360 | ||
320 | CompatibilityMatrix* matrix = findOrInsertBaseMatrix(matrices, error); | 361 | // Add from low to high FCM version so that optional <kernel> requirements are added correctly. |
321 | if (matrix == nullptr) { | 362 | // See comment in addAllAsOptional. |
322 | return nullptr; | 363 | std::sort(matrices->begin(), matrices->end(), |
323 | } | 364 | [](const auto& x, const auto& y) { return x.object.level() < y.object.level(); }); |
324 | 365 | ||
325 | matrix->mLevel = deviceLevel; | 366 | auto baseMatrix = std::make_unique<CompatibilityMatrix>(); |
367 | baseMatrix->mLevel = deviceLevel; | ||
368 | baseMatrix->mType = SchemaType::FRAMEWORK; | ||
326 | 369 | ||
370 | std::vector<std::string> parsedFiles; | ||
327 | for (auto& e : *matrices) { | 371 | for (auto& e : *matrices) { |
328 | if (&e.object != matrix && | 372 | if (e.object.level() < deviceLevel) { |
329 | (e.object.level() == deviceLevel || e.object.level() == Level::UNSPECIFIED)) { | 373 | continue; |
330 | if (!matrix->addAllHals(&e.object, error)) { | ||
331 | if (error) { | ||
332 | *error = "File \"" + e.name + "\" cannot be added: HAL " + *error + | ||
333 | " has a conflict."; | ||
334 | } | ||
335 | return nullptr; | ||
336 | } | ||
337 | |||
338 | if (!matrix->addAllXmlFiles(&e.object, error)) { | ||
339 | if (error) { | ||
340 | *error = "File \"" + e.name + "\" cannot be added: XML File entry " + *error + | ||
341 | " has a conflict."; | ||
342 | } | ||
343 | return nullptr; | ||
344 | } | ||
345 | } | 374 | } |
346 | } | ||
347 | |||
348 | for (auto& e : *matrices) { | ||
349 | if (&e.object != matrix && e.object.level() != Level::UNSPECIFIED && | ||
350 | e.object.level() > deviceLevel) { | ||
351 | if (!matrix->addAllHalsAsOptional(&e.object, error)) { | ||
352 | if (error) { | ||
353 | *error = "File \"" + e.name + "\" cannot be added: " + *error + | ||
354 | ". See <hal> with the same name " + | ||
355 | "in previously parsed files or previously declared in this file."; | ||
356 | } | ||
357 | return nullptr; | ||
358 | } | ||
359 | 375 | ||
360 | if (!matrix->addAllXmlFilesAsOptional(&e.object, error)) { | 376 | bool success = false; |
361 | if (error) { | 377 | if (e.object.level() == deviceLevel) { |
362 | *error = "File \"" + e.name + "\" cannot be added: XML File entry " + *error + | 378 | success = baseMatrix->addAll(&e, error); |
363 | " has a conflict."; | 379 | } else { |
364 | } | 380 | success = baseMatrix->addAllAsOptional(&e, error); |
365 | return nullptr; | ||
366 | } | ||
367 | } | 381 | } |
368 | } | 382 | if (!success) { |
369 | 383 | if (error) { | |
370 | // Add <kernel> from exact "level", then optionally add <kernel> from high levels to low levels. | 384 | *error = "Conflict when merging \"" + e.name + "\": " + *error + "\n" + |
371 | // For example, (each letter is a kernel version x.y.z) | 385 | "Previous files:\n" + base::Join(parsedFiles, "\n"); |
372 | // 1.xml: A1, B1 | ||
373 | // 2.xml: B2, C2, D2 | ||
374 | // 3.xml: D3, E3 | ||
375 | // Then the combined 1.xml should have | ||
376 | // A1, B1 (from 1.xml, required), C2, D2, E3 (optional, use earliest possible). | ||
377 | for (auto& e : *matrices) { | ||
378 | if (&e.object != matrix && e.object.level() == deviceLevel && | ||
379 | e.object.type() == SchemaType::FRAMEWORK) { | ||
380 | for (MatrixKernel& kernel : e.object.framework.mKernels) { | ||
381 | KernelVersion ver = kernel.minLts(); | ||
382 | if (!matrix->add(std::move(kernel))) { | ||
383 | if (error) { | ||
384 | *error = "Cannot add kernel version " + to_string(ver) + | ||
385 | " from FCM version " + to_string(deviceLevel); | ||
386 | } | ||
387 | return nullptr; | ||
388 | } | ||
389 | } | 386 | } |
387 | return nullptr; | ||
390 | } | 388 | } |
389 | parsedFiles.push_back(e.name); | ||
391 | } | 390 | } |
392 | 391 | ||
393 | // There is only one file per level, hence a map is used instead of a multimap. Also, there is | 392 | return baseMatrix; |
394 | // no good ordering (i.e. priority) for multiple files with the same level. | 393 | } |
395 | std::map<Level, Named<CompatibilityMatrix>*> matricesMap; | 394 | |
396 | for (auto& e : *matrices) { | 395 | bool CompatibilityMatrix::addAll(Named<CompatibilityMatrix>* inputMatrix, std::string* error) { |
397 | if (&e.object != matrix && e.object.level() != Level::UNSPECIFIED && | 396 | if (!addAllHals(&inputMatrix->object, error) || !addAllXmlFiles(&inputMatrix->object, error) || |
398 | e.object.level() > deviceLevel && e.object.type() == SchemaType::FRAMEWORK) { | 397 | !addAllKernels(&inputMatrix->object, error) || !addSepolicy(&inputMatrix->object, error) || |
399 | matricesMap.emplace(e.object.level(), &e); | 398 | !addAvbMetaVersion(&inputMatrix->object, error)) { |
399 | if (error) { | ||
400 | *error = "File \"" + inputMatrix->name + "\" cannot be added: " + *error + "."; | ||
400 | } | 401 | } |
401 | } | 402 | } |
403 | return true; | ||
404 | } | ||
402 | 405 | ||
403 | for (auto&& pair : matricesMap) { | 406 | bool CompatibilityMatrix::addAllAsOptional(Named<CompatibilityMatrix>* inputMatrix, |
404 | if (!matrix->addAllKernelsAsOptional(&pair.second->object, error)) { | 407 | std::string* error) { |
405 | if (error) { | 408 | if (!addAllHalsAsOptional(&inputMatrix->object, error) || |
406 | *error = "Cannot add new kernel versions from FCM version " + | 409 | !addAllXmlFilesAsOptional(&inputMatrix->object, error) || |
407 | to_string(pair.first) + " (" + pair.second->name + ")" + | 410 | !addAllKernelsAsOptional(&inputMatrix->object, error)) { |
408 | " to FCM version " + to_string(deviceLevel) + ": " + *error; | 411 | if (error) { |
409 | } | 412 | *error = "File \"" + inputMatrix->name + "\" cannot be added: " + *error; |
410 | return nullptr; | ||
411 | } | 413 | } |
414 | return false; | ||
412 | } | 415 | } |
413 | 416 | // ignore <sepolicy> requirement from higher level | |
414 | return matrix; | 417 | // ignore <avb> requirement from higher level |
418 | return true; | ||
415 | } | 419 | } |
416 | 420 | ||
417 | bool CompatibilityMatrix::forEachInstanceOfVersion( | 421 | bool CompatibilityMatrix::forEachInstanceOfVersion( |
diff --git a/VintfObject.cpp b/VintfObject.cpp index 1086590..aed9745 100644 --- a/VintfObject.cpp +++ b/VintfObject.cpp | |||
@@ -196,8 +196,7 @@ status_t VintfObject::getCombinedFrameworkMatrix( | |||
196 | return NAME_NOT_FOUND; | 196 | return NAME_NOT_FOUND; |
197 | } | 197 | } |
198 | 198 | ||
199 | CompatibilityMatrix* combined = | 199 | auto combined = CompatibilityMatrix::combine(deviceLevel, &matrixFragments, error); |
200 | CompatibilityMatrix::combine(deviceLevel, &matrixFragments, error); | ||
201 | if (combined == nullptr) { | 200 | if (combined == nullptr) { |
202 | return BAD_VALUE; | 201 | return BAD_VALUE; |
203 | } | 202 | } |
diff --git a/include/vintf/CompatibilityMatrix.h b/include/vintf/CompatibilityMatrix.h index 512d278..feb8ac8 100644 --- a/include/vintf/CompatibilityMatrix.h +++ b/include/vintf/CompatibilityMatrix.h | |||
@@ -18,6 +18,7 @@ | |||
18 | #define ANDROID_VINTF_COMPATIBILITY_MATRIX_H | 18 | #define ANDROID_VINTF_COMPATIBILITY_MATRIX_H |
19 | 19 | ||
20 | #include <map> | 20 | #include <map> |
21 | #include <memory> | ||
21 | #include <string> | 22 | #include <string> |
22 | 23 | ||
23 | #include <utils/Errors.h> | 24 | #include <utils/Errors.h> |
@@ -67,8 +68,23 @@ struct CompatibilityMatrix : public HalGroup<MatrixHal>, public XmlFileGroup<Mat | |||
67 | std::string getVendorNdkVersion() const; | 68 | std::string getVendorNdkVersion() const; |
68 | 69 | ||
69 | private: | 70 | private: |
70 | bool add(MatrixHal &&hal); | 71 | // Add everything in inputMatrix to "this" as requirements. |
71 | bool add(MatrixKernel &&kernel); | 72 | bool addAll(Named<CompatibilityMatrix>* inputMatrix, std::string* error); |
73 | |||
74 | // Add all <kernel> from other to "this". Error if there is a conflict. | ||
75 | bool addAllKernels(CompatibilityMatrix* other, std::string* error); | ||
76 | |||
77 | // Add a <kernel> tag to "this". Error if there is a conflict. | ||
78 | bool addKernel(MatrixKernel&& kernel, std::string* error); | ||
79 | |||
80 | // Merge <sepolicy> with other's <sepolicy>. Error if there is a conflict. | ||
81 | bool addSepolicy(CompatibilityMatrix* other, std::string* error); | ||
82 | |||
83 | // Merge <avb><vbmeta-version> with other's <avb><vbmeta-version>. Error if there is a conflict. | ||
84 | bool addAvbMetaVersion(CompatibilityMatrix* other, std::string* error); | ||
85 | |||
86 | // Add everything in inputMatrix to "this" as optional. | ||
87 | bool addAllAsOptional(Named<CompatibilityMatrix>* inputMatrix, std::string* error); | ||
72 | 88 | ||
73 | // Add all HALs as optional HALs from "other". This function moves MatrixHal objects | 89 | // Add all HALs as optional HALs from "other". This function moves MatrixHal objects |
74 | // from "other". | 90 | // from "other". |
@@ -81,23 +97,23 @@ struct CompatibilityMatrix : public HalGroup<MatrixHal>, public XmlFileGroup<Mat | |||
81 | // Similar to addAllHalsAsOptional but on <kernel> entries. | 97 | // Similar to addAllHalsAsOptional but on <kernel> entries. |
82 | bool addAllKernelsAsOptional(CompatibilityMatrix* other, std::string* error); | 98 | bool addAllKernelsAsOptional(CompatibilityMatrix* other, std::string* error); |
83 | 99 | ||
100 | // Combine a set of framework compatibility matrices. For each CompatibilityMatrix in matrices | ||
101 | // (in the order of level(), where UNSPECIFIED (empty) is treated as deviceLevel) | ||
102 | // - If level() < deviceLevel, ignore | ||
103 | // - If level() == UNSPECIFIED or level() == deviceLevel, | ||
104 | // - Add as hard requirements. See combineSameFcmVersion | ||
105 | // - If level() > deviceLevel, | ||
106 | // - all <hal> versions and <xmlfile>s are added as optional. | ||
107 | // - <kernel minlts="x.y.z"> is added only if x.y does not exist in a file | ||
108 | // with lower level() | ||
109 | // - <sepolicy>, <avb><vbmeta-version> is ignored | ||
110 | // Return the combined matrix, nullptr if any error (e.g. conflict of information). | ||
111 | static std::unique_ptr<CompatibilityMatrix> combine( | ||
112 | Level deviceLevel, std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error); | ||
113 | |||
84 | status_t fetchAllInformation(const FileSystem* fileSystem, const std::string& path, | 114 | status_t fetchAllInformation(const FileSystem* fileSystem, const std::string& path, |
85 | std::string* error = nullptr); | 115 | std::string* error = nullptr); |
86 | 116 | ||
87 | // Combine a subset of "matrices". For each CompatibilityMatrix in matrices, | ||
88 | // - If level() == UNSPECIFIED, use it as the base matrix (for non-HAL, non-XML-file | ||
89 | // requirements). | ||
90 | // - If level() < deviceLevel, ignore | ||
91 | // - If level() == deviceLevel, all HAL versions and XML files are added as is | ||
92 | // (optionality is kept) | ||
93 | // - If level() > deviceLevel, all HAL versions and XML files are added as optional. | ||
94 | // Return a pointer into one of the elements in "matrices". | ||
95 | static CompatibilityMatrix* combine(Level deviceLevel, | ||
96 | std::vector<Named<CompatibilityMatrix>>* matrices, | ||
97 | std::string* error); | ||
98 | static CompatibilityMatrix* findOrInsertBaseMatrix( | ||
99 | std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error); | ||
100 | |||
101 | MatrixHal* splitInstance(MatrixHal* existingHal, const std::string& interface, | 117 | MatrixHal* splitInstance(MatrixHal* existingHal, const std::string& interface, |
102 | const std::string& instance, bool isRegex); | 118 | const std::string& instance, bool isRegex); |
103 | 119 | ||
diff --git a/include/vintf/HalGroup.h b/include/vintf/HalGroup.h index a0a14cd..5cad867 100644 --- a/include/vintf/HalGroup.h +++ b/include/vintf/HalGroup.h | |||
@@ -39,7 +39,7 @@ struct HalGroup { | |||
39 | for (auto& pair : other->mHals) { | 39 | for (auto& pair : other->mHals) { |
40 | if (!add(std::move(pair.second))) { | 40 | if (!add(std::move(pair.second))) { |
41 | if (error) { | 41 | if (error) { |
42 | *error = pair.first; | 42 | *error = "HAL \"" + pair.first + "\" has a conflict."; |
43 | } | 43 | } |
44 | return false; | 44 | return false; |
45 | } | 45 | } |
diff --git a/include/vintf/MatrixKernel.h b/include/vintf/MatrixKernel.h index cee7e32..14cf5bf 100644 --- a/include/vintf/MatrixKernel.h +++ b/include/vintf/MatrixKernel.h | |||
@@ -58,6 +58,7 @@ struct MatrixKernel { | |||
58 | private: | 58 | private: |
59 | friend struct MatrixKernelConverter; | 59 | friend struct MatrixKernelConverter; |
60 | friend struct MatrixKernelConditionsConverter; | 60 | friend struct MatrixKernelConditionsConverter; |
61 | friend struct CompatibilityMatrix; | ||
61 | friend class AssembleVintfImpl; | 62 | friend class AssembleVintfImpl; |
62 | 63 | ||
63 | KernelVersion mMinLts; | 64 | KernelVersion mMinLts; |
diff --git a/include/vintf/XmlFileGroup.h b/include/vintf/XmlFileGroup.h index 524efc9..dd15647 100644 --- a/include/vintf/XmlFileGroup.h +++ b/include/vintf/XmlFileGroup.h | |||
@@ -62,7 +62,7 @@ struct XmlFileGroup { | |||
62 | for (auto& pair : other->mXmlFiles) { | 62 | for (auto& pair : other->mXmlFiles) { |
63 | if (!addXmlFile(std::move(pair.second))) { | 63 | if (!addXmlFile(std::move(pair.second))) { |
64 | if (error) { | 64 | if (error) { |
65 | *error = pair.first; | 65 | *error = "XML File \"" + pair.first + "\" has a conflict."; |
66 | } | 66 | } |
67 | return false; | 67 | return false; |
68 | } | 68 | } |
diff --git a/test/LibVintfTest.cpp b/test/LibVintfTest.cpp index 68af9bb..a3a98b8 100644 --- a/test/LibVintfTest.cpp +++ b/test/LibVintfTest.cpp | |||
@@ -59,7 +59,10 @@ public: | |||
59 | return cm.add(std::move(hal)); | 59 | return cm.add(std::move(hal)); |
60 | } | 60 | } |
61 | bool add(CompatibilityMatrix &cm, MatrixKernel &&kernel) { | 61 | bool add(CompatibilityMatrix &cm, MatrixKernel &&kernel) { |
62 | return cm.add(std::move(kernel)); | 62 | std::string error; |
63 | bool success = cm.addKernel(std::move(kernel), &error); | ||
64 | EXPECT_EQ(success, error == "") << "success: " << success << ", error: " << error; | ||
65 | return success; | ||
63 | } | 66 | } |
64 | bool add(HalManifest &vm, ManifestHal &&hal) { | 67 | bool add(HalManifest &vm, ManifestHal &&hal) { |
65 | return vm.add(std::move(hal)); | 68 | return vm.add(std::move(hal)); |