blob: bf83a9838319255431f8300a606b21865319844e [file] [log] [blame]
Yifan Hong676447a2016-11-15 12:57:23 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "CompatibilityMatrix.h"
18
Yifan Hongb6c7f492018-02-27 14:07:57 -080019#include <iostream>
Yifan Hongdbe9db32017-12-11 19:06:11 -080020#include <utility>
21
Yifan Hongbbfff302017-06-06 17:10:13 -070022#include "parse_string.h"
Yifan Hongddae77e2017-12-18 16:57:07 -080023#include "parse_xml.h"
Yifan Hongb6c7f492018-02-27 14:07:57 -080024#include "utils.h"
Yifan Hongddae77e2017-12-18 16:57:07 -080025
Yifan Hong676447a2016-11-15 12:57:23 -080026namespace android {
27namespace vintf {
28
Yifan Hong676447a2016-11-15 12:57:23 -080029bool CompatibilityMatrix::add(MatrixHal &&hal) {
Yifan Hong0fd7aef2017-05-24 14:37:19 -070030 return HalGroup<MatrixHal>::add(std::move(hal));
Yifan Hong676447a2016-11-15 12:57:23 -080031}
32
33bool CompatibilityMatrix::add(MatrixKernel &&kernel) {
Yifan Hong7c7d7062017-04-04 16:26:51 -070034 if (mType != SchemaType::FRAMEWORK) {
35 return false;
36 }
37 framework.mKernels.push_back(std::move(kernel));
Yifan Hong676447a2016-11-15 12:57:23 -080038 return true;
39}
40
Yifan Hong398f4c72017-04-13 20:18:01 -070041SchemaType CompatibilityMatrix::type() const {
42 return mType;
43}
44
Yifan Hong2027a492017-12-11 15:21:19 -080045Level CompatibilityMatrix::level() const {
46 return mLevel;
47}
48
Yifan Hongdb127cb2017-09-19 13:36:21 -070049Version CompatibilityMatrix::getMinimumMetaVersion() const {
50 // TODO(b/62801658): this needs to depend on whether there are 1.1 requirements
51 // (e.g. required <xmlfile> entry)
52 return {1, 0};
53}
Yifan Hong1e5a0542017-04-28 14:37:56 -070054
Yifan Hong60217032018-01-08 16:19:42 -080055status_t CompatibilityMatrix::fetchAllInformation(const std::string& path, std::string* error) {
56 return details::fetchAllInformation(path, gCompatibilityMatrixConverter, this, error);
Yifan Hong1e5a0542017-04-28 14:37:56 -070057}
58
Yifan Hongbbfff302017-06-06 17:10:13 -070059std::string CompatibilityMatrix::getXmlSchemaPath(const std::string& xmlFileName,
60 const Version& version) const {
61 using std::literals::string_literals::operator""s;
62 auto range = getXmlFiles(xmlFileName);
63 for (auto it = range.first; it != range.second; ++it) {
64 const MatrixXmlFile& matrixXmlFile = it->second;
65 if (matrixXmlFile.versionRange().contains(version)) {
66 if (!matrixXmlFile.overriddenPath().empty()) {
67 return matrixXmlFile.overriddenPath();
68 }
69 return "/"s + (type() == SchemaType::DEVICE ? "vendor" : "system") + "/etc/" +
70 xmlFileName + "_V" + std::to_string(matrixXmlFile.versionRange().majorVer) +
71 "_" + std::to_string(matrixXmlFile.versionRange().maxMinor) + "." +
72 to_string(matrixXmlFile.format());
73 }
74 }
75 return "";
76}
77
Yifan Hongdbe9db32017-12-11 19:06:11 -080078static VersionRange* findRangeWithMajorVersion(std::vector<VersionRange>& versionRanges,
79 size_t majorVer) {
80 for (VersionRange& vr : versionRanges) {
81 if (vr.majorVer == majorVer) {
82 return &vr;
83 }
84 }
85 return nullptr;
86}
87
88std::pair<MatrixHal*, VersionRange*> CompatibilityMatrix::getHalWithMajorVersion(
89 const std::string& name, size_t majorVer) {
90 for (MatrixHal* hal : getHals(name)) {
91 VersionRange* vr = findRangeWithMajorVersion(hal->versionRanges, majorVer);
92 if (vr != nullptr) {
93 return {hal, vr};
94 }
95 }
96 return {nullptr, nullptr};
97}
Yifan Hongf73ba512018-01-17 15:52:30 -080098std::pair<const MatrixHal*, const VersionRange*> CompatibilityMatrix::getHalWithMajorVersion(
99 const std::string& name, size_t majorVer) const {
100 return const_cast<CompatibilityMatrix*>(this)->getHalWithMajorVersion(name, majorVer);
101}
Yifan Hongdbe9db32017-12-11 19:06:11 -0800102
103bool CompatibilityMatrix::addAllHalsAsOptional(CompatibilityMatrix* other, std::string* error) {
104 if (other == nullptr || other->level() <= level()) {
105 return true;
106 }
107
108 for (auto& pair : other->mHals) {
109 const std::string& name = pair.first;
110 MatrixHal& halToAdd = pair.second;
111 for (const VersionRange& vr : halToAdd.versionRanges) {
112 MatrixHal* existingHal;
113 VersionRange* existingVr;
114 std::tie(existingHal, existingVr) = getHalWithMajorVersion(name, vr.majorVer);
115
116 if (existingHal == nullptr) {
Yifan Hongd8eea9e2017-12-22 15:13:42 -0800117 MatrixHal optionalHalToAdd(halToAdd);
118 optionalHalToAdd.optional = true;
119 optionalHalToAdd.versionRanges = {vr};
120 if (!add(std::move(optionalHalToAdd))) {
Yifan Hong78c911a2017-12-20 15:16:59 -0800121 if (error) {
122 *error = "Cannot add HAL " + name + " for unknown reason.";
123 }
124 return false;
125 }
Yifan Hongdbe9db32017-12-11 19:06:11 -0800126 continue;
127 }
128
129 if (!existingHal->optional && !existingHal->containsInstances(halToAdd)) {
130 if (error != nullptr) {
Yifan Hongb6c7f492018-02-27 14:07:57 -0800131 *error = "HAL " + toFQNameString(name, vr.minVer()) + " is a required " +
Yifan Hongdbe9db32017-12-11 19:06:11 -0800132 "HAL, but fully qualified instance names don't match (at FCM "
133 "Version " +
134 std::to_string(level()) + " and " + std::to_string(other->level()) +
135 ")";
136 }
137 return false;
138 }
139
140 existingVr->maxMinor = std::max(existingVr->maxMinor, vr.maxMinor);
141 }
142 }
143 return true;
144}
145
Yifan Hongd4b92fe2017-12-20 15:29:03 -0800146bool CompatibilityMatrix::addAllXmlFilesAsOptional(CompatibilityMatrix* other, std::string* error) {
147 if (other == nullptr || other->level() <= level()) {
148 return true;
149 }
150 for (auto& pair : other->mXmlFiles) {
151 const std::string& name = pair.first;
152 MatrixXmlFile& xmlFileToAdd = pair.second;
153
154 xmlFileToAdd.mOptional = true;
155 if (!addXmlFile(std::move(xmlFileToAdd))) {
156 if (error) {
157 *error = "Cannot add XML File " + name + " for unknown reason.";
158 }
159 return false;
160 }
161 }
162 return true;
163}
164
Yifan Hongfb7469c2017-04-05 19:15:21 -0700165bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) {
Yifan Hong2027a492017-12-11 15:21:19 -0800166 return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals &&
167 lft.mXmlFiles == rgt.mXmlFiles &&
Yifan Hongfeb454e2018-01-09 16:16:40 -0800168 (lft.mType != SchemaType::DEVICE ||
169 (
Yifan Hong0f529fa2018-01-10 14:51:59 -0800170#pragma clang diagnostic push
171#pragma clang diagnostic ignored "-Wdeprecated-declarations"
Yifan Hongfeb454e2018-01-09 16:16:40 -0800172 lft.device.mVndk == rgt.device.mVndk &&
Yifan Hong0f529fa2018-01-10 14:51:59 -0800173#pragma clang diagnostic pop
Yifan Honga28729e2018-01-17 13:40:35 -0800174 lft.device.mVendorNdk == rgt.device.mVendorNdk &&
175 lft.device.mSystemSdk == rgt.device.mSystemSdk)) &&
Yifan Hongd4857902017-06-13 14:13:56 -0700176 (lft.mType != SchemaType::FRAMEWORK ||
177 (lft.framework.mKernels == rgt.framework.mKernels &&
178 lft.framework.mSepolicy == rgt.framework.mSepolicy &&
179 lft.framework.mAvbMetaVersion == rgt.framework.mAvbMetaVersion));
Yifan Hongfb7469c2017-04-05 19:15:21 -0700180}
181
Yifan Hongddae77e2017-12-18 16:57:07 -0800182// Find compatibility_matrix.empty.xml (which has unspecified level) and use it
183// as a base matrix.
184CompatibilityMatrix* CompatibilityMatrix::findOrInsertBaseMatrix(
Yifan Hongffcaf992018-01-09 17:08:51 -0800185 std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error) {
Yifan Hongddae77e2017-12-18 16:57:07 -0800186 bool multipleFound = false;
187 CompatibilityMatrix* matrix = nullptr;
188 for (auto& e : *matrices) {
Yifan Hongffcaf992018-01-09 17:08:51 -0800189 if (e.object.level() == Level::UNSPECIFIED) {
190 if (!e.object.mHals.empty()) {
Yifan Hongddae77e2017-12-18 16:57:07 -0800191 if (error) {
Yifan Hongffcaf992018-01-09 17:08:51 -0800192 *error = "Error: File \"" + e.name + "\" should not contain " + "HAL elements.";
Yifan Hongddae77e2017-12-18 16:57:07 -0800193 }
194 return nullptr;
195 }
196
Yifan Hongffcaf992018-01-09 17:08:51 -0800197 if (!e.object.mXmlFiles.empty()) {
Yifan Hongddae77e2017-12-18 16:57:07 -0800198 if (error) {
Yifan Hongffcaf992018-01-09 17:08:51 -0800199 *error =
200 "Error: File \"" + e.name + "\" should not contain " + "XML File elements.";
Yifan Hongddae77e2017-12-18 16:57:07 -0800201 }
202 return nullptr;
203 }
204
205 if (matrix != nullptr) {
206 multipleFound = true;
207 }
208
Yifan Hongffcaf992018-01-09 17:08:51 -0800209 matrix = &e.object;
Yifan Hongddae77e2017-12-18 16:57:07 -0800210 // continue to detect multiple files with "unspecified" levels
211 }
212 }
213
214 if (multipleFound) {
215 if (error) {
216 *error =
217 "Error: multiple framework compatibility matrix files have "
218 "unspecified level; there should only be one such file.\n";
219 for (auto& e : *matrices) {
Yifan Hongffcaf992018-01-09 17:08:51 -0800220 if (e.object.level() == Level::UNSPECIFIED) {
221 *error += " " + e.name + "\n";
Yifan Hongddae77e2017-12-18 16:57:07 -0800222 }
223 }
224 }
225 return nullptr;
226 }
227
228 if (matrix == nullptr) {
Yifan Hongffcaf992018-01-09 17:08:51 -0800229 matrix = &matrices->emplace(matrices->end())->object;
Yifan Hongddae77e2017-12-18 16:57:07 -0800230 matrix->mType = SchemaType::FRAMEWORK;
231 matrix->mLevel = Level::UNSPECIFIED;
232 }
233
234 return matrix;
235}
236
Yifan Hongffcaf992018-01-09 17:08:51 -0800237CompatibilityMatrix* CompatibilityMatrix::combine(Level deviceLevel,
238 std::vector<Named<CompatibilityMatrix>>* matrices,
239 std::string* error) {
Yifan Hongddae77e2017-12-18 16:57:07 -0800240 if (deviceLevel == Level::UNSPECIFIED) {
241 if (error) {
242 *error = "Error: device level is unspecified.";
243 }
244 return nullptr;
245 }
246
247 CompatibilityMatrix* matrix = findOrInsertBaseMatrix(matrices, error);
248 if (matrix == nullptr) {
249 return nullptr;
250 }
251
252 matrix->mLevel = deviceLevel;
253
254 for (auto& e : *matrices) {
Yifan Hongffcaf992018-01-09 17:08:51 -0800255 if (&e.object != matrix && e.object.level() == deviceLevel) {
256 if (!matrix->addAllHals(&e.object, error)) {
Yifan Hongddae77e2017-12-18 16:57:07 -0800257 if (error) {
Yifan Hongffcaf992018-01-09 17:08:51 -0800258 *error = "File \"" + e.name + "\" cannot be added: HAL " + *error +
Yifan Hongddae77e2017-12-18 16:57:07 -0800259 " has a conflict.";
260 }
261 return nullptr;
262 }
263
Yifan Hongffcaf992018-01-09 17:08:51 -0800264 if (!matrix->addAllXmlFiles(&e.object, error)) {
Yifan Hongddae77e2017-12-18 16:57:07 -0800265 if (error) {
Yifan Hongffcaf992018-01-09 17:08:51 -0800266 *error = "File \"" + e.name + "\" cannot be added: XML File entry " + *error +
Yifan Hongddae77e2017-12-18 16:57:07 -0800267 " has a conflict.";
268 }
269 return nullptr;
270 }
271 }
272 }
273
274 for (auto& e : *matrices) {
Yifan Hongffcaf992018-01-09 17:08:51 -0800275 if (&e.object != matrix && e.object.level() != Level::UNSPECIFIED &&
276 e.object.level() > deviceLevel) {
277 if (!matrix->addAllHalsAsOptional(&e.object, error)) {
Yifan Hongddae77e2017-12-18 16:57:07 -0800278 if (error) {
Yifan Hongffcaf992018-01-09 17:08:51 -0800279 *error = "File \"" + e.name + "\" cannot be added: " + *error +
Yifan Hongddae77e2017-12-18 16:57:07 -0800280 ". See <hal> with the same name " +
281 "in previously parsed files or previously declared in this file.";
282 }
283 return nullptr;
284 }
285
Yifan Hongffcaf992018-01-09 17:08:51 -0800286 if (!matrix->addAllXmlFilesAsOptional(&e.object, error)) {
Yifan Hongddae77e2017-12-18 16:57:07 -0800287 if (error) {
Yifan Hongffcaf992018-01-09 17:08:51 -0800288 *error = "File \"" + e.name + "\" cannot be added: XML File entry " + *error +
Yifan Hongddae77e2017-12-18 16:57:07 -0800289 " has a conflict.";
290 }
291 return nullptr;
292 }
293 }
294 }
295
Yifan Hongf7e8b1b2018-01-23 15:30:35 -0800296 for (auto& e : *matrices) {
297 if (&e.object != matrix && e.object.level() == deviceLevel &&
298 e.object.type() == SchemaType::FRAMEWORK) {
299 for (MatrixKernel& kernel : e.object.framework.mKernels) {
300 KernelVersion ver = kernel.minLts();
301 if (!matrix->add(std::move(kernel))) {
302 if (error) {
303 *error = "Cannot add kernel version " + to_string(ver) +
304 " from FCM version " + to_string(deviceLevel);
305 }
306 return nullptr;
307 }
308 }
309 }
310 }
311
Yifan Hongddae77e2017-12-18 16:57:07 -0800312 return matrix;
313}
314
Yifan Hong2a90ffe2018-03-05 17:45:34 -0800315bool CompatibilityMatrix::forEachInstanceOfVersion(
316 const std::string& package, const Version& expectVersion,
317 const std::function<bool(const MatrixInstance&)>& func) const {
318 for (const MatrixHal* hal : getHals(package)) {
319 bool cont = hal->forEachInstance([&](const MatrixInstance& matrixInstance) {
320 if (matrixInstance.versionRange().contains(expectVersion)) {
321 return func(matrixInstance);
Yifan Honge3a92342018-01-25 17:00:16 -0800322 }
Yifan Hong2a90ffe2018-03-05 17:45:34 -0800323 return true;
324 });
325 if (!cont) return false;
Yifan Honge3a92342018-01-25 17:00:16 -0800326 }
Yifan Hong2a90ffe2018-03-05 17:45:34 -0800327 return true;
Yifan Honge3a92342018-01-25 17:00:16 -0800328}
329
Yifan Hong676447a2016-11-15 12:57:23 -0800330} // namespace vintf
331} // namespace android