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
19 #include <unordered_set>
20
21 #include "MapValueIterator.h"
22 #include "constants-private.h"
23 #include "parse_string.h"
24 #include "utils.h"
25
26 namespace android {
27 namespace vintf {
28
29 using details::canConvertToFqInstance;
30
isValid(std::string * error) const31 bool ManifestHal::isValid(std::string* error) const {
32 if (error) {
33 error->clear();
34 }
35
36 bool success = true;
37 std::map<size_t, Version> existing;
38 for (const auto &v : versions) {
39 auto&& [it, inserted] = existing.emplace(v.majorVer, v);
40 if (inserted) {
41 continue;
42 }
43 success = false;
44 if (error) {
45 *error += "Duplicated major version: " + to_string(v) + " vs. " +
46 to_string(it->second) + ".\n";
47 }
48 }
49
50 // Check legacy instances (i.e. <version> + <interface> + <instance>) can be
51 // converted into FqInstance because forEachInstance relies on FqInstance.
52 for (const auto& v : versions) {
53 for (const auto& intf : iterateValues(interfaces)) {
54 intf.forEachInstance(
55 [&](const auto& interface, const auto& instance, bool /* isRegex */) {
56 if (!canConvertToFqInstance(getName(), v, interface, instance, format, error)) {
57 success = false;
58 }
59 return true; // continue
60 });
61 }
62 }
63
64 std::string transportArchError;
65 if (!transportArch.isValid(&transportArchError)) {
66 success = false;
67 if (error) *error += transportArchError + "\n";
68 }
69 return success;
70 }
71
operator ==(const ManifestHal & other) const72 bool ManifestHal::operator==(const ManifestHal &other) const {
73 // ignore fileName().
74 if (format != other.format)
75 return false;
76 if (name != other.name)
77 return false;
78 if (versions != other.versions)
79 return false;
80 if (!(transportArch == other.transportArch)) return false;
81 if (interfaces != other.interfaces) return false;
82 if (isOverride() != other.isOverride()) return false;
83 if (updatableViaApex() != other.updatableViaApex()) return false;
84 if (mAdditionalInstances != other.mAdditionalInstances) return false;
85 return true;
86 }
87
forEachInstance(const std::function<bool (const ManifestInstance &)> & func) const88 bool ManifestHal::forEachInstance(const std::function<bool(const ManifestInstance&)>& func) const {
89 for (const auto& v : versions) {
90 for (const auto& intf : iterateValues(interfaces)) {
91 bool cont = intf.forEachInstance([&](const auto& interface, const auto& instance,
92 bool /* isRegex */) {
93 // TODO(b/73556059): Store ManifestInstance as well to avoid creating temps
94 FqInstance fqInstance;
95 if (fqInstance.setTo(getName(), v.majorVer, v.minorVer, interface, instance)) {
96 if (!func(ManifestInstance(std::move(fqInstance), TransportArch{transportArch},
97 format, updatableViaApex()))) {
98 return false;
99 }
100 }
101 return true;
102 });
103 if (!cont) {
104 return false;
105 }
106 }
107 }
108
109 for (const auto& manifestInstance : mAdditionalInstances) {
110 // For AIDL HALs, <version> tag is mangled with <fqname>. Note that if there's no
111 // <version> tag, libvintf will create one by default, so each <fqname> is executed
112 // at least once.
113 if (format == HalFormat::AIDL) {
114 for (const auto& v : versions) {
115 if (!func(manifestInstance.withVersion(v))) {
116 return false;
117 }
118 }
119 } else {
120 if (!func(manifestInstance)) {
121 return false;
122 }
123 }
124 }
125
126 return true;
127 }
128
isDisabledHal() const129 bool ManifestHal::isDisabledHal() const {
130 if (!isOverride()) return false;
131 bool hasInstance = false;
132 forEachInstance([&hasInstance](const auto&) {
133 hasInstance = true;
134 return false; // has at least one instance, stop here.
135 });
136 return !hasInstance;
137 }
138
appendAllVersions(std::set<Version> * ret) const139 void ManifestHal::appendAllVersions(std::set<Version>* ret) const {
140 ret->insert(versions.begin(), versions.end());
141 forEachInstance([&](const auto& e) {
142 ret->insert(e.version());
143 return true;
144 });
145 }
146
verifyInstance(const FqInstance & fqInstance,std::string * error) const147 bool ManifestHal::verifyInstance(const FqInstance& fqInstance, std::string* error) const {
148 if (fqInstance.hasPackage() && fqInstance.getPackage() != this->getName()) {
149 if (error) {
150 *error = "Should not add \"" + fqInstance.string() + "\" to a HAL with name " +
151 this->getName();
152 }
153 return false;
154 }
155 if (!fqInstance.hasVersion()) {
156 if (error) *error = "Should specify version: \"" + fqInstance.string() + "\"";
157 return false;
158 }
159 if (!fqInstance.hasInterface()) {
160 if (error) *error = "Should specify interface: \"" + fqInstance.string() + "\"";
161 return false;
162 }
163 if (!fqInstance.hasInstance()) {
164 if (error) *error = "Should specify instance: \"" + fqInstance.string() + "\"";
165 return false;
166 }
167 return true;
168 }
169
insertInstances(const std::set<FqInstance> & fqInstances,std::string * error)170 bool ManifestHal::insertInstances(const std::set<FqInstance>& fqInstances, std::string* error) {
171 for (const FqInstance& e : fqInstances) {
172 if (!insertInstance(e, error)) {
173 return false;
174 }
175 }
176 return true;
177 }
178
insertInstance(const FqInstance & e,std::string * error)179 bool ManifestHal::insertInstance(const FqInstance& e, std::string* error) {
180 if (!verifyInstance(e, error)) {
181 return false;
182 }
183
184 size_t minorVer = e.getMinorVersion();
185 for (auto it = mAdditionalInstances.begin(); it != mAdditionalInstances.end();) {
186 if (it->version().majorVer == e.getMajorVersion() && it->interface() == e.getInterface() &&
187 it->instance() == e.getInstance()) {
188 minorVer = std::max(minorVer, it->version().minorVer);
189 it = mAdditionalInstances.erase(it);
190 } else {
191 ++it;
192 }
193 }
194
195 FqInstance toAdd;
196 if (!toAdd.setTo(this->getName(), e.getMajorVersion(), minorVer, e.getInterface(),
197 e.getInstance())) {
198 if (error) {
199 *error = "Cannot create FqInstance with package='" + this->getName() + "', version='" +
200 to_string(Version(e.getMajorVersion(), minorVer)) + "', interface='" +
201 e.getInterface() + "', instance='" + e.getInstance() + "'";
202 }
203 return false;
204 }
205
206 mAdditionalInstances.emplace(std::move(toAdd), this->transportArch, this->format,
207 this->updatableViaApex());
208 return true;
209 }
210
211 } // namespace vintf
212 } // namespace android
213