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 #define LOG_TAG "libvintf"
18 #include <android-base/logging.h>
19
20 #include "HalManifest.h"
21
22 #include <dirent.h>
23
24 #include <mutex>
25 #include <set>
26
27 #include <android-base/strings.h>
28
29 #include "parse_string.h"
30 #include "parse_xml.h"
31 #include "utils.h"
32 #include "CompatibilityMatrix.h"
33
34 namespace android {
35 namespace vintf {
36
37 using details::Instances;
38 using details::InstancesOfVersion;
39
40 // Check <version> tag for all <hal> with the same name.
shouldAdd(const ManifestHal & hal) const41 bool HalManifest::shouldAdd(const ManifestHal& hal) const {
42 if (!hal.isValid()) {
43 return false;
44 }
45 if (hal.isOverride()) {
46 return true;
47 }
48 auto existingHals = mHals.equal_range(hal.name);
49 std::set<size_t> existingMajorVersions;
50 for (auto it = existingHals.first; it != existingHals.second; ++it) {
51 for (const auto& v : it->second.versions) {
52 // Assume integrity on existingHals, so no check on emplace().second
53 existingMajorVersions.insert(v.majorVer);
54 }
55 }
56 for (const auto& v : hal.versions) {
57 if (!existingMajorVersions.emplace(v.majorVer).second /* no insertion */) {
58 return false;
59 }
60 }
61 return true;
62 }
63
64 // Remove elements from "list" if p(element) returns true.
65 template <typename List, typename Predicate>
removeIf(List & list,Predicate predicate)66 static void removeIf(List& list, Predicate predicate) {
67 for (auto it = list.begin(); it != list.end();) {
68 if (predicate(*it)) {
69 it = list.erase(it);
70 } else {
71 ++it;
72 }
73 }
74 }
75
removeHals(const std::string & name,size_t majorVer)76 void HalManifest::removeHals(const std::string& name, size_t majorVer) {
77 removeIf(mHals, [&name, majorVer](auto& existingHalPair) {
78 auto& existingHal = existingHalPair.second;
79 if (existingHal.name != name) {
80 return false;
81 }
82 auto& existingVersions = existingHal.versions;
83 removeIf(existingVersions, [majorVer](const auto& existingVersion) {
84 return existingVersion.majorVer == majorVer;
85 });
86 return existingVersions.empty();
87 });
88 }
89
add(ManifestHal && halToAdd)90 bool HalManifest::add(ManifestHal&& halToAdd) {
91 if (halToAdd.isOverride()) {
92 if (halToAdd.isDisabledHal()) {
93 // Special syntax when there are no instances at all. Remove all existing HALs
94 // with the given name.
95 mHals.erase(halToAdd.name);
96 }
97 // If there are <version> tags, remove all existing major versions that causes a conflict.
98 for (const Version& versionToAdd : halToAdd.versions) {
99 removeHals(halToAdd.name, versionToAdd.majorVer);
100 }
101 }
102
103 return HalGroup::add(std::move(halToAdd));
104 }
105
shouldAddXmlFile(const ManifestXmlFile & xmlFile) const106 bool HalManifest::shouldAddXmlFile(const ManifestXmlFile& xmlFile) const {
107 auto existingXmlFiles = getXmlFiles(xmlFile.name());
108 for (auto it = existingXmlFiles.first; it != existingXmlFiles.second; ++it) {
109 if (xmlFile.version() == it->second.version()) {
110 return false;
111 }
112 }
113 return true;
114 }
115
getHalNames() const116 std::set<std::string> HalManifest::getHalNames() const {
117 std::set<std::string> names{};
118 for (const auto &hal : mHals) {
119 names.insert(hal.first);
120 }
121 return names;
122 }
123
getHalNamesAndVersions() const124 std::set<std::string> HalManifest::getHalNamesAndVersions() const {
125 std::set<std::string> names{};
126 forEachInstance([&names](const ManifestInstance& e) {
127 names.insert(toFQNameString(e.interface(), e.version()));
128 return true;
129 });
130 return names;
131 }
132
getTransport(const std::string & package,const Version & v,const std::string & interfaceName,const std::string & instanceName) const133 Transport HalManifest::getTransport(const std::string &package, const Version &v,
134 const std::string &interfaceName, const std::string &instanceName) const {
135 Transport transport{Transport::EMPTY};
136 forEachInstanceOfInterface(package, v, interfaceName, [&](const auto& e) {
137 if (e.instance() == instanceName) {
138 transport = e.transport();
139 }
140 return transport == Transport::EMPTY; // if not found, continue
141 });
142 if (transport == Transport::EMPTY) {
143 LOG(DEBUG) << "HalManifest::getTransport(" << mType << "): Cannot find "
144 << toFQNameString(package, v, interfaceName, instanceName);
145 }
146 return transport;
147 }
148
forEachInstanceOfVersion(const std::string & package,const Version & expectVersion,const std::function<bool (const ManifestInstance &)> & func) const149 bool HalManifest::forEachInstanceOfVersion(
150 const std::string& package, const Version& expectVersion,
151 const std::function<bool(const ManifestInstance&)>& func) const {
152 for (const ManifestHal* hal : getHals(package)) {
153 bool cont = hal->forEachInstance([&](const ManifestInstance& manifestInstance) {
154 if (manifestInstance.version().minorAtLeast(expectVersion)) {
155 return func(manifestInstance);
156 }
157 return true;
158 });
159 if (!cont) return false;
160 }
161 return true;
162 }
163
164 // indent = 2, {"foo"} => "foo"
165 // indent = 2, {"foo", "bar"} => "\n foo\n bar";
166 template <typename Container>
multilineIndent(std::ostream & os,size_t indent,const Container & lines)167 void multilineIndent(std::ostream& os, size_t indent, const Container& lines) {
168 if (lines.size() == 1) {
169 os << *lines.begin();
170 return;
171 }
172 for (const auto& line : lines) {
173 os << "\n";
174 for (size_t i = 0; i < indent; ++i) os << " ";
175 os << line;
176 }
177 }
178
179 // For each hal in mat, there must be a hal in manifest that supports this.
checkIncompatibleHals(const CompatibilityMatrix & mat) const180 std::vector<std::string> HalManifest::checkIncompatibleHals(const CompatibilityMatrix& mat) const {
181 std::vector<std::string> ret;
182 for (const MatrixHal &matrixHal : mat.getHals()) {
183 if (matrixHal.optional) {
184 continue;
185 }
186
187 std::set<FqInstance> manifestInstances;
188 std::set<FqInstance> manifestInstancesNoPackage;
189 std::set<Version> versions;
190 for (const ManifestHal* manifestHal : getHals(matrixHal.name)) {
191 manifestHal->forEachInstance([&](const auto& manifestInstance) {
192 manifestInstances.insert(manifestInstance.getFqInstance());
193 manifestInstancesNoPackage.insert(manifestInstance.getFqInstanceNoPackage());
194 return true;
195 });
196 manifestHal->appendAllVersions(&versions);
197 }
198
199 if (!matrixHal.isCompatible(manifestInstances, versions)) {
200 std::ostringstream oss;
201 oss << matrixHal.name << ":\n required: ";
202 multilineIndent(oss, 8, android::vintf::expandInstances(matrixHal));
203 oss << "\n provided: ";
204 if (manifestInstances.empty()) {
205 multilineIndent(oss, 8, versions);
206 } else {
207 multilineIndent(oss, 8, manifestInstancesNoPackage);
208 }
209
210 ret.insert(ret.end(), oss.str());
211 }
212 }
213 return ret;
214 }
215
checkUnusedHals(const CompatibilityMatrix & mat) const216 std::set<std::string> HalManifest::checkUnusedHals(const CompatibilityMatrix& mat) const {
217 std::set<std::string> ret;
218
219 forEachInstance([&ret, &mat](const auto& manifestInstance) {
220 const auto& fqInstance = manifestInstance.getFqInstance();
221 if (!mat.matchInstance(fqInstance.getPackage(), fqInstance.getVersion(),
222 fqInstance.getInterface(), fqInstance.getInstance())) {
223 ret.insert(fqInstance.string());
224 }
225 return true;
226 });
227
228 return ret;
229 }
230
checkVendorNdkCompatibility(const VendorNdk & matVendorNdk,const std::vector<VendorNdk> & manifestVendorNdk,std::string * error)231 static bool checkVendorNdkCompatibility(const VendorNdk& matVendorNdk,
232 const std::vector<VendorNdk>& manifestVendorNdk,
233 std::string* error) {
234 // For pre-P vendor images, device compatibility matrix does not specify <vendor-ndk>
235 // tag. Ignore the check for these devices.
236 if (matVendorNdk.version().empty()) {
237 return true;
238 }
239 for (const auto& vndk : manifestVendorNdk) {
240 if (vndk.version() != matVendorNdk.version()) {
241 continue;
242 }
243 // version matches, check libraries
244 std::vector<std::string> diff;
245 std::set_difference(matVendorNdk.libraries().begin(), matVendorNdk.libraries().end(),
246 vndk.libraries().begin(), vndk.libraries().end(),
247 std::inserter(diff, diff.begin()));
248 if (!diff.empty()) {
249 if (error != nullptr) {
250 *error = "Vndk libs incompatible for version " + matVendorNdk.version() +
251 ". These libs are not in framework manifest:";
252 for (const auto& name : diff) {
253 *error += " " + name;
254 }
255 }
256 return false;
257 }
258 return true;
259 }
260
261 // no match is found.
262 if (error != nullptr) {
263 *error = "Vndk version " + matVendorNdk.version() + " is not supported. " +
264 "Supported versions in framework manifest are:";
265 for (const auto& vndk : manifestVendorNdk) {
266 *error += " " + vndk.version();
267 }
268 }
269 return false;
270 }
271
checkSystemSdkCompatibility(const SystemSdk & matSystemSdk,const SystemSdk & manifestSystemSdk,std::string * error)272 static bool checkSystemSdkCompatibility(const SystemSdk& matSystemSdk,
273 const SystemSdk& manifestSystemSdk, std::string* error) {
274 SystemSdk notSupported = matSystemSdk.removeVersions(manifestSystemSdk);
275 if (!notSupported.empty()) {
276 if (error) {
277 *error =
278 "The following System SDK versions are required by device "
279 "compatibility matrix but not supported by the framework manifest: [" +
280 base::Join(notSupported.versions(), ", ") + "]. Supported versions are: [" +
281 base::Join(manifestSystemSdk.versions(), ", ") + "].";
282 }
283 return false;
284 }
285 return true;
286 }
287
checkCompatibility(const CompatibilityMatrix & mat,std::string * error) const288 bool HalManifest::checkCompatibility(const CompatibilityMatrix &mat, std::string *error) const {
289 if (mType == mat.mType) {
290 if (error != nullptr) {
291 *error = "Wrong type; checking " + to_string(mType) + " manifest against "
292 + to_string(mat.mType) + " compatibility matrix";
293 }
294 return false;
295 }
296 auto incompatibleHals = checkIncompatibleHals(mat);
297 if (!incompatibleHals.empty()) {
298 if (error != nullptr) {
299 *error = "HALs incompatible.";
300 if (mat.level() != Level::UNSPECIFIED)
301 *error += " Matrix level = " + to_string(mat.level()) + ".";
302 if (level() != Level::UNSPECIFIED)
303 *error += " Manifest level = " + to_string(level()) + ".";
304 *error += " The following requirements are not met:\n";
305 for (const auto& e : incompatibleHals) {
306 *error += e + "\n";
307 }
308 }
309 return false;
310 }
311 if (mType == SchemaType::FRAMEWORK) {
312 if (!checkVendorNdkCompatibility(mat.device.mVendorNdk, framework.mVendorNdks, error)) {
313 return false;
314 }
315
316 if (!checkSystemSdkCompatibility(mat.device.mSystemSdk, framework.mSystemSdk, error)) {
317 return false;
318 }
319 } else if (mType == SchemaType::DEVICE) {
320 bool match = false;
321 for (const auto &range : mat.framework.mSepolicy.sepolicyVersions()) {
322 if (range.supportedBy(device.mSepolicyVersion)) {
323 match = true;
324 break;
325 }
326 }
327 if (!match) {
328 if (error != nullptr) {
329 *error = "Sepolicy version " + to_string(device.mSepolicyVersion)
330 + " doesn't satisify the requirements.";
331 }
332 return false;
333 }
334 }
335
336 return true;
337 }
338
generateCompatibleMatrix() const339 CompatibilityMatrix HalManifest::generateCompatibleMatrix() const {
340 CompatibilityMatrix matrix;
341
342 forEachInstance([&matrix](const ManifestInstance& e) {
343 matrix.add(MatrixHal{
344 .format = e.format(),
345 .name = e.package(),
346 .optional = true,
347 .versionRanges = {VersionRange{e.version().majorVer, e.version().minorVer}},
348 .interfaces = {{e.interface(), HalInterface{e.interface(), {e.instance()}}}}});
349 return true;
350 });
351 if (mType == SchemaType::FRAMEWORK) {
352 matrix.mType = SchemaType::DEVICE;
353 // VNDK does not need to be added for compatibility
354 } else if (mType == SchemaType::DEVICE) {
355 matrix.mType = SchemaType::FRAMEWORK;
356 matrix.framework.mSepolicy = Sepolicy(0u /* kernelSepolicyVersion */,
357 {{device.mSepolicyVersion.majorVer, device.mSepolicyVersion.minorVer}});
358 }
359
360 return matrix;
361 }
362
fetchAllInformation(const std::string & path,std::string * error)363 status_t HalManifest::fetchAllInformation(const std::string& path, std::string* error) {
364 return details::fetchAllInformation(path, gHalManifestConverter, this, error);
365 }
366
type() const367 SchemaType HalManifest::type() const {
368 return mType;
369 }
370
setType(SchemaType type)371 void HalManifest::setType(SchemaType type) {
372 mType = type;
373 }
374
level() const375 Level HalManifest::level() const {
376 return mLevel;
377 }
378
getMetaVersion() const379 Version HalManifest::getMetaVersion() const {
380 return mMetaVersion;
381 }
382
sepolicyVersion() const383 const Version &HalManifest::sepolicyVersion() const {
384 CHECK(mType == SchemaType::DEVICE);
385 return device.mSepolicyVersion;
386 }
387
vendorNdks() const388 const std::vector<VendorNdk>& HalManifest::vendorNdks() const {
389 CHECK(mType == SchemaType::FRAMEWORK);
390 return framework.mVendorNdks;
391 }
392
getXmlFilePath(const std::string & xmlFileName,const Version & version) const393 std::string HalManifest::getXmlFilePath(const std::string& xmlFileName,
394 const Version& version) const {
395 using std::literals::string_literals::operator""s;
396 auto range = getXmlFiles(xmlFileName);
397 for (auto it = range.first; it != range.second; ++it) {
398 const ManifestXmlFile& manifestXmlFile = it->second;
399 if (manifestXmlFile.version() == version) {
400 if (!manifestXmlFile.overriddenPath().empty()) {
401 return manifestXmlFile.overriddenPath();
402 }
403 return "/"s + (type() == SchemaType::DEVICE ? "vendor" : "system") + "/etc/" +
404 xmlFileName + "_V" + std::to_string(version.majorVer) + "_" +
405 std::to_string(version.minorVer) + ".xml";
406 }
407 }
408 return "";
409 }
410
operator ==(const HalManifest & lft,const HalManifest & rgt)411 bool operator==(const HalManifest &lft, const HalManifest &rgt) {
412 return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals &&
413 lft.mXmlFiles == rgt.mXmlFiles &&
414 (lft.mType != SchemaType::DEVICE ||
415 (lft.device.mSepolicyVersion == rgt.device.mSepolicyVersion)) &&
416 (lft.mType != SchemaType::FRAMEWORK ||
417 (
418 #pragma clang diagnostic push
419 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
420 lft.framework.mVndks == rgt.framework.mVndks &&
421 #pragma clang diagnostic pop
422 lft.framework.mVendorNdks == rgt.framework.mVendorNdks &&
423 lft.framework.mSystemSdk == rgt.framework.mSystemSdk));
424 }
425
426 // Alternative to forEachInstance if you just need a set of instance names instead.
getInstances(const std::string & halName,const Version & version,const std::string & interfaceName) const427 std::set<std::string> HalManifest::getInstances(const std::string& halName, const Version& version,
428 const std::string& interfaceName) const {
429 std::set<std::string> ret;
430 (void)forEachInstanceOfInterface(halName, version, interfaceName, [&ret](const auto& e) {
431 ret.insert(e.instance());
432 return true;
433 });
434 return ret;
435 }
436
437 // Return whether instance is in getInstances(...).
hasInstance(const std::string & halName,const Version & version,const std::string & interfaceName,const std::string & instance) const438 bool HalManifest::hasInstance(const std::string& halName, const Version& version,
439 const std::string& interfaceName, const std::string& instance) const {
440 bool found = false;
441 (void)forEachInstanceOfInterface(halName, version, interfaceName,
442 [&found, &instance](const auto& e) {
443 found |= (instance == e.instance());
444 return !found; // if not found, continue
445 });
446 return found;
447 }
448
449 } // namespace vintf
450 } // namespace android
451