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 #include <getopt.h>
18 
19 #include <android-base/strings.h>
20 #include <vintf/VintfObject.h>
21 #include <vintf/parse_string.h>
22 #include <vintf/parse_xml.h>
23 #include <iomanip>
24 #include <iostream>
25 #include <string>
26 #include <vector>
27 
28 using namespace ::android::vintf;
29 
30 static const std::string kColumnSeperator = "   ";
31 
32 std::string existString(bool value) {
33     return value ? "GOOD" : "DOES NOT EXIST";
34 }
35 
36 std::string compatibleString(int32_t value) {
37     switch (value) {
38         case COMPATIBLE:
39             return "GOOD";
40         case INCOMPATIBLE:
41             return "INCOMPATIBLE";
42         default:
43             return strerror(-value);
44     }
45 }
46 
47 std::string boolCompatString(bool value) {
48     return compatibleString(value ? COMPATIBLE : INCOMPATIBLE);
49 }
50 
51 std::string deprecateString(int32_t value) {
52     switch (value) {
53         case NO_DEPRECATED_HALS:
54             return "GOOD";
55         case DEPRECATED:
56             return "DEPRECATED";
57         default:
58             return strerror(-value);
59     }
60 }
61 
62 enum Status : int {
63     OK = 0,
64     USAGE,
65 };
66 
67 struct ParsedOptions {
68     bool verbose = false;
69 };
70 
71 struct Option {
72     char shortOption = '\0';
73     std::string longOption;
74     std::string help;
75     std::function<Status(ParsedOptions*)> op;
76 };
77 
78 std::string getShortOptions(const std::vector<Option>& options) {
79     std::stringstream ret;
80     for (const auto& e : options)
81         if (e.shortOption != '\0') ret << e.shortOption;
82     return ret.str();
83 }
84 
85 std::unique_ptr<struct option[]> getLongOptions(const std::vector<Option>& options,
86                                                 int* longOptFlag) {
87     std::unique_ptr<struct option[]> ret{new struct option[options.size() + 1]};
88     int i = 0;
89     for (const auto& e : options) {
90         ret[i].name = e.longOption.c_str();
91         ret[i].has_arg = no_argument;
92         ret[i].flag = longOptFlag;
93         ret[i].val = i;
94 
95         i++;
96     }
97     // getopt_long last option has all zeros
98     ret[i].name = NULL;
99     ret[i].has_arg = 0;
100     ret[i].flag = NULL;
101     ret[i].val = 0;
102 
103     return ret;
104 }
105 
106 Status parseOptions(int argc, char** argv, const std::vector<Option>& options, ParsedOptions* out) {
107     int longOptFlag;
108     std::unique_ptr<struct option[]> longOptions = getLongOptions(options, &longOptFlag);
109     std::string shortOptions = getShortOptions(options);
110     int optionIndex;
111     for (;;) {
112         int c = getopt_long(argc, argv, shortOptions.c_str(), longOptions.get(), &optionIndex);
113         if (c == -1) {
114             break;
115         }
116         const Option* found = nullptr;
117         for (size_t i = 0; i < options.size(); ++i)
118             if ((c == 0 && longOptFlag == static_cast<int>(i)) ||
119                 (c != 0 && c == options[i].shortOption))
120 
121                 found = &options[i];
122 
123         if (found == nullptr) {
124             // see unrecognized options
125             std::cerr << "unrecognized option `" << argv[optind - 1] << "'" << std::endl;
126             return USAGE;
127         }
128 
129         Status status = found->op(out);
130         if (status != OK) return status;
131     }
132     if (optind < argc) {
133         // see non option
134         std::cerr << "unrecognized option `" << argv[optind] << "'" << std::endl;
135         return USAGE;
136     }
137     return OK;
138 }
139 
140 void usage(char* me, const std::vector<Option>& options) {
141     std::cerr << me << ": dump VINTF metadata via libvintf." << std::endl;
142     for (const auto& e : options) {
143         if (e.help.empty()) continue;
144         std::cerr << "        ";
145         if (e.shortOption != '\0') std::cerr << "-" << e.shortOption;
146         if (e.shortOption != '\0' && !e.longOption.empty()) std::cerr << ", ";
147         if (!e.longOption.empty()) std::cerr << "--" << e.longOption;
148         std::cerr << ": "
149                   << android::base::Join(android::base::Split(e.help, "\n"), "\n            ")
150                   << std::endl;
151     }
152 }
153 
154 struct TableRow {
155     // Whether the HAL version is in device manifest, framework manifest, device compatibility
156     // matrix, framework compatibility matrix, respectively.
157     bool dm = false;
158     bool fm = false;
159     bool dcm = false;
160     bool fcm = false;
161     // If the HAL version is in device / framework compatibility matrix, whether it is required
162     // or not.
163     bool required = false;
164 
165     // Return true if:
166     // - not a required HAL version; OR
167     // - required in device matrix and framework manifest;
168     // - required in framework matrix and device manifest.
169     bool meetsReqeuirement() const {
170         if (!required) return true;
171         if (dcm && !fm) return false;
172         if (fcm && !dm) return false;
173         return true;
174     }
175 };
176 
177 std::ostream& operator<<(std::ostream& out, const TableRow& row) {
178     return out << (row.required ? "R" : " ") << (row.meetsReqeuirement() ? " " : "!")
179                << kColumnSeperator << (row.dm ? "DM" : "  ") << kColumnSeperator
180                << (row.fm ? "FM" : "  ") << kColumnSeperator << (row.fcm ? "FCM" : "   ")
181                << kColumnSeperator << (row.dcm ? "DCM" : "   ");
182 }
183 
184 using RowMutator = std::function<void(TableRow*)>;
185 using Table = std::map<std::string, TableRow>;
186 
187 // Insert each fqInstanceName [email protected]::IFoo/instance to the table by inserting the key
188 // if it does not exist and setting the corresponding indicator (as specified by "mutate").
189 void insert(const HalManifest* manifest, Table* table, const RowMutator& mutate) {
190     if (manifest == nullptr) return;
191     manifest->forEachInstance([&](const auto& manifestInstance) {
192         std::string key = manifestInstance.description();
193         mutate(&(*table)[key]);
194         return true;
195     });
196 }
197 
198 void insert(const CompatibilityMatrix* matrix, Table* table, const RowMutator& mutate) {
199     if (matrix == nullptr) return;
200     matrix->forEachInstance([&](const auto& matrixInstance) {
201         for (auto minorVer = matrixInstance.versionRange().minMinor;
202              minorVer >= matrixInstance.versionRange().minMinor &&
203              minorVer <= matrixInstance.versionRange().maxMinor;
204              ++minorVer) {
205             Version version{matrixInstance.versionRange().majorVer, minorVer};
206             std::string key = matrixInstance.description(version);
207             auto it = table->find(key);
208             if (it == table->end()) {
209                 mutate(&(*table)[key]);
210             } else {
211                 mutate(&it->second);
212                 if (minorVer == matrixInstance.versionRange().minMinor) {
213                     it->second.required = !matrixInstance.optional();
214                 }
215             }
216         }
217         return true;
218     });
219 }
220 
221 Table generateHalSummary(const HalManifest* vm, const HalManifest* fm,
222                          const CompatibilityMatrix* vcm, const CompatibilityMatrix* fcm) {
223     Table table;
224     insert(vm, &table, [](auto* row) { row->dm = true; });
225     insert(fm, &table, [](auto* row) { row->fm = true; });
226     insert(vcm, &table, [](auto* row) { row->dcm = true; });
227     insert(fcm, &table, [](auto* row) { row->fcm = true; });
228 
229     return table;
230 }
231 
232 static const std::vector<Option> gAvailableOptions{
233     {'h', "help", "Print help message.", [](auto) { return USAGE; }},
234     {'v', "verbose", "Dump detailed and raw content, including kernel configurations", [](auto o) {
235          o->verbose = true;
236          return OK;
237      }}};
238 // A convenience binary to dump information available through libvintf.
239 int main(int argc, char** argv) {
240     ParsedOptions options;
241     Status status = parseOptions(argc, argv, gAvailableOptions, &options);
242     if (status == USAGE) usage(argv[0], gAvailableOptions);
243     if (status != OK) return status;
244 
245     auto vm = VintfObject::GetDeviceHalManifest();
246     auto fm = VintfObject::GetFrameworkHalManifest();
247     auto vcm = VintfObject::GetDeviceCompatibilityMatrix();
248     auto fcm = VintfObject::GetFrameworkCompatibilityMatrix();
249     auto ki = VintfObject::GetRuntimeInfo();
250 
251     if (!options.verbose) {
252         std::cout << "======== HALs =========" << std::endl
253                   << "R: required. (empty): optional or missing from matrices. "
254                   << "!: required and not in manifest." << std::endl
255                   << "DM: device manifest. FM: framework manifest." << std::endl
256                   << "FCM: framework compatibility matrix. DCM: device compatibility matrix."
257                   << std::endl
258                   << std::endl;
259         auto table = generateHalSummary(vm.get(), fm.get(), vcm.get(), fcm.get());
260 
261         for (const auto& pair : table)
262             std::cout << pair.second << kColumnSeperator << pair.first << std::endl;
263 
264         std::cout << std::endl;
265     }
266 
267     SerializeFlags::Type flags = SerializeFlags::EVERYTHING;
268     if (!options.verbose) {
269         flags = flags.disableHals().disableKernel();
270     }
271     std::cout << "======== Device HAL Manifest =========" << std::endl;
272     if (vm != nullptr) std::cout << gHalManifestConverter(*vm, flags);
273     std::cout << "======== Framework HAL Manifest =========" << std::endl;
274     if (fm != nullptr) std::cout << gHalManifestConverter(*fm, flags);
275     std::cout << "======== Device Compatibility Matrix =========" << std::endl;
276     if (vcm != nullptr) std::cout << gCompatibilityMatrixConverter(*vcm, flags);
277     std::cout << "======== Framework Compatibility Matrix =========" << std::endl;
278     if (fcm != nullptr) std::cout << gCompatibilityMatrixConverter(*fcm, flags);
279 
280     std::cout << "======== Runtime Info =========" << std::endl;
281     if (ki != nullptr) std::cout << dump(*ki, options.verbose);
282 
283     std::cout << std::endl;
284 
285     std::cout << "======== Summary =========" << std::endl;
286     std::cout << "Device Manifest?    " << existString(vm != nullptr) << std::endl
287               << "Device Matrix?      " << existString(vcm != nullptr) << std::endl
288               << "Framework Manifest? " << existString(fm != nullptr) << std::endl
289               << "Framework Matrix?   " << existString(fcm != nullptr) << std::endl;
290     std::string error;
291     if (vm && fcm) {
292         bool compatible = vm->checkCompatibility(*fcm, &error);
293         std::cout << "Device HAL Manifest <==> Framework Compatibility Matrix? "
294                   << boolCompatString(compatible);
295         if (!compatible)
296             std::cout << ", " << error;
297         std::cout << std::endl;
298     }
299     if (fm && vcm) {
300         bool compatible = fm->checkCompatibility(*vcm, &error);
301         std::cout << "Framework HAL Manifest <==> Device Compatibility Matrix? "
302                   << boolCompatString(compatible);
303         if (!compatible)
304             std::cout << ", " << error;
305         std::cout << std::endl;
306     }
307     if (ki && fcm) {
308         bool compatible = ki->checkCompatibility(*fcm, &error);
309         std::cout << "Runtime info <==> Framework Compatibility Matrix?        "
310                   << boolCompatString(compatible);
311         if (!compatible) std::cout << ", " << error;
312         std::cout << std::endl;
313     }
314 
315     {
316         auto compatible = VintfObject::GetInstance()->checkCompatibility(&error);
317         std::cout << "VintfObject::checkCompatibility?                         "
318                   << compatibleString(compatible);
319         if (compatible != COMPATIBLE) std::cout << ", " << error;
320         std::cout << std::endl;
321     }
322 
323     if (vm && fcm) {
324         // TODO(b/131717099): Use correct information from libhidlmetadata
325         auto deprecate = VintfObject::GetInstance()->checkDeprecation({}, &error);
326         std::cout << "VintfObject::CheckDeprecation (against device manifest) (w/o hidlmetadata)? "
327                   << deprecateString(deprecate);
328         if (deprecate != NO_DEPRECATED_HALS) std::cout << ", " << error;
329         std::cout << std::endl;
330     }
331 }
332