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