1 /*
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
19 #include <iostream>
20 #include <utility>
21
22 #include <android-base/logging.h>
23 #include <android-base/strings.h>
24
25 #include "parse_string.h"
26 #include "parse_xml.h"
27 #include "utils.h"
28
29 namespace android {
30 namespace vintf {
31
32 using details::mergeField;
33
addKernel(MatrixKernel && kernel,std::string * error)34 bool CompatibilityMatrix::addKernel(MatrixKernel&& kernel, std::string* error) {
35 if (mType != SchemaType::FRAMEWORK) {
36 if (error) {
37 *error = "Cannot add <kernel> to a " + to_string(mType) + " compatibility matrix.";
38 }
39 return false;
40 }
41
42 if (kernel.getSourceMatrixLevel() == Level::UNSPECIFIED) {
43 kernel.setSourceMatrixLevel(level());
44 }
45
46 auto it = framework.mKernels.begin();
47 for (; it != framework.mKernels.end(); ++it) {
48 if (it->getSourceMatrixLevel() != kernel.getSourceMatrixLevel()) {
49 continue;
50 }
51 if (it->minLts() == kernel.minLts()) {
52 break;
53 }
54 if (it->minLts().dropMinor() == kernel.minLts().dropMinor()) {
55 if (error) {
56 *error = "Kernel version mismatch; for level " +
57 to_string(kernel.getSourceMatrixLevel()) + ", cannot add " +
58 to_string(kernel.minLts()) + " because " + to_string(it->minLts()) +
59 " was added.";
60 }
61 return false;
62 }
63 }
64
65 bool seenVersion = it != framework.mKernels.end();
66
67 if (seenVersion) {
68 // If no conditions, must be the first among the same minLts
69 // because O libvintf only checks the first <kernel> tag that version matches.
70 if (kernel.conditions().empty()) {
71 // Found first <kernel> with the same minLts.
72 // Append config if it does not have <condition>s, else error.
73 if (it->conditions().empty()) {
74 const auto& configs = kernel.configs();
75 it->mConfigs.insert(it->mConfigs.end(), configs.begin(), configs.end());
76 } else {
77 if (error) {
78 *error =
79 "Base compatibility matrix has <condition> for the first <kernel> "
80 "with minlts " +
81 to_string(kernel.minLts()) + " for unknown reason.";
82 }
83 return false;
84 }
85 return true;
86 }
87 } else {
88 // First <kernel> of a minLts must not have <condition>'s for backwards compatibility
89 // with O libvintf.
90 if (!kernel.conditions().empty()) {
91 framework.mKernels.push_back(MatrixKernel(KernelVersion{kernel.minLts()}, {}));
92 }
93 }
94
95 framework.mKernels.push_back(std::move(kernel));
96 return true;
97 }
98
type() const99 SchemaType CompatibilityMatrix::type() const {
100 return mType;
101 }
102
level() const103 Level CompatibilityMatrix::level() const {
104 return mLevel;
105 }
106
fetchAllInformation(const FileSystem * fileSystem,const std::string & path,std::string * error)107 status_t CompatibilityMatrix::fetchAllInformation(const FileSystem* fileSystem,
108 const std::string& path, std::string* error) {
109 return details::fetchAllInformation(fileSystem, path, gCompatibilityMatrixConverter, this,
110 error);
111 }
112
getXmlSchemaPath(const std::string & xmlFileName,const Version & version) const113 std::string CompatibilityMatrix::getXmlSchemaPath(const std::string& xmlFileName,
114 const Version& version) const {
115 using std::literals::string_literals::operator""s;
116 auto range = getXmlFiles(xmlFileName);
117 for (auto it = range.first; it != range.second; ++it) {
118 const MatrixXmlFile& matrixXmlFile = it->second;
119 if (matrixXmlFile.versionRange().contains(version)) {
120 if (!matrixXmlFile.overriddenPath().empty()) {
121 return matrixXmlFile.overriddenPath();
122 }
123 return "/"s + (type() == SchemaType::DEVICE ? "vendor" : "system") + "/etc/" +
124 xmlFileName + "_V" + std::to_string(matrixXmlFile.versionRange().majorVer) +
125 "_" + std::to_string(matrixXmlFile.versionRange().maxMinor) + "." +
126 to_string(matrixXmlFile.format());
127 }
128 }
129 return "";
130 }
131
132 // Split existingHal into a HAL that contains only interface/instance and a HAL
133 // that does not contain it. Return the HAL that contains only interface/instance.
134 // - Return nullptr if existingHal does not contain interface/instance
135 // - Return existingHal if existingHal contains only interface/instance
136 // - Remove interface/instance from existingHal, and return a new MatrixHal (that is added
137 // to "this") that contains only interface/instance.
splitInstance(MatrixHal * existingHal,const std::string & interface,const std::string & instanceOrPattern,bool isRegex)138 MatrixHal* CompatibilityMatrix::splitInstance(MatrixHal* existingHal, const std::string& interface,
139 const std::string& instanceOrPattern, bool isRegex) {
140 bool found = false;
141 bool foundOthers = false;
142 existingHal->forEachInstance([&](const auto& matrixInstance) {
143 bool interfaceMatch = matrixInstance.interface() == interface;
144 bool instanceMatch = false;
145 if (matrixInstance.isRegex() && isRegex) {
146 instanceMatch = (matrixInstance.regexPattern() == instanceOrPattern);
147 } else if (!matrixInstance.isRegex() && !isRegex) {
148 instanceMatch = (matrixInstance.exactInstance() == instanceOrPattern);
149 }
150
151 bool match = interfaceMatch && instanceMatch;
152
153 found |= match;
154 foundOthers |= (!match);
155
156 return !found || !foundOthers;
157 });
158
159 if (!found) {
160 return nullptr;
161 }
162
163 if (!foundOthers) {
164 return existingHal;
165 }
166
167 existingHal->removeInstance(interface, instanceOrPattern, isRegex);
168 MatrixHal copy = *existingHal;
169 copy.clearInstances();
170 copy.insertInstance(interface, instanceOrPattern, isRegex);
171
172 return addInternal(std::move(copy));
173 }
174
175 // Add all package@other_version::interface/instance as an optional instance.
176 // If package@this_version::interface/instance is in this (that is, some instance
177 // with the same package and interface and instance exists), then other_version is
178 // considered a possible replacement to this_version.
179 // See LibVintfTest.AddOptionalHal* tests for details.
addAllHalsAsOptional(CompatibilityMatrix * other,std::string * error)180 bool CompatibilityMatrix::addAllHalsAsOptional(CompatibilityMatrix* other, std::string* error) {
181 if (other == nullptr || other->level() <= level()) {
182 return true;
183 }
184
185 for (auto& pair : other->mHals) {
186 const std::string& name = pair.first;
187 MatrixHal& halToAdd = pair.second;
188
189 std::set<std::pair<std::string, std::string>> insertedInstances;
190 std::set<std::pair<std::string, std::string>> insertedRegex;
191 auto existingHals = getHals(name);
192
193 halToAdd.forEachInstance([&](const std::vector<VersionRange>& versionRanges,
194 const std::string& interface,
195 const std::string& instanceOrPattern, bool isRegex) {
196 for (auto* existingHal : existingHals) {
197 // Ignore HALs with different format.
198 if (halToAdd.format != existingHal->format) {
199 continue;
200 }
201
202 MatrixHal* splitInstance =
203 this->splitInstance(existingHal, interface, instanceOrPattern, isRegex);
204 if (splitInstance != nullptr) {
205 splitInstance->insertVersionRanges(versionRanges);
206 if (isRegex) {
207 insertedRegex.insert(std::make_pair(interface, instanceOrPattern));
208 } else {
209 insertedInstances.insert(std::make_pair(interface, instanceOrPattern));
210 }
211 }
212 }
213 return true;
214 });
215
216 // Add the remaining instances.
217 for (const auto& pair : insertedInstances) {
218 halToAdd.removeInstance(pair.first, pair.second, false /* isRegex */);
219 }
220 for (const auto& pair : insertedRegex) {
221 halToAdd.removeInstance(pair.first, pair.second, true /* isRegex */);
222 }
223
224 if (halToAdd.instancesCount() > 0) {
225 halToAdd.setOptional(true);
226 if (!add(std::move(halToAdd))) {
227 if (error) {
228 *error = "Cannot add HAL " + name + " for unknown reason.";
229 }
230 return false;
231 }
232 }
233 }
234 return true;
235 }
236
addAllXmlFilesAsOptional(CompatibilityMatrix * other,std::string * error)237 bool CompatibilityMatrix::addAllXmlFilesAsOptional(CompatibilityMatrix* other, std::string* error) {
238 if (other == nullptr || other->level() <= level()) {
239 return true;
240 }
241 for (auto& pair : other->mXmlFiles) {
242 const std::string& name = pair.first;
243 MatrixXmlFile& xmlFileToAdd = pair.second;
244
245 xmlFileToAdd.mOptional = true;
246 if (!addXmlFile(std::move(xmlFileToAdd))) {
247 if (error) {
248 *error = "Cannot add XML File " + name + " for unknown reason.";
249 }
250 return false;
251 }
252 }
253 return true;
254 }
255
256 // Merge Kernel. See KernelInfo::getMatchedKernelRequirements for details on compatibility checks.
addAllKernels(CompatibilityMatrix * other,std::string * error)257 bool CompatibilityMatrix::addAllKernels(CompatibilityMatrix* other, std::string* error) {
258 for (MatrixKernel& kernel : other->framework.mKernels) {
259 if (kernel.getSourceMatrixLevel() == Level::UNSPECIFIED) {
260 kernel.setSourceMatrixLevel(other->level());
261 }
262 KernelVersion ver = kernel.minLts();
263 if (!addKernel(std::move(kernel), error)) {
264 if (error) {
265 *error = "Cannot add kernel version " + to_string(ver) + ": " + *error;
266 }
267 return false;
268 }
269 }
270 return true;
271 }
272
addSepolicy(CompatibilityMatrix * other,std::string * error)273 bool CompatibilityMatrix::addSepolicy(CompatibilityMatrix* other, std::string* error) {
274 bool success = mergeField(&this->framework.mSepolicy, &other->framework.mSepolicy);
275 if (!success && error) *error = "<sepolicy> is already defined";
276 return success;
277 }
278
addAvbMetaVersion(CompatibilityMatrix * other,std::string * error)279 bool CompatibilityMatrix::addAvbMetaVersion(CompatibilityMatrix* other, std::string* error) {
280 bool success = mergeField(&this->framework.mAvbMetaVersion, &other->framework.mAvbMetaVersion);
281 if (!success && error) *error = "<avb><vbmeta-version> is already defined";
282 return success;
283 }
284
addVndk(CompatibilityMatrix * other,std::string * error)285 bool CompatibilityMatrix::addVndk(CompatibilityMatrix* other, std::string* error) {
286 #pragma clang diagnostic push
287 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
288 bool success = mergeField(&this->device.mVndk, &other->device.mVndk);
289 #pragma clang diagnostic pop
290 if (!success && error) *error = "<vndk> is already defined";
291 return success;
292 }
293
addVendorNdk(CompatibilityMatrix * other,std::string * error)294 bool CompatibilityMatrix::addVendorNdk(CompatibilityMatrix* other, std::string* error) {
295 bool success = mergeField(&this->device.mVendorNdk, &other->device.mVendorNdk);
296 if (!success && error) *error = "<vendor-ndk> is already defined";
297 return success;
298 }
299
addSystemSdk(CompatibilityMatrix * other,std::string *)300 bool CompatibilityMatrix::addSystemSdk(CompatibilityMatrix* other, std::string* /* error */) {
301 this->device.mSystemSdk.addAll(&other->device.mSystemSdk);
302 return true;
303 }
304
operator ==(const CompatibilityMatrix & lft,const CompatibilityMatrix & rgt)305 bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) {
306 return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals &&
307 lft.mXmlFiles == rgt.mXmlFiles &&
308 (lft.mType != SchemaType::DEVICE ||
309 (
310 #pragma clang diagnostic push
311 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
312 lft.device.mVndk == rgt.device.mVndk &&
313 #pragma clang diagnostic pop
314 lft.device.mVendorNdk == rgt.device.mVendorNdk &&
315 lft.device.mSystemSdk == rgt.device.mSystemSdk)) &&
316 (lft.mType != SchemaType::FRAMEWORK ||
317 (lft.framework.mKernels == rgt.framework.mKernels &&
318 lft.framework.mSepolicy == rgt.framework.mSepolicy &&
319 lft.framework.mAvbMetaVersion == rgt.framework.mAvbMetaVersion));
320 }
321
combine(Level deviceLevel,std::vector<Named<CompatibilityMatrix>> * matrices,std::string * error)322 std::unique_ptr<CompatibilityMatrix> CompatibilityMatrix::combine(
323 Level deviceLevel, std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error) {
324 // Check type.
325 for (const auto& e : *matrices) {
326 if (e.object.type() != SchemaType::FRAMEWORK) {
327 if (error) {
328 *error = "File \"" + e.name + "\" is not a framework compatibility matrix.";
329 return nullptr;
330 }
331 }
332 }
333
334 // Matrices with unspecified (empty) level are auto-filled with deviceLevel.
335 for (auto& e : *matrices) {
336 if (e.object.level() == Level::UNSPECIFIED) {
337 e.object.mLevel = deviceLevel;
338 }
339 }
340
341 // Add from low to high FCM version so that optional <kernel> requirements are added correctly.
342 // See comment in addAllAsOptional.
343 std::sort(matrices->begin(), matrices->end(),
344 [](const auto& x, const auto& y) { return x.object.level() < y.object.level(); });
345
346 auto baseMatrix = std::make_unique<CompatibilityMatrix>();
347 baseMatrix->mLevel = deviceLevel;
348 baseMatrix->mType = SchemaType::FRAMEWORK;
349
350 std::vector<std::string> parsedFiles;
351 for (auto& e : *matrices) {
352 if (e.object.level() < deviceLevel) {
353 continue;
354 }
355
356 bool success = false;
357 if (e.object.level() == deviceLevel) {
358 success = baseMatrix->addAll(&e, error);
359 } else {
360 success = baseMatrix->addAllAsOptional(&e, error);
361 }
362 if (!success) {
363 if (error) {
364 *error = "Conflict when merging \"" + e.name + "\": " + *error + "\n" +
365 "Previous files:\n" + base::Join(parsedFiles, "\n");
366 }
367 return nullptr;
368 }
369 parsedFiles.push_back(e.name);
370 }
371
372 return baseMatrix;
373 }
374
combineDeviceMatrices(std::vector<Named<CompatibilityMatrix>> * matrices,std::string * error)375 std::unique_ptr<CompatibilityMatrix> CompatibilityMatrix::combineDeviceMatrices(
376 std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error) {
377 auto baseMatrix = std::make_unique<CompatibilityMatrix>();
378 baseMatrix->mType = SchemaType::DEVICE;
379
380 std::vector<std::string> parsedFiles;
381 for (auto& e : *matrices) {
382 bool success = baseMatrix->addAll(&e, error);
383 if (!success) {
384 if (error) {
385 *error = "Conflict when merging \"" + e.name + "\": " + *error + "\n" +
386 "Previous files:\n" + base::Join(parsedFiles, "\n");
387 }
388 return nullptr;
389 }
390 parsedFiles.push_back(e.name);
391 }
392 return baseMatrix;
393 }
394
addAll(Named<CompatibilityMatrix> * inputMatrix,std::string * error)395 bool CompatibilityMatrix::addAll(Named<CompatibilityMatrix>* inputMatrix, std::string* error) {
396 if (!addAllHals(&inputMatrix->object, error) || !addAllXmlFiles(&inputMatrix->object, error) ||
397 !addAllKernels(&inputMatrix->object, error) || !addSepolicy(&inputMatrix->object, error) ||
398 !addAvbMetaVersion(&inputMatrix->object, error) || !addVndk(&inputMatrix->object, error) ||
399 !addVendorNdk(&inputMatrix->object, error) || !addSystemSdk(&inputMatrix->object, error)) {
400 if (error) {
401 *error = "File \"" + inputMatrix->name + "\" cannot be added: " + *error + ".";
402 }
403 return false;
404 }
405 return true;
406 }
407
addAllAsOptional(Named<CompatibilityMatrix> * inputMatrix,std::string * error)408 bool CompatibilityMatrix::addAllAsOptional(Named<CompatibilityMatrix>* inputMatrix,
409 std::string* error) {
410 if (!addAllHalsAsOptional(&inputMatrix->object, error) ||
411 !addAllXmlFilesAsOptional(&inputMatrix->object, error) ||
412 !addAllKernels(&inputMatrix->object, error)) {
413 if (error) {
414 *error = "File \"" + inputMatrix->name + "\" cannot be added: " + *error;
415 }
416 return false;
417 }
418 // ignore <sepolicy> requirement from higher level
419 // ignore <avb> requirement from higher level
420 return true;
421 }
422
forEachInstanceOfVersion(HalFormat format,const std::string & package,const Version & expectVersion,const std::function<bool (const MatrixInstance &)> & func) const423 bool CompatibilityMatrix::forEachInstanceOfVersion(
424 HalFormat format, const std::string& package, const Version& expectVersion,
425 const std::function<bool(const MatrixInstance&)>& func) const {
426 for (const MatrixHal* hal : getHals(package)) {
427 bool cont = hal->forEachInstance([&](const MatrixInstance& matrixInstance) {
428 if (matrixInstance.format() == format &&
429 matrixInstance.versionRange().contains(expectVersion)) {
430 return func(matrixInstance);
431 }
432 return true;
433 });
434 if (!cont) return false;
435 }
436 return true;
437 }
438
matchInstance(HalFormat format,const std::string & halName,const Version & version,const std::string & interfaceName,const std::string & instance) const439 bool CompatibilityMatrix::matchInstance(HalFormat format, const std::string& halName,
440 const Version& version, const std::string& interfaceName,
441 const std::string& instance) const {
442 bool found = false;
443 (void)forEachInstanceOfInterface(format, halName, version, interfaceName,
444 [&found, &instance](const auto& e) {
445 found |= (e.matchInstance(instance));
446 return !found; // if not found, continue
447 });
448 return found;
449 }
450
getVendorNdkVersion() const451 std::string CompatibilityMatrix::getVendorNdkVersion() const {
452 return type() == SchemaType::DEVICE ? device.mVendorNdk.version() : "";
453 }
454
getSourceMatrixLevel(const MatrixKernel * matrixKernel) const455 Level CompatibilityMatrix::getSourceMatrixLevel(const MatrixKernel* matrixKernel) const {
456 CHECK(std::find_if(framework.mKernels.begin(), framework.mKernels.end(),
457 [matrixKernel](const auto& e) { return &e == matrixKernel; }) !=
458 framework.mKernels.end());
459 Level ret = matrixKernel->getSourceMatrixLevel();
460 if (ret != Level::UNSPECIFIED) return ret;
461 return level();
462 }
463
464 } // namespace vintf
465 } // namespace android
466