blob: 98d4414e42a8c14cf05540bcf5cae52cad49ab55 [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 Hongdbe9db32017-12-11 19:06:11 -080019#include <utility>
20
Yifan Hongbbfff302017-06-06 17:10:13 -070021#include "parse_string.h"
Yifan Hong1e5a0542017-04-28 14:37:56 -070022#include "utils.h"
23
Yifan Hongddae77e2017-12-18 16:57:07 -080024#include <iostream>
25#include "parse_xml.h"
26
Yifan Hong676447a2016-11-15 12:57:23 -080027namespace android {
28namespace vintf {
29
Yifan Hong676447a2016-11-15 12:57:23 -080030bool CompatibilityMatrix::add(MatrixHal &&hal) {
Yifan Hong0fd7aef2017-05-24 14:37:19 -070031 return HalGroup<MatrixHal>::add(std::move(hal));
Yifan Hong676447a2016-11-15 12:57:23 -080032}
33
34bool CompatibilityMatrix::add(MatrixKernel &&kernel) {
Yifan Hong7c7d7062017-04-04 16:26:51 -070035 if (mType != SchemaType::FRAMEWORK) {
36 return false;
37 }
38 framework.mKernels.push_back(std::move(kernel));
Yifan Hong676447a2016-11-15 12:57:23 -080039 return true;
40}
41
Yifan Hong398f4c72017-04-13 20:18:01 -070042SchemaType CompatibilityMatrix::type() const {
43 return mType;
44}
45
Yifan Hong2027a492017-12-11 15:21:19 -080046Level CompatibilityMatrix::level() const {
47 return mLevel;
48}
49
Yifan Hongdb127cb2017-09-19 13:36:21 -070050Version CompatibilityMatrix::getMinimumMetaVersion() const {
51 // TODO(b/62801658): this needs to depend on whether there are 1.1 requirements
52 // (e.g. required <xmlfile> entry)
53 return {1, 0};
54}
Yifan Hong1e5a0542017-04-28 14:37:56 -070055
Yifan Hong60217032018-01-08 16:19:42 -080056status_t CompatibilityMatrix::fetchAllInformation(const std::string& path, std::string* error) {
57 return details::fetchAllInformation(path, gCompatibilityMatrixConverter, this, error);
Yifan Hong1e5a0542017-04-28 14:37:56 -070058}
59
Yifan Hongbbfff302017-06-06 17:10:13 -070060std::string CompatibilityMatrix::getXmlSchemaPath(const std::string& xmlFileName,
61 const Version& version) const {
62 using std::literals::string_literals::operator""s;
63 auto range = getXmlFiles(xmlFileName);
64 for (auto it = range.first; it != range.second; ++it) {
65 const MatrixXmlFile& matrixXmlFile = it->second;
66 if (matrixXmlFile.versionRange().contains(version)) {
67 if (!matrixXmlFile.overriddenPath().empty()) {
68 return matrixXmlFile.overriddenPath();
69 }
70 return "/"s + (type() == SchemaType::DEVICE ? "vendor" : "system") + "/etc/" +
71 xmlFileName + "_V" + std::to_string(matrixXmlFile.versionRange().majorVer) +
72 "_" + std::to_string(matrixXmlFile.versionRange().maxMinor) + "." +
73 to_string(matrixXmlFile.format());
74 }
75 }
76 return "";
77}
78
Yifan Hongdbe9db32017-12-11 19:06:11 -080079static VersionRange* findRangeWithMajorVersion(std::vector<VersionRange>& versionRanges,
80 size_t majorVer) {
81 for (VersionRange& vr : versionRanges) {
82 if (vr.majorVer == majorVer) {
83 return &vr;
84 }
85 }
86 return nullptr;
87}
88
89std::pair<MatrixHal*, VersionRange*> CompatibilityMatrix::getHalWithMajorVersion(
90 const std::string& name, size_t majorVer) {
91 for (MatrixHal* hal : getHals(name)) {
92 VersionRange* vr = findRangeWithMajorVersion(hal->versionRanges, majorVer);
93 if (vr != nullptr) {
94 return {hal, vr};
95 }
96 }
97 return {nullptr, nullptr};
98}
99
100bool CompatibilityMatrix::addAllHalsAsOptional(CompatibilityMatrix* other, std::string* error) {
101 if (other == nullptr || other->level() <= level()) {
102 return true;
103 }
104
105 for (auto& pair : other->mHals) {
106 const std::string& name = pair.first;
107 MatrixHal& halToAdd = pair.second;
108 for (const VersionRange& vr : halToAdd.versionRanges) {
109 MatrixHal* existingHal;
110 VersionRange* existingVr;
111 std::tie(existingHal, existingVr) = getHalWithMajorVersion(name, vr.majorVer);
112
113 if (existingHal == nullptr) {
Yifan Hongd8eea9e2017-12-22 15:13:42 -0800114 MatrixHal optionalHalToAdd(halToAdd);
115 optionalHalToAdd.optional = true;
116 optionalHalToAdd.versionRanges = {vr};
117 if (!add(std::move(optionalHalToAdd))) {
Yifan Hong78c911a2017-12-20 15:16:59 -0800118 if (error) {
119 *error = "Cannot add HAL " + name + " for unknown reason.";
120 }
121 return false;
122 }
Yifan Hongdbe9db32017-12-11 19:06:11 -0800123 continue;
124 }
125
126 if (!existingHal->optional && !existingHal->containsInstances(halToAdd)) {
127 if (error != nullptr) {
128 *error = "HAL " + name + "@" + to_string(vr.minVer()) + " is a required " +
129 "HAL, but fully qualified instance names don't match (at FCM "
130 "Version " +
131 std::to_string(level()) + " and " + std::to_string(other->level()) +
132 ")";
133 }
134 return false;
135 }
136
137 existingVr->maxMinor = std::max(existingVr->maxMinor, vr.maxMinor);
138 }
139 }
140 return true;
141}
142
Yifan Hongd4b92fe2017-12-20 15:29:03 -0800143bool CompatibilityMatrix::addAllXmlFilesAsOptional(CompatibilityMatrix* other, std::string* error) {
144 if (other == nullptr || other->level() <= level()) {
145 return true;
146 }
147 for (auto& pair : other->mXmlFiles) {
148 const std::string& name = pair.first;
149 MatrixXmlFile& xmlFileToAdd = pair.second;
150
151 xmlFileToAdd.mOptional = true;
152 if (!addXmlFile(std::move(xmlFileToAdd))) {
153 if (error) {
154 *error = "Cannot add XML File " + name + " for unknown reason.";
155 }
156 return false;
157 }
158 }
159 return true;
160}
161
Yifan Hongfb7469c2017-04-05 19:15:21 -0700162bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) {
Yifan Hong2027a492017-12-11 15:21:19 -0800163 return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals &&
164 lft.mXmlFiles == rgt.mXmlFiles &&
Yifan Hongd4857902017-06-13 14:13:56 -0700165 (lft.mType != SchemaType::DEVICE || (lft.device.mVndk == rgt.device.mVndk)) &&
166 (lft.mType != SchemaType::FRAMEWORK ||
167 (lft.framework.mKernels == rgt.framework.mKernels &&
168 lft.framework.mSepolicy == rgt.framework.mSepolicy &&
169 lft.framework.mAvbMetaVersion == rgt.framework.mAvbMetaVersion));
Yifan Hongfb7469c2017-04-05 19:15:21 -0700170}
171
Yifan Hongddae77e2017-12-18 16:57:07 -0800172// Find compatibility_matrix.empty.xml (which has unspecified level) and use it
173// as a base matrix.
174CompatibilityMatrix* CompatibilityMatrix::findOrInsertBaseMatrix(
175 std::vector<std::pair<std::string, CompatibilityMatrix>>* matrices, std::string* error) {
176 bool multipleFound = false;
177 CompatibilityMatrix* matrix = nullptr;
178 for (auto& e : *matrices) {
179 if (e.second.level() == Level::UNSPECIFIED) {
180 if (!e.second.mHals.empty()) {
181 if (error) {
182 *error =
183 "Error: File \"" + e.first + "\" should not contain " + "HAL elements.";
184 }
185 return nullptr;
186 }
187
188 if (!e.second.mXmlFiles.empty()) {
189 if (error) {
190 *error = "Error: File \"" + e.first + "\" should not contain " +
191 "XML File elements.";
192 }
193 return nullptr;
194 }
195
196 if (matrix != nullptr) {
197 multipleFound = true;
198 }
199
200 matrix = &e.second;
201 // continue to detect multiple files with "unspecified" levels
202 }
203 }
204
205 if (multipleFound) {
206 if (error) {
207 *error =
208 "Error: multiple framework compatibility matrix files have "
209 "unspecified level; there should only be one such file.\n";
210 for (auto& e : *matrices) {
211 if (e.second.level() == Level::UNSPECIFIED) {
212 *error += " " + e.first + "\n";
213 }
214 }
215 }
216 return nullptr;
217 }
218
219 if (matrix == nullptr) {
220 matrix = &matrices->emplace(matrices->end())->second;
221 matrix->mType = SchemaType::FRAMEWORK;
222 matrix->mLevel = Level::UNSPECIFIED;
223 }
224
225 return matrix;
226}
227
228CompatibilityMatrix* CompatibilityMatrix::combine(
229 Level deviceLevel, std::vector<std::pair<std::string, CompatibilityMatrix>>* matrices,
230 std::string* error) {
231 if (deviceLevel == Level::UNSPECIFIED) {
232 if (error) {
233 *error = "Error: device level is unspecified.";
234 }
235 return nullptr;
236 }
237
238 CompatibilityMatrix* matrix = findOrInsertBaseMatrix(matrices, error);
239 if (matrix == nullptr) {
240 return nullptr;
241 }
242
243 matrix->mLevel = deviceLevel;
244
245 for (auto& e : *matrices) {
246 if (&e.second != matrix && e.second.level() == deviceLevel) {
247 if (!matrix->addAllHals(&e.second, error)) {
248 if (error) {
249 *error = "File \"" + e.first + "\" cannot be added: HAL " + *error +
250 " has a conflict.";
251 }
252 return nullptr;
253 }
254
255 if (!matrix->addAllXmlFiles(&e.second, error)) {
256 if (error) {
257 *error = "File \"" + e.first + "\" cannot be added: XML File entry " + *error +
258 " has a conflict.";
259 }
260 return nullptr;
261 }
262 }
263 }
264
265 for (auto& e : *matrices) {
266 if (&e.second != matrix && e.second.level() != Level::UNSPECIFIED &&
267 e.second.level() > deviceLevel) {
268 if (!matrix->addAllHalsAsOptional(&e.second, error)) {
269 if (error) {
270 *error = "File \"" + e.first + "\" cannot be added: " + *error +
271 ". See <hal> with the same name " +
272 "in previously parsed files or previously declared in this file.";
273 }
274 return nullptr;
275 }
276
277 if (!matrix->addAllXmlFilesAsOptional(&e.second, error)) {
278 if (error) {
279 *error = "File \"" + e.first + "\" cannot be added: XML File entry " + *error +
280 " has a conflict.";
281 }
282 return nullptr;
283 }
284 }
285 }
286
287 return matrix;
288}
289
Yifan Hong676447a2016-11-15 12:57:23 -0800290} // namespace vintf
291} // namespace android