• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 <getopt.h>
18 #include <sysexits.h>
19 
20 #include <algorithm>
21 #include <map>
22 
23 #include <android-base/file.h>
24 #include <android-base/logging.h>
25 #include <android-base/strings.h>
26 #include <vintf/Dirmap.h>
27 #include <vintf/HostFileSystem.h>
28 #include <vintf/VintfFm.h>
29 #include <vintf/VintfObject.h>
30 #include <vintf/parse_string.h>
31 #include <vintf/parse_xml.h>
32 
33 #include "utils.h"
34 
35 namespace android::vintf {
36 
37 namespace {
38 
usage()39 int usage() {
40     LOG(ERROR) << R"(
41 vintffm: Utility to deprecate framework manifest.
42 usage:
43 vintffm <-c|--check> <--dirmap /system:system_dir> frozen_dir
44   Check framework manifest under system_root against frozen dir. root is the
45   root directory of the device, e.g. $ANDROID_PRODUCT_OUT.
46 vintffm <-u|--update> <--dirmap /system:system_dir> <-l|--level>=current_level output_frozen_dir
47   Update ${output_frozen_dir}/${current_level}.xml using framework manifest.
48 vintffm <-h|--help>
49   Print help message.
50 
51 Example:
52 
53 # Freeze a framework manifest for Android R.
54 m check-vintf-all # Build framework manifest.
55 vintffm --update --dirmap /system:$ANDROID_PRODUCT_OUT/system --level 5 \
56   system/libhidl/vintfdata/frozen
57 
58 # Check that the framework manifest is aligned with the frozen data.
59 vintffm --check --dirmap /system:$ANDROID_PRODUCT_OUT/system \
60   system/libhidl/vintfdata/frozen
61 )";
62     return EX_USAGE;
63 }
64 
65 class WritableFileSystemImpl : public WritableFileSystem {
66    public:
fetch(const std::string & path,std::string * fetched,std::string * error) const67     status_t fetch(const std::string& path, std::string* fetched,
68                    std::string* error) const override {
69         return mRoFileSystem.fetch(path, fetched, error);
70     }
listFiles(const std::string & path,std::vector<std::string> * out,std::string * error) const71     status_t listFiles(const std::string& path, std::vector<std::string>* out,
72                        std::string* error) const override {
73         return mRoFileSystem.listFiles(path, out, error);
74     }
modifiedTime(const std::string & path,timespec * out,std::string * error) const75     status_t modifiedTime(const std::string& path, timespec* out,
76                           std::string* error) const override {
77         return mRoFileSystem.modifiedTime(path, out, error);
78     }
write(const std::string & path,const std::string & content,std::string * error) const79     status_t write(const std::string& path, const std::string& content,
80                    std::string* error) const override {
81         if (!android::base::WriteStringToFile(content, path)) {
82             int saved_errno = errno;
83             if (error) {
84                 *error = "Can't write to " + path + ": " + strerror(saved_errno);
85             }
86             return saved_errno == 0 ? UNKNOWN_ERROR : -saved_errno;
87         }
88         return OK;
89     }
deleteFile(const std::string & path,std::string * error) const90     status_t deleteFile(const std::string& path, std::string* error) const override {
91         if (unlink(path.c_str()) == -1) {
92             int saved_errno = errno;
93             if (error) {
94                 *error = "Can't unlink " + path + ": " + strerror(saved_errno);
95             }
96             return saved_errno == 0 ? UNKNOWN_ERROR : -saved_errno;
97         }
98         return OK;
99     }
100 
101    private:
102     details::FileSystemImpl mRoFileSystem;
103 };
104 
105 }  // namespace
106 
107 namespace details {
108 
109 // A VintfObject with a proper framework manifest and a fake device manifest with
110 // only targetFcmVersion.
111 class FmOnlyVintfObject : public VintfObject {
112    public:
FmOnlyVintfObject(std::unique_ptr<FileSystem> && fs,Level targetFcmVersion)113     FmOnlyVintfObject(std::unique_ptr<FileSystem>&& fs, Level targetFcmVersion)
114         : mFs(std::move(fs)) {
115         mDeviceManifest = std::make_shared<HalManifest>();
116         mDeviceManifest->mLevel = targetFcmVersion;
117     }
118 
getDeviceHalManifest()119     std::shared_ptr<const HalManifest> getDeviceHalManifest() override { return mDeviceManifest; }
getFrameworkCompatibilityMatrix()120     std::shared_ptr<const CompatibilityMatrix> getFrameworkCompatibilityMatrix() override {
121         return nullptr;
122     }
getDeviceCompatibilityMatrix()123     std::shared_ptr<const CompatibilityMatrix> getDeviceCompatibilityMatrix() override {
124         return nullptr;
125     }
126 
127    protected:
getFileSystem()128     const std::unique_ptr<FileSystem>& getFileSystem() override { return mFs; }
129     // Set environment to empty to prevent accidentally reading other things.
getPropertyFetcher()130     const std::unique_ptr<PropertyFetcher>& getPropertyFetcher() override { return mNoOpProp; }
131 
132    private:
133     std::unique_ptr<FileSystem> mFs;
134     std::shared_ptr<HalManifest> mDeviceManifest;
135     std::unique_ptr<PropertyFetcher> mNoOpProp = std::make_unique<details::PropertyFetcherNoOp>();
136 };
137 
138 }  // namespace details
139 
VintfFm()140 VintfFm::VintfFm() : VintfFm(std::make_unique<WritableFileSystemImpl>()) {}
141 
main(int argc,char ** argv)142 int VintfFm::main(int argc, char** argv) {
143     // clang-format off
144     const struct option longopts[] = {
145         {"check", no_argument, nullptr, 'c'},
146         {"dirmap", required_argument, nullptr, 'd'},
147         {"help", no_argument, nullptr, 'h'},
148         {"level", required_argument, nullptr, 'l'},
149         {"update", no_argument, nullptr, 'u'},
150         {0, 0, 0, 0}};
151     // clang-format on
152 
153     bool checking = false;
154     bool updating = false;
155     Level current = Level::UNSPECIFIED;
156     std::vector<std::string> dirmapVec;
157 
158     int res;
159     optind = 1;
160     while ((res = getopt_long(argc, argv, "cdhlu", longopts, nullptr)) >= 0) {
161         switch (res) {
162             case 'c': {
163                 checking = true;
164             } break;
165 
166             case 'd': {
167                 dirmapVec.push_back(optarg);
168             } break;
169 
170             case 'l': {
171                 if (!parse(optarg, &current)) {
172                     LOG(ERROR) << "Unable to parse '" << optarg << "' as level.";
173                     return usage();
174                 }
175             } break;
176 
177             case 'u': {
178                 updating = true;
179             } break;
180 
181             case 'h':
182             default: {
183                 return usage();
184             } break;
185         }
186     }
187 
188     if ((checking + updating) != 1) {
189         LOG(ERROR) << "Exactly one of --check or --update must be set.";
190         return usage();
191     }
192 
193     auto dirmap = details::getDirmap(dirmapVec);
194     auto vintfFsFactory = [&] {
195         return std::make_unique<details::HostFileSystem>(dirmap, NAME_NOT_FOUND, mFs.get());
196     };
197 
198     argc -= optind;
199     argv += optind;
200 
201     if (argc != 1) {
202         LOG(ERROR) << "There must be exactly 1 positional arguments.";
203         return usage();
204     }
205     auto dir = argv[0];
206 
207     if (updating) {
208         return update(vintfFsFactory, dir, current);
209     }
210     return check(vintfFsFactory, dir);
211 }
212 
update(const FsFactory & vintfFsFactory,const std::string & dir,Level level)213 int VintfFm::update(const FsFactory& vintfFsFactory, const std::string& dir, Level level) {
214     if (level == Level::UNSPECIFIED) {
215         LOG(ERROR) << "Must specify last frozen level with --level for --update option.";
216         return usage();
217     }
218 
219     auto manifest = getManifestForLevel(vintfFsFactory, level);
220     if (manifest == nullptr) {
221         LOG(ERROR) << "Unable to determine manifests for level " << level;
222         return EX_SOFTWARE;
223     }
224 
225     if (!dumpMatrix(*manifest, dir, level)) {
226         return EX_SOFTWARE;
227     }
228 
229     return EX_OK;
230 }
231 
check(const FsFactory & vintfFsFactory,const std::string & dir)232 int VintfFm::check(const FsFactory& vintfFsFactory, const std::string& dir) {
233     // Treat all HALs in these frozen matrices as mandatory to have installed.
234     // This is a list of HALs that are not installed on all GSIs (like TVs, Wear
235     // devices, automotive).
236     const std::set<std::string> kOptionalInterfaces = {
237         "android.frameworks.cameraservice.service",
238         "android.frameworks.vibrator",
239         "android.hardware.security.keymint",
240     };
241     auto frozenMatrices = loadMatrices(dir);
242     if (!frozenMatrices.has_value()) {
243         return EX_SOFTWARE;
244     }
245     for (const auto& [level, matrix] : *frozenMatrices) {
246         auto manifest = getManifestForLevel(vintfFsFactory, level);
247         if (manifest == nullptr) {
248             LOG(ERROR) << "Unable to determine manifests for level " << level;
249             return EX_SOFTWARE;
250         }
251         std::string error;
252         if (!manifest->checkCompatibility(matrix, &error)) {
253             LOG(ERROR) << "Framework manifest is incompatible with frozen matrix at level " << level
254                        << ": " << error;
255             return EX_SOFTWARE;
256         }
257         bool mandatoryError = false;
258         matrix.forEachInstance([&](const MatrixInstance& hal) {
259             if (!kOptionalInterfaces.contains(hal.package())) {
260                 bool found = false;
261                 manifest->forEachInstance([&](const ManifestInstance& manifestHal) {
262                     if (hal.package() == manifestHal.package()) {
263                         found = true;
264                     }
265                     return true;
266                 });
267                 if (found == false) {
268                     LOG(ERROR) << "ERROR: " << hal.package()
269                                << " is not declared in the VINTF manifest but is mandatory";
270                     mandatoryError = true;
271                 }
272             }
273             return true;
274         });
275         if (mandatoryError) {
276             LOG(ERROR) << "ERROR: Framework manifest at level "
277                        << std::to_string(static_cast<size_t>(level))
278                        << " is not compatible with the frozen device matrix:\n    "
279                        << matrix.fileName();
280             return EX_SOFTWARE;
281         }
282     }
283     return OK;
284 }
285 
getManifestForLevel(const FsFactory & vintfFsFactory,Level level)286 std::shared_ptr<const HalManifest> VintfFm::getManifestForLevel(const FsFactory& vintfFsFactory,
287                                                                 Level level) {
288     auto vintfObject = std::make_unique<details::FmOnlyVintfObject>(vintfFsFactory(), level);
289     auto frameworkManifest = vintfObject->getFrameworkHalManifest();
290     if (frameworkManifest == nullptr) {
291         LOG(ERROR) << "Unable to get framework HAL manifest for target FCM version " << level;
292     }
293     return frameworkManifest;
294 }
295 
dumpMatrix(const HalManifest & frameworkManifest,const std::string & dir,Level level)296 bool VintfFm::dumpMatrix(const HalManifest& frameworkManifest, const std::string& dir,
297                          Level level) {
298     auto matrix = frameworkManifest.generateCompatibleMatrix();
299     std::string path = dir + "/" + to_string(level) + ".xml";
300     std::string error;
301     if (OK != mFs->write(path, toXml(matrix), &error)) {
302         LOG(ERROR) << "Unable to dump matrix to " << path << ": " << error;
303         return false;
304     }
305     return true;
306 }
307 
loadMatrices(const std::string & dir)308 std::optional<VintfFm::FrozenMatrices> VintfFm::loadMatrices(const std::string& dir) {
309     std::string error;
310     std::vector<std::string> allFiles;
311     if (OK != mFs->listFiles(dir, &allFiles, &error)) {
312         LOG(ERROR) << "Unable to list files under " << dir << ": " << error;
313         return std::nullopt;
314     }
315     if (allFiles.empty()) {
316         LOG(ERROR) << "Unable to load frozen interfaces under " << dir << ": directory is empty.";
317         return std::nullopt;
318     }
319     auto ret = std::make_optional<FrozenMatrices>();
320     for (const auto& filename : allFiles) {
321         std::string path = dir + "/" + filename;
322         std::string xmlString;
323         if (OK != mFs->fetch(path, &xmlString, &error)) {
324             LOG(ERROR) << "Unable to read " << path << ": " << error;
325             return std::nullopt;
326         }
327         CompatibilityMatrix matrix;
328         if (!fromXml(&matrix, xmlString, &error)) {
329             LOG(ERROR) << "Unable to parse " << path << ": " << error;
330             return std::nullopt;
331         }
332         matrix.setFileName(dir + filename);
333         std::string_view filenameSv{filename};
334         (void)android::base::ConsumeSuffix(&filenameSv, ".xml");
335         std::string levelString{filenameSv};
336         Level matrixLevel;
337         if (!parse(levelString, &matrixLevel)) {
338             LOG(ERROR) << "Unable to parse " << path << ": " << levelString << " is not a level.";
339             return std::nullopt;
340         }
341         if (ret->find(matrixLevel) != ret->end()) {
342             LOG(ERROR) << "Duplicated level " << matrixLevel << ", second one is at " << path;
343             return std::nullopt;
344         }
345         ret->emplace(matrixLevel, std::move(matrix));
346     }
347     return ret;
348 }
349 
350 }  // namespace android::vintf
351