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 "ManifestHal.h"
18 #include <unordered_set>
19 
20 #include "MapValueIterator.h"
21 #include "constants-private.h"
22 #include "parse_string.h"
23 
24 namespace android {
25 namespace vintf {
26 
27 bool ManifestHal::isValid() const {
28     std::unordered_set<size_t> existing;
29     for (const auto &v : versions) {
30         if (existing.find(v.majorVer) != existing.end()) {
31             return false;
32         }
33         existing.insert(v.majorVer);
34     }
35     return transportArch.isValid();
36 }
37 
38 bool ManifestHal::operator==(const ManifestHal &other) const {
39     if (format != other.format)
40         return false;
41     if (name != other.name)
42         return false;
43     if (versions != other.versions)
44         return false;
45     if (!(transportArch == other.transportArch)) return false;
46     if (interfaces != other.interfaces) return false;
47     if (isOverride() != other.isOverride()) return false;
48     if (mAdditionalInstances != other.mAdditionalInstances) return false;
49     return true;
50 }
51 
52 bool ManifestHal::forEachInstance(const std::function<bool(const ManifestInstance&)>& func) const {
53     for (const auto& v : versions) {
54         for (const auto& intf : iterateValues(interfaces)) {
55             bool cont = intf.forEachInstance([&](const auto& interface, const auto& instance,
56                                                  bool /* isRegex */) {
57                 // TODO(b/73556059): Store ManifestInstance as well to avoid creating temps
58                 FqInstance fqInstance;
59                 if (fqInstance.setTo(getName(), v.majorVer, v.minorVer, interface, instance)) {
60                     if (!func(ManifestInstance(std::move(fqInstance), TransportArch{transportArch},
61                                                format))) {
62                         return false;
63                     }
64                 }
65                 return true;
66             });
67             if (!cont) {
68                 return false;
69             }
70         }
71     }
72 
73     for (const auto& manifestInstance : mAdditionalInstances) {
74         if (!func(manifestInstance)) {
75             return false;
76         }
77     }
78 
79     return true;
80 }
81 
82 bool ManifestHal::isDisabledHal() const {
83     if (!isOverride()) return false;
84     bool hasInstance = false;
85     forEachInstance([&hasInstance](const auto&) {
86         hasInstance = true;
87         return false;  // has at least one instance, stop here.
88     });
89     return !hasInstance;
90 }
91 
92 void ManifestHal::appendAllVersions(std::set<Version>* ret) const {
93     ret->insert(versions.begin(), versions.end());
94     forEachInstance([&](const auto& e) {
95         ret->insert(e.version());
96         return true;
97     });
98 }
99 
100 bool ManifestHal::verifyInstance(const FqInstance& fqInstance, std::string* error) const {
101     if (fqInstance.hasPackage() && fqInstance.getPackage() != this->getName()) {
102         if (error) {
103             *error = "Should not add \"" + fqInstance.string() + "\" to a HAL with name " +
104                      this->getName();
105         }
106         return false;
107     }
108     if (!fqInstance.hasVersion()) {
109         if (error) *error = "Should specify version: \"" + fqInstance.string() + "\"";
110         return false;
111     }
112     if (!fqInstance.hasInterface()) {
113         if (error) *error = "Should specify interface: \"" + fqInstance.string() + "\"";
114         return false;
115     }
116     if (!fqInstance.hasInstance()) {
117         if (error) *error = "Should specify instance: \"" + fqInstance.string() + "\"";
118         return false;
119     }
120     return true;
121 }
122 
123 bool ManifestHal::insertInstances(const std::set<FqInstance>& fqInstances, std::string* error) {
124     for (const FqInstance& e : fqInstances) {
125         if (!insertInstance(e, error)) {
126             return false;
127         }
128     }
129     return true;
130 }
131 
132 bool ManifestHal::insertInstance(const FqInstance& e, std::string* error) {
133     if (!verifyInstance(e, error)) {
134         return false;
135     }
136 
137     size_t minorVer = e.getMinorVersion();
138     for (auto it = mAdditionalInstances.begin(); it != mAdditionalInstances.end();) {
139         if (it->version().majorVer == e.getMajorVersion() && it->interface() == e.getInterface() &&
140             it->instance() == e.getInstance()) {
141             minorVer = std::max(minorVer, it->version().minorVer);
142             it = mAdditionalInstances.erase(it);
143         } else {
144             ++it;
145         }
146     }
147 
148     FqInstance toAdd;
149     if (!toAdd.setTo(this->getName(), e.getMajorVersion(), minorVer, e.getInterface(),
150                      e.getInstance())) {
151         if (error) {
152             *error = "Cannot create FqInstance with package='" + this->getName() + "', version='" +
153                      to_string(Version(e.getMajorVersion(), minorVer)) + "', interface='" +
154                      e.getInterface() + "', instance='" + e.getInstance() + "'";
155         }
156         return false;
157     }
158 
159     mAdditionalInstances.emplace(std::move(toAdd), this->transportArch, this->format);
160     return true;
161 }
162 
163 } // namespace vintf
164 } // namespace android
165