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