AssembleVintf.cpp revision fb9e8b655e67031c156687eeb4e561c976d04015
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 <stdlib.h> 18#include <unistd.h> 19 20#include <fstream> 21#include <iostream> 22#include <sstream> 23#include <string> 24#include <unordered_map> 25 26#include <android-base/file.h> 27#include <android-base/parseint.h> 28#include <android-base/strings.h> 29 30#include <vintf/AssembleVintf.h> 31#include <vintf/KernelConfigParser.h> 32#include <vintf/parse_string.h> 33#include <vintf/parse_xml.h> 34#include "utils.h" 35 36#define BUFFER_SIZE sysconf(_SC_PAGESIZE) 37 38namespace android { 39namespace vintf { 40 41static const std::string gConfigPrefix = "android-base-"; 42static const std::string gConfigSuffix = ".cfg"; 43static const std::string gBaseConfig = "android-base.cfg"; 44 45// An input stream with a name. 46// The input stream may be an actual file, or a stringstream for testing. 47// It takes ownership on the istream. 48class NamedIstream { 49 public: 50 NamedIstream(const std::string& name, std::unique_ptr<std::istream>&& stream) 51 : mName(name), mStream(std::move(stream)) {} 52 const std::string& name() const { return mName; } 53 std::istream& stream() { return *mStream; } 54 55 private: 56 std::string mName; 57 std::unique_ptr<std::istream> mStream; 58}; 59 60/** 61 * Slurps the device manifest file and add build time flag to it. 62 */ 63class AssembleVintfImpl : public AssembleVintf { 64 using Condition = std::unique_ptr<KernelConfig>; 65 using ConditionedConfig = std::pair<Condition, std::vector<KernelConfig> /* configs */>; 66 67 public: 68 void setFakeEnv(const std::string& key, const std::string& value) { mFakeEnv[key] = value; } 69 70 std::string getEnv(const std::string& key) const { 71 auto it = mFakeEnv.find(key); 72 if (it != mFakeEnv.end()) { 73 return it->second; 74 } 75 const char* envValue = getenv(key.c_str()); 76 return envValue != nullptr ? std::string(envValue) : std::string(); 77 } 78 79 // Get environment variable and split with space. 80 std::vector<std::string> getEnvList(const std::string& key) const { 81 std::vector<std::string> ret; 82 for (auto&& v : base::Split(getEnv(key), " ")) { 83 v = base::Trim(v); 84 if (!v.empty()) { 85 ret.push_back(v); 86 } 87 } 88 return ret; 89 } 90 91 template <typename T> 92 bool getFlag(const std::string& key, T* value) const { 93 std::string envValue = getEnv(key); 94 if (envValue.empty()) { 95 std::cerr << "Warning: " << key << " is missing, defaulted to " << (*value) << "." 96 << std::endl; 97 return true; 98 } 99 100 if (!parse(envValue, value)) { 101 std::cerr << "Cannot parse " << envValue << "." << std::endl; 102 return false; 103 } 104 return true; 105 } 106 107 /** 108 * Set *out to environment variable if *out is not a dummy value (i.e. default constructed). 109 */ 110 template <typename T> 111 bool getFlagIfUnset(const std::string& envKey, T* out, bool log = true) const { 112 bool hasExistingValue = !(*out == T{}); 113 114 bool hasEnvValue = false; 115 T envValue; 116 std::string envStrValue = getEnv(envKey); 117 if (!envStrValue.empty()) { 118 if (!parse(envStrValue, &envValue)) { 119 if (log) { 120 std::cerr << "Cannot parse " << envValue << "." << std::endl; 121 } 122 return false; 123 } 124 hasEnvValue = true; 125 } 126 127 if (hasExistingValue) { 128 if (hasEnvValue && log) { 129 std::cerr << "Warning: cannot override existing value " << *out << " with " 130 << envKey << " (which is " << envValue << ")." << std::endl; 131 } 132 return false; 133 } 134 if (!hasEnvValue) { 135 if (log) { 136 std::cerr << "Warning: " << envKey << " is not specified. Default to " << T{} << "." 137 << std::endl; 138 } 139 return false; 140 } 141 *out = envValue; 142 return true; 143 } 144 145 bool getBooleanFlag(const std::string& key) const { return getEnv(key) == std::string("true"); } 146 147 size_t getIntegerFlag(const std::string& key, size_t defaultValue = 0) const { 148 std::string envValue = getEnv(key); 149 if (envValue.empty()) { 150 return defaultValue; 151 } 152 size_t value; 153 if (!base::ParseUint(envValue, &value)) { 154 std::cerr << "Error: " << key << " must be a number." << std::endl; 155 return defaultValue; 156 } 157 return value; 158 } 159 160 static std::string read(std::basic_istream<char>& is) { 161 std::stringstream ss; 162 ss << is.rdbuf(); 163 return ss.str(); 164 } 165 166 static bool isCommonConfig(const std::string& path) { 167 return ::android::base::Basename(path) == gBaseConfig; 168 } 169 170 // nullptr on any error, otherwise the condition. 171 static Condition generateCondition(const std::string& path) { 172 std::string fname = ::android::base::Basename(path); 173 if (fname.size() <= gConfigPrefix.size() + gConfigSuffix.size() || 174 !std::equal(gConfigPrefix.begin(), gConfigPrefix.end(), fname.begin()) || 175 !std::equal(gConfigSuffix.rbegin(), gConfigSuffix.rend(), fname.rbegin())) { 176 return nullptr; 177 } 178 179 std::string sub = fname.substr(gConfigPrefix.size(), 180 fname.size() - gConfigPrefix.size() - gConfigSuffix.size()); 181 if (sub.empty()) { 182 return nullptr; // should not happen 183 } 184 for (size_t i = 0; i < sub.size(); ++i) { 185 if (sub[i] == '-') { 186 sub[i] = '_'; 187 continue; 188 } 189 if (isalnum(sub[i])) { 190 sub[i] = toupper(sub[i]); 191 continue; 192 } 193 std::cerr << "'" << fname << "' (in " << path 194 << ") is not a valid kernel config file name. Must match regex: " 195 << "android-base(-[0-9a-zA-Z-]+)?\\.cfg" << std::endl; 196 return nullptr; 197 } 198 sub.insert(0, "CONFIG_"); 199 return std::make_unique<KernelConfig>(std::move(sub), Tristate::YES); 200 } 201 202 static bool parseFileForKernelConfigs(std::basic_istream<char>& stream, 203 std::vector<KernelConfig>* out) { 204 KernelConfigParser parser(true /* processComments */, true /* relaxedFormat */); 205 std::string content = read(stream); 206 status_t err = parser.process(content.c_str(), content.size()); 207 if (err != OK) { 208 std::cerr << parser.error(); 209 return false; 210 } 211 err = parser.finish(); 212 if (err != OK) { 213 std::cerr << parser.error(); 214 return false; 215 } 216 217 for (auto& configPair : parser.configs()) { 218 out->push_back({}); 219 KernelConfig& config = out->back(); 220 config.first = std::move(configPair.first); 221 if (!parseKernelConfigTypedValue(configPair.second, &config.second)) { 222 std::cerr << "Unknown value type for key = '" << config.first << "', value = '" 223 << configPair.second << "'\n"; 224 return false; 225 } 226 } 227 return true; 228 } 229 230 static bool parseFilesForKernelConfigs(std::vector<NamedIstream>* streams, 231 std::vector<ConditionedConfig>* out) { 232 out->clear(); 233 ConditionedConfig commonConfig; 234 bool foundCommonConfig = false; 235 bool ret = true; 236 237 for (auto& namedStream : *streams) { 238 if (isCommonConfig(namedStream.name())) { 239 ret &= parseFileForKernelConfigs(namedStream.stream(), &commonConfig.second); 240 foundCommonConfig = true; 241 } else { 242 Condition condition = generateCondition(namedStream.name()); 243 ret &= (condition != nullptr); 244 245 std::vector<KernelConfig> kernelConfigs; 246 if ((ret &= parseFileForKernelConfigs(namedStream.stream(), &kernelConfigs))) 247 out->emplace_back(std::move(condition), std::move(kernelConfigs)); 248 } 249 } 250 251 if (!foundCommonConfig) { 252 std::cerr << "No android-base.cfg is found in these paths:" << std::endl; 253 for (auto& namedStream : *streams) { 254 std::cerr << " " << namedStream.name() << std::endl; 255 } 256 } 257 ret &= foundCommonConfig; 258 // first element is always common configs (no conditions). 259 out->insert(out->begin(), std::move(commonConfig)); 260 return ret; 261 } 262 263 std::basic_ostream<char>& out() const { return mOutRef == nullptr ? std::cout : *mOutRef; } 264 265 template <typename S> 266 using Schemas = std::vector<Named<S>>; 267 using HalManifests = Schemas<HalManifest>; 268 using CompatibilityMatrices = Schemas<CompatibilityMatrix>; 269 270 bool assembleHalManifest(HalManifests* halManifests) { 271 std::string error; 272 HalManifest* halManifest = &halManifests->front().object; 273 for (auto it = halManifests->begin() + 1; it != halManifests->end(); ++it) { 274 const std::string& path = it->name; 275 HalManifest& halToAdd = it->object; 276 277 if (halToAdd.level() != Level::UNSPECIFIED) { 278 if (halManifest->level() == Level::UNSPECIFIED) { 279 halManifest->mLevel = halToAdd.level(); 280 } else if (halManifest->level() != halToAdd.level()) { 281 std::cerr << "Inconsistent FCM Version in HAL manifests:" << std::endl 282 << " File '" << halManifests->front().name << "' has level " 283 << halManifest->level() << std::endl 284 << " File '" << path << "' has level " << halToAdd.level() 285 << std::endl; 286 return false; 287 } 288 } 289 290 if (!halManifest->addAllHals(&halToAdd, &error)) { 291 std::cerr << "File \"" << path << "\" cannot be added: conflict on HAL \"" << error 292 << "\" with an existing HAL. See <hal> with the same name " 293 << "in previously parsed files or previously declared in this file." 294 << std::endl; 295 return false; 296 } 297 } 298 299 if (halManifest->mType == SchemaType::DEVICE) { 300 if (!getFlag("BOARD_SEPOLICY_VERS", &halManifest->device.mSepolicyVersion)) { 301 return false; 302 } 303 if (!setDeviceFcmVersion(halManifest)) { 304 return false; 305 } 306 } 307 308 if (halManifest->mType == SchemaType::FRAMEWORK) { 309 for (auto&& v : getEnvList("PROVIDED_VNDK_VERSIONS")) { 310 halManifest->framework.mVendorNdks.emplace_back(std::move(v)); 311 } 312 313 for (auto&& v : getEnvList("PLATFORM_SYSTEMSDK_VERSIONS")) { 314 halManifest->framework.mSystemSdk.mVersions.emplace(std::move(v)); 315 } 316 } 317 318 if (mOutputMatrix) { 319 CompatibilityMatrix generatedMatrix = halManifest->generateCompatibleMatrix(); 320 if (!halManifest->checkCompatibility(generatedMatrix, &error)) { 321 std::cerr << "FATAL ERROR: cannot generate a compatible matrix: " << error 322 << std::endl; 323 } 324 out() << "<!-- \n" 325 " Autogenerated skeleton compatibility matrix. \n" 326 " Use with caution. Modify it to suit your needs.\n" 327 " All HALs are set to optional.\n" 328 " Many entries other than HALs are zero-filled and\n" 329 " require human attention. \n" 330 "-->\n" 331 << gCompatibilityMatrixConverter(generatedMatrix, mSerializeFlags); 332 } else { 333 out() << gHalManifestConverter(*halManifest, mSerializeFlags); 334 } 335 out().flush(); 336 337 if (mCheckFile != nullptr) { 338 CompatibilityMatrix checkMatrix; 339 if (!gCompatibilityMatrixConverter(&checkMatrix, read(*mCheckFile))) { 340 std::cerr << "Cannot parse check file as a compatibility matrix: " 341 << gCompatibilityMatrixConverter.lastError() << std::endl; 342 return false; 343 } 344 if (!halManifest->checkCompatibility(checkMatrix, &error)) { 345 std::cerr << "Not compatible: " << error << std::endl; 346 return false; 347 } 348 } 349 350 return true; 351 } 352 353 bool assembleFrameworkCompatibilityMatrixKernels(CompatibilityMatrix* matrix) { 354 for (auto& pair : mKernels) { 355 std::vector<ConditionedConfig> conditionedConfigs; 356 if (!parseFilesForKernelConfigs(&pair.second, &conditionedConfigs)) { 357 return false; 358 } 359 for (ConditionedConfig& conditionedConfig : conditionedConfigs) { 360 MatrixKernel kernel(KernelVersion{pair.first}, std::move(conditionedConfig.second)); 361 if (conditionedConfig.first != nullptr) 362 kernel.mConditions.push_back(std::move(*conditionedConfig.first)); 363 matrix->framework.mKernels.push_back(std::move(kernel)); 364 } 365 } 366 return true; 367 } 368 369 bool setDeviceFcmVersion(HalManifest* manifest) { 370 size_t shippingApiLevel = getIntegerFlag("PRODUCT_SHIPPING_API_LEVEL"); 371 372 if (manifest->level() != Level::UNSPECIFIED) { 373 return true; 374 } 375 if (!getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST")) { 376 manifest->mLevel = Level::LEGACY; 377 return true; 378 } 379 // TODO(b/70628538): Do not infer from Shipping API level. 380 if (shippingApiLevel) { 381 std::cerr << "Warning: Shipping FCM Version is inferred from Shipping API level. " 382 << "Declare Shipping FCM Version in device manifest directly." << std::endl; 383 manifest->mLevel = details::convertFromApiLevel(shippingApiLevel); 384 if (manifest->mLevel == Level::UNSPECIFIED) { 385 std::cerr << "Error: Shipping FCM Version cannot be inferred from Shipping API " 386 << "level " << shippingApiLevel << "." 387 << "Declare Shipping FCM Version in device manifest directly." 388 << std::endl; 389 return false; 390 } 391 return true; 392 } 393 // TODO(b/69638851): should be an error if Shipping API level is not defined. 394 // For now, just leave it empty; when framework compatibility matrix is built, 395 // lowest FCM Version is assumed. 396 std::cerr << "Warning: Shipping FCM Version cannot be inferred, because:" << std::endl 397 << " (1) It is not explicitly declared in device manifest;" << std::endl 398 << " (2) PRODUCT_ENFORCE_VINTF_MANIFEST is set to true;" << std::endl 399 << " (3) PRODUCT_SHIPPING_API_LEVEL is undefined." << std::endl 400 << "Assuming 'unspecified' Shipping FCM Version. " << std::endl 401 << "To remove this warning, define 'level' attribute in device manifest." 402 << std::endl; 403 return true; 404 } 405 406 Level getLowestFcmVersion(const CompatibilityMatrices& matrices) { 407 Level ret = Level::UNSPECIFIED; 408 for (const auto& e : matrices) { 409 if (ret == Level::UNSPECIFIED || ret > e.object.level()) { 410 ret = e.object.level(); 411 } 412 } 413 return ret; 414 } 415 416 bool assembleCompatibilityMatrix(CompatibilityMatrices* matrices) { 417 std::string error; 418 CompatibilityMatrix* matrix = nullptr; 419 std::unique_ptr<HalManifest> checkManifest; 420 if (matrices->front().object.mType == SchemaType::DEVICE) { 421 matrix = &matrices->front().object; 422 423 auto vndkVersion = base::Trim(getEnv("REQUIRED_VNDK_VERSION")); 424 if (!vndkVersion.empty()) { 425 auto& valueInMatrix = matrix->device.mVendorNdk; 426 if (!valueInMatrix.version().empty() && valueInMatrix.version() != vndkVersion) { 427 std::cerr << "Hard-coded <vendor-ndk> version in device compatibility matrix (" 428 << matrices->front().name << "), '" << valueInMatrix.version() 429 << "', does not match value inferred " 430 << "from BOARD_VNDK_VERSION '" << vndkVersion << "'" << std::endl; 431 return false; 432 } 433 valueInMatrix = VendorNdk{std::move(vndkVersion)}; 434 } 435 436 for (auto&& v : getEnvList("BOARD_SYSTEMSDK_VERSIONS")) { 437 matrix->device.mSystemSdk.mVersions.emplace(std::move(v)); 438 } 439 } 440 441 if (matrices->front().object.mType == SchemaType::FRAMEWORK) { 442 Level deviceLevel = Level::UNSPECIFIED; 443 if (mCheckFile != nullptr) { 444 checkManifest = std::make_unique<HalManifest>(); 445 if (!gHalManifestConverter(checkManifest.get(), read(*mCheckFile))) { 446 std::cerr << "Cannot parse check file as a HAL manifest: " 447 << gHalManifestConverter.lastError() << std::endl; 448 return false; 449 } 450 deviceLevel = checkManifest->level(); 451 } 452 453 if (deviceLevel == Level::UNSPECIFIED) { 454 // For GSI build, legacy devices that do not have a HAL manifest, 455 // and devices in development, merge all compatibility matrices. 456 deviceLevel = getLowestFcmVersion(*matrices); 457 } 458 459 if (deviceLevel == Level::UNSPECIFIED) { 460 // building empty.xml 461 matrix = &matrices->front().object; 462 } else { 463 matrix = CompatibilityMatrix::combine(deviceLevel, matrices, &error); 464 if (matrix == nullptr) { 465 std::cerr << error << std::endl; 466 return false; 467 } 468 } 469 470 if (!assembleFrameworkCompatibilityMatrixKernels(matrix)) { 471 return false; 472 } 473 474 // set sepolicy.sepolicy-version to BOARD_SEPOLICY_VERS when none is specified. 475 std::vector<VersionRange>* sepolicyVrs = 476 &matrix->framework.mSepolicy.mSepolicyVersionRanges; 477 VersionRange sepolicyVr; 478 if (!sepolicyVrs->empty()) sepolicyVr = sepolicyVrs->front(); 479 if (getFlagIfUnset("BOARD_SEPOLICY_VERS", &sepolicyVr, 480 deviceLevel == Level::UNSPECIFIED /* log */)) { 481 *sepolicyVrs = {{sepolicyVr}}; 482 } 483 484 getFlagIfUnset("POLICYVERS", &matrix->framework.mSepolicy.mKernelSepolicyVersion, 485 deviceLevel == Level::UNSPECIFIED /* log */); 486 getFlagIfUnset("FRAMEWORK_VBMETA_VERSION", &matrix->framework.mAvbMetaVersion, 487 deviceLevel == Level::UNSPECIFIED /* log */); 488 489 out() << "<!--" << std::endl; 490 out() << " Input:" << std::endl; 491 for (const auto& e : *matrices) { 492 if (!e.name.empty()) { 493 out() << " " << base::Basename(e.name) << std::endl; 494 } 495 } 496 out() << "-->" << std::endl; 497 } 498 out() << gCompatibilityMatrixConverter(*matrix, mSerializeFlags); 499 out().flush(); 500 501 if (checkManifest != nullptr && getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST") && 502 !checkManifest->checkCompatibility(*matrix, &error)) { 503 std::cerr << "Not compatible: " << error << std::endl; 504 return false; 505 } 506 507 return true; 508 } 509 510 enum AssembleStatus { SUCCESS, FAIL_AND_EXIT, TRY_NEXT }; 511 template <typename Schema, typename AssembleFunc> 512 AssembleStatus tryAssemble(const XmlConverter<Schema>& converter, const std::string& schemaName, 513 AssembleFunc assemble) { 514 Schemas<Schema> schemas; 515 Schema schema; 516 if (!converter(&schema, read(mInFiles.front().stream()))) { 517 return TRY_NEXT; 518 } 519 auto firstType = schema.type(); 520 schemas.emplace_back(mInFiles.front().name(), std::move(schema)); 521 522 for (auto it = mInFiles.begin() + 1; it != mInFiles.end(); ++it) { 523 Schema additionalSchema; 524 const std::string& fileName = it->name(); 525 if (!converter(&additionalSchema, read(it->stream()))) { 526 std::cerr << "File \"" << fileName << "\" is not a valid " << firstType << " " 527 << schemaName << " (but the first file is a valid " << firstType << " " 528 << schemaName << "). Error: " << converter.lastError() << std::endl; 529 return FAIL_AND_EXIT; 530 } 531 if (additionalSchema.type() != firstType) { 532 std::cerr << "File \"" << fileName << "\" is a " << additionalSchema.type() << " " 533 << schemaName << " (but a " << firstType << " " << schemaName 534 << " is expected)." << std::endl; 535 return FAIL_AND_EXIT; 536 } 537 538 schemas.emplace_back(fileName, std::move(additionalSchema)); 539 } 540 return assemble(&schemas) ? SUCCESS : FAIL_AND_EXIT; 541 } 542 543 bool assemble() override { 544 using std::placeholders::_1; 545 if (mInFiles.empty()) { 546 std::cerr << "Missing input file." << std::endl; 547 return false; 548 } 549 550 auto status = tryAssemble(gHalManifestConverter, "manifest", 551 std::bind(&AssembleVintfImpl::assembleHalManifest, this, _1)); 552 if (status == SUCCESS) return true; 553 if (status == FAIL_AND_EXIT) return false; 554 555 resetInFiles(); 556 557 status = tryAssemble(gCompatibilityMatrixConverter, "compatibility matrix", 558 std::bind(&AssembleVintfImpl::assembleCompatibilityMatrix, this, _1)); 559 if (status == SUCCESS) return true; 560 if (status == FAIL_AND_EXIT) return false; 561 562 std::cerr << "Input file has unknown format." << std::endl 563 << "Error when attempting to convert to manifest: " 564 << gHalManifestConverter.lastError() << std::endl 565 << "Error when attempting to convert to compatibility matrix: " 566 << gCompatibilityMatrixConverter.lastError() << std::endl; 567 return false; 568 } 569 570 std::ostream& setOutputStream(Ostream&& out) override { 571 mOutRef = std::move(out); 572 return *mOutRef; 573 } 574 575 std::istream& addInputStream(const std::string& name, Istream&& in) override { 576 auto it = mInFiles.emplace(mInFiles.end(), name, std::move(in)); 577 return it->stream(); 578 } 579 580 std::istream& setCheckInputStream(Istream&& in) override { 581 mCheckFile = std::move(in); 582 return *mCheckFile; 583 } 584 585 bool hasKernelVersion(const KernelVersion& kernelVer) const override { 586 return mKernels.find(kernelVer) != mKernels.end(); 587 } 588 589 std::istream& addKernelConfigInputStream(const KernelVersion& kernelVer, 590 const std::string& name, Istream&& in) override { 591 auto&& kernel = mKernels[kernelVer]; 592 auto it = kernel.emplace(kernel.end(), name, std::move(in)); 593 return it->stream(); 594 } 595 596 void resetInFiles() { 597 for (auto& inFile : mInFiles) { 598 inFile.stream().clear(); 599 inFile.stream().seekg(0); 600 } 601 } 602 603 void setOutputMatrix() override { mOutputMatrix = true; } 604 605 bool setHalsOnly() override { 606 if (mSerializeFlags) return false; 607 mSerializeFlags |= SerializeFlag::HALS_ONLY; 608 return true; 609 } 610 611 bool setNoHals() override { 612 if (mSerializeFlags) return false; 613 mSerializeFlags |= SerializeFlag::NO_HALS; 614 return true; 615 } 616 617 private: 618 std::vector<NamedIstream> mInFiles; 619 Ostream mOutRef; 620 Istream mCheckFile; 621 bool mOutputMatrix = false; 622 SerializeFlags mSerializeFlags = SerializeFlag::EVERYTHING; 623 std::map<KernelVersion, std::vector<NamedIstream>> mKernels; 624 std::map<std::string, std::string> mFakeEnv; 625}; 626 627bool AssembleVintf::openOutFile(const std::string& path) { 628 return static_cast<std::ofstream&>(setOutputStream(std::make_unique<std::ofstream>(path))) 629 .is_open(); 630} 631 632bool AssembleVintf::openInFile(const std::string& path) { 633 return static_cast<std::ifstream&>(addInputStream(path, std::make_unique<std::ifstream>(path))) 634 .is_open(); 635} 636 637bool AssembleVintf::openCheckFile(const std::string& path) { 638 return static_cast<std::ifstream&>(setCheckInputStream(std::make_unique<std::ifstream>(path))) 639 .is_open(); 640} 641 642bool AssembleVintf::addKernel(const std::string& kernelArg) { 643 auto tokens = base::Split(kernelArg, ":"); 644 if (tokens.size() <= 1) { 645 std::cerr << "Unrecognized --kernel option '" << kernelArg << "'" << std::endl; 646 return false; 647 } 648 KernelVersion kernelVer; 649 if (!parse(tokens.front(), &kernelVer)) { 650 std::cerr << "Unrecognized kernel version '" << tokens.front() << "'" << std::endl; 651 return false; 652 } 653 if (hasKernelVersion(kernelVer)) { 654 std::cerr << "Multiple --kernel for " << kernelVer << " is specified." << std::endl; 655 return false; 656 } 657 for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) { 658 bool opened = 659 static_cast<std::ifstream&>( 660 addKernelConfigInputStream(kernelVer, *it, std::make_unique<std::ifstream>(*it))) 661 .is_open(); 662 if (!opened) { 663 std::cerr << "Cannot open file '" << *it << "'." << std::endl; 664 return false; 665 } 666 } 667 return true; 668} 669 670std::unique_ptr<AssembleVintf> AssembleVintf::newInstance() { 671 return std::make_unique<AssembleVintfImpl>(); 672} 673 674} // namespace vintf 675} // namespace android 676