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 "VintfObject.h"
18 
19 #include "CompatibilityMatrix.h"
20 #include "parse_xml.h"
21 #include "utils.h"
22 
23 #include <functional>
24 #include <memory>
25 #include <mutex>
26 
27 namespace android {
28 namespace vintf {
29 
30 template <typename T>
31 struct LockedUniquePtr {
32     std::unique_ptr<T> object;
33     std::mutex mutex;
34 };
35 
36 static LockedUniquePtr<HalManifest> gDeviceManifest;
37 static LockedUniquePtr<HalManifest> gFrameworkManifest;
38 static LockedUniquePtr<CompatibilityMatrix> gDeviceMatrix;
39 static LockedUniquePtr<CompatibilityMatrix> gFrameworkMatrix;
40 static LockedUniquePtr<RuntimeInfo> gDeviceRuntimeInfo;
41 
42 template <typename T, typename F>
Get(LockedUniquePtr<T> * ptr,bool skipCache,const F & fetchAllInformation)43 static const T *Get(
44         LockedUniquePtr<T> *ptr,
45         bool skipCache,
46         const F &fetchAllInformation) {
47     std::unique_lock<std::mutex> _lock(ptr->mutex);
48     if (skipCache || ptr->object == nullptr) {
49         ptr->object = std::make_unique<T>();
50         if (fetchAllInformation(ptr->object.get()) != OK) {
51             ptr->object = nullptr; // frees the old object
52         }
53     }
54     return ptr->object.get();
55 }
56 
57 // static
GetDeviceHalManifest(bool skipCache)58 const HalManifest *VintfObject::GetDeviceHalManifest(bool skipCache) {
59     return Get(&gDeviceManifest, skipCache,
60             std::bind(&HalManifest::fetchAllInformation, std::placeholders::_1,
61                 "/vendor/manifest.xml"));
62 }
63 
64 // static
GetFrameworkHalManifest(bool skipCache)65 const HalManifest *VintfObject::GetFrameworkHalManifest(bool skipCache) {
66     return Get(&gFrameworkManifest, skipCache,
67             std::bind(&HalManifest::fetchAllInformation, std::placeholders::_1,
68                 "/system/manifest.xml"));
69 }
70 
71 
72 // static
GetDeviceCompatibilityMatrix(bool skipCache)73 const CompatibilityMatrix *VintfObject::GetDeviceCompatibilityMatrix(bool skipCache) {
74     return Get(&gDeviceMatrix, skipCache,
75             std::bind(&CompatibilityMatrix::fetchAllInformation, std::placeholders::_1,
76                 "/vendor/compatibility_matrix.xml"));
77 }
78 
79 // static
GetFrameworkCompatibilityMatrix(bool skipCache)80 const CompatibilityMatrix *VintfObject::GetFrameworkCompatibilityMatrix(bool skipCache) {
81     return Get(&gFrameworkMatrix, skipCache,
82             std::bind(&CompatibilityMatrix::fetchAllInformation, std::placeholders::_1,
83                 "/system/compatibility_matrix.xml"));
84 }
85 
86 // static
GetRuntimeInfo(bool skipCache)87 const RuntimeInfo *VintfObject::GetRuntimeInfo(bool skipCache) {
88     return Get(&gDeviceRuntimeInfo, skipCache,
89             std::bind(&RuntimeInfo::fetchAllInformation, std::placeholders::_1));
90 }
91 
92 namespace details {
93 
94 enum class ParseStatus {
95     OK,
96     PARSE_ERROR,
97     DUPLICATED_FWK_ENTRY,
98     DUPLICATED_DEV_ENTRY,
99 };
100 
toString(ParseStatus status)101 static std::string toString(ParseStatus status) {
102     switch(status) {
103         case ParseStatus::OK:                   return "OK";
104         case ParseStatus::PARSE_ERROR:          return "parse error";
105         case ParseStatus::DUPLICATED_FWK_ENTRY: return "duplicated framework";
106         case ParseStatus::DUPLICATED_DEV_ENTRY: return "duplicated device";
107     }
108     return "";
109 }
110 
111 template<typename T>
tryParse(const std::string & xml,const XmlConverter<T> & parse,std::unique_ptr<T> * fwk,std::unique_ptr<T> * dev)112 static ParseStatus tryParse(const std::string &xml, const XmlConverter<T> &parse,
113         std::unique_ptr<T> *fwk, std::unique_ptr<T> *dev) {
114     std::unique_ptr<T> ret = std::make_unique<T>();
115     if (!parse(ret.get(), xml)) {
116         return ParseStatus::PARSE_ERROR;
117     }
118     if (ret->type() == SchemaType::FRAMEWORK) {
119         if (fwk->get() != nullptr) {
120             return ParseStatus::DUPLICATED_FWK_ENTRY;
121         }
122         *fwk = std::move(ret);
123     } else if (ret->type() == SchemaType::DEVICE) {
124         if (dev->get() != nullptr) {
125             return ParseStatus::DUPLICATED_DEV_ENTRY;
126         }
127         *dev = std::move(ret);
128     }
129     return ParseStatus::OK;
130 }
131 
132 template<typename T, typename GetFunction>
getMissing(const T * pkg,bool mount,std::function<status_t (void)> mountFunction,const T ** updated,GetFunction getFunction)133 static status_t getMissing(const T *pkg, bool mount,
134         std::function<status_t(void)> mountFunction,
135         const T **updated,
136         GetFunction getFunction) {
137     if (pkg != nullptr) {
138         *updated = pkg;
139     } else {
140         if (mount) {
141             (void)mountFunction(); // ignore mount errors
142         }
143         *updated = getFunction();
144     }
145     return OK;
146 }
147 
148 #define ADD_MESSAGE(__error__)  \
149     if (error != nullptr) {     \
150         *error += (__error__);  \
151     }                           \
152 
153 struct PackageInfo {
154     struct Pair {
155         std::unique_ptr<HalManifest>         manifest;
156         std::unique_ptr<CompatibilityMatrix> matrix;
157     };
158     Pair dev;
159     Pair fwk;
160 };
161 
162 struct UpdatedInfo {
163     struct Pair {
164         const HalManifest         *manifest;
165         const CompatibilityMatrix *matrix;
166     };
167     Pair dev;
168     Pair fwk;
169     const RuntimeInfo *runtimeInfo;
170 };
171 
172 // Checks given compatibility info against info on the device. If no
173 // compatability info is given then the device info will be checked against
174 // itself.
checkCompatibility(const std::vector<std::string> & xmls,bool mount,const PartitionMounter & mounter,std::string * error)175 int32_t checkCompatibility(const std::vector<std::string> &xmls, bool mount,
176         const PartitionMounter &mounter, std::string *error) {
177 
178     status_t status;
179     ParseStatus parseStatus;
180     PackageInfo pkg; // All information from package.
181     UpdatedInfo updated; // All files and runtime info after the update.
182 
183     // parse all information from package
184     for (const auto &xml : xmls) {
185         parseStatus = tryParse(xml, gHalManifestConverter, &pkg.fwk.manifest, &pkg.dev.manifest);
186         if (parseStatus == ParseStatus::OK) {
187             continue; // work on next one
188         }
189         if (parseStatus != ParseStatus::PARSE_ERROR) {
190             ADD_MESSAGE(toString(parseStatus) + " manifest");
191             return ALREADY_EXISTS;
192         }
193         parseStatus = tryParse(xml, gCompatibilityMatrixConverter, &pkg.fwk.matrix, &pkg.dev.matrix);
194         if (parseStatus == ParseStatus::OK) {
195             continue; // work on next one
196         }
197         if (parseStatus != ParseStatus::PARSE_ERROR) {
198             ADD_MESSAGE(toString(parseStatus) + " matrix");
199             return ALREADY_EXISTS;
200         }
201         ADD_MESSAGE(toString(parseStatus)); // parse error
202         return BAD_VALUE;
203     }
204 
205     // get missing info from device
206     // use functions instead of std::bind because std::bind doesn't work well with mock objects
207     auto mountSystem = [&mounter] { return mounter.mountSystem(); };
208     auto mountVendor = [&mounter] { return mounter.mountVendor(); };
209     if ((status = getMissing(
210              pkg.fwk.manifest.get(), mount, mountSystem, &updated.fwk.manifest,
211              std::bind(VintfObject::GetFrameworkHalManifest, true /* skipCache */))) != OK) {
212         return status;
213     }
214     if ((status = getMissing(
215              pkg.dev.manifest.get(), mount, mountVendor, &updated.dev.manifest,
216              std::bind(VintfObject::GetDeviceHalManifest, true /* skipCache */))) != OK) {
217         return status;
218     }
219     if ((status = getMissing(
220              pkg.fwk.matrix.get(), mount, mountSystem, &updated.fwk.matrix,
221              std::bind(VintfObject::GetFrameworkCompatibilityMatrix, true /* skipCache */))) !=
222         OK) {
223         return status;
224     }
225     if ((status = getMissing(
226              pkg.dev.matrix.get(), mount, mountVendor, &updated.dev.matrix,
227              std::bind(VintfObject::GetDeviceCompatibilityMatrix, true /* skipCache */))) != OK) {
228         return status;
229     }
230 
231     if (mount) {
232         (void)mounter.umountSystem(); // ignore errors
233         (void)mounter.umountVendor(); // ignore errors
234     }
235 
236     updated.runtimeInfo = VintfObject::GetRuntimeInfo(true /* skipCache */);
237 
238     // null checks for files and runtime info after the update
239     // TODO(b/37321309) if a compat mat is missing, it is not matched and considered compatible.
240     if (updated.fwk.manifest == nullptr) {
241         ADD_MESSAGE("No framework manifest file from device or from update package");
242         return NO_INIT;
243     }
244     if (updated.dev.manifest == nullptr) {
245         ADD_MESSAGE("No device manifest file from device or from update package");
246         return NO_INIT;
247     }
248     if (updated.fwk.matrix == nullptr) {
249         ADD_MESSAGE("No framework matrix, skipping;");
250         // TODO(b/37321309) consider missing matricies as errors.
251     }
252     if (updated.dev.matrix == nullptr) {
253         ADD_MESSAGE("No device matrix, skipping;");
254         // TODO(b/37321309) consider missing matricies as errors.
255     }
256     if (updated.runtimeInfo == nullptr) {
257         ADD_MESSAGE("No runtime info from device");
258         return NO_INIT;
259     }
260 
261     // compatiblity check.
262     // TODO(b/37321309) outer if checks can be removed if we consider missing matrices as errors.
263     if (updated.dev.manifest && updated.fwk.matrix) {
264         if (!updated.dev.manifest->checkCompatibility(*updated.fwk.matrix, error)) {
265             if (error)
266                 error->insert(0, "Device manifest and framework compatibility matrix "
267                                  "are incompatible: ");
268             return INCOMPATIBLE;
269         }
270     }
271     if (updated.fwk.manifest && updated.dev.matrix) {
272         if (!updated.fwk.manifest->checkCompatibility(*updated.dev.matrix, error)) {
273             if (error)
274                 error->insert(0, "Framework manifest and device compatibility matrix "
275                                  "are incompatible: ");
276             return INCOMPATIBLE;
277         }
278     }
279     if (updated.runtimeInfo && updated.fwk.matrix) {
280         if (!updated.runtimeInfo->checkCompatibility(*updated.fwk.matrix, error)) {
281             if (error)
282                 error->insert(0, "Runtime info and framework compatibility matrix "
283                                  "are incompatible: ");
284             return INCOMPATIBLE;
285         }
286     }
287 
288     return COMPATIBLE;
289 }
290 
291 } // namespace details
292 
293 // static
CheckCompatibility(const std::vector<std::string> & xmls,std::string * error)294 int32_t VintfObject::CheckCompatibility(
295         const std::vector<std::string> &xmls, std::string *error) {
296     return details::checkCompatibility(xmls, false /* mount */,
297             *details::gPartitionMounter,
298             error);
299 }
300 
301 
302 } // namespace vintf
303 } // namespace android
304