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