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