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 "parse_string.h" 23 #include "parse_xml.h" 24 #include "utils.h" 25 26 namespace android { 27 namespace vintf { 28 29 bool CompatibilityMatrix::add(MatrixHal &&hal) { 30 return HalGroup<MatrixHal>::add(std::move(hal)); 31 } 32 33 bool CompatibilityMatrix::add(MatrixKernel &&kernel) { 34 if (mType != SchemaType::FRAMEWORK) { 35 return false; 36 } 37 framework.mKernels.push_back(std::move(kernel)); 38 return true; 39 } 40 41 SchemaType CompatibilityMatrix::type() const { 42 return mType; 43 } 44 45 Level CompatibilityMatrix::level() const { 46 return mLevel; 47 } 48 49 Version 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 } 54 55 status_t CompatibilityMatrix::fetchAllInformation(const std::string& path, std::string* error) { 56 return details::fetchAllInformation(path, gCompatibilityMatrixConverter, this, error); 57 } 58 59 std::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 78 // Split existingHal into a HAL that contains only interface/instance and a HAL 79 // that does not contain it. Return the HAL that contains only interface/instance. 80 // - Return nullptr if existingHal does not contain interface/instance 81 // - Return existingHal if existingHal contains only interface/instance 82 // - Remove interface/instance from existingHal, and return a new MatrixHal (that is added 83 // to "this") that contains only interface/instance. 84 MatrixHal* CompatibilityMatrix::splitInstance(MatrixHal* existingHal, const std::string& interface, 85 const std::string& instanceOrPattern, bool isRegex) { 86 bool found = false; 87 bool foundOthers = false; 88 existingHal->forEachInstance([&](const auto& matrixInstance) { 89 bool interfaceMatch = matrixInstance.interface() == interface; 90 bool instanceMatch = false; 91 if (matrixInstance.isRegex() && isRegex) { 92 instanceMatch = (matrixInstance.regexPattern() == instanceOrPattern); 93 } else if (!matrixInstance.isRegex() && !isRegex) { 94 instanceMatch = (matrixInstance.exactInstance() == instanceOrPattern); 95 } 96 97 bool match = interfaceMatch && instanceMatch; 98 99 found |= match; 100 foundOthers |= (!match); 101 102 return !found || !foundOthers; 103 }); 104 105 if (!found) { 106 return nullptr; 107 } 108 109 if (!foundOthers) { 110 return existingHal; 111 } 112 113 existingHal->removeInstance(interface, instanceOrPattern, isRegex); 114 MatrixHal copy = *existingHal; 115 copy.clearInstances(); 116 copy.insertInstance(interface, instanceOrPattern, isRegex); 117 118 return addInternal(std::move(copy)); 119 } 120 121 // Add all package@other_version::interface/instance as an optional instance. 122 // If package@this_version::interface/instance is in this (that is, some instance 123 // with the same package and interface and instance exists), then other_version is 124 // considered a possible replacement to this_version. 125 // See LibVintfTest.AddOptionalHal* tests for details. 126 bool CompatibilityMatrix::addAllHalsAsOptional(CompatibilityMatrix* other, std::string* error) { 127 if (other == nullptr || other->level() <= level()) { 128 return true; 129 } 130 131 for (auto& pair : other->mHals) { 132 const std::string& name = pair.first; 133 MatrixHal& halToAdd = pair.second; 134 135 std::set<std::pair<std::string, std::string>> insertedInstances; 136 std::set<std::pair<std::string, std::string>> insertedRegex; 137 auto existingHals = getHals(name); 138 139 halToAdd.forEachInstance([&](const std::vector<VersionRange>& versionRanges, 140 const std::string& interface, 141 const std::string& instanceOrPattern, bool isRegex) { 142 for (auto* existingHal : existingHals) { 143 MatrixHal* splitInstance = 144 this->splitInstance(existingHal, interface, instanceOrPattern, isRegex); 145 if (splitInstance != nullptr) { 146 splitInstance->insertVersionRanges(versionRanges); 147 if (isRegex) { 148 insertedRegex.insert(std::make_pair(interface, instanceOrPattern)); 149 } else { 150 insertedInstances.insert(std::make_pair(interface, instanceOrPattern)); 151 } 152 } 153 } 154 return true; 155 }); 156 157 // Add the remaining instances. 158 for (const auto& pair : insertedInstances) { 159 halToAdd.removeInstance(pair.first, pair.second, false /* isRegex */); 160 } 161 for (const auto& pair : insertedRegex) { 162 halToAdd.removeInstance(pair.first, pair.second, true /* isRegex */); 163 } 164 165 if (halToAdd.instancesCount() > 0) { 166 halToAdd.setOptional(true); 167 if (!add(std::move(halToAdd))) { 168 if (error) { 169 *error = "Cannot add HAL " + name + " for unknown reason."; 170 } 171 return false; 172 } 173 } 174 } 175 return true; 176 } 177 178 bool CompatibilityMatrix::addAllXmlFilesAsOptional(CompatibilityMatrix* other, std::string* error) { 179 if (other == nullptr || other->level() <= level()) { 180 return true; 181 } 182 for (auto& pair : other->mXmlFiles) { 183 const std::string& name = pair.first; 184 MatrixXmlFile& xmlFileToAdd = pair.second; 185 186 xmlFileToAdd.mOptional = true; 187 if (!addXmlFile(std::move(xmlFileToAdd))) { 188 if (error) { 189 *error = "Cannot add XML File " + name + " for unknown reason."; 190 } 191 return false; 192 } 193 } 194 return true; 195 } 196 197 bool CompatibilityMatrix::addAllKernelsAsOptional(CompatibilityMatrix* other, std::string* error) { 198 if (other == nullptr || other->level() <= level()) { 199 return true; 200 } 201 202 for (MatrixKernel& kernelToAdd : other->framework.mKernels) { 203 bool exists = 204 std::any_of(this->framework.mKernels.begin(), this->framework.mKernels.end(), 205 [&kernelToAdd](const MatrixKernel& existing) { 206 return kernelToAdd.minLts().version == existing.minLts().version && 207 kernelToAdd.minLts().majorRev == existing.minLts().majorRev; 208 }); 209 210 if (exists) { 211 // Shouldn't retroactively add requirements to minLts(), so ignore this. 212 // This happens even when kernelToAdd.conditions() != existing.conditions(). 213 continue; 214 } 215 216 KernelVersion minLts = kernelToAdd.minLts(); 217 if (!add(std::move(kernelToAdd))) { 218 if (error) { 219 *error = "Cannot add " + to_string(minLts) + " for unknown reason."; 220 } 221 return false; 222 } 223 } 224 return true; 225 } 226 227 bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) { 228 return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals && 229 lft.mXmlFiles == rgt.mXmlFiles && 230 (lft.mType != SchemaType::DEVICE || 231 ( 232 #pragma clang diagnostic push 233 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 234 lft.device.mVndk == rgt.device.mVndk && 235 #pragma clang diagnostic pop 236 lft.device.mVendorNdk == rgt.device.mVendorNdk && 237 lft.device.mSystemSdk == rgt.device.mSystemSdk)) && 238 (lft.mType != SchemaType::FRAMEWORK || 239 (lft.framework.mKernels == rgt.framework.mKernels && 240 lft.framework.mSepolicy == rgt.framework.mSepolicy && 241 lft.framework.mAvbMetaVersion == rgt.framework.mAvbMetaVersion)); 242 } 243 244 // Find compatibility_matrix.empty.xml (which has unspecified level) and use it 245 // as a base matrix. 246 CompatibilityMatrix* CompatibilityMatrix::findOrInsertBaseMatrix( 247 std::vector<Named<CompatibilityMatrix>>* matrices, std::string* error) { 248 std::vector<CompatibilityMatrix*> matricesUnspecified; 249 std::vector<CompatibilityMatrix*> matricesEmpty; 250 for (auto& e : *matrices) { 251 if (e.object.level() == Level::UNSPECIFIED) { 252 matricesUnspecified.push_back(&e.object); 253 254 if (!e.object.mHals.empty()) { 255 continue; 256 } 257 258 if (!e.object.mXmlFiles.empty()) { 259 continue; 260 } 261 262 matricesEmpty.push_back(&e.object); 263 } 264 } 265 266 if (matricesEmpty.size() > 1) { 267 if (error) { 268 *error = 269 "Error: multiple framework compatibility matrix files have " 270 "unspecified level; there should only be one such file.\n"; 271 for (auto& e : *matrices) { 272 if (e.object.level() == Level::UNSPECIFIED) { 273 *error += " " + e.name + "\n"; 274 } 275 } 276 } 277 return nullptr; 278 } 279 if (matricesEmpty.size() == 1) { 280 return matricesEmpty.front(); 281 } 282 if (!matricesUnspecified.empty()) { 283 return matricesUnspecified.front(); 284 } 285 auto matrix = &matrices->emplace(matrices->end())->object; 286 matrix->mType = SchemaType::FRAMEWORK; 287 matrix->mLevel = Level::UNSPECIFIED; 288 return matrix; 289 } 290 291 // Check if there are two files declaring level="1", for example, because 292 // combine() use this assumption to simplify a lot of logic. 293 static bool checkDuplicateLevels(const std::vector<Named<CompatibilityMatrix>>& matrices, 294 std::string* error) { 295 std::map<Level, const std::string*> existingLevels; 296 for (const auto& e : matrices) { 297 if (e.object.level() == Level::UNSPECIFIED && 298 existingLevels.find(e.object.level()) != existingLevels.end()) { 299 if (error) { 300 *error = "Conflict of levels: file \"" + 301 *existingLevels.find(e.object.level())->second + "\" and \"" + e.name + 302 " both have level " + to_string(e.object.level()); 303 } 304 return false; 305 } 306 existingLevels.emplace(e.object.level(), &e.name); 307 } 308 return true; 309 } 310 311 CompatibilityMatrix* CompatibilityMatrix::combine(Level deviceLevel, 312 std::vector<Named<CompatibilityMatrix>>* matrices, 313 std::string* error) { 314 if (!checkDuplicateLevels(*matrices, error)) { 315 return nullptr; 316 } 317 318 CompatibilityMatrix* matrix = findOrInsertBaseMatrix(matrices, error); 319 if (matrix == nullptr) { 320 return nullptr; 321 } 322 323 matrix->mLevel = deviceLevel; 324 325 for (auto& e : *matrices) { 326 if (&e.object != matrix && 327 (e.object.level() == deviceLevel || e.object.level() == Level::UNSPECIFIED)) { 328 if (!matrix->addAllHals(&e.object, error)) { 329 if (error) { 330 *error = "File \"" + e.name + "\" cannot be added: HAL " + *error + 331 " has a conflict."; 332 } 333 return nullptr; 334 } 335 336 if (!matrix->addAllXmlFiles(&e.object, error)) { 337 if (error) { 338 *error = "File \"" + e.name + "\" cannot be added: XML File entry " + *error + 339 " has a conflict."; 340 } 341 return nullptr; 342 } 343 } 344 } 345 346 for (auto& e : *matrices) { 347 if (&e.object != matrix && e.object.level() != Level::UNSPECIFIED && 348 e.object.level() > deviceLevel) { 349 if (!matrix->addAllHalsAsOptional(&e.object, error)) { 350 if (error) { 351 *error = "File \"" + e.name + "\" cannot be added: " + *error + 352 ". See <hal> with the same name " + 353 "in previously parsed files or previously declared in this file."; 354 } 355 return nullptr; 356 } 357 358 if (!matrix->addAllXmlFilesAsOptional(&e.object, error)) { 359 if (error) { 360 *error = "File \"" + e.name + "\" cannot be added: XML File entry " + *error + 361 " has a conflict."; 362 } 363 return nullptr; 364 } 365 } 366 } 367 368 // Add <kernel> from exact "level", then optionally add <kernel> from high levels to low levels. 369 // For example, (each letter is a kernel version x.y.z) 370 // 1.xml: A1, B1 371 // 2.xml: B2, C2, D2 372 // 3.xml: D3, E3 373 // Then the combined 1.xml should have 374 // A1, B1 (from 1.xml, required), C2, D2, E3 (optional, use earliest possible). 375 for (auto& e : *matrices) { 376 if (&e.object != matrix && e.object.level() == deviceLevel && 377 e.object.type() == SchemaType::FRAMEWORK) { 378 for (MatrixKernel& kernel : e.object.framework.mKernels) { 379 KernelVersion ver = kernel.minLts(); 380 if (!matrix->add(std::move(kernel))) { 381 if (error) { 382 *error = "Cannot add kernel version " + to_string(ver) + 383 " from FCM version " + to_string(deviceLevel); 384 } 385 return nullptr; 386 } 387 } 388 } 389 } 390 391 // There is only one file per level, hence a map is used instead of a multimap. Also, there is 392 // no good ordering (i.e. priority) for multiple files with the same level. 393 std::map<Level, Named<CompatibilityMatrix>*> matricesMap; 394 for (auto& e : *matrices) { 395 if (&e.object != matrix && e.object.level() != Level::UNSPECIFIED && 396 e.object.level() > deviceLevel && e.object.type() == SchemaType::FRAMEWORK) { 397 matricesMap.emplace(e.object.level(), &e); 398 } 399 } 400 401 for (auto&& pair : matricesMap) { 402 if (!matrix->addAllKernelsAsOptional(&pair.second->object, error)) { 403 if (error) { 404 *error = "Cannot add new kernel versions from FCM version " + 405 to_string(pair.first) + " (" + pair.second->name + ")" + 406 " to FCM version " + to_string(deviceLevel) + ": " + *error; 407 } 408 return nullptr; 409 } 410 } 411 412 return matrix; 413 } 414 415 bool CompatibilityMatrix::forEachInstanceOfVersion( 416 const std::string& package, const Version& expectVersion, 417 const std::function<bool(const MatrixInstance&)>& func) const { 418 for (const MatrixHal* hal : getHals(package)) { 419 bool cont = hal->forEachInstance([&](const MatrixInstance& matrixInstance) { 420 if (matrixInstance.versionRange().contains(expectVersion)) { 421 return func(matrixInstance); 422 } 423 return true; 424 }); 425 if (!cont) return false; 426 } 427 return true; 428 } 429 430 bool CompatibilityMatrix::matchInstance(const std::string& halName, const Version& version, 431 const std::string& interfaceName, 432 const std::string& instance) const { 433 bool found = false; 434 (void)forEachInstanceOfInterface(halName, version, interfaceName, 435 [&found, &instance](const auto& e) { 436 found |= (e.matchInstance(instance)); 437 return !found; // if not found, continue 438 }); 439 return found; 440 } 441 442 } // namespace vintf 443 } // namespace android 444