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