blob: 132cc92b4507fa70ac241a477764d0ad512e2254 [file] [log] [blame]
Yifan Hongf23a6092020-10-13 14:11:31 -07001/*
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
35namespace android::vintf {
36
37namespace {
38
39int usage() {
40 LOG(ERROR) << R"(
41vintffm: Utility to deprecate framework manifest.
42usage:
43vintffm <-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.
46vintffm <-u|--update> <--dirmap /system:system_dir> <-l|--level>=current_level output_frozen_dir
47 Update ${output_frozen_dir}/${current_level}.xml using framework manifest.
48vintffm <-h|--help>
49 Print help message.
50
51Example:
52
53# Freeze a framework manifest for Android R.
54m check-vintf-all # Build framework manifest.
55vintffm --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.
59vintffm --check --dirmap /system:$ANDROID_PRODUCT_OUT/system \
60 system/libhidl/vintfdata/frozen
61)";
62 return EX_USAGE;
63}
64
65class WritableFileSystemImpl : public WritableFileSystem {
66 public:
67 status_t fetch(const std::string& path, std::string* fetched,
68 std::string* error) const override {
69 return mRoFileSystem.fetch(path, fetched, error);
70 }
71 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 }
75 status_t write(const std::string& path, const std::string& content,
76 std::string* error) const override {
77 if (!android::base::WriteStringToFile(content, path)) {
78 int saved_errno = errno;
79 if (error) {
80 *error = "Can't write to " + path + ": " + strerror(saved_errno);
81 }
82 return saved_errno == 0 ? UNKNOWN_ERROR : -saved_errno;
83 }
84 return OK;
85 }
86 status_t deleteFile(const std::string& path, std::string* error) const override {
87 if (unlink(path.c_str()) == -1) {
88 int saved_errno = errno;
89 if (error) {
90 *error = "Can't unlink " + path + ": " + strerror(saved_errno);
91 }
92 return saved_errno == 0 ? UNKNOWN_ERROR : -saved_errno;
93 }
94 return OK;
95 }
96
97 private:
98 details::FileSystemImpl mRoFileSystem;
99};
100
101} // namespace
102
103namespace details {
104
105// A VintfObject with a proper framework manifest and a fake device manifest with
106// only targetFcmVersion.
107class FmOnlyVintfObject : public VintfObject {
108 public:
109 FmOnlyVintfObject(std::unique_ptr<FileSystem>&& fs, Level targetFcmVersion)
110 : mFs(std::move(fs)) {
111 mDeviceManifest = std::make_shared<HalManifest>();
112 mDeviceManifest->mLevel = targetFcmVersion;
113 }
114
115 std::shared_ptr<const HalManifest> getDeviceHalManifest() override { return mDeviceManifest; }
116 std::shared_ptr<const CompatibilityMatrix> getFrameworkCompatibilityMatrix() override {
117 return nullptr;
118 }
119 std::shared_ptr<const CompatibilityMatrix> getDeviceCompatibilityMatrix() override {
120 return nullptr;
121 }
122
123 protected:
124 const std::unique_ptr<FileSystem>& getFileSystem() override { return mFs; }
125 // Set environment to empty to prevent accidentally reading other things.
126 const std::unique_ptr<PropertyFetcher>& getPropertyFetcher() override { return mNoOpProp; }
127
128 private:
129 std::unique_ptr<FileSystem> mFs;
130 std::shared_ptr<HalManifest> mDeviceManifest;
131 std::unique_ptr<PropertyFetcher> mNoOpProp = std::make_unique<details::PropertyFetcherNoOp>();
132};
133
134} // namespace details
135
136VintfFm::VintfFm() : VintfFm(std::make_unique<WritableFileSystemImpl>()) {}
137
138int VintfFm::main(int argc, char** argv) {
139 // clang-format off
140 const struct option longopts[] = {
141 {"check", no_argument, nullptr, 'c'},
142 {"dirmap", required_argument, nullptr, 'd'},
143 {"help", no_argument, nullptr, 'h'},
144 {"level", required_argument, nullptr, 'l'},
145 {"update", no_argument, nullptr, 'u'},
146 {0, 0, 0, 0}};
147 // clang-format on
148
149 bool checking = false;
150 bool updating = false;
151 Level current = Level::UNSPECIFIED;
152 std::vector<std::string> dirmapVec;
153
154 int res;
155 optind = 1;
156 while ((res = getopt_long(argc, argv, "cdhlu", longopts, nullptr)) >= 0) {
157 switch (res) {
158 case 'c': {
159 checking = true;
160 } break;
161
162 case 'd': {
163 dirmapVec.push_back(optarg);
164 } break;
165
166 case 'l': {
167 if (!parse(optarg, &current)) {
168 LOG(ERROR) << "Unable to parse '" << optarg << "' as level.";
169 return usage();
170 }
171 } break;
172
173 case 'u': {
174 updating = true;
175 } break;
176
177 case 'h':
178 default: {
179 return usage();
180 } break;
181 }
182 }
183
184 if ((checking + updating) != 1) {
185 LOG(ERROR) << "Exactly one of --check or --update must be set.";
186 return usage();
187 }
188
189 auto dirmap = details::getDirmap(dirmapVec);
190 auto vintfFsFactory = [&] {
191 return std::make_unique<details::HostFileSystem>(dirmap, NAME_NOT_FOUND, mFs.get());
192 };
193
194 argc -= optind;
195 argv += optind;
196
197 if (argc != 1) {
198 LOG(ERROR) << "There must be exactly 1 positional arguments.";
199 return usage();
200 }
201 auto dir = argv[0];
202
203 if (updating) {
204 return update(vintfFsFactory, dir, current);
205 }
206 return check(vintfFsFactory, dir);
207}
208
209int VintfFm::update(const FsFactory& vintfFsFactory, const std::string& dir, Level level) {
210 if (level == Level::UNSPECIFIED) {
211 LOG(ERROR) << "Must specify last frozen level with --level for --update option.";
212 return usage();
213 }
214
215 auto manifest = getManifestForLevel(vintfFsFactory, level);
216 if (manifest == nullptr) {
217 LOG(ERROR) << "Unable to determine manifests for level " << level;
218 return EX_SOFTWARE;
219 }
220
221 if (!dumpMatrix(*manifest, dir, level)) {
222 return EX_SOFTWARE;
223 }
224
225 return EX_OK;
226}
227
228int VintfFm::check(const FsFactory& vintfFsFactory, const std::string& dir) {
229 auto frozenMatrices = loadMatrices(dir);
230 if (!frozenMatrices.has_value()) {
231 return EX_SOFTWARE;
232 }
233 for (const auto& [level, matrix] : *frozenMatrices) {
234 auto manifest = getManifestForLevel(vintfFsFactory, level);
235 if (manifest == nullptr) {
236 LOG(ERROR) << "Unable to determine manifests for level " << level;
237 return EX_SOFTWARE;
238 }
239 std::string error;
240 if (!manifest->checkCompatibility(matrix, &error)) {
241 LOG(ERROR) << "Framework manifest is incompatible with frozen matrix at level " << level
242 << ": " << error;
243 return EX_SOFTWARE;
244 }
245 }
246 return OK;
247}
248
249std::shared_ptr<const HalManifest> VintfFm::getManifestForLevel(const FsFactory& vintfFsFactory,
250 Level level) {
251 auto vintfObject = std::make_unique<details::FmOnlyVintfObject>(vintfFsFactory(), level);
252 auto frameworkManifest = vintfObject->getFrameworkHalManifest();
253 if (frameworkManifest == nullptr) {
254 LOG(ERROR) << "Unable to get framework HAL manifest for target FCM version " << level;
255 }
256 return frameworkManifest;
257}
258
259bool VintfFm::dumpMatrix(const HalManifest& frameworkManifest, const std::string& dir,
260 Level level) {
261 auto matrix = frameworkManifest.generateCompatibleMatrix(false /*optional*/);
262 std::string path = dir + "/" + to_string(level) + ".xml";
263 std::string error;
Yifan Hong25e2a772021-04-16 18:34:28 -0700264 if (OK != mFs->write(path, toXml(matrix), &error)) {
Yifan Hongf23a6092020-10-13 14:11:31 -0700265 LOG(ERROR) << "Unable to dump matrix to " << path << ": " << error;
266 return false;
267 }
268 return true;
269}
270
271std::optional<VintfFm::FrozenMatrices> VintfFm::loadMatrices(const std::string& dir) {
272 std::string error;
273 std::vector<std::string> allFiles;
274 if (OK != mFs->listFiles(dir, &allFiles, &error)) {
275 LOG(ERROR) << "Unable to list files under " << dir << ": " << error;
276 return std::nullopt;
277 }
278 if (allFiles.empty()) {
279 LOG(ERROR) << "Unable to load frozen interfaces under " << dir << ": directory is empty.";
280 return std::nullopt;
281 }
282 auto ret = std::make_optional<FrozenMatrices>();
283 for (const auto& filename : allFiles) {
284 std::string path = dir + "/" + filename;
285 std::string xmlString;
286 if (OK != mFs->fetch(path, &xmlString, &error)) {
287 LOG(ERROR) << "Unable to read " << path << ": " << error;
288 return std::nullopt;
289 }
290 CompatibilityMatrix matrix;
Yifan Hong25e2a772021-04-16 18:34:28 -0700291 if (!fromXml(&matrix, xmlString, &error)) {
Yifan Hongf23a6092020-10-13 14:11:31 -0700292 LOG(ERROR) << "Unable to parse " << path << ": " << error;
293 return std::nullopt;
294 }
295 std::string_view filenameSv{filename};
296 (void)android::base::ConsumeSuffix(&filenameSv, ".xml");
297 std::string levelString{filenameSv};
298 Level matrixLevel;
299 if (!parse(levelString, &matrixLevel)) {
300 LOG(ERROR) << "Unable to parse " << path << ": " << levelString << " is not a level.";
301 return std::nullopt;
302 }
303 if (ret->find(matrixLevel) != ret->end()) {
304 LOG(ERROR) << "Duplicated level " << matrixLevel << ", second one is at " << path;
305 return std::nullopt;
306 }
307 ret->emplace(matrixLevel, std::move(matrix));
308 }
309 return ret;
310}
311
312} // namespace android::vintf