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 "MatrixHal.h"
18 
19 #include <algorithm>
20 
21 #include "MapValueIterator.h"
22 #include "constants-private.h"
23 #include "utils.h"
24 
25 namespace android {
26 namespace vintf {
27 
28 using details::canConvertToFqInstance;
29 
30 bool MatrixHal::isValid(std::string* error) const {
31     bool success = true;
32 
33     // Check legacy instances (i.e. <version> + <interface> + <instance>) can be
34     // converted into FqInstance because forEachInstance relies on FqInstance.
35     // Because <version> is a range, check both ends of the range.
36     for (const auto& vr : versionRanges) {
37         for (const auto& v : {vr.minVer(), vr.maxVer()}) {
38             for (const auto& intf : iterateValues(interfaces)) {
39                 intf.forEachInstance([&](const auto& interface, const auto& instance,
40                                          bool /*isRegex*/) {
41                     if (!canConvertToFqInstance(getName(), v, interface, instance, format, error)) {
42                         success = false;
43                     }
44                     return true;  // continue
45                 });
46             }
47         }
48     }
49 
50     return success;
51 }
52 
53 bool MatrixHal::operator==(const MatrixHal &other) const {
54     if (format != other.format)
55         return false;
56     if (name != other.name)
57         return false;
58     if (versionRanges != other.versionRanges)
59         return false;
60     if (interfaces != other.interfaces)
61         return false;
62     // do not compare optional
63     return true;
64 }
65 
66 bool MatrixHal::containsVersion(const Version& version) const {
67     for (VersionRange vRange : versionRanges) {
68         if (vRange.contains(version)) return true;
69     }
70     return false;
71 }
72 
73 bool MatrixHal::forEachInstance(const std::function<bool(const MatrixInstance&)>& func) const {
74     for (const auto& vr : versionRanges) {
75         if (!forEachInstance(vr, func)) {
76             return false;
77         }
78     }
79     return true;
80 }
81 
82 bool MatrixHal::forEachInstance(const VersionRange& vr,
83                                 const std::function<bool(const MatrixInstance&)>& func) const {
84     for (const auto& intf : iterateValues(interfaces)) {
85         bool cont =
86             intf.forEachInstance([&](const auto& interface, const auto& instance, bool isRegex) {
87                 // TODO(b/73556059): Store MatrixInstance as well to avoid creating temps
88                 FqInstance fqInstance;
89                 if (fqInstance.setTo(getName(), vr.majorVer, vr.minMinor, interface, instance)) {
90                     if (!func(MatrixInstance(format, std::move(fqInstance), VersionRange(vr),
91                                              optional, isRegex))) {
92                         return false;
93                     }
94                 }
95                 return true;
96             });
97         if (!cont) {
98             return false;
99         }
100     }
101     return true;
102 }
103 
104 bool MatrixHal::forEachInstance(
105     const std::function<bool(const std::vector<VersionRange>&, const std::string&,
106                              const std::string&, bool isRegex)>& func) const {
107     for (const auto& intf : iterateValues(interfaces)) {
108         bool cont =
109             intf.forEachInstance([&](const auto& interface, const auto& instance, bool isRegex) {
110                 return func(this->versionRanges, interface, instance, isRegex);
111             });
112         if (!cont) {
113             return false;
114         }
115     }
116     return true;
117 }
118 
119 bool MatrixHal::isCompatible(const std::set<FqInstance>& providedInstances,
120                              const std::set<Version>& providedVersions) const {
121     // <version>'s are related by OR.
122     return std::any_of(versionRanges.begin(), versionRanges.end(), [&](const VersionRange& vr) {
123         return isCompatible(vr, providedInstances, providedVersions);
124     });
125 }
126 
127 bool MatrixHal::isCompatible(const VersionRange& vr, const std::set<FqInstance>& providedInstances,
128                              const std::set<Version>& providedVersions) const {
129     bool hasAnyInstance = false;
130     bool versionUnsatisfied = false;
131 
132     // Look at each interface/instance, and ensure that they are in providedInstances.
133     forEachInstance(vr, [&](const MatrixInstance& matrixInstance) {
134         hasAnyInstance = true;
135 
136         versionUnsatisfied |=
137             !std::any_of(providedInstances.begin(), providedInstances.end(),
138                          [&](const FqInstance& providedInstance) {
139                              return matrixInstance.isSatisfiedBy(providedInstance);
140                          });
141 
142         return !versionUnsatisfied;  // if any interface/instance is unsatisfied, break
143     });
144 
145     if (hasAnyInstance) {
146         return !versionUnsatisfied;
147     }
148 
149     // In some cases (e.g. tests and native HALs), compatibility matrix doesn't specify
150     // any instances. Check versions only.
151     return std::any_of(
152         providedVersions.begin(), providedVersions.end(),
153         [&](const auto& providedVersion) { return vr.supportedBy(providedVersion); });
154 }
155 
156 void MatrixHal::setOptional(bool o) {
157     this->optional = o;
158 }
159 
160 void MatrixHal::insertVersionRanges(const std::vector<VersionRange>& other) {
161     for (const VersionRange& otherVr : other) {
162         auto existingVr = std::find_if(this->versionRanges.begin(), this->versionRanges.end(),
163                                        [&](const auto& e) { return e.overlaps(otherVr); });
164 
165         if (existingVr == this->versionRanges.end()) {
166             this->versionRanges.push_back(otherVr);
167         } else {
168             existingVr->minMinor = std::min(existingVr->minMinor, otherVr.minMinor);
169             existingVr->maxMinor = std::max(existingVr->maxMinor, otherVr.maxMinor);
170         }
171     }
172 }
173 
174 void MatrixHal::insertInstance(const std::string& interface, const std::string& instance,
175                                bool isRegex) {
176     auto it = interfaces.find(interface);
177     if (it == interfaces.end())
178         it = interfaces.emplace(interface, HalInterface{interface, {}}).first;
179     it->second.insertInstance(instance, isRegex);
180 }
181 
182 size_t MatrixHal::instancesCount() const {
183     size_t count = 0;
184     forEachInstance([&](const MatrixInstance&) {
185         ++count;
186         return true;  // continue;
187     });
188     return count;
189 }
190 
191 bool MatrixHal::removeInstance(const std::string& interface, const std::string& instance,
192                                bool isRegex) {
193     auto it = interfaces.find(interface);
194     if (it == interfaces.end()) return false;
195     bool removed = it->second.removeInstance(instance, isRegex);
196     if (!it->second.hasAnyInstance()) interfaces.erase(it);
197     return removed;
198 }
199 
200 void MatrixHal::clearInstances() {
201     this->interfaces.clear();
202 }
203 
204 } // namespace vintf
205 } // namespace android
206