1 /*
2  * Copyright (c) 2021, 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 #define LOG_TAG "carwatchdogd"
18 
19 #include "OveruseConfigurationXmlHelper.h"
20 
21 #include <aidl/android/automotive/watchdog/PerStateBytes.h>
22 #include <aidl/android/automotive/watchdog/internal/ApplicationCategoryType.h>
23 #include <aidl/android/automotive/watchdog/internal/ComponentType.h>
24 #include <aidl/android/automotive/watchdog/internal/IoOveruseAlertThreshold.h>
25 #include <aidl/android/automotive/watchdog/internal/IoOveruseConfiguration.h>
26 #include <aidl/android/automotive/watchdog/internal/PackageMetadata.h>
27 #include <aidl/android/automotive/watchdog/internal/PerStateIoOveruseThreshold.h>
28 #include <aidl/android/automotive/watchdog/internal/ResourceSpecificConfiguration.h>
29 #include <android-base/parseint.h>
30 #include <android-base/stringprintf.h>
31 #include <android-base/strings.h>
32 
33 #include <tinyxml2.h>
34 
35 #include <unordered_set>
36 #include <vector>
37 
38 namespace android {
39 namespace automotive {
40 namespace watchdog {
41 
42 using ::aidl::android::automotive::watchdog::PerStateBytes;
43 using ::aidl::android::automotive::watchdog::internal::ApplicationCategoryType;
44 using ::aidl::android::automotive::watchdog::internal::ComponentType;
45 using ::aidl::android::automotive::watchdog::internal::IoOveruseAlertThreshold;
46 using ::aidl::android::automotive::watchdog::internal::IoOveruseConfiguration;
47 using ::aidl::android::automotive::watchdog::internal::PackageMetadata;
48 using ::aidl::android::automotive::watchdog::internal::PerStateIoOveruseThreshold;
49 using ::aidl::android::automotive::watchdog::internal::ResourceOveruseConfiguration;
50 using ::aidl::android::automotive::watchdog::internal::ResourceSpecificConfiguration;
51 using ::android::base::EqualsIgnoreCase;
52 using ::android::base::Error;
53 using ::android::base::Join;
54 using ::android::base::ParseInt;
55 using ::android::base::Result;
56 using ::android::base::StartsWith;
57 using ::android::base::StringAppendF;
58 using ::android::base::StringPrintf;
59 using ::android::base::Trim;
60 using ::tinyxml2::XML_SUCCESS;
61 using ::tinyxml2::XMLDeclaration;
62 using ::tinyxml2::XMLDocument;
63 using ::tinyxml2::XMLElement;
64 
65 namespace {
66 constexpr const char kTagResourceOveruseConfiguration[] = "resourceOveruseConfiguration";
67 constexpr const char kTagComponentType[] = "componentType";
68 
69 constexpr const char kTagSafeToKillPackages[] = "safeToKillPackages";
70 constexpr const char kTagPackage[] = "package";
71 
72 constexpr const char kTagVendorPackagePrefixes[] = "vendorPackagePrefixes";
73 constexpr const char kTagPackagePrefix[] = "packagePrefix";
74 
75 constexpr const char kTagPackageToAppCategoryTypes[] = "packagesToAppCategoryTypes";
76 constexpr const char kTagPackageAppCategory[] = "packageAppCategory";
77 
78 constexpr const char kTagIoOveruseConfiguration[] = "ioOveruseConfiguration";
79 constexpr const char kTagComponentLevelThresholds[] = "componentLevelThresholds";
80 constexpr const char kTagPackageSpecificThresholds[] = "packageSpecificThresholds";
81 constexpr const char kTagAppCategorySpecificThresholds[] = "appCategorySpecificThresholds";
82 constexpr const char kTagPerStateThreshold[] = "perStateThreshold";
83 constexpr const char kTagState[] = "state";
84 constexpr const char kStateIdForegroundMode[] = "foreground_mode";
85 constexpr const char kStateIdBackgroundMode[] = "background_mode";
86 constexpr const char kStateIdGarageMode[] = "garage_mode";
87 constexpr int kNumStates = 3;
88 
89 constexpr const char kTagSystemWideThresholds[] = "systemWideThresholds";
90 constexpr const char kTagAlertThreshold[] = "alertThreshold";
91 constexpr const char kTagParam[] = "param";
92 constexpr const char kParamIdDurationSeconds[] = "duration_seconds";
93 constexpr const char kParamIdWrittenBytesPerSecond[] = "written_bytes_per_second";
94 constexpr int kNumParams = 2;
95 
96 constexpr const char kAttrId[] = "id";
97 constexpr const char kAttrType[] = "type";
98 constexpr const char kAttrVersion[] = "version";
99 constexpr const char kVersionNumber[] = "1.0";
100 
readExactlyOneElement(const char * tag,const XMLElement * rootElement)101 Result<const XMLElement*> readExactlyOneElement(const char* tag, const XMLElement* rootElement) {
102     const XMLElement* element = rootElement->FirstChildElement(tag);
103     if (element == nullptr) {
104         return Error() << "Must specify value for the tag '" << tag << "'";
105     }
106     if (element->NextSiblingElement(tag) != nullptr) {
107         return Error() << "Must specify only one entry for the tag '" << tag << "'";
108     }
109     return element;
110 }
111 
readComponentType(const XMLElement * rootElement)112 Result<ComponentType> readComponentType(const XMLElement* rootElement) {
113     const XMLElement* componentTypeElement;
114     if (const auto result = readExactlyOneElement(kTagComponentType, rootElement); result.ok()) {
115         componentTypeElement = *result;
116     } else {
117         return Error() << "Failed to read tag '" << kTagComponentType << "': " << result.error();
118     }
119     std::string componentTypeStr;
120     if (const auto text = componentTypeElement->GetText(); text == nullptr) {
121         return Error() << "Must specify non-empty component type";
122     } else if (componentTypeStr = Trim(text); componentTypeStr.empty()) {
123         return Error() << "Must specify non-empty component type";
124     }
125     static const std::string* const kSystemComponent =
126             new std::string(toString(ComponentType::SYSTEM));
127     static const std::string* const kVendorComponent =
128             new std::string(toString(ComponentType::VENDOR));
129     static const std::string* const kThirdPartyComponent =
130             new std::string(toString(ComponentType::THIRD_PARTY));
131     if (EqualsIgnoreCase(componentTypeStr, *kSystemComponent)) {
132         return ComponentType::SYSTEM;
133     } else if (EqualsIgnoreCase(componentTypeStr, *kVendorComponent)) {
134         return ComponentType::VENDOR;
135     } else if (EqualsIgnoreCase(componentTypeStr, *kThirdPartyComponent)) {
136         return ComponentType::THIRD_PARTY;
137     }
138     return Error() << "Must specify valid component type. Received " << componentTypeStr;
139 }
140 
readSafeToKillPackages(const XMLElement * rootElement)141 Result<std::vector<std::string>> readSafeToKillPackages(const XMLElement* rootElement) {
142     std::vector<std::string> safeToKillPackages;
143     for (const XMLElement* outerElement = rootElement->FirstChildElement(kTagSafeToKillPackages);
144          outerElement != nullptr;
145          outerElement = outerElement->NextSiblingElement(kTagSafeToKillPackages)) {
146         for (const XMLElement* innerElement = outerElement->FirstChildElement(kTagPackage);
147              innerElement != nullptr;
148              innerElement = innerElement->NextSiblingElement(kTagPackage)) {
149             std::string packageName;
150             if (const auto text = innerElement->GetText(); text == nullptr) {
151                 return Error() << "Must specify non-empty safe-to-kill package name";
152             } else if (packageName = Trim(text); packageName.empty()) {
153                 return Error() << "Must specify non-empty safe-to-kill package name";
154             }
155             safeToKillPackages.push_back(std::string(packageName));
156         }
157     }
158     return safeToKillPackages;
159 }
160 
readVendorPackagePrefixes(const XMLElement * rootElement)161 Result<std::vector<std::string>> readVendorPackagePrefixes(const XMLElement* rootElement) {
162     std::vector<std::string> vendorPackagePrefixes;
163     for (const XMLElement* outerElement = rootElement->FirstChildElement(kTagVendorPackagePrefixes);
164          outerElement != nullptr;
165          outerElement = outerElement->NextSiblingElement(kTagVendorPackagePrefixes)) {
166         for (const XMLElement* innerElement = outerElement->FirstChildElement(kTagPackagePrefix);
167              innerElement != nullptr;
168              innerElement = innerElement->NextSiblingElement(kTagPackagePrefix)) {
169             std::string packagePrefix;
170             if (const auto text = innerElement->GetText(); text == nullptr) {
171                 return Error() << "Must specify non-empty vendor package prefix";
172             } else if (packagePrefix = Trim(text); packagePrefix.empty()) {
173                 return Error() << "Must specify non-empty vendor package prefix";
174             }
175             vendorPackagePrefixes.push_back(std::string(packagePrefix));
176         }
177     }
178     return vendorPackagePrefixes;
179 }
180 
toApplicationCategoryType(std::string_view value)181 ApplicationCategoryType toApplicationCategoryType(std::string_view value) {
182     static const std::string* const kMapsAppCategory =
183             new std::string(toString(ApplicationCategoryType::MAPS));
184     static const std::string* const kMediaAppCategory =
185             new std::string(toString(ApplicationCategoryType::MEDIA));
186     if (EqualsIgnoreCase(value, *kMapsAppCategory)) {
187         return ApplicationCategoryType::MAPS;
188     } else if (EqualsIgnoreCase(value, *kMediaAppCategory)) {
189         return ApplicationCategoryType::MEDIA;
190     }
191     return ApplicationCategoryType::OTHERS;
192 }
193 
readPackageToAppCategoryTypes(const XMLElement * rootElement)194 Result<std::vector<PackageMetadata>> readPackageToAppCategoryTypes(const XMLElement* rootElement) {
195     std::vector<PackageMetadata> packageMetadata;
196     for (const XMLElement* outerElement =
197                  rootElement->FirstChildElement(kTagPackageToAppCategoryTypes);
198          outerElement != nullptr;
199          outerElement = outerElement->NextSiblingElement(kTagPackageToAppCategoryTypes)) {
200         for (const XMLElement* innerElement =
201                      outerElement->FirstChildElement(kTagPackageAppCategory);
202              innerElement != nullptr;
203              innerElement = innerElement->NextSiblingElement(kTagPackageAppCategory)) {
204             const char* type = nullptr;
205             if (innerElement->QueryStringAttribute(kAttrType, &type) != XML_SUCCESS) {
206                 return Error() << "Failed to read '" << kAttrType << "' attribute in '"
207                                << kTagPackageAppCategory << "' tag";
208             }
209             PackageMetadata meta;
210             if (meta.appCategoryType = toApplicationCategoryType(type);
211                 meta.appCategoryType == ApplicationCategoryType::OTHERS) {
212                 return Error() << "Must specify valid app category type. Received " << type;
213             }
214             if (const auto text = innerElement->GetText(); text == nullptr) {
215                 return Error() << "Must specify non-empty package name";
216             } else if (meta.packageName = Trim(text); meta.packageName.empty()) {
217                 return Error() << "Must specify non-empty package name";
218             }
219             packageMetadata.push_back(meta);
220         }
221     }
222     return packageMetadata;
223 }
224 
readPerStateBytes(const XMLElement * rootElement)225 Result<PerStateBytes> readPerStateBytes(const XMLElement* rootElement) {
226     PerStateBytes perStateBytes;
227     std::unordered_set<std::string> seenStates;
228     for (const XMLElement* childElement = rootElement->FirstChildElement(kTagState);
229          childElement != nullptr; childElement = childElement->NextSiblingElement(kTagState)) {
230         const char* state = nullptr;
231         if (childElement->QueryStringAttribute(kAttrId, &state) != XML_SUCCESS) {
232             return Error() << "Failed to read '" << kAttrId << "' attribute in '" << kTagState
233                            << "' tag";
234         }
235         if (seenStates.find(state) != seenStates.end()) {
236             return Error() << "Duplicate threshold specified for state '" << state << "'";
237         }
238         int64_t megaBytes = 0;
239         if (const auto text = childElement->GetText(); text == nullptr) {
240             return Error() << "Must specify non-empty threshold for state '" << state << "'";
241         } else if (const auto megaBytesStr = Trim(text);
242                    !ParseInt(megaBytesStr.c_str(), &megaBytes)) {
243             return Error() << "Failed to parse threshold for the state '" << state
244                            << "': Received threshold value '" << megaBytesStr << "'";
245         }
246         if (!strcmp(state, kStateIdForegroundMode)) {
247             seenStates.insert(kStateIdForegroundMode);
248             perStateBytes.foregroundBytes = megaBytes * kOneMegaByte;
249         } else if (!strcmp(state, kStateIdBackgroundMode)) {
250             seenStates.insert(kStateIdBackgroundMode);
251             perStateBytes.backgroundBytes = megaBytes * kOneMegaByte;
252         } else if (!strcmp(state, kStateIdGarageMode)) {
253             seenStates.insert(kStateIdGarageMode);
254             perStateBytes.garageModeBytes = megaBytes * kOneMegaByte;
255         } else {
256             return Error() << "Invalid state '" << state << "' in per-state bytes";
257         }
258     }
259     if (seenStates.size() != kNumStates) {
260         return Error() << "Thresholds not specified for all states. Specified only for ["
261                        << Join(seenStates, ", ") << "] states";
262     }
263     return perStateBytes;
264 }
265 
readComponentLevelThresholds(ComponentType componentType,const XMLElement * rootElement)266 Result<PerStateIoOveruseThreshold> readComponentLevelThresholds(ComponentType componentType,
267                                                                 const XMLElement* rootElement) {
268     const XMLElement* componentLevelThresholdElement = nullptr;
269     if (const auto result = readExactlyOneElement(kTagComponentLevelThresholds, rootElement);
270         result.ok()) {
271         componentLevelThresholdElement = *result;
272     } else {
273         return Error() << "Failed to read tag '" << kTagComponentLevelThresholds
274                        << "': " << result.error();
275     }
276     PerStateIoOveruseThreshold thresholds;
277     thresholds.name = toString(componentType);
278     if (const auto result = readPerStateBytes(componentLevelThresholdElement); result.ok()) {
279         thresholds.perStateWriteBytes = *result;
280     } else {
281         return Error() << "Failed to read component level thresholds for component '"
282                        << thresholds.name << "': " << result.error();
283     }
284     return thresholds;
285 }
286 
readPerStateThresholds(const XMLElement * rootElement)287 Result<std::vector<PerStateIoOveruseThreshold>> readPerStateThresholds(
288         const XMLElement* rootElement) {
289     std::vector<PerStateIoOveruseThreshold> thresholds;
290     for (const XMLElement* childElement = rootElement->FirstChildElement(kTagPerStateThreshold);
291          childElement != nullptr;
292          childElement = childElement->NextSiblingElement(kTagPerStateThreshold)) {
293         PerStateIoOveruseThreshold threshold;
294         if (const char* name = nullptr;
295             childElement->QueryStringAttribute(kAttrId, &name) != XML_SUCCESS) {
296             return Error() << "Failed to read '" << kAttrId << "' attribute";
297         } else if (threshold.name = name; threshold.name.empty()) {
298             return Error() << "Must provide non-empty value in '" << kAttrId << "' attribute";
299         }
300         if (const auto result = readPerStateBytes(childElement); result.ok()) {
301             threshold.perStateWriteBytes = *result;
302         } else {
303             return Error() << "Failed to read thresholds for id '" << threshold.name
304                            << "': " << result.error();
305         }
306         thresholds.push_back(threshold);
307     }
308     return thresholds;
309 }
310 
readPackageSpecificThresholds(const XMLElement * rootElement)311 Result<std::vector<PerStateIoOveruseThreshold>> readPackageSpecificThresholds(
312         const XMLElement* rootElement) {
313     std::vector<PerStateIoOveruseThreshold> thresholds;
314     for (const XMLElement* childElement =
315                  rootElement->FirstChildElement(kTagPackageSpecificThresholds);
316          childElement != nullptr;
317          childElement = childElement->NextSiblingElement(kTagPackageSpecificThresholds)) {
318         if (const auto result = readPerStateThresholds(childElement); result.ok()) {
319             thresholds.insert(thresholds.end(), result->begin(), result->end());
320         } else {
321             return Error() << "Failed to read package specific thresholds from tag'"
322                            << kTagPackageSpecificThresholds << "': " << result.error();
323         }
324     }
325     return thresholds;
326 }
327 
readAppCategorySpecificThresholds(const XMLElement * rootElement)328 Result<std::vector<PerStateIoOveruseThreshold>> readAppCategorySpecificThresholds(
329         const XMLElement* rootElement) {
330     std::vector<PerStateIoOveruseThreshold> thresholds;
331     for (const XMLElement* childElement =
332                  rootElement->FirstChildElement(kTagAppCategorySpecificThresholds);
333          childElement != nullptr;
334          childElement = childElement->NextSiblingElement(kTagAppCategorySpecificThresholds)) {
335         if (const auto result = readPerStateThresholds(childElement); result.ok()) {
336             thresholds.insert(thresholds.end(), result->begin(), result->end());
337         } else {
338             return Error() << "Failed to read app category specific thresholds from tag'"
339                            << kTagAppCategorySpecificThresholds << "': " << result.error();
340         }
341     }
342     return thresholds;
343 }
344 
readIoOveruseAlertThreshold(const XMLElement * rootElement)345 Result<IoOveruseAlertThreshold> readIoOveruseAlertThreshold(const XMLElement* rootElement) {
346     IoOveruseAlertThreshold alertThreshold;
347     std::unordered_set<std::string> seenParams;
348     for (const XMLElement* childElement = rootElement->FirstChildElement(kTagParam);
349          childElement != nullptr; childElement = childElement->NextSiblingElement(kTagParam)) {
350         const char* param = nullptr;
351         if (childElement->QueryStringAttribute(kAttrId, &param) != XML_SUCCESS) {
352             return Error() << "Failed to read '" << kAttrId << "' attribute in '" << kTagParam
353                            << "' tag";
354         }
355         if (seenParams.find(param) != seenParams.end()) {
356             return Error() << "Duplicate threshold specified for param '" << param << "'";
357         }
358         int64_t value = 0;
359         if (const auto text = childElement->GetText(); text == nullptr) {
360             return Error() << "Must specify non-empty threshold for param '" << param << "'";
361         } else if (const auto valueStr = Trim(text); !ParseInt(valueStr.c_str(), &value)) {
362             return Error() << "Failed to parse threshold for the param '" << param
363                            << "': Received threshold value '" << valueStr << "'";
364         }
365         if (!strcmp(param, kParamIdDurationSeconds)) {
366             seenParams.insert(kParamIdDurationSeconds);
367             alertThreshold.durationInSeconds = value;
368         } else if (!strcmp(param, kParamIdWrittenBytesPerSecond)) {
369             seenParams.insert(kParamIdWrittenBytesPerSecond);
370             alertThreshold.writtenBytesPerSecond = value;
371         } else {
372             return Error() << "Invalid param '" << param << "' in I/O overuse alert thresholds";
373         }
374     }
375     if (seenParams.size() != kNumParams) {
376         return Error() << "Thresholds not specified for all params. Specified only for ["
377                        << Join(seenParams, ", ") << "] params";
378     }
379     return alertThreshold;
380 }
381 
readSystemWideThresholds(const XMLElement * rootElement)382 Result<std::vector<IoOveruseAlertThreshold>> readSystemWideThresholds(
383         const XMLElement* rootElement) {
384     std::vector<IoOveruseAlertThreshold> alertThresholds;
385     for (const XMLElement* outerElement = rootElement->FirstChildElement(kTagSystemWideThresholds);
386          outerElement != nullptr;
387          outerElement = outerElement->NextSiblingElement(kTagSystemWideThresholds)) {
388         for (const XMLElement* innerElement = outerElement->FirstChildElement(kTagAlertThreshold);
389              innerElement != nullptr;
390              innerElement = innerElement->NextSiblingElement(kTagAlertThreshold)) {
391             const auto result = readIoOveruseAlertThreshold(innerElement);
392             if (!result.ok()) {
393                 return Error() << "Failed to system wide thresholds from tag '"
394                                << kTagAlertThreshold << "': " << result.error();
395             }
396             alertThresholds.push_back(*result);
397         }
398     }
399     return alertThresholds;
400 }
401 
readIoOveruseConfiguration(ComponentType componentType,const XMLElement * rootElement)402 Result<IoOveruseConfiguration> readIoOveruseConfiguration(ComponentType componentType,
403                                                           const XMLElement* rootElement) {
404     const XMLElement* childElement = nullptr;
405     if (const auto result = readExactlyOneElement(kTagIoOveruseConfiguration, rootElement);
406         result.ok()) {
407         childElement = *result;
408     } else {
409         return Error() << "Failed to read tag '" << kTagIoOveruseConfiguration
410                        << "': " << result.error();
411     }
412     IoOveruseConfiguration configuration;
413     if (const auto result = readComponentLevelThresholds(componentType, childElement);
414         result.ok()) {
415         configuration.componentLevelThresholds = *result;
416     } else {
417         return Error() << "Failed to read component-level thresholds: " << result.error();
418     }
419     if (const auto result = readPackageSpecificThresholds(childElement); result.ok()) {
420         configuration.packageSpecificThresholds = *result;
421     } else {
422         return Error() << "Failed to read package specific thresholds: " << result.error();
423     }
424     if (const auto result = readAppCategorySpecificThresholds(childElement); result.ok()) {
425         configuration.categorySpecificThresholds = *result;
426     } else {
427         return Error() << "Failed to read category specific thresholds: " << result.error();
428     }
429     if (const auto result = readSystemWideThresholds(childElement); result.ok()) {
430         configuration.systemWideThresholds = *result;
431     } else {
432         return Error() << "Failed to read system-wide thresholds: " << result.error();
433     }
434     return configuration;
435 }
436 
writeComponentType(ComponentType componentType,XMLElement * rootElement)437 Result<void> writeComponentType(ComponentType componentType, XMLElement* rootElement) {
438     XMLElement* childElement = rootElement->InsertNewChildElement(kTagComponentType);
439     if (!childElement) {
440         return Error() << "Failed to insert new child element with tag '" << kTagComponentType
441                        << "'";
442     }
443     childElement->SetText(toString(componentType).c_str());
444     return {};
445 }
446 
writeSafeToKillPackages(const std::vector<std::string> & safeToKillPackages,XMLElement * rootElement)447 Result<void> writeSafeToKillPackages(const std::vector<std::string>& safeToKillPackages,
448                                      XMLElement* rootElement) {
449     if (safeToKillPackages.empty()) {
450         return {};
451     }
452     XMLElement* outerElement = rootElement->InsertNewChildElement(kTagSafeToKillPackages);
453     if (!outerElement) {
454         return Error() << "Failed to insert new child element with tag '" << kTagSafeToKillPackages
455                        << "'";
456     }
457     for (const auto& package : safeToKillPackages) {
458         XMLElement* innerElement = outerElement->InsertNewChildElement(kTagPackage);
459         if (!innerElement) {
460             return Error() << "Failed to insert new child element with tag '" << kTagPackage << "'";
461         }
462         innerElement->SetText(package.c_str());
463     }
464     return {};
465 }
466 
writeVendorPackagePrefixes(const std::vector<std::string> & vendorPackagePrefixes,XMLElement * rootElement)467 Result<void> writeVendorPackagePrefixes(const std::vector<std::string>& vendorPackagePrefixes,
468                                         XMLElement* rootElement) {
469     if (vendorPackagePrefixes.empty()) {
470         return {};
471     }
472     XMLElement* outerElement = rootElement->InsertNewChildElement(kTagVendorPackagePrefixes);
473     if (!outerElement) {
474         return Error() << "Failed to insert new child element with tag '"
475                        << kTagVendorPackagePrefixes << "'";
476     }
477     for (const auto& packagePrefix : vendorPackagePrefixes) {
478         XMLElement* innerElement = outerElement->InsertNewChildElement(kTagPackagePrefix);
479         if (!innerElement) {
480             return Error() << "Failed to insert new child element with tag '" << kTagPackagePrefix
481                            << "'";
482         }
483         innerElement->SetText(packagePrefix.c_str());
484     }
485     return {};
486 }
487 
writePackageToAppCategoryTypes(const std::vector<PackageMetadata> & packageMetadata,XMLElement * rootElement)488 Result<void> writePackageToAppCategoryTypes(const std::vector<PackageMetadata>& packageMetadata,
489                                             XMLElement* rootElement) {
490     if (packageMetadata.empty()) {
491         return {};
492     }
493     XMLElement* outerElement = rootElement->InsertNewChildElement(kTagPackageToAppCategoryTypes);
494     if (!outerElement) {
495         return Error() << "Failed to insert new child element with tag '"
496                        << kTagPackageToAppCategoryTypes << "'";
497     }
498     for (const auto& meta : packageMetadata) {
499         XMLElement* innerElement = outerElement->InsertNewChildElement(kTagPackageAppCategory);
500         if (!innerElement) {
501             return Error() << "Failed to insert new child element with tag '"
502                            << kTagPackageAppCategory << "'";
503         }
504         innerElement->SetAttribute(kAttrType, toString(meta.appCategoryType).c_str());
505         innerElement->SetText(meta.packageName.c_str());
506     }
507     return {};
508 }
509 
writePerStateBytes(const PerStateBytes & perStateBytes,XMLElement * rootElement)510 Result<void> writePerStateBytes(const PerStateBytes& perStateBytes, XMLElement* rootElement) {
511     const auto writeStateElement = [&](const char* state, int64_t value) -> Result<void> {
512         XMLElement* childElement = rootElement->InsertNewChildElement(kTagState);
513         if (!childElement) {
514             return Error() << "Failed to insert new child element with tag '" << kTagState << "'";
515         }
516         childElement->SetAttribute(kAttrId, state);
517         childElement->SetText(value);
518         return {};
519     };
520     if (const auto result = writeStateElement(kStateIdForegroundMode,
521                                               perStateBytes.foregroundBytes / kOneMegaByte);
522         !result.ok()) {
523         return Error() << "Failed to write bytes for state '" << kStateIdForegroundMode
524                        << "': " << result.error();
525     }
526     if (const auto result = writeStateElement(kStateIdBackgroundMode,
527                                               perStateBytes.backgroundBytes / kOneMegaByte);
528         !result.ok()) {
529         return Error() << "Failed to write bytes for state '" << kStateIdBackgroundMode
530                        << "': " << result.error();
531     }
532     if (const auto result =
533                 writeStateElement(kStateIdGarageMode, perStateBytes.garageModeBytes / kOneMegaByte);
534         !result.ok()) {
535         return Error() << "Failed to write bytes for state '" << kStateIdGarageMode
536                        << "': " << result.error();
537     }
538     return {};
539 }
540 
writeComponentLevelThresholds(const PerStateIoOveruseThreshold & thresholds,XMLElement * rootElement)541 Result<void> writeComponentLevelThresholds(const PerStateIoOveruseThreshold& thresholds,
542                                            XMLElement* rootElement) {
543     XMLElement* childElement = rootElement->InsertNewChildElement(kTagComponentLevelThresholds);
544     if (!childElement) {
545         return Error() << "Failed to insert new child element with tag '"
546                        << kTagComponentLevelThresholds << "'";
547     }
548     if (const auto result = writePerStateBytes(thresholds.perStateWriteBytes, childElement);
549         !result.ok()) {
550         return Error() << "Failed to write per-state bytes: " << result.error();
551     }
552     return {};
553 }
554 
writePerStateThresholds(const PerStateIoOveruseThreshold & thresholds,XMLElement * rootElement)555 Result<void> writePerStateThresholds(const PerStateIoOveruseThreshold& thresholds,
556                                      XMLElement* rootElement) {
557     XMLElement* childElement = rootElement->InsertNewChildElement(kTagPerStateThreshold);
558     if (!childElement) {
559         return Error() << "Failed to insert new child element with tag '" << kTagPerStateThreshold
560                        << "'";
561     }
562     childElement->SetAttribute(kAttrId, thresholds.name.c_str());
563     if (const auto result = writePerStateBytes(thresholds.perStateWriteBytes, childElement);
564         !result.ok()) {
565         return Error() << "Failed to write per-state bytes: " << result.error();
566     }
567     return {};
568 }
569 
writePackageSpecificThresholds(const std::vector<PerStateIoOveruseThreshold> & thresholds,XMLElement * rootElement)570 Result<void> writePackageSpecificThresholds(
571         const std::vector<PerStateIoOveruseThreshold>& thresholds, XMLElement* rootElement) {
572     XMLElement* childElement = rootElement->InsertNewChildElement(kTagPackageSpecificThresholds);
573     if (!childElement) {
574         return Error() << "Failed to insert new child element with tag '"
575                        << kTagPackageSpecificThresholds << "'";
576     }
577     for (const auto threshold : thresholds) {
578         if (const auto result = writePerStateThresholds(threshold, childElement); !result.ok()) {
579             return Error() << "Failed to write per-state thresholds for '" << threshold.name
580                            << "': " << result.error();
581         }
582     }
583     return {};
584 }
585 
writeAppCategorySpecificThresholds(const std::vector<PerStateIoOveruseThreshold> & thresholds,XMLElement * rootElement)586 Result<void> writeAppCategorySpecificThresholds(
587         const std::vector<PerStateIoOveruseThreshold>& thresholds, XMLElement* rootElement) {
588     XMLElement* childElement =
589             rootElement->InsertNewChildElement(kTagAppCategorySpecificThresholds);
590     if (!childElement) {
591         return Error() << "Failed to insert new child element with tag '"
592                        << kTagAppCategorySpecificThresholds << "'";
593     }
594     for (const auto threshold : thresholds) {
595         if (const auto result = writePerStateThresholds(threshold, childElement); !result.ok()) {
596             return Error() << "Failed to write per-state thresholds for '" << threshold.name
597                            << "': " << result.error();
598         }
599     }
600     return {};
601 }
602 
writeAlertThresholds(const IoOveruseAlertThreshold & alertThresholds,XMLElement * rootElement)603 Result<void> writeAlertThresholds(const IoOveruseAlertThreshold& alertThresholds,
604                                   XMLElement* rootElement) {
605     XMLElement* outerElement = rootElement->InsertNewChildElement(kTagAlertThreshold);
606     if (!outerElement) {
607         return Error() << "Failed to insert new child element with tag '" << kTagAlertThreshold
608                        << "'";
609     }
610     const auto writeParamElement = [&](const char* param, int64_t value) -> Result<void> {
611         XMLElement* innerElement = outerElement->InsertNewChildElement(kTagParam);
612         if (!innerElement) {
613             return Error() << "Failed to insert new child element with tag '" << kTagParam << "'";
614         }
615         innerElement->SetAttribute(kAttrId, param);
616         innerElement->SetText(value);
617         return {};
618     };
619     if (const auto result =
620                 writeParamElement(kParamIdDurationSeconds, alertThresholds.durationInSeconds);
621         !result.ok()) {
622         return Error() << "Failed to write duration for param '" << kParamIdDurationSeconds
623                        << "': " << result.error();
624     }
625     if (const auto result = writeParamElement(kParamIdWrittenBytesPerSecond,
626                                               alertThresholds.writtenBytesPerSecond);
627         !result.ok()) {
628         return Error() << "Failed to write bps for param '" << kParamIdWrittenBytesPerSecond
629                        << "': " << result.error();
630     }
631     return {};
632 }
633 
writeSystemWideThresholds(const std::vector<IoOveruseAlertThreshold> & thresholds,XMLElement * rootElement)634 Result<void> writeSystemWideThresholds(const std::vector<IoOveruseAlertThreshold>& thresholds,
635                                        XMLElement* rootElement) {
636     XMLElement* childElement = rootElement->InsertNewChildElement(kTagSystemWideThresholds);
637     if (!childElement) {
638         return Error() << "Failed to insert new child element with tag '"
639                        << kTagSystemWideThresholds << "'";
640     }
641     for (const auto threshold : thresholds) {
642         if (const auto result = writeAlertThresholds(threshold, childElement); !result.ok()) {
643             return Error() << "Failed to write I/O overuse alert thresholds:" << result.error();
644         }
645     }
646     return {};
647 }
648 
writeIoOveruseConfiguration(const IoOveruseConfiguration & configuration,XMLElement * rootElement)649 Result<void> writeIoOveruseConfiguration(const IoOveruseConfiguration& configuration,
650                                          XMLElement* rootElement) {
651     XMLElement* childElement = rootElement->InsertNewChildElement(kTagIoOveruseConfiguration);
652     if (!childElement) {
653         return Error() << "Failed to insert new child element with tag '"
654                        << kTagIoOveruseConfiguration << "'";
655     }
656     if (const auto result =
657                 writeComponentLevelThresholds(configuration.componentLevelThresholds, childElement);
658         !result.ok()) {
659         return Error() << "Failed to write component-wide thresholds: " << result.error();
660     }
661     if (const auto result = writePackageSpecificThresholds(configuration.packageSpecificThresholds,
662                                                            childElement);
663         !result.ok()) {
664         return Error() << "Failed to write package specific thresholds: " << result.error();
665     }
666     if (const auto result =
667                 writeAppCategorySpecificThresholds(configuration.categorySpecificThresholds,
668                                                    childElement);
669         !result.ok()) {
670         return Error() << "Failed to write app category specific thresholds: " << result.error();
671     }
672     if (const auto result =
673                 writeSystemWideThresholds(configuration.systemWideThresholds, childElement);
674         !result.ok()) {
675         return Error() << "Failed to write system-wide thresholds: " << result.error();
676     }
677     return {};
678 }
679 
680 }  // namespace
681 
parseXmlFile(const char * filePath)682 Result<ResourceOveruseConfiguration> OveruseConfigurationXmlHelper::parseXmlFile(
683         const char* filePath) {
684     XMLDocument xmlDoc;
685     xmlDoc.LoadFile(filePath);
686     if (xmlDoc.ErrorID() != XML_SUCCESS) {
687         return Error() << "Failed to read and/or parse '" << filePath << "'";
688     }
689     ResourceOveruseConfiguration configuration;
690     const XMLElement* rootElement = xmlDoc.RootElement();
691     if (!rootElement || strcmp(rootElement->Name(), kTagResourceOveruseConfiguration)) {
692         return Error() << "XML file doesn't have the root element '"
693                        << kTagResourceOveruseConfiguration << "'";
694     }
695     if (const auto result = readComponentType(rootElement); result.ok()) {
696         configuration.componentType = *result;
697     } else {
698         return Error() << "Failed to read component type: " << result.error();
699     }
700     if (const auto result = readSafeToKillPackages(rootElement); result.ok()) {
701         configuration.safeToKillPackages = *result;
702     } else {
703         return Error() << "Failed to read safe-to-kill packages: " << result.error();
704     }
705     if (const auto result = readVendorPackagePrefixes(rootElement); result.ok()) {
706         configuration.vendorPackagePrefixes = *result;
707     } else {
708         return Error() << "Failed to read vendor package prefixes: " << result.error();
709     }
710     if (const auto result = readPackageToAppCategoryTypes(rootElement); result.ok()) {
711         configuration.packageMetadata = *result;
712     } else {
713         return Error() << "Failed to read package to app category types: " << result.error();
714     }
715     if (const auto result = readIoOveruseConfiguration(configuration.componentType, rootElement);
716         result.ok()) {
717         configuration.resourceSpecificConfigurations.emplace_back(
718                 ResourceSpecificConfiguration(*result));
719     } else {
720         return Error() << "Failed to read I/O overuse configuration: " << result.error();
721     }
722     return configuration;
723 }
724 
writeXmlFile(const ResourceOveruseConfiguration & configuration,const char * filePath)725 Result<void> OveruseConfigurationXmlHelper::writeXmlFile(
726         const ResourceOveruseConfiguration& configuration, const char* filePath) {
727     XMLDocument xmlDoc;
728     if (XMLDeclaration* declaration = xmlDoc.NewDeclaration(); declaration) {
729         xmlDoc.InsertEndChild(declaration);
730     } else {
731         return Error() << "Failed to create new xml declaration";
732     }
733     XMLElement* rootElement = xmlDoc.NewElement(kTagResourceOveruseConfiguration);
734     if (!rootElement) {
735         return Error() << "Failed to create new xml element for tag '"
736                        << kTagResourceOveruseConfiguration << "'";
737     }
738     rootElement->SetAttribute(kAttrVersion, kVersionNumber);
739     xmlDoc.InsertEndChild(rootElement);
740     if (const auto result = writeComponentType(configuration.componentType, rootElement);
741         !result.ok()) {
742         return Error() << "Failed to write component type: " << result.error();
743     }
744     if (const auto result = writeSafeToKillPackages(configuration.safeToKillPackages, rootElement);
745         !result.ok()) {
746         return Error() << "Failed to write safe-to-kill packages: " << result.error();
747     }
748     if (const auto result =
749                 writeVendorPackagePrefixes(configuration.vendorPackagePrefixes, rootElement);
750         !result.ok()) {
751         return Error() << "Failed to write vendor package prefixes: " << result.error();
752     }
753     if (const auto result =
754                 writePackageToAppCategoryTypes(configuration.packageMetadata, rootElement);
755         !result.ok()) {
756         return Error() << "Failed to write package to app category types: " << result.error();
757     }
758     if (configuration.resourceSpecificConfigurations.size() != 1 ||
759         configuration.resourceSpecificConfigurations[0].getTag() !=
760                 ResourceSpecificConfiguration::ioOveruseConfiguration) {
761         return Error() << "Must provide exactly one I/O overuse configuration";
762     }
763     IoOveruseConfiguration ioOveruseConfig =
764             configuration.resourceSpecificConfigurations[0]
765                     .get<ResourceSpecificConfiguration::ioOveruseConfiguration>();
766     if (const auto result = writeIoOveruseConfiguration(ioOveruseConfig, rootElement);
767         !result.ok()) {
768         return Error() << "Failed to write I/O overuse configuration: " << result.error();
769     }
770     if (const auto xmlError = xmlDoc.SaveFile(filePath); xmlError != XML_SUCCESS) {
771         return Error() << "Failed to write XML configuration to file '" << filePath
772                        << "': " << XMLDocument::ErrorIDToName(xmlError);
773     }
774     return {};
775 }
776 
777 }  // namespace watchdog
778 }  // namespace automotive
779 }  // namespace android
780