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, ¤t)) {
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