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