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, ¶m) != 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