• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 using details::mergeField;
40 
41 // Check <version> tag for all <hal> with the same name.
shouldAdd(const ManifestHal & hal) const42 bool HalManifest::shouldAdd(const ManifestHal& hal) const {
43     if (!hal.isValid()) {
44         return false;
45     }
46     if (hal.isOverride()) {
47         return true;
48     }
49     auto existingHals = mHals.equal_range(hal.name);
50     std::set<size_t> existingMajorVersions;
51     for (auto it = existingHals.first; it != existingHals.second; ++it) {
52         for (const auto& v : it->second.versions) {
53             // Assume integrity on existingHals, so no check on emplace().second
54             existingMajorVersions.insert(v.majorVer);
55         }
56     }
57     for (const auto& v : hal.versions) {
58         if (!existingMajorVersions.emplace(v.majorVer).second /* no insertion */) {
59             return false;
60         }
61     }
62     return true;
63 }
64 
65 // Remove elements from "list" if p(element) returns true.
66 template <typename List, typename Predicate>
removeIf(List & list,Predicate predicate)67 static void removeIf(List& list, Predicate predicate) {
68     for (auto it = list.begin(); it != list.end();) {
69         if (predicate(*it)) {
70             it = list.erase(it);
71         } else {
72             ++it;
73         }
74     }
75 }
76 
removeHals(const std::string & name,size_t majorVer)77 void HalManifest::removeHals(const std::string& name, size_t majorVer) {
78     removeIf(mHals, [&name, majorVer](auto& existingHalPair) {
79         auto& existingHal = existingHalPair.second;
80         if (existingHal.name != name) {
81             return false;
82         }
83         auto& existingVersions = existingHal.versions;
84         removeIf(existingVersions, [majorVer](const auto& existingVersion) {
85             return existingVersion.majorVer == majorVer;
86         });
87         return existingVersions.empty();
88     });
89 }
90 
add(ManifestHal && halToAdd)91 bool HalManifest::add(ManifestHal&& halToAdd) {
92     if (halToAdd.isOverride()) {
93         if (halToAdd.isDisabledHal()) {
94             // Special syntax when there are no instances at all. Remove all existing HALs
95             // with the given name.
96             mHals.erase(halToAdd.name);
97         }
98         // If there are <version> tags, remove all existing major versions that causes a conflict.
99         for (const Version& versionToAdd : halToAdd.versions) {
100             removeHals(halToAdd.name, versionToAdd.majorVer);
101         }
102     }
103 
104     return HalGroup::add(std::move(halToAdd));
105 }
106 
shouldAddXmlFile(const ManifestXmlFile & xmlFile) const107 bool HalManifest::shouldAddXmlFile(const ManifestXmlFile& xmlFile) const {
108     auto existingXmlFiles = getXmlFiles(xmlFile.name());
109     for (auto it = existingXmlFiles.first; it != existingXmlFiles.second; ++it) {
110         if (xmlFile.version() == it->second.version()) {
111             return false;
112         }
113     }
114     return true;
115 }
116 
getHalNames() const117 std::set<std::string> HalManifest::getHalNames() const {
118     std::set<std::string> names{};
119     for (const auto &hal : mHals) {
120         names.insert(hal.first);
121     }
122     return names;
123 }
124 
getHalNamesAndVersions() const125 std::set<std::string> HalManifest::getHalNamesAndVersions() const {
126     std::set<std::string> names{};
127     forEachInstance([&names](const ManifestInstance& e) {
128         names.insert(toFQNameString(e.package(), e.version()));
129         return true;
130     });
131     return names;
132 }
133 
getTransport(const std::string & package,const Version & v,const std::string & interfaceName,const std::string & instanceName) const134 Transport HalManifest::getTransport(const std::string &package, const Version &v,
135             const std::string &interfaceName, const std::string &instanceName) const {
136     Transport transport{Transport::EMPTY};
137     forEachInstanceOfInterface(package, v, interfaceName, [&](const auto& e) {
138         if (e.instance() == instanceName) {
139             transport = e.transport();
140         }
141         return transport == Transport::EMPTY;  // if not found, continue
142     });
143     if (transport == Transport::EMPTY) {
144         LOG(DEBUG) << "HalManifest::getTransport(" << mType << "): Cannot find "
145                    << toFQNameString(package, v, interfaceName, instanceName);
146     }
147     return transport;
148 }
149 
forEachInstanceOfVersion(const std::string & package,const Version & expectVersion,const std::function<bool (const ManifestInstance &)> & func) const150 bool HalManifest::forEachInstanceOfVersion(
151     const std::string& package, const Version& expectVersion,
152     const std::function<bool(const ManifestInstance&)>& func) const {
153     for (const ManifestHal* hal : getHals(package)) {
154         bool cont = hal->forEachInstance([&](const ManifestInstance& manifestInstance) {
155             if (manifestInstance.version().minorAtLeast(expectVersion)) {
156                 return func(manifestInstance);
157             }
158             return true;
159         });
160         if (!cont) return false;
161     }
162     return true;
163 }
164 
165 // indent = 2, {"foo"} => "foo"
166 // indent = 2, {"foo", "bar"} => "\n  foo\n  bar";
167 template <typename Container>
multilineIndent(std::ostream & os,size_t indent,const Container & lines)168 void multilineIndent(std::ostream& os, size_t indent, const Container& lines) {
169     if (lines.size() == 1) {
170         os << *lines.begin();
171         return;
172     }
173     for (const auto& line : lines) {
174         os << "\n";
175         for (size_t i = 0; i < indent; ++i) os << " ";
176         os << line;
177     }
178 }
179 
180 // For each hal in mat, there must be a hal in manifest that supports this.
checkIncompatibleHals(const CompatibilityMatrix & mat) const181 std::vector<std::string> HalManifest::checkIncompatibleHals(const CompatibilityMatrix& mat) const {
182     std::vector<std::string> ret;
183     for (const MatrixHal &matrixHal : mat.getHals()) {
184         if (matrixHal.optional) {
185             continue;
186         }
187 
188         std::set<FqInstance> manifestInstances;
189         std::set<FqInstance> manifestInstancesNoPackage;
190         std::set<Version> versions;
191         for (const ManifestHal* manifestHal : getHals(matrixHal.name)) {
192             manifestHal->forEachInstance([&](const auto& manifestInstance) {
193                 manifestInstances.insert(manifestInstance.getFqInstance());
194                 manifestInstancesNoPackage.insert(manifestInstance.getFqInstanceNoPackage());
195                 return true;
196             });
197             manifestHal->appendAllVersions(&versions);
198         }
199 
200         if (!matrixHal.isCompatible(manifestInstances, versions)) {
201             std::ostringstream oss;
202             oss << matrixHal.name << ":\n    required: ";
203             multilineIndent(oss, 8, android::vintf::expandInstances(matrixHal));
204             oss << "\n    provided: ";
205             if (manifestInstances.empty()) {
206                 multilineIndent(oss, 8, versions);
207             } else {
208                 multilineIndent(oss, 8, manifestInstancesNoPackage);
209             }
210 
211             ret.insert(ret.end(), oss.str());
212         }
213     }
214     return ret;
215 }
216 
checkUnusedHals(const CompatibilityMatrix & mat) const217 std::set<std::string> HalManifest::checkUnusedHals(const CompatibilityMatrix& mat) const {
218     std::set<std::string> ret;
219 
220     forEachInstance([&ret, &mat](const auto& manifestInstance) {
221         const auto& fqInstance = manifestInstance.getFqInstance();
222         if (!mat.matchInstance(fqInstance.getPackage(), fqInstance.getVersion(),
223                                fqInstance.getInterface(), fqInstance.getInstance())) {
224             ret.insert(fqInstance.string());
225         }
226         return true;
227     });
228 
229     return ret;
230 }
231 
checkVendorNdkCompatibility(const VendorNdk & matVendorNdk,const std::vector<VendorNdk> & manifestVendorNdk,std::string * error)232 static bool checkVendorNdkCompatibility(const VendorNdk& matVendorNdk,
233                                         const std::vector<VendorNdk>& manifestVendorNdk,
234                                         std::string* error) {
235     // For pre-P vendor images, device compatibility matrix does not specify <vendor-ndk>
236     // tag. Ignore the check for these devices.
237     if (matVendorNdk.version().empty()) {
238         return true;
239     }
240     for (const auto& vndk : manifestVendorNdk) {
241         if (vndk.version() != matVendorNdk.version()) {
242             continue;
243         }
244         // version matches, check libraries
245         std::vector<std::string> diff;
246         std::set_difference(matVendorNdk.libraries().begin(), matVendorNdk.libraries().end(),
247                             vndk.libraries().begin(), vndk.libraries().end(),
248                             std::inserter(diff, diff.begin()));
249         if (!diff.empty()) {
250             if (error != nullptr) {
251                 *error = "Vndk libs incompatible for version " + matVendorNdk.version() +
252                          ". These libs are not in framework manifest:";
253                 for (const auto& name : diff) {
254                     *error += " " + name;
255                 }
256             }
257             return false;
258         }
259         return true;
260     }
261 
262     // no match is found.
263     if (error != nullptr) {
264         *error = "Vndk version " + matVendorNdk.version() + " is not supported. " +
265                  "Supported versions in framework manifest are:";
266         for (const auto& vndk : manifestVendorNdk) {
267             *error += " " + vndk.version();
268         }
269     }
270     return false;
271 }
272 
checkSystemSdkCompatibility(const SystemSdk & matSystemSdk,const SystemSdk & manifestSystemSdk,std::string * error)273 static bool checkSystemSdkCompatibility(const SystemSdk& matSystemSdk,
274                                         const SystemSdk& manifestSystemSdk, std::string* error) {
275     SystemSdk notSupported = matSystemSdk.removeVersions(manifestSystemSdk);
276     if (!notSupported.empty()) {
277         if (error) {
278             *error =
279                 "The following System SDK versions are required by device "
280                 "compatibility matrix but not supported by the framework manifest: [" +
281                 base::Join(notSupported.versions(), ", ") + "]. Supported versions are: [" +
282                 base::Join(manifestSystemSdk.versions(), ", ") + "].";
283         }
284         return false;
285     }
286     return true;
287 }
288 
checkCompatibility(const CompatibilityMatrix & mat,std::string * error) const289 bool HalManifest::checkCompatibility(const CompatibilityMatrix &mat, std::string *error) const {
290     if (mType == mat.mType) {
291         if (error != nullptr) {
292             *error = "Wrong type; checking " + to_string(mType) + " manifest against "
293                     + to_string(mat.mType) + " compatibility matrix";
294         }
295         return false;
296     }
297     auto incompatibleHals = checkIncompatibleHals(mat);
298     if (!incompatibleHals.empty()) {
299         if (error != nullptr) {
300             *error = "HALs incompatible.";
301             if (mat.level() != Level::UNSPECIFIED)
302                 *error += " Matrix level = " + to_string(mat.level()) + ".";
303             if (level() != Level::UNSPECIFIED)
304                 *error += " Manifest level = " + to_string(level()) + ".";
305             *error += " The following requirements are not met:\n";
306             for (const auto& e : incompatibleHals) {
307                 *error += e + "\n";
308             }
309         }
310         return false;
311     }
312     if (mType == SchemaType::FRAMEWORK) {
313         if (!checkVendorNdkCompatibility(mat.device.mVendorNdk, framework.mVendorNdks, error)) {
314             return false;
315         }
316 
317         if (!checkSystemSdkCompatibility(mat.device.mSystemSdk, framework.mSystemSdk, error)) {
318             return false;
319         }
320     } else if (mType == SchemaType::DEVICE) {
321         bool sepolicyMatch = false;
322         for (const auto &range : mat.framework.mSepolicy.sepolicyVersions()) {
323             if (range.supportedBy(device.mSepolicyVersion)) {
324                 sepolicyMatch = true;
325                 break;
326             }
327         }
328         if (!sepolicyMatch) {
329             if (error != nullptr) {
330                 *error = "Sepolicy version " + to_string(device.mSepolicyVersion)
331                         + " doesn't satisify the requirements.";
332             }
333             return false;
334         }
335 
336         if (!!kernel() && !kernel()->matchKernelRequirements(mat.framework.mKernels, error)) {
337             return false;
338         }
339     }
340 
341     return true;
342 }
343 
generateCompatibleMatrix() const344 CompatibilityMatrix HalManifest::generateCompatibleMatrix() const {
345     CompatibilityMatrix matrix;
346 
347     forEachInstance([&matrix](const ManifestInstance& e) {
348         matrix.add(MatrixHal{
349             .format = e.format(),
350             .name = e.package(),
351             .optional = true,
352             .versionRanges = {VersionRange{e.version().majorVer, e.version().minorVer}},
353             .interfaces = {{e.interface(), HalInterface{e.interface(), {e.instance()}}}}});
354         return true;
355     });
356     if (mType == SchemaType::FRAMEWORK) {
357         matrix.mType = SchemaType::DEVICE;
358         // VNDK does not need to be added for compatibility
359     } else if (mType == SchemaType::DEVICE) {
360         matrix.mType = SchemaType::FRAMEWORK;
361         matrix.framework.mSepolicy = Sepolicy(0u /* kernelSepolicyVersion */,
362                 {{device.mSepolicyVersion.majorVer, device.mSepolicyVersion.minorVer}});
363     }
364 
365     return matrix;
366 }
367 
fetchAllInformation(const FileSystem * fileSystem,const std::string & path,std::string * error)368 status_t HalManifest::fetchAllInformation(const FileSystem* fileSystem, const std::string& path,
369                                           std::string* error) {
370     return details::fetchAllInformation(fileSystem, path, gHalManifestConverter, this, error);
371 }
372 
type() const373 SchemaType HalManifest::type() const {
374     return mType;
375 }
376 
setType(SchemaType type)377 void HalManifest::setType(SchemaType type) {
378     mType = type;
379 }
380 
level() const381 Level HalManifest::level() const {
382     return mLevel;
383 }
384 
getMetaVersion() const385 Version HalManifest::getMetaVersion() const {
386     return mMetaVersion;
387 }
388 
sepolicyVersion() const389 const Version &HalManifest::sepolicyVersion() const {
390     CHECK(mType == SchemaType::DEVICE);
391     return device.mSepolicyVersion;
392 }
393 
vendorNdks() const394 const std::vector<VendorNdk>& HalManifest::vendorNdks() const {
395     CHECK(mType == SchemaType::FRAMEWORK);
396     return framework.mVendorNdks;
397 }
398 
getXmlFilePath(const std::string & xmlFileName,const Version & version) const399 std::string HalManifest::getXmlFilePath(const std::string& xmlFileName,
400                                         const Version& version) const {
401     using std::literals::string_literals::operator""s;
402     auto range = getXmlFiles(xmlFileName);
403     for (auto it = range.first; it != range.second; ++it) {
404         const ManifestXmlFile& manifestXmlFile = it->second;
405         if (manifestXmlFile.version() == version) {
406             if (!manifestXmlFile.overriddenPath().empty()) {
407                 return manifestXmlFile.overriddenPath();
408             }
409             return "/"s + (type() == SchemaType::DEVICE ? "vendor" : "system") + "/etc/" +
410                    xmlFileName + "_V" + std::to_string(version.majorVer) + "_" +
411                    std::to_string(version.minorVer) + ".xml";
412         }
413     }
414     return "";
415 }
416 
operator ==(const HalManifest & lft,const HalManifest & rgt)417 bool operator==(const HalManifest &lft, const HalManifest &rgt) {
418     return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals &&
419            lft.mXmlFiles == rgt.mXmlFiles &&
420            (lft.mType != SchemaType::DEVICE ||
421             (lft.device.mSepolicyVersion == rgt.device.mSepolicyVersion &&
422              lft.device.mKernel == rgt.device.mKernel)) &&
423            (lft.mType != SchemaType::FRAMEWORK ||
424             (
425 #pragma clang diagnostic push
426 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
427                 lft.framework.mVndks == rgt.framework.mVndks &&
428 #pragma clang diagnostic pop
429                 lft.framework.mVendorNdks == rgt.framework.mVendorNdks &&
430                 lft.framework.mSystemSdk == rgt.framework.mSystemSdk));
431 }
432 
433 // 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) const434 std::set<std::string> HalManifest::getInstances(const std::string& halName, const Version& version,
435                                                 const std::string& interfaceName) const {
436     std::set<std::string> ret;
437     (void)forEachInstanceOfInterface(halName, version, interfaceName, [&ret](const auto& e) {
438         ret.insert(e.instance());
439         return true;
440     });
441     return ret;
442 }
443 
444 // Return whether instance is in getInstances(...).
hasInstance(const std::string & halName,const Version & version,const std::string & interfaceName,const std::string & instance) const445 bool HalManifest::hasInstance(const std::string& halName, const Version& version,
446                               const std::string& interfaceName, const std::string& instance) const {
447     bool found = false;
448     (void)forEachInstanceOfInterface(halName, version, interfaceName,
449                                      [&found, &instance](const auto& e) {
450                                          found |= (instance == e.instance());
451                                          return !found;  // if not found, continue
452                                      });
453     return found;
454 }
455 
insertInstance(const FqInstance & fqInstance,Transport transport,Arch arch,HalFormat format,std::string * error)456 bool HalManifest::insertInstance(const FqInstance& fqInstance, Transport transport, Arch arch,
457                                  HalFormat format, std::string* error) {
458     for (ManifestHal& hal : getHals()) {
459         if (hal.name == fqInstance.getPackage() && hal.format == format &&
460             hal.transport() == transport && hal.arch() == arch) {
461             return hal.insertInstance(fqInstance, error);
462         }
463     }
464 
465     ManifestHal hal;
466     hal.name = fqInstance.getPackage();
467     hal.format = format;
468     hal.transportArch = TransportArch(transport, arch);
469     if (!hal.insertInstance(fqInstance, error)) return false;
470     return add(std::move(hal));
471 }
472 
empty() const473 bool HalManifest::empty() const {
474     HalManifest emptyManifest;
475     emptyManifest.setType(type());
476     return (*this) == emptyManifest;
477 }
478 
kernel() const479 const std::optional<KernelInfo>& HalManifest::kernel() const {
480     return device.mKernel;
481 }
482 
addAll(HalManifest * other,std::string * error)483 bool HalManifest::addAll(HalManifest* other, std::string* error) {
484     if (other->mMetaVersion.majorVer != mMetaVersion.majorVer) {
485         if (error) {
486             *error = "Cannot merge manifest version " + to_string(mMetaVersion) + " and " +
487                      to_string(other->mMetaVersion);
488         }
489         return false;
490     }
491     mMetaVersion.minorVer = std::max(mMetaVersion.minorVer, other->mMetaVersion.minorVer);
492 
493     if (type() != other->type()) {
494         if (error) {
495             *error = "Cannot add a " + to_string(other->type()) + " manifest to a " +
496                      to_string(type()) + " manifest";
497         }
498         return false;
499     }
500 
501     if (!addAllHals(other, error)) {
502         return false;
503     }
504 
505     if (!addAllXmlFiles(other, error)) {
506         return false;
507     }
508 
509     if (!mergeField(&mLevel, &other->mLevel, Level::UNSPECIFIED)) {
510         if (error) {
511             *error = "Conflicting target-level: " + to_string(level()) + " vs. " +
512                      to_string(other->level());
513         }
514         return false;
515     }
516 
517     if (type() == SchemaType::DEVICE) {
518         if (!mergeField(&device.mSepolicyVersion, &other->device.mSepolicyVersion)) {
519             if (error) {
520                 *error = "Conflicting sepolicy version: " + to_string(sepolicyVersion()) + " vs. " +
521                          to_string(other->sepolicyVersion());
522             }
523             return false;
524         }
525 
526         if (!mergeField(&device.mKernel, &other->device.mKernel)) {
527             // If fails, both have values.
528             if (error) {
529                 *error = "Conflicting kernel: " + to_string(device.mKernel->version()) + " vs. " +
530                          to_string(other->device.mKernel->version());
531             }
532             return false;
533         }
534     } else if (type() == SchemaType::FRAMEWORK) {
535 #pragma clang diagnostic push
536 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
537         framework.mVndks.insert(framework.mVndks.end(), other->framework.mVndks.begin(),
538                                 other->framework.mVndks.end());
539         other->framework.mVndks.clear();
540 #pragma clang diagnostic pop
541 
542         framework.mVendorNdks.insert(framework.mVendorNdks.end(),
543                                      other->framework.mVendorNdks.begin(),
544                                      other->framework.mVendorNdks.end());
545         other->framework.mVendorNdks.clear();
546 
547         framework.mSystemSdk.addAll(&other->framework.mSystemSdk);
548     } else {
549         LOG(FATAL) << "unknown SchemaType: "
550                    << static_cast<std::underlying_type_t<SchemaType>>(type());
551     }
552 
553     if (!other->empty()) {
554         if (error) {
555             *error =
556                 "Cannot add another manifest because it contains extraneous entries that "
557                 "are not recognized.";
558         }
559         return false;
560     }
561 
562     return true;
563 }
564 
565 } // namespace vintf
566 } // namespace android
567