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 #include <libvts_vintf_test_common/common.h>
30 #include <vintf/AssembleVintf.h>
31 #include <vintf/KernelConfigParser.h>
32 #include <vintf/parse_string.h>
33 #include <vintf/parse_xml.h>
34 #include "constants-private.h"
35 #include "utils.h"
36
37 #define BUFFER_SIZE sysconf(_SC_PAGESIZE)
38
39 namespace android {
40 namespace vintf {
41
42 static const std::string gConfigPrefix = "android-base-";
43 static const std::string gConfigSuffix = ".config";
44 static const std::string gBaseConfig = "android-base.config";
45
46 // An input stream with a name.
47 // The input stream may be an actual file, or a stringstream for testing.
48 // It takes ownership on the istream.
49 class NamedIstream {
50 public:
51 NamedIstream() = default;
NamedIstream(const std::string & name,std::unique_ptr<std::istream> && stream)52 NamedIstream(const std::string& name, std::unique_ptr<std::istream>&& stream)
53 : mName(name), mStream(std::move(stream)) {}
name() const54 const std::string& name() const { return mName; }
stream()55 std::istream& stream() { return *mStream; }
hasStream()56 bool hasStream() { return mStream != nullptr; }
57
58 private:
59 std::string mName;
60 std::unique_ptr<std::istream> mStream;
61 };
62
63 /**
64 * Slurps the device manifest file and add build time flag to it.
65 */
66 class AssembleVintfImpl : public AssembleVintf {
67 using Condition = std::unique_ptr<KernelConfig>;
68 using ConditionedConfig = std::pair<Condition, std::vector<KernelConfig> /* configs */>;
69
70 public:
setFakeEnv(const std::string & key,const std::string & value)71 void setFakeEnv(const std::string& key, const std::string& value) { mFakeEnv[key] = value; }
72
getEnv(const std::string & key) const73 std::string getEnv(const std::string& key) const {
74 auto it = mFakeEnv.find(key);
75 if (it != mFakeEnv.end()) {
76 return it->second;
77 }
78 const char* envValue = getenv(key.c_str());
79 return envValue != nullptr ? std::string(envValue) : std::string();
80 }
81
82 // Get environment variable and split with space.
getEnvList(const std::string & key) const83 std::vector<std::string> getEnvList(const std::string& key) const {
84 std::vector<std::string> ret;
85 for (auto&& v : base::Split(getEnv(key), " ")) {
86 v = base::Trim(v);
87 if (!v.empty()) {
88 ret.push_back(v);
89 }
90 }
91 return ret;
92 }
93
94 template <typename T>
getFlag(const std::string & key,T * value,bool log=true) const95 bool getFlag(const std::string& key, T* value, bool log = true) const {
96 std::string envValue = getEnv(key);
97 if (envValue.empty()) {
98 if (log) {
99 err() << "Warning: " << key << " is missing, defaulted to " << (*value) << "."
100 << std::endl;
101 }
102 return true;
103 }
104
105 if (!parse(envValue, value)) {
106 err() << "Cannot parse " << envValue << "." << std::endl;
107 return false;
108 }
109 return true;
110 }
111
112 /**
113 * Set *out to environment variable only if *out is default constructed.
114 * Return false if a fatal error has occurred:
115 * - The environment variable has an unknown format
116 * - The value of the environment variable does not match a predefined variable in the files
117 */
118 template <typename T>
getFlagIfUnset(const std::string & envKey,T * out) const119 bool getFlagIfUnset(const std::string& envKey, T* out) const {
120 bool hasExistingValue = !(*out == T{});
121
122 bool hasEnvValue = false;
123 T envValue;
124 std::string envStrValue = getEnv(envKey);
125 if (!envStrValue.empty()) {
126 if (!parse(envStrValue, &envValue)) {
127 err() << "Cannot parse " << envValue << "." << std::endl;
128 return false;
129 }
130 hasEnvValue = true;
131 }
132
133 if (hasExistingValue) {
134 if (hasEnvValue && (*out != envValue)) {
135 err() << "Cannot override existing value " << *out << " with " << envKey
136 << " (which is " << envValue << ")." << std::endl;
137 return false;
138 }
139 return true;
140 }
141 if (hasEnvValue) {
142 *out = envValue;
143 }
144 return true;
145 }
146
getBooleanFlag(const std::string & key) const147 bool getBooleanFlag(const std::string& key) const { return getEnv(key) == std::string("true"); }
148
getIntegerFlag(const std::string & key,size_t defaultValue=0) const149 size_t getIntegerFlag(const std::string& key, size_t defaultValue = 0) const {
150 std::string envValue = getEnv(key);
151 if (envValue.empty()) {
152 return defaultValue;
153 }
154 size_t value;
155 if (!base::ParseUint(envValue, &value)) {
156 err() << "Error: " << key << " must be a number." << std::endl;
157 return defaultValue;
158 }
159 return value;
160 }
161
read(std::basic_istream<char> & is)162 static std::string read(std::basic_istream<char>& is) {
163 std::stringstream ss;
164 ss << is.rdbuf();
165 return ss.str();
166 }
167
168 // Return true if name of file is "android-base.config". This file must be specified
169 // exactly once for each kernel version. These requirements do not have any conditions.
isCommonConfig(const std::string & path)170 static bool isCommonConfig(const std::string& path) {
171 return ::android::base::Basename(path) == gBaseConfig;
172 }
173
174 // Return true if name of file matches "android-base-foo.config".
175 // Zero or more conditional configs may be specified for each kernel version. These
176 // requirements are conditional on CONFIG_FOO=y.
isConditionalConfig(const std::string & path)177 static bool isConditionalConfig(const std::string& path) {
178 auto fname = ::android::base::Basename(path);
179 return ::android::base::StartsWith(fname, gConfigPrefix) &&
180 ::android::base::EndsWith(fname, gConfigSuffix);
181 }
182
183 // Return true for all other file names (i.e. not android-base.config, and not conditional
184 // configs.)
185 // Zero or more conditional configs may be specified for each kernel version.
186 // These requirements do not have any conditions.
isExtraCommonConfig(const std::string & path)187 static bool isExtraCommonConfig(const std::string& path) {
188 return !isCommonConfig(path) && !isConditionalConfig(path);
189 }
190
191 // nullptr on any error, otherwise the condition.
generateCondition(const std::string & path)192 Condition generateCondition(const std::string& path) {
193 if (!isConditionalConfig(path)) {
194 return nullptr;
195 }
196 auto fname = ::android::base::Basename(path);
197 std::string sub = fname.substr(gConfigPrefix.size(),
198 fname.size() - gConfigPrefix.size() - gConfigSuffix.size());
199 if (sub.empty()) {
200 return nullptr; // should not happen
201 }
202 for (size_t i = 0; i < sub.size(); ++i) {
203 if (sub[i] == '-') {
204 sub[i] = '_';
205 continue;
206 }
207 if (isalnum(sub[i])) {
208 sub[i] = toupper(sub[i]);
209 continue;
210 }
211 err() << "'" << fname << "' (in " << path
212 << ") is not a valid kernel config file name. Must match regex: "
213 << "android-base(-[0-9a-zA-Z-]+)?\\" << gConfigSuffix << std::endl;
214 return nullptr;
215 }
216 sub.insert(0, "CONFIG_");
217 return std::make_unique<KernelConfig>(std::move(sub), Tristate::YES);
218 }
219
parseFileForKernelConfigs(std::basic_istream<char> & stream,std::vector<KernelConfig> * out)220 bool parseFileForKernelConfigs(std::basic_istream<char>& stream,
221 std::vector<KernelConfig>* out) {
222 KernelConfigParser parser(true /* processComments */, true /* relaxedFormat */);
223 status_t status = parser.processAndFinish(read(stream));
224 if (status != OK) {
225 err() << parser.error();
226 return false;
227 }
228
229 for (auto& configPair : parser.configs()) {
230 out->push_back({});
231 KernelConfig& config = out->back();
232 config.first = std::move(configPair.first);
233 if (!parseKernelConfigTypedValue(configPair.second, &config.second)) {
234 err() << "Unknown value type for key = '" << config.first << "', value = '"
235 << configPair.second << "'\n";
236 return false;
237 }
238 }
239 return true;
240 }
241
parseFilesForKernelConfigs(std::vector<NamedIstream> * streams,std::vector<ConditionedConfig> * out)242 bool parseFilesForKernelConfigs(std::vector<NamedIstream>* streams,
243 std::vector<ConditionedConfig>* out) {
244 out->clear();
245 ConditionedConfig commonConfig;
246 bool foundCommonConfig = false;
247 bool ret = true;
248
249 for (auto& namedStream : *streams) {
250 if (isCommonConfig(namedStream.name()) || isExtraCommonConfig(namedStream.name())) {
251 if (!parseFileForKernelConfigs(namedStream.stream(), &commonConfig.second)) {
252 err() << "Failed to generate common configs for file " << namedStream.name();
253 ret = false;
254 }
255 if (isCommonConfig(namedStream.name())) {
256 foundCommonConfig = true;
257 }
258 } else {
259 Condition condition = generateCondition(namedStream.name());
260 if (condition == nullptr) {
261 err() << "Failed to generate conditional configs for file "
262 << namedStream.name();
263 ret = false;
264 }
265
266 std::vector<KernelConfig> kernelConfigs;
267 if ((ret &= parseFileForKernelConfigs(namedStream.stream(), &kernelConfigs)))
268 out->emplace_back(std::move(condition), std::move(kernelConfigs));
269 }
270 }
271
272 if (!foundCommonConfig) {
273 err() << "No " << gBaseConfig << " is found in these paths:" << std::endl;
274 for (auto& namedStream : *streams) {
275 err() << " " << namedStream.name() << std::endl;
276 }
277 ret = false;
278 }
279 // first element is always common configs (no conditions).
280 out->insert(out->begin(), std::move(commonConfig));
281 return ret;
282 }
283
out() const284 std::basic_ostream<char>& out() const { return mOutRef == nullptr ? std::cout : *mOutRef; }
err() const285 std::basic_ostream<char>& err() const override {
286 return mErrRef == nullptr ? std::cerr : *mErrRef;
287 }
288
289 // If -c is provided, check it.
checkDualFile(const HalManifest & manifest,const CompatibilityMatrix & matrix)290 bool checkDualFile(const HalManifest& manifest, const CompatibilityMatrix& matrix) {
291 if (getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST")) {
292 std::string error;
293 if (!manifest.checkCompatibility(matrix, &error, mCheckFlags)) {
294 err() << "Not compatible: " << error << std::endl;
295 return false;
296 }
297 }
298 return true;
299 }
300
301 using HalManifests = std::vector<HalManifest>;
302 using CompatibilityMatrices = std::vector<CompatibilityMatrix>;
303
304 template <typename M>
outputInputs(const std::vector<M> & inputs)305 void outputInputs(const std::vector<M>& inputs) {
306 out() << "<!--" << std::endl;
307 out() << " Input:" << std::endl;
308 for (const auto& e : inputs) {
309 if (!e.fileName().empty()) {
310 out() << " " << e.fileName() << std::endl;
311 }
312 }
313 out() << "-->" << std::endl;
314 }
315
316 // Parse --kernel arguments and write to output manifest.
setDeviceManifestKernel(HalManifest * manifest)317 bool setDeviceManifestKernel(HalManifest* manifest) {
318 if (mKernels.empty()) {
319 return true;
320 }
321 if (mKernels.size() > 1) {
322 err() << "Warning: multiple --kernel is specified when building device manifest. "
323 << "Only the first one will be used." << std::endl;
324 }
325 auto& kernelArg = *mKernels.begin();
326 const auto& kernelVer = kernelArg.first;
327 auto& kernelConfigFiles = kernelArg.second;
328 // addKernel() guarantees that !kernelConfigFiles.empty().
329 if (kernelConfigFiles.size() > 1) {
330 err() << "Warning: multiple config files are specified in --kernel when building "
331 << "device manfiest. Only the first one will be used." << std::endl;
332 }
333
334 KernelConfigParser parser(true /* processComments */, false /* relaxedFormat */);
335 status_t status = parser.processAndFinish(read(kernelConfigFiles[0].stream()));
336 if (status != OK) {
337 err() << parser.error();
338 return false;
339 }
340
341 // Set version and configs in manifest.
342 auto kernel_info = std::make_optional<KernelInfo>();
343 kernel_info->mVersion = kernelVer;
344 kernel_info->mConfigs = parser.configs();
345 std::string error;
346 if (!manifest->mergeKernel(&kernel_info, &error)) {
347 err() << error << "\n";
348 return false;
349 }
350 return true;
351 }
352
checkDeviceManifestNoKernelLevel(const HalManifest & manifest)353 bool checkDeviceManifestNoKernelLevel(const HalManifest& manifest) {
354 if (manifest.level() != Level::UNSPECIFIED &&
355 manifest.level() >= details::kEnforceDeviceManifestNoKernelLevel &&
356 // Use manifest.kernel()->level() directly because inferredKernelLevel()
357 // reads manifest.level().
358 manifest.kernel().has_value() && manifest.kernel()->level() != Level::UNSPECIFIED) {
359 err() << "Error: Device manifest with level " << manifest.level()
360 << " must not set kernel level " << manifest.kernel()->level() << std::endl;
361 return false;
362 }
363 return true;
364 }
365
assembleHalManifest(HalManifests * halManifests)366 bool assembleHalManifest(HalManifests* halManifests) {
367 std::string error;
368 HalManifest* halManifest = &halManifests->front();
369 HalManifest* manifestWithLevel = nullptr;
370 if (halManifest->level() != Level::UNSPECIFIED) {
371 manifestWithLevel = halManifest;
372 }
373
374 for (auto it = halManifests->begin() + 1; it != halManifests->end(); ++it) {
375 const std::string& path = it->fileName();
376 HalManifest& manifestToAdd = *it;
377
378 if (manifestToAdd.level() != Level::UNSPECIFIED) {
379 if (halManifest->level() == Level::UNSPECIFIED) {
380 halManifest->mLevel = manifestToAdd.level();
381 manifestWithLevel = &manifestToAdd;
382 } else if (halManifest->level() != manifestToAdd.level()) {
383 err() << "Inconsistent FCM Version in HAL manifests:" << std::endl
384 << " File '"
385 << (manifestWithLevel ? manifestWithLevel->fileName() : "<unknown>")
386 << "' has level " << halManifest->level() << std::endl
387 << " File '" << path << "' has level " << manifestToAdd.level()
388 << std::endl;
389 return false;
390 }
391 }
392
393 if (!halManifest->addAll(&manifestToAdd, &error)) {
394 err() << "File \"" << path << "\" cannot be added: " << error << std::endl;
395 return false;
396 }
397 }
398
399 if (halManifest->mType == SchemaType::DEVICE) {
400 if (!getFlagIfUnset("BOARD_SEPOLICY_VERS", &halManifest->device.mSepolicyVersion)) {
401 return false;
402 }
403
404 if (!setDeviceFcmVersion(halManifest)) {
405 return false;
406 }
407
408 if (!setDeviceManifestKernel(halManifest)) {
409 return false;
410 }
411
412 if (!checkDeviceManifestNoKernelLevel(*halManifest)) {
413 return false;
414 }
415 }
416
417 if (halManifest->mType == SchemaType::FRAMEWORK) {
418 for (auto&& v : getEnvList("PROVIDED_VNDK_VERSIONS")) {
419 halManifest->framework.mVendorNdks.emplace_back(std::move(v));
420 }
421
422 for (auto&& v : getEnvList("PLATFORM_SYSTEMSDK_VERSIONS")) {
423 halManifest->framework.mSystemSdk.mVersions.emplace(std::move(v));
424 }
425 }
426
427 outputInputs(*halManifests);
428
429 if (mOutputMatrix) {
430 CompatibilityMatrix generatedMatrix = halManifest->generateCompatibleMatrix();
431 if (!halManifest->checkCompatibility(generatedMatrix, &error, mCheckFlags)) {
432 err() << "FATAL ERROR: cannot generate a compatible matrix: " << error << std::endl;
433 }
434 out() << "<!-- \n"
435 " Autogenerated skeleton compatibility matrix. \n"
436 " Use with caution. Modify it to suit your needs.\n"
437 " All HALs are set to optional.\n"
438 " Many entries other than HALs are zero-filled and\n"
439 " require human attention. \n"
440 "-->\n"
441 << toXml(generatedMatrix, mSerializeFlags);
442 } else {
443 out() << toXml(*halManifest, mSerializeFlags);
444 }
445 out().flush();
446
447 if (mCheckFile.hasStream()) {
448 CompatibilityMatrix checkMatrix;
449 checkMatrix.setFileName(mCheckFile.name());
450 if (!fromXml(&checkMatrix, read(mCheckFile.stream()), &error)) {
451 err() << "Cannot parse check file as a compatibility matrix: " << error
452 << std::endl;
453 return false;
454 }
455 if (!checkDualFile(*halManifest, checkMatrix)) {
456 return false;
457 }
458 }
459
460 return true;
461 }
462
463 // Parse --kernel arguments and write to output matrix.
assembleFrameworkCompatibilityMatrixKernels(CompatibilityMatrix * matrix)464 bool assembleFrameworkCompatibilityMatrixKernels(CompatibilityMatrix* matrix) {
465 for (auto& pair : mKernels) {
466 std::vector<ConditionedConfig> conditionedConfigs;
467 if (!parseFilesForKernelConfigs(&pair.second, &conditionedConfigs)) {
468 return false;
469 }
470 for (ConditionedConfig& conditionedConfig : conditionedConfigs) {
471 MatrixKernel kernel(KernelVersion{pair.first}, std::move(conditionedConfig.second));
472 if (conditionedConfig.first != nullptr)
473 kernel.mConditions.push_back(std::move(*conditionedConfig.first));
474 std::string error;
475 if (!matrix->addKernel(std::move(kernel), &error)) {
476 err() << "Error:" << error << std::endl;
477 return false;
478 };
479 }
480 }
481 return true;
482 }
483
setDeviceFcmVersion(HalManifest * manifest)484 bool setDeviceFcmVersion(HalManifest* manifest) {
485 // Not needed for generating empty manifest for DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE.
486 if (getBooleanFlag("VINTF_IGNORE_TARGET_FCM_VERSION")) {
487 return true;
488 }
489
490 size_t shippingApiLevel = getIntegerFlag("PRODUCT_SHIPPING_API_LEVEL");
491
492 if (manifest->level() != Level::UNSPECIFIED) {
493 if (shippingApiLevel != 0) {
494 auto res = android::vintf::testing::TestTargetFcmVersion(manifest->level(),
495 shippingApiLevel);
496 if (!res.ok()) err() << "Warning: " << res.error() << std::endl;
497 }
498 return true;
499 }
500 if (!getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST")) {
501 manifest->mLevel = Level::LEGACY;
502 return true;
503 }
504 // TODO(b/70628538): Do not infer from Shipping API level.
505 if (shippingApiLevel) {
506 err() << "Warning: Shipping FCM Version is inferred from Shipping API level. "
507 << "Declare Shipping FCM Version in device manifest directly." << std::endl;
508 manifest->mLevel = details::convertFromApiLevel(shippingApiLevel);
509 if (manifest->mLevel == Level::UNSPECIFIED) {
510 err() << "Error: Shipping FCM Version cannot be inferred from Shipping API "
511 << "level " << shippingApiLevel << "."
512 << "Declare Shipping FCM Version in device manifest directly." << std::endl;
513 return false;
514 }
515 return true;
516 }
517 // TODO(b/69638851): should be an error if Shipping API level is not defined.
518 // For now, just leave it empty; when framework compatibility matrix is built,
519 // lowest FCM Version is assumed.
520 err() << "Warning: Shipping FCM Version cannot be inferred, because:" << std::endl
521 << " (1) It is not explicitly declared in device manifest;" << std::endl
522 << " (2) PRODUCT_ENFORCE_VINTF_MANIFEST is set to true;" << std::endl
523 << " (3) PRODUCT_SHIPPING_API_LEVEL is undefined." << std::endl
524 << "Assuming 'unspecified' Shipping FCM Version. " << std::endl
525 << "To remove this warning, define 'level' attribute in device manifest."
526 << std::endl;
527 return true;
528 }
529
getLowestFcmVersion(const CompatibilityMatrices & matrices)530 Level getLowestFcmVersion(const CompatibilityMatrices& matrices) {
531 Level ret = Level::UNSPECIFIED;
532 for (const auto& e : matrices) {
533 if (ret == Level::UNSPECIFIED || ret > e.level()) {
534 ret = e.level();
535 }
536 }
537 return ret;
538 }
539
assembleCompatibilityMatrix(CompatibilityMatrices * matrices)540 bool assembleCompatibilityMatrix(CompatibilityMatrices* matrices) {
541 std::string error;
542 CompatibilityMatrix* matrix = nullptr;
543 std::unique_ptr<HalManifest> checkManifest;
544 std::unique_ptr<CompatibilityMatrix> builtMatrix;
545
546 if (mCheckFile.hasStream()) {
547 checkManifest = std::make_unique<HalManifest>();
548 checkManifest->setFileName(mCheckFile.name());
549 if (!fromXml(checkManifest.get(), read(mCheckFile.stream()), &error)) {
550 err() << "Cannot parse check file as a HAL manifest: " << error << std::endl;
551 return false;
552 }
553 }
554
555 if (matrices->front().mType == SchemaType::DEVICE) {
556 builtMatrix = CompatibilityMatrix::combineDeviceMatrices(matrices, &error);
557 matrix = builtMatrix.get();
558
559 if (matrix == nullptr) {
560 err() << error << std::endl;
561 return false;
562 }
563
564 auto vndkVersion = base::Trim(getEnv("REQUIRED_VNDK_VERSION"));
565 if (!vndkVersion.empty()) {
566 auto& valueInMatrix = matrix->device.mVendorNdk;
567 if (!valueInMatrix.version().empty() && valueInMatrix.version() != vndkVersion) {
568 err() << "Hard-coded <vendor-ndk> version in device compatibility matrix ("
569 << matrices->front().fileName() << "), '" << valueInMatrix.version()
570 << "', does not match value inferred "
571 << "from BOARD_VNDK_VERSION '" << vndkVersion << "'" << std::endl;
572 return false;
573 }
574 valueInMatrix = VendorNdk{std::move(vndkVersion)};
575 }
576
577 for (auto&& v : getEnvList("BOARD_SYSTEMSDK_VERSIONS")) {
578 matrix->device.mSystemSdk.mVersions.emplace(std::move(v));
579 }
580 }
581
582 if (matrices->front().mType == SchemaType::FRAMEWORK) {
583 Level deviceLevel =
584 checkManifest != nullptr ? checkManifest->level() : Level::UNSPECIFIED;
585 if (deviceLevel == Level::UNSPECIFIED) {
586 deviceLevel = getLowestFcmVersion(*matrices);
587 if (checkManifest != nullptr && deviceLevel != Level::UNSPECIFIED) {
588 err() << "Warning: No Target FCM Version for device. Assuming \""
589 << to_string(deviceLevel)
590 << "\" when building final framework compatibility matrix." << std::endl;
591 }
592 }
593 // No <kernel> tags to assemble at this point
594 const auto kernelLevel = Level::UNSPECIFIED;
595 builtMatrix = CompatibilityMatrix::combine(deviceLevel, kernelLevel, matrices, &error);
596 matrix = builtMatrix.get();
597
598 if (matrix == nullptr) {
599 err() << error << std::endl;
600 return false;
601 }
602
603 if (!assembleFrameworkCompatibilityMatrixKernels(matrix)) {
604 return false;
605 }
606
607 // Add PLATFORM_SEPOLICY_* to sepolicy.sepolicy-version. Remove dupes.
608 std::set<Version> sepolicyVersions;
609 auto sepolicyVersionStrings = getEnvList("PLATFORM_SEPOLICY_COMPAT_VERSIONS");
610 auto currentSepolicyVersionString = getEnv("PLATFORM_SEPOLICY_VERSION");
611 if (!currentSepolicyVersionString.empty()) {
612 sepolicyVersionStrings.push_back(currentSepolicyVersionString);
613 }
614 for (auto&& s : sepolicyVersionStrings) {
615 Version v;
616 if (!parse(s, &v)) {
617 err() << "Error: unknown sepolicy version '" << s << "' specified by "
618 << (s == currentSepolicyVersionString
619 ? "PLATFORM_SEPOLICY_VERSION"
620 : "PLATFORM_SEPOLICY_COMPAT_VERSIONS")
621 << ".";
622 return false;
623 }
624 sepolicyVersions.insert(v);
625 }
626 for (auto&& v : sepolicyVersions) {
627 matrix->framework.mSepolicy.mSepolicyVersionRanges.emplace_back(v.majorVer,
628 v.minorVer);
629 }
630
631 if (!getFlagIfUnset("POLICYVERS",
632 &matrix->framework.mSepolicy.mKernelSepolicyVersion)) {
633 return false;
634 }
635 if (!getFlagIfUnset("FRAMEWORK_VBMETA_VERSION", &matrix->framework.mAvbMetaVersion)) {
636 return false;
637 }
638 // Hard-override existing AVB version
639 getFlag("FRAMEWORK_VBMETA_VERSION_OVERRIDE", &matrix->framework.mAvbMetaVersion,
640 false /* log */);
641 }
642 outputInputs(*matrices);
643 out() << toXml(*matrix, mSerializeFlags);
644 out().flush();
645
646 if (checkManifest != nullptr && !checkDualFile(*checkManifest, *matrix)) {
647 return false;
648 }
649
650 return true;
651 }
652
653 enum AssembleStatus { SUCCESS, FAIL_AND_EXIT, TRY_NEXT };
654 template <typename Schema, typename AssembleFunc>
tryAssemble(const std::string & schemaName,AssembleFunc assemble,std::string * error)655 AssembleStatus tryAssemble(const std::string& schemaName, AssembleFunc assemble,
656 std::string* error) {
657 std::vector<Schema> schemas;
658 Schema schema;
659 schema.setFileName(mInFiles.front().name());
660 if (!fromXml(&schema, read(mInFiles.front().stream()), error)) {
661 return TRY_NEXT;
662 }
663 auto firstType = schema.type();
664 schemas.emplace_back(std::move(schema));
665
666 for (auto it = mInFiles.begin() + 1; it != mInFiles.end(); ++it) {
667 Schema additionalSchema;
668 const std::string& fileName = it->name();
669 additionalSchema.setFileName(fileName);
670 if (!fromXml(&additionalSchema, read(it->stream()), error)) {
671 err() << "File \"" << fileName << "\" is not a valid " << firstType << " "
672 << schemaName << " (but the first file is a valid " << firstType << " "
673 << schemaName << "). Error: " << *error << std::endl;
674 return FAIL_AND_EXIT;
675 }
676 if (additionalSchema.type() != firstType) {
677 err() << "File \"" << fileName << "\" is a " << additionalSchema.type() << " "
678 << schemaName << " (but a " << firstType << " " << schemaName
679 << " is expected)." << std::endl;
680 return FAIL_AND_EXIT;
681 }
682
683 schemas.emplace_back(std::move(additionalSchema));
684 }
685 return assemble(&schemas) ? SUCCESS : FAIL_AND_EXIT;
686 }
687
assemble()688 bool assemble() override {
689 using std::placeholders::_1;
690 if (mInFiles.empty()) {
691 err() << "Missing input file." << std::endl;
692 return false;
693 }
694
695 std::string manifestError;
696 auto status = tryAssemble<HalManifest>(
697 "manifest", std::bind(&AssembleVintfImpl::assembleHalManifest, this, _1),
698 &manifestError);
699 if (status == SUCCESS) return true;
700 if (status == FAIL_AND_EXIT) return false;
701
702 resetInFiles();
703
704 std::string matrixError;
705 status = tryAssemble<CompatibilityMatrix>(
706 "compatibility matrix",
707 std::bind(&AssembleVintfImpl::assembleCompatibilityMatrix, this, _1), &matrixError);
708 if (status == SUCCESS) return true;
709 if (status == FAIL_AND_EXIT) return false;
710
711 err() << "Input file has unknown format." << std::endl
712 << "Error when attempting to convert to manifest: " << manifestError << std::endl
713 << "Error when attempting to convert to compatibility matrix: " << matrixError
714 << std::endl;
715 return false;
716 }
717
setOutputStream(Ostream && out)718 std::ostream& setOutputStream(Ostream&& out) override {
719 mOutRef = std::move(out);
720 return *mOutRef;
721 }
722
setErrorStream(Ostream && err)723 std::ostream& setErrorStream(Ostream&& err) override {
724 mErrRef = std::move(err);
725 return *mErrRef;
726 }
727
addInputStream(const std::string & name,Istream && in)728 std::istream& addInputStream(const std::string& name, Istream&& in) override {
729 auto it = mInFiles.emplace(mInFiles.end(), name, std::move(in));
730 return it->stream();
731 }
732
setCheckInputStream(const std::string & name,Istream && in)733 std::istream& setCheckInputStream(const std::string& name, Istream&& in) override {
734 mCheckFile = NamedIstream(name, std::move(in));
735 return mCheckFile.stream();
736 }
737
hasKernelVersion(const KernelVersion & kernelVer) const738 bool hasKernelVersion(const KernelVersion& kernelVer) const override {
739 return mKernels.find(kernelVer) != mKernels.end();
740 }
741
addKernelConfigInputStream(const KernelVersion & kernelVer,const std::string & name,Istream && in)742 std::istream& addKernelConfigInputStream(const KernelVersion& kernelVer,
743 const std::string& name, Istream&& in) override {
744 auto&& kernel = mKernels[kernelVer];
745 auto it = kernel.emplace(kernel.end(), name, std::move(in));
746 return it->stream();
747 }
748
resetInFiles()749 void resetInFiles() {
750 for (auto& inFile : mInFiles) {
751 inFile.stream().clear();
752 inFile.stream().seekg(0);
753 }
754 }
755
setOutputMatrix()756 void setOutputMatrix() override { mOutputMatrix = true; }
757
setHalsOnly()758 bool setHalsOnly() override {
759 if (mHasSetHalsOnlyFlag) {
760 err() << "Error: Cannot set --hals-only with --no-hals." << std::endl;
761 return false;
762 }
763 // Just override it with HALS_ONLY because other flags that modify mSerializeFlags
764 // does not interfere with this (except --no-hals).
765 mSerializeFlags = SerializeFlags::HALS_ONLY;
766 mHasSetHalsOnlyFlag = true;
767 return true;
768 }
769
setNoHals()770 bool setNoHals() override {
771 if (mHasSetHalsOnlyFlag) {
772 err() << "Error: Cannot set --hals-only with --no-hals." << std::endl;
773 return false;
774 }
775 mSerializeFlags = mSerializeFlags.disableHals();
776 mHasSetHalsOnlyFlag = true;
777 return true;
778 }
779
setNoKernelRequirements()780 bool setNoKernelRequirements() override {
781 mSerializeFlags = mSerializeFlags.disableKernelConfigs().disableKernelMinorRevision();
782 mCheckFlags = mCheckFlags.disableKernel();
783 return true;
784 }
785
786 private:
787 std::vector<NamedIstream> mInFiles;
788 Ostream mOutRef;
789 Ostream mErrRef;
790 NamedIstream mCheckFile;
791 bool mOutputMatrix = false;
792 bool mHasSetHalsOnlyFlag = false;
793 SerializeFlags::Type mSerializeFlags = SerializeFlags::EVERYTHING;
794 std::map<KernelVersion, std::vector<NamedIstream>> mKernels;
795 std::map<std::string, std::string> mFakeEnv;
796 CheckFlags::Type mCheckFlags = CheckFlags::DEFAULT;
797 };
798
openOutFile(const std::string & path)799 bool AssembleVintf::openOutFile(const std::string& path) {
800 return static_cast<std::ofstream&>(setOutputStream(std::make_unique<std::ofstream>(path)))
801 .is_open();
802 }
803
openInFile(const std::string & path)804 bool AssembleVintf::openInFile(const std::string& path) {
805 return static_cast<std::ifstream&>(addInputStream(path, std::make_unique<std::ifstream>(path)))
806 .is_open();
807 }
808
openCheckFile(const std::string & path)809 bool AssembleVintf::openCheckFile(const std::string& path) {
810 return static_cast<std::ifstream&>(
811 setCheckInputStream(path, std::make_unique<std::ifstream>(path)))
812 .is_open();
813 }
814
addKernel(const std::string & kernelArg)815 bool AssembleVintf::addKernel(const std::string& kernelArg) {
816 auto tokens = base::Split(kernelArg, ":");
817 if (tokens.size() <= 1) {
818 err() << "Unrecognized --kernel option '" << kernelArg << "'" << std::endl;
819 return false;
820 }
821 KernelVersion kernelVer;
822 if (!parse(tokens.front(), &kernelVer)) {
823 err() << "Unrecognized kernel version '" << tokens.front() << "'" << std::endl;
824 return false;
825 }
826 if (hasKernelVersion(kernelVer)) {
827 err() << "Multiple --kernel for " << kernelVer << " is specified." << std::endl;
828 return false;
829 }
830 for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) {
831 bool opened =
832 static_cast<std::ifstream&>(
833 addKernelConfigInputStream(kernelVer, *it, std::make_unique<std::ifstream>(*it)))
834 .is_open();
835 if (!opened) {
836 err() << "Cannot open file '" << *it << "'." << std::endl;
837 return false;
838 }
839 }
840 return true;
841 }
842
newInstance()843 std::unique_ptr<AssembleVintf> AssembleVintf::newInstance() {
844 return std::make_unique<AssembleVintfImpl>();
845 }
846
847 } // namespace vintf
848 } // namespace android
849