1 /*
2 * Copyright (C) 2022 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 #include "thermal_info.h"
17
18 #include <android-base/file.h>
19 #include <android-base/logging.h>
20 #include <android-base/properties.h>
21 #include <android-base/strings.h>
22 #include <json/reader.h>
23
24 #include <cmath>
25 #include <unordered_set>
26
27 namespace aidl {
28 namespace android {
29 namespace hardware {
30 namespace thermal {
31 namespace implementation {
32
33 constexpr std::string_view kPowerLinkDisabledProperty("vendor.disable.thermal.powerlink");
34
35 namespace {
36
37 template <typename T>
38 // Return false when failed parsing
getTypeFromString(std::string_view str,T * out)39 bool getTypeFromString(std::string_view str, T *out) {
40 auto types = ::ndk::enum_range<T>();
41 for (const auto &type : types) {
42 if (::aidl::android::hardware::thermal::toString(type) == str) {
43 *out = type;
44 return true;
45 }
46 }
47 return false;
48 }
49
getFloatFromValue(const Json::Value & value)50 float getFloatFromValue(const Json::Value &value) {
51 if (value.isString()) {
52 return std::atof(value.asString().c_str());
53 } else {
54 return value.asFloat();
55 }
56 }
57
getIntFromValue(const Json::Value & value)58 int getIntFromValue(const Json::Value &value) {
59 if (value.isString()) {
60 return (value.asString() == "max") ? std::numeric_limits<int>::max()
61 : std::stoul(value.asString());
62 } else {
63 return value.asInt();
64 }
65 }
66
getIntFromJsonValues(const Json::Value & values,CdevArray * out,bool inc_check,bool dec_check)67 bool getIntFromJsonValues(const Json::Value &values, CdevArray *out, bool inc_check,
68 bool dec_check) {
69 CdevArray ret;
70
71 if (inc_check && dec_check) {
72 LOG(ERROR) << "Cannot enable inc_check and dec_check at the same time";
73 return false;
74 }
75
76 if (values.size() != kThrottlingSeverityCount) {
77 LOG(ERROR) << "Values size is invalid";
78 return false;
79 } else {
80 int last = (inc_check) ? std::numeric_limits<int>::min() : std::numeric_limits<int>::max();
81 for (Json::Value::ArrayIndex i = 0; i < kThrottlingSeverityCount; ++i) {
82 ret[i] = getIntFromValue(values[i]);
83 if (inc_check && ret[i] < last) {
84 LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " min=" << last;
85 return false;
86 }
87 if (dec_check && ret[i] > last) {
88 LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " max=" << last;
89 return false;
90 }
91 last = ret[i];
92 LOG(INFO) << "[" << i << "]: " << ret[i];
93 }
94 }
95
96 *out = ret;
97 return true;
98 }
99
getFloatFromJsonValues(const Json::Value & values,ThrottlingArray * out,bool inc_check,bool dec_check)100 bool getFloatFromJsonValues(const Json::Value &values, ThrottlingArray *out, bool inc_check,
101 bool dec_check) {
102 ThrottlingArray ret;
103
104 if (inc_check && dec_check) {
105 LOG(ERROR) << "Cannot enable inc_check and dec_check at the same time";
106 return false;
107 }
108
109 if (values.size() != kThrottlingSeverityCount) {
110 LOG(ERROR) << "Values size is invalid";
111 return false;
112 } else {
113 float last = std::nanf("");
114 for (Json::Value::ArrayIndex i = 0; i < kThrottlingSeverityCount; ++i) {
115 ret[i] = getFloatFromValue(values[i]);
116 if (inc_check && !std::isnan(last) && !std::isnan(ret[i]) && ret[i] < last) {
117 LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " min=" << last;
118 return false;
119 }
120 if (dec_check && !std::isnan(last) && !std::isnan(ret[i]) && ret[i] > last) {
121 LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " max=" << last;
122 return false;
123 }
124 last = std::isnan(ret[i]) ? last : ret[i];
125 LOG(INFO) << "[" << i << "]: " << ret[i];
126 }
127 }
128
129 *out = ret;
130 return true;
131 }
132
getTempRangeInfoFromJsonValues(const Json::Value & values,TempRangeInfo * temp_range_info)133 bool getTempRangeInfoFromJsonValues(const Json::Value &values, TempRangeInfo *temp_range_info) {
134 if (values.size() != 2) {
135 LOG(ERROR) << "Temp Range Values size: " << values.size() << "is invalid.";
136 return false;
137 }
138
139 float min_temp = getFloatFromValue(values[0]);
140 float max_temp = getFloatFromValue(values[1]);
141
142 if (std::isnan(min_temp) || std::isnan(max_temp)) {
143 LOG(ERROR) << "Illegal temp range: thresholds not defined properly " << min_temp << " : "
144 << max_temp;
145 return false;
146 }
147
148 if (min_temp > max_temp) {
149 LOG(ERROR) << "Illegal temp range: temp_min_threshold(" << min_temp
150 << ") > temp_max_threshold(" << max_temp << ")";
151 return false;
152 }
153 temp_range_info->min_temp_threshold = min_temp;
154 temp_range_info->max_temp_threshold = max_temp;
155 LOG(INFO) << "Temp Range Info: " << temp_range_info->min_temp_threshold
156 << " <= t <= " << temp_range_info->max_temp_threshold;
157 return true;
158 }
159
getTempStuckInfoFromJsonValue(const Json::Value & values,TempStuckInfo * temp_stuck_info)160 bool getTempStuckInfoFromJsonValue(const Json::Value &values, TempStuckInfo *temp_stuck_info) {
161 if (values["MinStuckDuration"].empty()) {
162 LOG(ERROR) << "Minimum stuck duration not present.";
163 return false;
164 }
165 int min_stuck_duration_int = getIntFromValue(values["MinStuckDuration"]);
166 if (min_stuck_duration_int <= 0) {
167 LOG(ERROR) << "Invalid Minimum stuck duration " << min_stuck_duration_int;
168 return false;
169 }
170
171 if (values["MinPollingCount"].empty()) {
172 LOG(ERROR) << "Minimum polling count not present.";
173 return false;
174 }
175 int min_polling_count = getIntFromValue(values["MinPollingCount"]);
176 if (min_polling_count <= 0) {
177 LOG(ERROR) << "Invalid Minimum stuck duration " << min_polling_count;
178 return false;
179 }
180 temp_stuck_info->min_stuck_duration = std::chrono::milliseconds(min_stuck_duration_int);
181 temp_stuck_info->min_polling_count = min_polling_count;
182 LOG(INFO) << "Temp Stuck Info: polling_count=" << temp_stuck_info->min_polling_count
183 << " stuck_duration=" << temp_stuck_info->min_stuck_duration.count();
184 return true;
185 }
186 } // namespace
187
operator <<(std::ostream & stream,const SensorFusionType & sensor_fusion_type)188 std::ostream &operator<<(std::ostream &stream, const SensorFusionType &sensor_fusion_type) {
189 switch (sensor_fusion_type) {
190 case SensorFusionType::SENSOR:
191 return stream << "SENSOR";
192 case SensorFusionType::ODPM:
193 return stream << "ODPM";
194 case SensorFusionType::CONSTANT:
195 return stream << "CONSTANT";
196 case SensorFusionType::CDEV:
197 return stream << "CDEV";
198 default:
199 return stream << "UNDEFINED";
200 }
201 }
202
LoadThermalConfig(std::string_view config_path,Json::Value * config)203 bool LoadThermalConfig(std::string_view config_path, Json::Value *config) {
204 std::string json_doc;
205 if (!::android::base::ReadFileToString(config_path.data(), &json_doc)) {
206 LOG(ERROR) << "Failed to read JSON config from " << config_path;
207 return false;
208 }
209 Json::CharReaderBuilder builder;
210 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
211 std::string errorMessage;
212 if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), config, &errorMessage)) {
213 LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
214 return false;
215 }
216 return true;
217 }
218
MergeConfigEntries(Json::Value * config,Json::Value * sub_config,std::string_view member_name)219 void MergeConfigEntries(Json::Value *config, Json::Value *sub_config,
220 std::string_view member_name) {
221 Json::Value &config_entries = (*config)[member_name.data()];
222 Json::Value &sub_config_entries = (*sub_config)[member_name.data()];
223 std::unordered_set<std::string> config_entries_set;
224
225 if (sub_config_entries.size() == 0) {
226 return;
227 }
228
229 for (Json::Value::ArrayIndex i = 0; i < config_entries.size(); i++) {
230 config_entries_set.insert(config_entries[i]["Name"].asString());
231 }
232
233 // Iterate through subconfig and add entries not found in main config
234 for (Json::Value::ArrayIndex i = 0; i < sub_config_entries.size(); ++i) {
235 if (config_entries_set.count(sub_config_entries[i]["Name"].asString()) == 0) {
236 config_entries.append(sub_config_entries[i]);
237 } else {
238 LOG(INFO) << "Base config entry " << sub_config_entries[i]["Name"].asString()
239 << " is overwritten in main config";
240 }
241 }
242 }
243
ParseThermalConfig(std::string_view config_path,Json::Value * config,std::unordered_set<std::string> * loaded_config_paths)244 bool ParseThermalConfig(std::string_view config_path, Json::Value *config,
245 std::unordered_set<std::string> *loaded_config_paths) {
246 if (loaded_config_paths->count(config_path.data())) {
247 LOG(ERROR) << "Circular dependency detected in config " << config_path;
248 return false;
249 }
250
251 if (!LoadThermalConfig(config_path, config)) {
252 LOG(ERROR) << "Failed to read JSON config at " << config_path;
253 return false;
254 }
255
256 loaded_config_paths->insert(config_path.data());
257
258 Json::Value sub_configs_paths = (*config)["Include"];
259 for (Json::Value::ArrayIndex i = 0; i < sub_configs_paths.size(); ++i) {
260 const std::string sub_configs_path = "/vendor/etc/" + sub_configs_paths[i].asString();
261 Json::Value sub_config;
262
263 if (!ParseThermalConfig(sub_configs_path, &sub_config, loaded_config_paths)) {
264 return false;
265 }
266
267 MergeConfigEntries(config, &sub_config, "Sensors");
268 MergeConfigEntries(config, &sub_config, "CoolingDevices");
269 MergeConfigEntries(config, &sub_config, "PowerRails");
270
271 if (!sub_config["Stats"].empty()) {
272 if ((*config)["Stats"].empty()) {
273 (*config)["Stats"] = sub_config["Stats"];
274 } else {
275 MergeConfigEntries(&(*config)["Stats"]["Sensors"], &sub_config["Stats"]["Sensors"],
276 "RecordWithThreshold");
277 }
278 }
279 }
280
281 return true;
282 }
283
ParseOffsetThresholds(const std::string_view name,const Json::Value & sensor,std::vector<float> * offset_thresholds,std::vector<float> * offset_values)284 bool ParseOffsetThresholds(const std::string_view name, const Json::Value &sensor,
285 std::vector<float> *offset_thresholds,
286 std::vector<float> *offset_values) {
287 Json::Value config_offset_thresholds = sensor["OffsetThresholds"];
288 Json::Value config_offset_values = sensor["OffsetValues"];
289
290 if (config_offset_thresholds.empty()) {
291 return true;
292 }
293
294 if (config_offset_thresholds.size() != config_offset_values.size()) {
295 LOG(ERROR) << "Sensor[" << name
296 << "]'s offset_thresholds size does not match with offset_values size";
297 return false;
298 }
299
300 for (Json::Value::ArrayIndex i = 0; i < config_offset_thresholds.size(); ++i) {
301 float offset_threshold = config_offset_thresholds[i].asFloat();
302 float offset_value = config_offset_values[i].asFloat();
303 if (std::isnan(offset_threshold) || std::isnan(offset_value)) {
304 LOG(ERROR) << "Nan offset_threshold or offset_value unexpected for sensor " << name;
305 return false;
306 }
307
308 if ((i != 0) && (offset_threshold < (*offset_thresholds).back())) {
309 LOG(ERROR) << "offset_thresholds are not in increasing order for sensor " << name;
310 return false;
311 }
312
313 (*offset_thresholds).emplace_back(offset_threshold);
314 (*offset_values).emplace_back(offset_value);
315
316 LOG(INFO) << "Sensor[" << name << "]'s offset_thresholds[" << i
317 << "]: " << (*offset_thresholds)[i] << " offset_values[" << i
318 << "]: " << (*offset_values)[i];
319 }
320
321 return true;
322 }
323
ParseVirtualSensorInfo(const std::string_view name,const Json::Value & sensor,std::unique_ptr<VirtualSensorInfo> * virtual_sensor_info)324 bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sensor,
325 std::unique_ptr<VirtualSensorInfo> *virtual_sensor_info) {
326 if (sensor["VirtualSensor"].empty() || !sensor["VirtualSensor"].isBool()) {
327 LOG(INFO) << "Failed to read Sensor[" << name << "]'s VirtualSensor";
328 return true;
329 }
330 bool is_virtual_sensor = sensor["VirtualSensor"].asBool();
331 LOG(INFO) << "Sensor[" << name << "]'s' VirtualSensor: " << is_virtual_sensor;
332 if (!is_virtual_sensor) {
333 return true;
334 }
335 float offset = 0;
336 std::vector<std::string> linked_sensors;
337 std::vector<SensorFusionType> linked_sensors_type;
338 std::vector<std::string> trigger_sensors;
339 std::vector<std::string> coefficients;
340 std::vector<SensorFusionType> coefficients_type;
341 FormulaOption formula = FormulaOption::COUNT_THRESHOLD;
342 std::string vt_estimator_model_file;
343 std::unique_ptr<::thermal::vtestimator::VirtualTempEstimator> vt_estimator;
344 std::string backup_sensor;
345
346 Json::Value values = sensor["Combination"];
347 if (values.size()) {
348 linked_sensors.reserve(values.size());
349 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
350 linked_sensors.emplace_back(values[j].asString());
351 LOG(INFO) << "Sensor[" << name << "]'s Combination[" << j << "]: " << linked_sensors[j];
352 }
353 } else {
354 LOG(ERROR) << "Sensor[" << name << "] has no Combination setting";
355 return false;
356 }
357
358 if (sensor["Formula"].asString().compare("COUNT_THRESHOLD") == 0) {
359 formula = FormulaOption::COUNT_THRESHOLD;
360 } else if (sensor["Formula"].asString().compare("WEIGHTED_AVG") == 0) {
361 formula = FormulaOption::WEIGHTED_AVG;
362 } else if (sensor["Formula"].asString().compare("MAXIMUM") == 0) {
363 formula = FormulaOption::MAXIMUM;
364 } else if (sensor["Formula"].asString().compare("MINIMUM") == 0) {
365 formula = FormulaOption::MINIMUM;
366 } else if (sensor["Formula"].asString().compare("USE_ML_MODEL") == 0) {
367 formula = FormulaOption::USE_ML_MODEL;
368 } else if (sensor["Formula"].asString().compare("USE_LINEAR_MODEL") == 0) {
369 formula = FormulaOption::USE_LINEAR_MODEL;
370 } else if (sensor["Formula"].asString().compare("PREVIOUSLY_PREDICTED") == 0) {
371 formula = FormulaOption::PREVIOUSLY_PREDICTED;
372 } else {
373 LOG(ERROR) << "Sensor[" << name << "]'s Formula: " << sensor["Formula"].asString()
374 << " is invalid";
375 return false;
376 }
377
378 values = sensor["CombinationType"];
379 if (!values.size()) {
380 linked_sensors_type.reserve(linked_sensors.size());
381 for (size_t j = 0; j < linked_sensors.size(); ++j) {
382 linked_sensors_type.emplace_back(SensorFusionType::SENSOR);
383 }
384 } else if (values.size() != linked_sensors.size()) {
385 LOG(ERROR) << "Sensor[" << name << "] has invalid CombinationType size";
386 return false;
387 } else {
388 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
389 if (values[j].asString().compare("SENSOR") == 0) {
390 linked_sensors_type.emplace_back(SensorFusionType::SENSOR);
391 } else if (values[j].asString().compare("ODPM") == 0) {
392 linked_sensors_type.emplace_back(SensorFusionType::ODPM);
393 } else if (values[j].asString().compare("CONSTANT") == 0) {
394 linked_sensors_type.emplace_back(SensorFusionType::CONSTANT);
395 } else if (values[j].asString().compare("CDEV") == 0) {
396 linked_sensors_type.emplace_back(SensorFusionType::CDEV);
397 } else {
398 LOG(ERROR) << "Sensor[" << name << "] has invalid CombinationType settings "
399 << values[j].asString();
400 return false;
401 }
402 LOG(INFO) << "Sensor[" << name << "]'s CombinationType[" << j
403 << "]: " << linked_sensors_type[j];
404 }
405 }
406
407 values = sensor["Coefficient"];
408 if (values.size()) {
409 coefficients.reserve(values.size());
410 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
411 coefficients.emplace_back(values[j].asString());
412 LOG(INFO) << "Sensor[" << name << "]'s coefficient[" << j << "]: " << coefficients[j];
413 }
414 } else if ((formula != FormulaOption::USE_ML_MODEL) &&
415 (formula != FormulaOption::PREVIOUSLY_PREDICTED)) {
416 LOG(ERROR) << "Sensor[" << name << "] has no Coefficient setting";
417 return false;
418 }
419 if ((linked_sensors.size() != coefficients.size()) &&
420 (formula != FormulaOption::USE_ML_MODEL) && (formula != FormulaOption::USE_LINEAR_MODEL) &&
421 (formula != FormulaOption::PREVIOUSLY_PREDICTED)) {
422 LOG(ERROR) << "Sensor[" << name << "] has invalid Coefficient size";
423 return false;
424 }
425
426 values = sensor["CoefficientType"];
427 if (!values.size()) {
428 coefficients_type.reserve(linked_sensors.size());
429 for (size_t j = 0; j < linked_sensors.size(); ++j) {
430 coefficients_type.emplace_back(SensorFusionType::CONSTANT);
431 }
432 } else if (values.size() != coefficients.size()) {
433 LOG(ERROR) << "Sensor[" << name << "] has invalid coefficient type size";
434 return false;
435 } else {
436 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
437 if (values[j].asString().compare("CONSTANT") == 0) {
438 coefficients_type.emplace_back(SensorFusionType::CONSTANT);
439 } else if (values[j].asString().compare("SENSOR") == 0) {
440 coefficients_type.emplace_back(SensorFusionType::SENSOR);
441 } else if (values[j].asString().compare("ODPM") == 0) {
442 coefficients_type.emplace_back(SensorFusionType::ODPM);
443 } else {
444 LOG(ERROR) << "Sensor[" << name << "] has invalid coefficient options "
445 << values[j].asString();
446 return false;
447 }
448 LOG(INFO) << "Sensor[" << name << "]'s coefficient type[" << j
449 << "]: " << coefficients_type[j];
450 }
451 }
452
453 if (linked_sensors.size() != coefficients_type.size()) {
454 LOG(ERROR) << "Sensor[" << name
455 << "]'s combination size is not matched with coefficient type size";
456 return false;
457 }
458
459 if (!sensor["Offset"].empty()) {
460 offset = sensor["Offset"].asFloat();
461 }
462
463 if (!sensor["BackupSensor"].empty()) {
464 backup_sensor = sensor["BackupSensor"].asString();
465 }
466
467 values = sensor["TriggerSensor"];
468 if (!values.empty()) {
469 if (values.isString()) {
470 trigger_sensors.emplace_back(values.asString());
471 LOG(INFO) << "Sensor[" << name << "]'s TriggerSensor: " << values.asString();
472 } else if (values.size()) {
473 trigger_sensors.reserve(values.size());
474 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
475 if (!values[j].isString()) {
476 LOG(ERROR) << name << " TriggerSensor should be an array of string";
477 return false;
478 }
479 trigger_sensors.emplace_back(values[j].asString());
480 LOG(INFO) << "Sensor[" << name << "]'s TriggerSensor[" << j
481 << "]: " << trigger_sensors[j];
482 }
483 } else {
484 LOG(ERROR) << "Sensor[" << name << "]'s TriggerSensor should be a string";
485 return false;
486 }
487 }
488
489 if (formula == FormulaOption::USE_ML_MODEL) {
490 ::thermal::vtestimator::VtEstimationInitData init_data(::thermal::vtestimator::kUseMLModel);
491 if (sensor["ModelPath"].empty()) {
492 LOG(ERROR) << "Sensor[" << name << "] has no ModelPath";
493 return false;
494 }
495
496 if (!linked_sensors.size()) {
497 LOG(ERROR) << "Sensor[" << name << "] uses USE_ML_MODEL and has zero linked_sensors";
498 return false;
499 }
500
501 vt_estimator = std::make_unique<::thermal::vtestimator::VirtualTempEstimator>(
502 name, ::thermal::vtestimator::kUseMLModel, linked_sensors.size());
503 if (!vt_estimator) {
504 LOG(ERROR) << "Failed to create vt estimator for Sensor[" << name
505 << "] with linked sensor size : " << linked_sensors.size();
506 return false;
507 }
508
509 vt_estimator_model_file = "vendor/etc/" + sensor["ModelPath"].asString();
510 init_data.ml_model_init_data.model_path = vt_estimator_model_file;
511
512 if (!ParseOffsetThresholds(name, sensor, &init_data.ml_model_init_data.offset_thresholds,
513 &init_data.ml_model_init_data.offset_values)) {
514 LOG(ERROR) << "Failed to parse offset thresholds and values for Sensor[" << name << "]";
515 return false;
516 }
517
518 if (!sensor["PreviousSampleCount"].empty()) {
519 init_data.ml_model_init_data.use_prev_samples = true;
520 init_data.ml_model_init_data.prev_samples_order = sensor["PreviousSampleCount"].asInt();
521 LOG(INFO) << "Sensor[" << name << "] takes "
522 << init_data.ml_model_init_data.prev_samples_order << " historic samples";
523 }
524
525 if (!sensor["OutputLabelCount"].empty()) {
526 init_data.ml_model_init_data.output_label_count = sensor["OutputLabelCount"].asInt();
527 LOG(INFO) << "Sensor[" << name << "] outputs "
528 << init_data.ml_model_init_data.output_label_count << " labels";
529 }
530
531 if (!sensor["PredictHotSpotCount"].empty()) {
532 init_data.ml_model_init_data.num_hot_spots = sensor["PredictHotSpotCount"].asInt();
533 LOG(INFO) << "Sensor[" << name << "] predicts temperature at "
534 << init_data.ml_model_init_data.num_hot_spots << " hot spots";
535 }
536
537 if (sensor["ValidateInput"].asBool()) {
538 init_data.ml_model_init_data.enable_input_validation = true;
539 LOG(INFO) << "Sensor[" << name << "] enable input validation.";
540 }
541
542 if (sensor["SupportUnderSampling"].asBool()) {
543 init_data.ml_model_init_data.support_under_sampling = true;
544 LOG(INFO) << "Sensor[" << name << "] supports under sampling estimation.";
545 }
546
547 ::thermal::vtestimator::VtEstimatorStatus ret = vt_estimator->Initialize(init_data);
548 if (ret != ::thermal::vtestimator::kVtEstimatorOk) {
549 LOG(ERROR) << "Failed to initialize vt estimator for Sensor[" << name
550 << "] with ModelPath: " << vt_estimator_model_file
551 << " with ret code : " << ret;
552 return false;
553 }
554
555 LOG(INFO) << "Successfully created vt_estimator for Sensor[" << name
556 << "] with input samples: " << linked_sensors.size();
557
558 } else if (formula == FormulaOption::USE_LINEAR_MODEL) {
559 ::thermal::vtestimator::VtEstimationInitData init_data(
560 ::thermal::vtestimator::kUseLinearModel);
561
562 if ((!linked_sensors.size()) || (linked_sensors.size() > coefficients.size())) {
563 LOG(ERROR) << "Sensor[" << name
564 << "] uses USE_LINEAR_MODEL and has invalid linked_sensors size["
565 << linked_sensors.size() << "] or coefficients size[" << coefficients.size()
566 << "]";
567 return false;
568 }
569
570 vt_estimator = std::make_unique<::thermal::vtestimator::VirtualTempEstimator>(
571 name, ::thermal::vtestimator::kUseLinearModel, linked_sensors.size());
572 if (!vt_estimator) {
573 LOG(ERROR) << "Failed to create vt estimator for Sensor[" << name
574 << "] with linked sensor size : " << linked_sensors.size();
575 return false;
576 }
577
578 init_data.linear_model_init_data.prev_samples_order =
579 coefficients.size() / linked_sensors.size();
580
581 if (!ParseOffsetThresholds(name, sensor,
582 &init_data.linear_model_init_data.offset_thresholds,
583 &init_data.linear_model_init_data.offset_values)) {
584 LOG(ERROR) << "Failed to parse offset thresholds and values for Sensor[" << name << "]";
585 return false;
586 }
587
588 for (size_t i = 0; i < coefficients.size(); ++i) {
589 float coefficient = getFloatFromValue(coefficients[i]);
590 if (std::isnan(coefficient)) {
591 LOG(ERROR) << "Nan coefficient unexpected for sensor " << name;
592 return false;
593 }
594 init_data.linear_model_init_data.coefficients.emplace_back(coefficient);
595 }
596 if (coefficients.size() > linked_sensors.size()) {
597 init_data.linear_model_init_data.use_prev_samples = true;
598 }
599
600 ::thermal::vtestimator::VtEstimatorStatus ret = vt_estimator->Initialize(init_data);
601 if (ret != ::thermal::vtestimator::kVtEstimatorOk) {
602 LOG(ERROR) << "Failed to initialize vt estimator for Sensor[" << name
603 << "] with ret code : " << ret;
604 return false;
605 }
606
607 LOG(INFO) << "Successfully created vt_estimator for Sensor[" << name
608 << "] with input samples: " << linked_sensors.size();
609 }
610
611 virtual_sensor_info->reset(
612 new VirtualSensorInfo{linked_sensors, linked_sensors_type, coefficients,
613 coefficients_type, offset, trigger_sensors, formula,
614 vt_estimator_model_file, std::move(vt_estimator), backup_sensor});
615 return true;
616 }
617
ParsePredictorInfo(const std::string_view name,const Json::Value & sensor,std::unique_ptr<PredictorInfo> * predictor_info)618 bool ParsePredictorInfo(const std::string_view name, const Json::Value &sensor,
619 std::unique_ptr<PredictorInfo> *predictor_info) {
620 Json::Value predictor = sensor["PredictorInfo"];
621 std::string predict_sensor;
622 bool support_pid_compensation = false;
623 std::vector<float> prediction_weights;
624 ThrottlingArray k_p_compensate;
625
626 bool supports_predictions = false;
627 int prediction_sample_interval = 0;
628 int num_prediction_samples = 0;
629 int prediction_duration = 0;
630 bool set_predictor_info = false;
631
632 if (!predictor.empty()) {
633 set_predictor_info = true;
634 LOG(INFO) << "Start to parse Sensor[" << name << "]'s PredictorInfo";
635 if (predictor["Sensor"].empty()) {
636 LOG(ERROR) << "Failed to parse Sensor [" << name << "]'s PredictorInfo";
637 return false;
638 }
639
640 predict_sensor = predictor["Sensor"].asString();
641 LOG(INFO) << "Sensor [" << name << "]'s predictor name is " << predict_sensor;
642 // parse pid compensation configuration
643 if ((!predictor["PredictionWeight"].empty()) && (!predictor["KPCompensate"].empty())) {
644 support_pid_compensation = true;
645 if (!predictor["PredictionWeight"].size()) {
646 LOG(ERROR) << "Failed to parse PredictionWeight";
647 return false;
648 }
649 prediction_weights.reserve(predictor["PredictionWeight"].size());
650 for (Json::Value::ArrayIndex i = 0; i < predictor["PredictionWeight"].size(); ++i) {
651 float weight = predictor["PredictionWeight"][i].asFloat();
652 if (std::isnan(weight)) {
653 LOG(ERROR) << "Unexpected NAN prediction weight for sensor [" << name << "]";
654 }
655 prediction_weights.emplace_back(weight);
656 LOG(INFO) << "Sensor[" << name << "]'s prediction weights [" << i
657 << "]: " << weight;
658 }
659 if (!getFloatFromJsonValues(predictor["KPCompensate"], &k_p_compensate, false, false)) {
660 LOG(ERROR) << "Failed to parse KPCompensate";
661 return false;
662 }
663 }
664 }
665
666 if (sensor["SupportPrediction"].asBool()) {
667 set_predictor_info = true;
668 supports_predictions = true;
669 LOG(INFO) << "Sensor[" << name << "] supports predictions.";
670
671 if (sensor["SampleDuration"].empty()) {
672 LOG(ERROR) << "SampleDuration is empty for predictor sensor: " << name;
673 return false;
674 }
675
676 if (sensor["OutputLabelCount"].empty()) {
677 LOG(ERROR) << "OutputLabelCount is empty for predictor sensor: " << name;
678 return false;
679 }
680
681 prediction_sample_interval = sensor["SampleDuration"].asInt();
682 num_prediction_samples = sensor["OutputLabelCount"].asInt();
683 }
684
685 if (sensor["Formula"].asString().compare("PREVIOUSLY_PREDICTED") == 0) {
686 set_predictor_info = true;
687 if (sensor["PredictionDuration"].empty()) {
688 LOG(ERROR) << "Sensor[" << name
689 << "] is a PREVIOUSLY_PREDICTED sensor and has no PredictionDuration";
690 return false;
691 }
692
693 prediction_duration = sensor["PredictionDuration"].asInt();
694 }
695
696 if (set_predictor_info) {
697 LOG(INFO) << "Successfully created PredictorInfo for Sensor[" << name << "]";
698 predictor_info->reset(new PredictorInfo{predict_sensor, support_pid_compensation,
699 prediction_weights, k_p_compensate,
700 supports_predictions, prediction_sample_interval,
701 num_prediction_samples, prediction_duration});
702 }
703
704 return true;
705 }
706
ParseBindedCdevInfo(const Json::Value & values,std::unordered_map<std::string,BindedCdevInfo> * binded_cdev_info_map,const bool support_pid,bool * support_hard_limit,const std::unordered_map<std::string,std::vector<int>> & scaling_frequency_map,const std::unordered_map<std::string,CdevInfo> & cooling_device_info_map_)707 bool ParseBindedCdevInfo(
708 const Json::Value &values,
709 std::unordered_map<std::string, BindedCdevInfo> *binded_cdev_info_map,
710 const bool support_pid, bool *support_hard_limit,
711 const std::unordered_map<std::string, std::vector<int>> &scaling_frequency_map,
712 const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_) {
713 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
714 Json::Value sub_values;
715 const std::string &cdev_name = values[j]["CdevRequest"].asString();
716
717 if (cooling_device_info_map_.find(cdev_name) == cooling_device_info_map_.end()) {
718 LOG(ERROR) << "Binded cdev " << cdev_name << " is not defined in cooling devices";
719 return false;
720 }
721
722 ThrottlingArray cdev_weight_for_pid;
723 cdev_weight_for_pid.fill(NAN);
724 CdevArray cdev_ceiling;
725 cdev_ceiling.fill(std::numeric_limits<int>::max());
726 int max_release_step = std::numeric_limits<int>::max();
727 int max_throttle_step = std::numeric_limits<int>::max();
728 if (support_pid) {
729 if (!values[j]["CdevWeightForPID"].empty()) {
730 LOG(INFO) << "Star to parse " << cdev_name << "'s CdevWeightForPID";
731 if (!getFloatFromJsonValues(values[j]["CdevWeightForPID"], &cdev_weight_for_pid,
732 false, false)) {
733 LOG(ERROR) << "Failed to parse CdevWeightForPID";
734 binded_cdev_info_map->clear();
735 return false;
736 }
737 }
738
739 if (!values[j]["CdevCeiling"].empty() && !values[j]["CdevCeilingFrequency"].empty()) {
740 LOG(ERROR) << "Both CdevCeiling and CdevCeilingFrequency are configured for "
741 << cdev_name << ", please remove one of them";
742 binded_cdev_info_map->clear();
743 return false;
744 }
745
746 if (!values[j]["CdevCeiling"].empty()) {
747 LOG(INFO) << "Start to parse CdevCeiling: " << cdev_name;
748 if (!getIntFromJsonValues(values[j]["CdevCeiling"], &cdev_ceiling, false, false)) {
749 LOG(ERROR) << "Failed to parse CdevCeiling for " << cdev_name;
750 binded_cdev_info_map->clear();
751 return false;
752 }
753 }
754
755 if (!values[j]["CdevCeilingFrequency"].empty()) {
756 LOG(INFO) << "Start to parse CdevCeilingFrequency: " << cdev_name;
757 CdevArray cdev_ceiling_frequency;
758 if (scaling_frequency_map.find(cdev_name) == scaling_frequency_map.end()) {
759 LOG(ERROR) << "Scaling frequency path is not found in config for " << cdev_name;
760 binded_cdev_info_map->clear();
761 return false;
762 }
763 const std::vector<int> &cdev_scaling_frequency =
764 scaling_frequency_map.find(cdev_name)->second;
765 if (!getIntFromJsonValues(values[j]["CdevCeilingFrequency"],
766 &cdev_ceiling_frequency, false, true)) {
767 LOG(ERROR) << "Failed to parse CdevCeilingFrequency";
768 binded_cdev_info_map->clear();
769 return false;
770 }
771
772 LOG(INFO) << "Start to search CdevCeiling based on frequency: " << cdev_name;
773 // Find the max frequency level that is lower than or equal to CdevCeilingFrequency
774 // value
775 for (size_t cdev_scaling_idx = 0, cdev_ceiling_idx = 0;
776 cdev_scaling_idx < cdev_scaling_frequency.size() &&
777 cdev_ceiling_idx < cdev_ceiling.size();) {
778 if (cdev_scaling_frequency.at(cdev_scaling_idx) <=
779 cdev_ceiling_frequency.at(cdev_ceiling_idx)) {
780 cdev_ceiling[cdev_ceiling_idx] = cdev_scaling_idx;
781 LOG(INFO) << "[" << cdev_ceiling_idx
782 << "]: " << cdev_ceiling[cdev_ceiling_idx];
783 cdev_ceiling_idx += 1;
784 } else {
785 cdev_scaling_idx += 1;
786 }
787 }
788 }
789
790 if (!values[j]["MaxReleaseStep"].empty()) {
791 max_release_step = getIntFromValue(values[j]["MaxReleaseStep"]);
792 if (max_release_step < 0) {
793 LOG(ERROR) << cdev_name << " MaxReleaseStep: " << max_release_step;
794 binded_cdev_info_map->clear();
795 return false;
796 } else {
797 LOG(INFO) << cdev_name << " MaxReleaseStep: " << max_release_step;
798 }
799 }
800 if (!values[j]["MaxThrottleStep"].empty()) {
801 max_throttle_step = getIntFromValue(values[j]["MaxThrottleStep"]);
802 if (max_throttle_step < 0) {
803 LOG(ERROR) << cdev_name << " MaxThrottleStep: " << max_throttle_step;
804 binded_cdev_info_map->clear();
805 return false;
806 } else {
807 LOG(INFO) << cdev_name << " MaxThrottleStep: " << max_throttle_step;
808 }
809 }
810 }
811 CdevArray limit_info;
812 limit_info.fill(0);
813 ThrottlingArray power_thresholds;
814 power_thresholds.fill(NAN);
815 ReleaseLogic release_logic = ReleaseLogic::NONE;
816
817 if (!values[j]["LimitInfo"].empty() && !values[j]["LimitInfoFrequency"].empty()) {
818 LOG(ERROR) << "Both LimitInfo and LimitInfoFrequency are configured for " << cdev_name
819 << ", please remove one of them";
820 binded_cdev_info_map->clear();
821 return false;
822 }
823
824 if (!values[j]["LimitInfo"].empty()) {
825 LOG(INFO) << "Start to parse LimitInfo: " << cdev_name;
826 if (!getIntFromJsonValues(values[j]["LimitInfo"], &limit_info, false, false)) {
827 LOG(ERROR) << "Failed to parse LimitInfo";
828 binded_cdev_info_map->clear();
829 return false;
830 }
831 *support_hard_limit = true;
832 }
833
834 if (!values[j]["LimitInfoFrequency"].empty()) {
835 LOG(INFO) << "Start to parse LimitInfoFrequency: " << cdev_name;
836 CdevArray limit_info_frequency;
837 if (scaling_frequency_map.find(cdev_name) == scaling_frequency_map.end()) {
838 LOG(ERROR) << "Scaling frequency path is not found for " << cdev_name;
839 binded_cdev_info_map->clear();
840 return false;
841 }
842
843 const std::vector<int> &cdev_scaling_frequency =
844 scaling_frequency_map.find(cdev_name)->second;
845 if (!getIntFromJsonValues(values[j]["LimitInfoFrequency"], &limit_info_frequency, false,
846 true)) {
847 LOG(ERROR) << "Failed to parse LimitInfoFrequency for " << cdev_name;
848 binded_cdev_info_map->clear();
849 return false;
850 }
851
852 LOG(INFO) << "Start to search LimitInfo based on frequency: " << cdev_name;
853 // Find the max frequency level that is lower than or equal to imitInfoFrequency value
854 for (size_t cdev_scaling_idx = 0, limit_info_idx = 0;
855 cdev_scaling_idx < cdev_scaling_frequency.size() &&
856 limit_info_idx < limit_info.size();) {
857 if (cdev_scaling_frequency.at(cdev_scaling_idx) <=
858 limit_info_frequency.at(limit_info_idx)) {
859 limit_info[limit_info_idx] = cdev_scaling_idx;
860 LOG(INFO) << "[" << limit_info_idx << "]: " << limit_info[limit_info_idx];
861 limit_info_idx += 1;
862 } else {
863 cdev_scaling_idx += 1;
864 }
865 }
866 *support_hard_limit = true;
867 }
868 // Parse linked power info
869 std::string power_rail;
870 bool high_power_check = false;
871 bool throttling_with_power_link = false;
872 bool enabled = true;
873 CdevArray cdev_floor_with_power_link;
874 cdev_floor_with_power_link.fill(0);
875
876 const bool power_link_disabled =
877 ::android::base::GetBoolProperty(kPowerLinkDisabledProperty.data(), false);
878 if (!power_link_disabled) {
879 power_rail = values[j]["BindedPowerRail"].asString();
880
881 if (values[j]["HighPowerCheck"].asBool()) {
882 high_power_check = true;
883 }
884 LOG(INFO) << "Highpowercheck: " << std::boolalpha << high_power_check;
885
886 if (values[j]["ThrottlingWithPowerLink"].asBool()) {
887 throttling_with_power_link = true;
888 }
889 LOG(INFO) << "ThrottlingwithPowerLink: " << std::boolalpha
890 << throttling_with_power_link;
891
892 sub_values = values[j]["CdevFloorWithPowerLink"];
893 if (sub_values.size()) {
894 LOG(INFO) << "Start to parse " << cdev_name << "'s CdevFloorWithPowerLink";
895 if (!getIntFromJsonValues(sub_values, &cdev_floor_with_power_link, false, false)) {
896 LOG(ERROR) << "Failed to parse CdevFloor";
897 binded_cdev_info_map->clear();
898 return false;
899 }
900 }
901 sub_values = values[j]["PowerThreshold"];
902 if (sub_values.size()) {
903 LOG(INFO) << "Start to parse " << cdev_name << "'s PowerThreshold";
904 if (!getFloatFromJsonValues(sub_values, &power_thresholds, false, false)) {
905 LOG(ERROR) << "Failed to parse power thresholds";
906 binded_cdev_info_map->clear();
907 return false;
908 }
909 if (values[j]["ReleaseLogic"].asString() == "INCREASE") {
910 release_logic = ReleaseLogic::INCREASE;
911 LOG(INFO) << "Release logic: INCREASE";
912 } else if (values[j]["ReleaseLogic"].asString() == "DECREASE") {
913 release_logic = ReleaseLogic::DECREASE;
914 LOG(INFO) << "Release logic: DECREASE";
915 } else if (values[j]["ReleaseLogic"].asString() == "STEPWISE") {
916 release_logic = ReleaseLogic::STEPWISE;
917 LOG(INFO) << "Release logic: STEPWISE";
918 } else if (values[j]["ReleaseLogic"].asString() == "RELEASE_TO_FLOOR") {
919 release_logic = ReleaseLogic::RELEASE_TO_FLOOR;
920 LOG(INFO) << "Release logic: RELEASE_TO_FLOOR";
921 } else {
922 LOG(ERROR) << "Release logic is invalid";
923 binded_cdev_info_map->clear();
924 return false;
925 }
926 }
927 }
928 if (values[j]["Disabled"].asBool()) {
929 enabled = false;
930 }
931
932 (*binded_cdev_info_map)[cdev_name] = {
933 .limit_info = limit_info,
934 .power_thresholds = power_thresholds,
935 .release_logic = release_logic,
936 .cdev_weight_for_pid = cdev_weight_for_pid,
937 .cdev_ceiling = cdev_ceiling,
938 .max_release_step = max_release_step,
939 .max_throttle_step = max_throttle_step,
940 .cdev_floor_with_power_link = cdev_floor_with_power_link,
941 .power_rail = power_rail,
942 .high_power_check = high_power_check,
943 .throttling_with_power_link = throttling_with_power_link,
944 .enabled = enabled,
945 };
946 }
947 return true;
948 }
949
ParseSensorThrottlingInfo(const std::string_view name,const Json::Value & sensor,bool * support_throttling,std::shared_ptr<ThrottlingInfo> * throttling_info,const std::unordered_map<std::string,std::vector<int>> & scaling_frequency_map,const std::unordered_map<std::string,CdevInfo> & cooling_device_info_map_)950 bool ParseSensorThrottlingInfo(
951 const std::string_view name, const Json::Value &sensor, bool *support_throttling,
952 std::shared_ptr<ThrottlingInfo> *throttling_info,
953 const std::unordered_map<std::string, std::vector<int>> &scaling_frequency_map,
954 const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_) {
955 std::array<float, kThrottlingSeverityCount> k_po;
956 k_po.fill(0.0);
957 std::array<float, kThrottlingSeverityCount> k_pu;
958 k_pu.fill(0.0);
959 std::array<float, kThrottlingSeverityCount> k_io;
960 k_io.fill(0.0);
961 std::array<float, kThrottlingSeverityCount> k_iu;
962 k_iu.fill(0.0);
963 std::array<float, kThrottlingSeverityCount> k_d;
964 k_d.fill(0.0);
965 std::array<float, kThrottlingSeverityCount> i_max;
966 i_max.fill(NAN);
967 std::array<float, kThrottlingSeverityCount> max_alloc_power;
968 max_alloc_power.fill(NAN);
969 std::array<float, kThrottlingSeverityCount> min_alloc_power;
970 min_alloc_power.fill(NAN);
971 std::array<float, kThrottlingSeverityCount> s_power;
972 s_power.fill(NAN);
973 std::array<float, kThrottlingSeverityCount> i_cutoff;
974 i_cutoff.fill(NAN);
975 float i_default = 0.0;
976 float i_default_pct = NAN;
977 int tran_cycle = 0;
978 bool support_pid = false;
979 bool support_hard_limit = false;
980
981 // Parse PID parameters
982 if (!sensor["PIDInfo"].empty()) {
983 LOG(INFO) << "Start to parse"
984 << " Sensor[" << name << "]'s K_Po";
985 if (sensor["PIDInfo"]["K_Po"].empty() ||
986 !getFloatFromJsonValues(sensor["PIDInfo"]["K_Po"], &k_po, false, false)) {
987 LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Po";
988 return false;
989 }
990 LOG(INFO) << "Start to parse"
991 << " Sensor[" << name << "]'s K_Pu";
992 if (sensor["PIDInfo"]["K_Pu"].empty() ||
993 !getFloatFromJsonValues(sensor["PIDInfo"]["K_Pu"], &k_pu, false, false)) {
994 LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Pu";
995 return false;
996 }
997 if (!sensor["PIDInfo"]["K_I"].empty()) {
998 if (!sensor["PIDInfo"]["K_Io"].empty() || !sensor["PIDInfo"]["K_Iu"].empty()) {
999 LOG(ERROR) << "Sensor[" << name << "]: K_Io or K_Iu cannot coexist with K_I";
1000 return false;
1001 }
1002 LOG(INFO) << "Start to parse" << " Sensor[" << name << "]'s K_I";
1003 if (!getFloatFromJsonValues(sensor["PIDInfo"]["K_I"], &k_io, false, false) ||
1004 !getFloatFromJsonValues(sensor["PIDInfo"]["K_I"], &k_iu, false, false)) {
1005 LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_I";
1006 return false;
1007 }
1008 } else if (!sensor["PIDInfo"]["K_Io"].empty() && !sensor["PIDInfo"]["K_Iu"].empty()) {
1009 LOG(INFO) << "Start to parse" << " Sensor[" << name << "]'s K_Io";
1010 if (!getFloatFromJsonValues(sensor["PIDInfo"]["K_Io"], &k_io, false, false)) {
1011 LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Io";
1012 return false;
1013 }
1014 LOG(INFO) << "Start to parse" << " Sensor[" << name << "]'s K_Iu";
1015 if (!getFloatFromJsonValues(sensor["PIDInfo"]["K_Iu"], &k_iu, false, false)) {
1016 LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Iu";
1017 return false;
1018 }
1019 } else {
1020 LOG(ERROR) << "Sensor[" << name << "]: No K_I related settings";
1021 return false;
1022 }
1023
1024 LOG(INFO) << "Start to parse"
1025 << " Sensor[" << name << "]'s K_D";
1026 if (sensor["PIDInfo"]["K_D"].empty() ||
1027 !getFloatFromJsonValues(sensor["PIDInfo"]["K_D"], &k_d, false, false)) {
1028 LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_D";
1029 return false;
1030 }
1031 LOG(INFO) << "Start to parse"
1032 << " Sensor[" << name << "]'s I_Max";
1033 if (sensor["PIDInfo"]["I_Max"].empty() ||
1034 !getFloatFromJsonValues(sensor["PIDInfo"]["I_Max"], &i_max, false, false)) {
1035 LOG(ERROR) << "Sensor[" << name << "]: Failed to parse I_Max";
1036 return false;
1037 }
1038 LOG(INFO) << "Start to parse"
1039 << " Sensor[" << name << "]'s MaxAllocPower";
1040 if (sensor["PIDInfo"]["MaxAllocPower"].empty() ||
1041 !getFloatFromJsonValues(sensor["PIDInfo"]["MaxAllocPower"], &max_alloc_power, false,
1042 true)) {
1043 LOG(ERROR) << "Sensor[" << name << "]: Failed to parse MaxAllocPower";
1044 return false;
1045 }
1046 LOG(INFO) << "Start to parse"
1047 << " Sensor[" << name << "]'s MinAllocPower";
1048 if (sensor["PIDInfo"]["MinAllocPower"].empty() ||
1049 !getFloatFromJsonValues(sensor["PIDInfo"]["MinAllocPower"], &min_alloc_power, false,
1050 true)) {
1051 LOG(ERROR) << "Sensor[" << name << "]: Failed to parse MinAllocPower";
1052 return false;
1053 }
1054 LOG(INFO) << "Start to parse Sensor[" << name << "]'s S_Power";
1055 if (sensor["PIDInfo"]["S_Power"].empty() ||
1056 !getFloatFromJsonValues(sensor["PIDInfo"]["S_Power"], &s_power, false, true)) {
1057 LOG(ERROR) << "Sensor[" << name << "]: Failed to parse S_Power";
1058 return false;
1059 }
1060 LOG(INFO) << "Start to parse Sensor[" << name << "]'s I_Cutoff";
1061 if (sensor["PIDInfo"]["I_Cutoff"].empty() ||
1062 !getFloatFromJsonValues(sensor["PIDInfo"]["I_Cutoff"], &i_cutoff, false, false)) {
1063 LOG(ERROR) << "Sensor[" << name << "]: Failed to parse I_Cutoff";
1064 return false;
1065 }
1066
1067 if (!sensor["PIDInfo"]["I_Default"].empty() &&
1068 !sensor["PIDInfo"]["I_Default_Pct"].empty()) {
1069 LOG(ERROR) << "I_Default and I_Default_P cannot be applied together";
1070 return false;
1071 }
1072
1073 if (!sensor["PIDInfo"]["I_Default"].empty()) {
1074 i_default = getFloatFromValue(sensor["PIDInfo"]["I_Default"]);
1075 LOG(INFO) << "Sensor[" << name << "]'s I_Default: " << i_default;
1076 } else if (!sensor["PIDInfo"]["I_Default_Pct"].empty()) {
1077 i_default_pct = getFloatFromValue(sensor["PIDInfo"]["I_Default_Pct"]);
1078 LOG(INFO) << "Sensor[" << name << "]'s I_Default_Pct: " << i_default_pct;
1079 }
1080 tran_cycle = getFloatFromValue(sensor["PIDInfo"]["TranCycle"]);
1081 LOG(INFO) << "Sensor[" << name << "]'s TranCycle: " << tran_cycle;
1082
1083 // Confirm we have at least one valid PID combination
1084 bool valid_pid_combination = false;
1085 for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
1086 if (!std::isnan(s_power[j])) {
1087 if (std::isnan(k_po[j]) || std::isnan(k_pu[j]) || std::isnan(k_io[j]) ||
1088 std::isnan(k_iu[j]) || std::isnan(k_d[j]) || std::isnan(i_max[j]) ||
1089 std::isnan(max_alloc_power[j]) || std::isnan(min_alloc_power[j]) ||
1090 std::isnan(i_cutoff[j])) {
1091 valid_pid_combination = false;
1092 break;
1093 } else {
1094 valid_pid_combination = true;
1095 }
1096 }
1097 }
1098 if (!valid_pid_combination) {
1099 LOG(ERROR) << "Sensor[" << name << "]: Invalid PID parameters combinations";
1100 return false;
1101 } else {
1102 support_pid = true;
1103 }
1104 }
1105
1106 // Parse binded cooling device
1107 std::unordered_map<std::string, BindedCdevInfo> binded_cdev_info_map;
1108 if (!ParseBindedCdevInfo(sensor["BindedCdevInfo"], &binded_cdev_info_map, support_pid,
1109 &support_hard_limit, scaling_frequency_map,
1110 cooling_device_info_map_)) {
1111 LOG(ERROR) << "Sensor[" << name << "]: failed to parse BindedCdevInfo";
1112 return false;
1113 }
1114
1115 Json::Value values;
1116 ProfileMap profile_map;
1117 values = sensor["Profile"];
1118 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
1119 Json::Value sub_values;
1120 const std::string &mode = values[j]["Mode"].asString();
1121 std::unordered_map<std::string, BindedCdevInfo> binded_cdev_info_map_profile;
1122 if (!ParseBindedCdevInfo(values[j]["BindedCdevInfo"], &binded_cdev_info_map_profile,
1123 support_pid, &support_hard_limit, scaling_frequency_map,
1124 cooling_device_info_map_)) {
1125 LOG(ERROR) << "Sensor[" << name << " failed to parse BindedCdevInfo profile";
1126 }
1127 // Check if the binded_cdev_info_map_profile is valid
1128 if (binded_cdev_info_map.size() != binded_cdev_info_map_profile.size()) {
1129 LOG(ERROR) << "Sensor[" << name << "]:'s profile map size should not be changed";
1130 return false;
1131 } else {
1132 for (const auto &binded_cdev_info_pair : binded_cdev_info_map_profile) {
1133 if (binded_cdev_info_map.count(binded_cdev_info_pair.first)) {
1134 if (binded_cdev_info_pair.second.power_rail !=
1135 binded_cdev_info_map.at(binded_cdev_info_pair.first).power_rail) {
1136 LOG(ERROR) << "Sensor[" << name << "]:'s profile " << mode << " binded "
1137 << binded_cdev_info_pair.first
1138 << "'s power rail is not included in default rules";
1139 return false;
1140 } else {
1141 LOG(INFO) << "Sensor[" << name << "]:'s profile " << mode
1142 << " is parsed successfully";
1143 }
1144 } else {
1145 LOG(ERROR) << "Sensor[" << name << "]'s profile " << mode << " binded "
1146 << binded_cdev_info_pair.first
1147 << " is not included in default rules";
1148 return false;
1149 }
1150 }
1151 }
1152 profile_map[mode] = binded_cdev_info_map_profile;
1153 }
1154
1155 std::unordered_map<std::string, ThrottlingArray> excluded_power_info_map;
1156 values = sensor["ExcludedPowerInfo"];
1157 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
1158 Json::Value sub_values;
1159 const std::string &power_rail = values[j]["PowerRail"].asString();
1160 if (power_rail.empty()) {
1161 LOG(ERROR) << "Sensor[" << name << "] failed to parse excluded PowerRail";
1162 return false;
1163 }
1164 ThrottlingArray power_weight;
1165 power_weight.fill(1);
1166 if (!values[j]["PowerWeight"].empty()) {
1167 LOG(INFO) << "Sensor[" << name << "]: Start to parse " << power_rail
1168 << "'s PowerWeight";
1169 if (!getFloatFromJsonValues(values[j]["PowerWeight"], &power_weight, false, false)) {
1170 LOG(ERROR) << "Failed to parse PowerWeight";
1171 return false;
1172 }
1173 }
1174 excluded_power_info_map[power_rail] = power_weight;
1175 }
1176 throttling_info->reset(new ThrottlingInfo{k_po, k_pu, k_io, k_iu, k_d, i_max, max_alloc_power,
1177 min_alloc_power, s_power, i_cutoff, i_default,
1178 i_default_pct, tran_cycle, excluded_power_info_map,
1179 binded_cdev_info_map, profile_map});
1180 *support_throttling = support_pid | support_hard_limit;
1181 return true;
1182 }
1183
ParseSensorInfo(const Json::Value & config,std::unordered_map<std::string,SensorInfo> * sensors_parsed,const std::unordered_map<std::string,CdevInfo> & cooling_device_info_map_)1184 bool ParseSensorInfo(const Json::Value &config,
1185 std::unordered_map<std::string, SensorInfo> *sensors_parsed,
1186 const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_) {
1187 Json::Value sensors = config["Sensors"];
1188 Json::Value cdevs = config["CoolingDevices"];
1189 std::unordered_map<std::string, std::vector<int>> scaling_frequency_map;
1190
1191 LOG(INFO) << "Start reading ScalingAvailableFrequenciesPath from config";
1192 for (Json::Value::ArrayIndex i = 0; i < cdevs.size(); ++i) {
1193 if (cdevs[i]["ScalingAvailableFrequenciesPath"].empty() ||
1194 cdevs[i]["isDisabled"].asBool()) {
1195 continue;
1196 }
1197
1198 const std::string &path = cdevs[i]["ScalingAvailableFrequenciesPath"].asString();
1199 const std::string &name = cdevs[i]["Name"].asString();
1200
1201 LOG(INFO) << "Cdev[" << name << "]'s scaling frequency path: " << path;
1202 std::string scaling_frequency_str;
1203 if (::android::base::ReadFileToString(path, &scaling_frequency_str)) {
1204 std::istringstream frequencies(scaling_frequency_str);
1205 int frequency;
1206 while (frequencies >> frequency) {
1207 LOG(INFO) << "Cdev[" << name << "]'s available frequency: " << frequency;
1208 scaling_frequency_map[name].push_back(frequency);
1209 }
1210
1211 // Reverse the vector if it starts from small value
1212 if (scaling_frequency_map[name].front() < scaling_frequency_map[name].back()) {
1213 std::reverse(scaling_frequency_map[name].begin(),
1214 scaling_frequency_map[name].end());
1215 }
1216
1217 // Make sure the scaling frequencies strictly decreasing
1218 if (std::adjacent_find(scaling_frequency_map[name].begin(),
1219 scaling_frequency_map[name].end(),
1220 std::less_equal<int>()) != scaling_frequency_map[name].end()) {
1221 LOG(ERROR) << "Cdev[" << name << "]'s scaling frequencies is not monotonic";
1222 sensors_parsed->clear();
1223 return false;
1224 }
1225 } else {
1226 LOG(ERROR) << "Cdev[" << name << "]'s scaling frequency path is invalid.";
1227 sensors_parsed->clear();
1228 return false;
1229 }
1230 }
1231
1232 std::size_t total_parsed = 0;
1233 std::unordered_set<std::string> sensors_name_parsed;
1234
1235 for (Json::Value::ArrayIndex i = 0; i < sensors.size(); ++i) {
1236 const std::string &name = sensors[i]["Name"].asString();
1237 LOG(INFO) << "Sensor[" << i << "]'s Name: " << name;
1238 if (name.empty()) {
1239 LOG(ERROR) << "Failed to read Sensor[" << i << "]'s Name";
1240 sensors_parsed->clear();
1241 return false;
1242 }
1243
1244 if (sensors[i]["isDisabled"].asBool()) {
1245 LOG(INFO) << "sensors[" << name << "] is disabled. Skipping parsing";
1246 continue;
1247 }
1248
1249 auto result = sensors_name_parsed.insert(name);
1250 if (!result.second) {
1251 LOG(ERROR) << "Duplicate Sensor[" << i << "]'s Name";
1252 sensors_parsed->clear();
1253 return false;
1254 }
1255
1256 std::string sensor_type_str = sensors[i]["Type"].asString();
1257 LOG(INFO) << "Sensor[" << name << "]'s Type: " << sensor_type_str;
1258 TemperatureType sensor_type;
1259
1260 if (!getTypeFromString(sensor_type_str, &sensor_type)) {
1261 LOG(ERROR) << "Invalid Sensor[" << name << "]'s Type: " << sensor_type_str;
1262 sensors_parsed->clear();
1263 return false;
1264 }
1265
1266 bool send_cb = false;
1267 if (!sensors[i]["Monitor"].empty() && sensors[i]["Monitor"].isBool()) {
1268 send_cb = sensors[i]["Monitor"].asBool();
1269 } else if (!sensors[i]["SendCallback"].empty() && sensors[i]["SendCallback"].isBool()) {
1270 send_cb = sensors[i]["SendCallback"].asBool();
1271 }
1272 LOG(INFO) << "Sensor[" << name << "]'s SendCallback: " << std::boolalpha << send_cb
1273 << std::noboolalpha;
1274
1275 bool send_powerhint = false;
1276 if (sensors[i]["SendPowerHint"].empty() || !sensors[i]["SendPowerHint"].isBool()) {
1277 LOG(INFO) << "Failed to read Sensor[" << name << "]'s SendPowerHint, set to 'false'";
1278 } else if (sensors[i]["SendPowerHint"].asBool()) {
1279 send_powerhint = true;
1280 }
1281 LOG(INFO) << "Sensor[" << name << "]'s SendPowerHint: " << std::boolalpha << send_powerhint
1282 << std::noboolalpha;
1283
1284 bool is_trip_point_ignorable = false;
1285 if (sensors[i]["TripPointIgnorable"].empty() ||
1286 !sensors[i]["TripPointIgnorable"].isBool()) {
1287 LOG(INFO) << "Failed to read Sensor[" << name
1288 << "]'s TripPointIgnorable, set to 'false'";
1289 } else if (sensors[i]["TripPointIgnorable"].asBool()) {
1290 is_trip_point_ignorable = true;
1291 }
1292 LOG(INFO) << "Sensor[" << name << "]'s TripPointIgnorable: " << std::boolalpha
1293 << is_trip_point_ignorable << std::noboolalpha;
1294
1295 bool is_hidden = false;
1296 if (sensors[i]["Hidden"].empty() || !sensors[i]["Hidden"].isBool()) {
1297 LOG(INFO) << "Failed to read Sensor[" << name << "]'s Hidden, set to 'false'";
1298 } else if (sensors[i]["Hidden"].asBool()) {
1299 is_hidden = true;
1300 }
1301 LOG(INFO) << "Sensor[" << name << "]'s Hidden: " << std::boolalpha << is_hidden
1302 << std::noboolalpha;
1303
1304 ThrottlingSeverity log_level = ThrottlingSeverity::NONE;
1305 if (!sensors[i]["LogLevel"].empty()) {
1306 const auto level = sensors[i]["LogLevel"].asInt();
1307 if (level > static_cast<int>(ThrottlingSeverity::SHUTDOWN)) {
1308 LOG(ERROR) << "Sensor[" << name << "]'s LogLevel is invalid";
1309 } else {
1310 log_level = static_cast<ThrottlingSeverity>(level);
1311 }
1312 }
1313 LOG(INFO) << "Sensor[" << name << "]'s LogLevel: " << toString(log_level);
1314
1315 std::array<float, kThrottlingSeverityCount> hot_thresholds;
1316 hot_thresholds.fill(NAN);
1317 std::array<float, kThrottlingSeverityCount> cold_thresholds;
1318 cold_thresholds.fill(NAN);
1319 std::array<float, kThrottlingSeverityCount> hot_hysteresis;
1320 hot_hysteresis.fill(0.0);
1321 std::array<float, kThrottlingSeverityCount> cold_hysteresis;
1322 cold_hysteresis.fill(0.0);
1323
1324 Json::Value values = sensors[i]["HotThreshold"];
1325 if (!values.size()) {
1326 LOG(INFO) << "Sensor[" << name << "]'s HotThreshold, default all to NAN";
1327 } else if (values.size() != kThrottlingSeverityCount) {
1328 LOG(ERROR) << "Invalid Sensor[" << name << "]'s HotThreshold count:" << values.size();
1329 sensors_parsed->clear();
1330 return false;
1331 } else {
1332 float min = std::numeric_limits<float>::min();
1333 for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
1334 hot_thresholds[j] = getFloatFromValue(values[j]);
1335 if (!std::isnan(hot_thresholds[j])) {
1336 if (hot_thresholds[j] < min) {
1337 LOG(ERROR) << "Invalid "
1338 << "Sensor[" << name << "]'s HotThreshold[j" << j
1339 << "]: " << hot_thresholds[j] << " < " << min;
1340 sensors_parsed->clear();
1341 return false;
1342 }
1343 min = hot_thresholds[j];
1344 }
1345 LOG(INFO) << "Sensor[" << name << "]'s HotThreshold[" << j
1346 << "]: " << hot_thresholds[j];
1347 }
1348 }
1349
1350 values = sensors[i]["HotHysteresis"];
1351 if (!values.size()) {
1352 LOG(INFO) << "Sensor[" << name << "]'s HotHysteresis, default all to 0.0";
1353 } else if (values.size() != kThrottlingSeverityCount) {
1354 LOG(ERROR) << "Invalid Sensor[" << name << "]'s HotHysteresis, count:" << values.size();
1355 sensors_parsed->clear();
1356 return false;
1357 } else {
1358 for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
1359 hot_hysteresis[j] = getFloatFromValue(values[j]);
1360 if (std::isnan(hot_hysteresis[j])) {
1361 LOG(ERROR) << "Invalid Sensor[" << name
1362 << "]'s HotHysteresis: " << hot_hysteresis[j];
1363 sensors_parsed->clear();
1364 return false;
1365 }
1366 LOG(INFO) << "Sensor[" << name << "]'s HotHysteresis[" << j
1367 << "]: " << hot_hysteresis[j];
1368 }
1369 }
1370
1371 for (Json::Value::ArrayIndex j = 0; j < (kThrottlingSeverityCount - 1); ++j) {
1372 if (std::isnan(hot_thresholds[j])) {
1373 continue;
1374 }
1375 for (auto k = j + 1; k < kThrottlingSeverityCount; ++k) {
1376 if (std::isnan(hot_thresholds[k])) {
1377 continue;
1378 } else if (hot_thresholds[j] > (hot_thresholds[k] - hot_hysteresis[k])) {
1379 LOG(ERROR) << "Sensor[" << name << "]'s hot threshold " << j
1380 << " is overlapped";
1381 sensors_parsed->clear();
1382 return false;
1383 } else {
1384 break;
1385 }
1386 }
1387 }
1388
1389 values = sensors[i]["ColdThreshold"];
1390 if (!values.size()) {
1391 LOG(INFO) << "Sensor[" << name << "]'s ColdThreshold, default all to NAN";
1392 } else if (values.size() != kThrottlingSeverityCount) {
1393 LOG(ERROR) << "Invalid Sensor[" << name << "]'s ColdThreshold count:" << values.size();
1394 sensors_parsed->clear();
1395 return false;
1396 } else {
1397 float max = std::numeric_limits<float>::max();
1398 for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
1399 cold_thresholds[j] = getFloatFromValue(values[j]);
1400 if (!std::isnan(cold_thresholds[j])) {
1401 if (cold_thresholds[j] > max) {
1402 LOG(ERROR) << "Invalid "
1403 << "Sensor[" << name << "]'s ColdThreshold[j" << j
1404 << "]: " << cold_thresholds[j] << " > " << max;
1405 sensors_parsed->clear();
1406 return false;
1407 }
1408 max = cold_thresholds[j];
1409 }
1410 LOG(INFO) << "Sensor[" << name << "]'s ColdThreshold[" << j
1411 << "]: " << cold_thresholds[j];
1412 }
1413 }
1414
1415 values = sensors[i]["ColdHysteresis"];
1416 if (!values.size()) {
1417 LOG(INFO) << "Sensor[" << name << "]'s ColdHysteresis, default all to 0.0";
1418 } else if (values.size() != kThrottlingSeverityCount) {
1419 LOG(ERROR) << "Invalid Sensor[" << name << "]'s ColdHysteresis count:" << values.size();
1420 sensors_parsed->clear();
1421 return false;
1422 } else {
1423 for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
1424 cold_hysteresis[j] = getFloatFromValue(values[j]);
1425 if (std::isnan(cold_hysteresis[j])) {
1426 LOG(ERROR) << "Invalid Sensor[" << name
1427 << "]'s ColdHysteresis: " << cold_hysteresis[j];
1428 sensors_parsed->clear();
1429 return false;
1430 }
1431 LOG(INFO) << "Sensor[" << name << "]'s ColdHysteresis[" << j
1432 << "]: " << cold_hysteresis[j];
1433 }
1434 }
1435
1436 for (Json::Value::ArrayIndex j = 0; j < (kThrottlingSeverityCount - 1); ++j) {
1437 if (std::isnan(cold_thresholds[j])) {
1438 continue;
1439 }
1440 for (auto k = j + 1; k < kThrottlingSeverityCount; ++k) {
1441 if (std::isnan(cold_thresholds[k])) {
1442 continue;
1443 } else if (cold_thresholds[j] < (cold_thresholds[k] + cold_hysteresis[k])) {
1444 LOG(ERROR) << "Sensor[" << name << "]'s cold threshold " << j
1445 << " is overlapped";
1446 sensors_parsed->clear();
1447 return false;
1448 } else {
1449 break;
1450 }
1451 }
1452 }
1453
1454 std::string temp_path;
1455 if (!sensors[i]["TempPath"].empty()) {
1456 temp_path = sensors[i]["TempPath"].asString();
1457 LOG(INFO) << "Sensor[" << name << "]'s TempPath: " << temp_path;
1458 }
1459
1460 std::vector<std::string> severity_reference;
1461
1462 values = sensors[i]["SeverityReference"];
1463 if (values.isString()) {
1464 severity_reference.emplace_back(values.asString());
1465 LOG(INFO) << "Sensor[" << name << "]'s SeverityReference:" << values.asString();
1466 } else if (values.size()) {
1467 severity_reference.reserve(values.size());
1468 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
1469 severity_reference.emplace_back(values[j].asString());
1470 LOG(INFO) << "Sensor[" << name << "]'s SeverityReference[" << j
1471 << "]: " << severity_reference[j];
1472 }
1473 }
1474
1475 float vr_threshold = NAN;
1476 if (!sensors[i]["VrThreshold"].empty()) {
1477 vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]);
1478 LOG(INFO) << "Sensor[" << name << "]'s VrThreshold: " << vr_threshold;
1479 }
1480
1481 float multiplier = 1.0;
1482 if (!sensors[i]["Multiplier"].empty()) {
1483 multiplier = sensors[i]["Multiplier"].asFloat();
1484 }
1485 LOG(INFO) << "Sensor[" << name << "]'s Multiplier: " << multiplier;
1486
1487 std::chrono::milliseconds polling_delay = kUeventPollTimeoutMs;
1488 if (!sensors[i]["PollingDelay"].empty()) {
1489 const auto value = getIntFromValue(sensors[i]["PollingDelay"]);
1490 polling_delay = (value > 0) ? std::chrono::milliseconds(value)
1491 : std::chrono::milliseconds::max();
1492 }
1493 LOG(INFO) << "Sensor[" << name << "]'s Polling delay: " << polling_delay.count();
1494
1495 std::chrono::milliseconds passive_delay = kMinPollIntervalMs;
1496 if (!sensors[i]["PassiveDelay"].empty()) {
1497 const auto value = getIntFromValue(sensors[i]["PassiveDelay"]);
1498 passive_delay = (value > 0) ? std::chrono::milliseconds(value)
1499 : std::chrono::milliseconds::max();
1500 }
1501 LOG(INFO) << "Sensor[" << name << "]'s Passive delay: " << passive_delay.count();
1502
1503 std::chrono::milliseconds time_resolution;
1504 if (sensors[i]["TimeResolution"].empty()) {
1505 time_resolution = kMinPollIntervalMs;
1506 } else {
1507 time_resolution =
1508 std::chrono::milliseconds(getIntFromValue(sensors[i]["TimeResolution"]));
1509 }
1510 LOG(INFO) << "Sensor[" << name << "]'s Time resolution: " << time_resolution.count();
1511
1512 float step_ratio = NAN;
1513 if (!sensors[i]["StepRatio"].empty()) {
1514 step_ratio = sensors[i]["StepRatio"].asFloat();
1515 if (step_ratio < 0 || step_ratio > 1) {
1516 LOG(ERROR) << "Sensor[" << name << "]'s StepRatio should be set 0 ~ 1";
1517 sensors_parsed->clear();
1518 return false;
1519 }
1520
1521 if (sensors[i]["PassiveDelay"].empty()) {
1522 LOG(ERROR) << "Sensor[" << name << "] has StepRatio but no explicit PassiveDelay";
1523 sensors_parsed->clear();
1524 return false;
1525 }
1526 }
1527
1528 if (is_hidden && send_cb) {
1529 LOG(ERROR) << "is_hidden and send_cb cannot be enabled together";
1530 sensors_parsed->clear();
1531 return false;
1532 }
1533
1534 std::unique_ptr<VirtualSensorInfo> virtual_sensor_info;
1535 if (!ParseVirtualSensorInfo(name, sensors[i], &virtual_sensor_info)) {
1536 LOG(ERROR) << "Sensor[" << name << "]: failed to parse virtual sensor info";
1537 sensors_parsed->clear();
1538 return false;
1539 }
1540
1541 std::unique_ptr<PredictorInfo> predictor_info;
1542 if (!ParsePredictorInfo(name, sensors[i], &predictor_info)) {
1543 LOG(ERROR) << "Sensor[" << name << "]: failed to parse virtual sensor info";
1544 sensors_parsed->clear();
1545 return false;
1546 }
1547
1548 bool support_throttling = false; // support pid or hard limit
1549 std::shared_ptr<ThrottlingInfo> throttling_info;
1550 if (!ParseSensorThrottlingInfo(name, sensors[i], &support_throttling, &throttling_info,
1551 scaling_frequency_map, cooling_device_info_map_)) {
1552 LOG(ERROR) << "Sensor[" << name << "]: failed to parse throttling info";
1553 sensors_parsed->clear();
1554 return false;
1555 }
1556
1557 bool is_watch = (send_cb | send_powerhint | support_throttling);
1558 LOG(INFO) << "Sensor[" << name << "]'s is_watch: " << std::boolalpha << is_watch;
1559
1560 (*sensors_parsed)[name] = {
1561 .type = sensor_type,
1562 .hot_thresholds = hot_thresholds,
1563 .cold_thresholds = cold_thresholds,
1564 .hot_hysteresis = hot_hysteresis,
1565 .cold_hysteresis = cold_hysteresis,
1566 .temp_path = temp_path,
1567 .severity_reference = severity_reference,
1568 .vr_threshold = vr_threshold,
1569 .multiplier = multiplier,
1570 .polling_delay = polling_delay,
1571 .passive_delay = passive_delay,
1572 .time_resolution = time_resolution,
1573 .step_ratio = step_ratio,
1574 .send_cb = send_cb,
1575 .send_powerhint = send_powerhint,
1576 .is_watch = is_watch,
1577 .is_hidden = is_hidden,
1578 .is_trip_point_ignorable = is_trip_point_ignorable,
1579 .log_level = log_level,
1580 .virtual_sensor_info = std::move(virtual_sensor_info),
1581 .throttling_info = std::move(throttling_info),
1582 .predictor_info = std::move(predictor_info),
1583 };
1584
1585 ++total_parsed;
1586 }
1587 LOG(INFO) << total_parsed << " Sensors parsed successfully";
1588 return true;
1589 }
1590
ParseCoolingDevice(const Json::Value & config,std::unordered_map<std::string,CdevInfo> * cooling_devices_parsed)1591 bool ParseCoolingDevice(const Json::Value &config,
1592 std::unordered_map<std::string, CdevInfo> *cooling_devices_parsed) {
1593 Json::Value cooling_devices = config["CoolingDevices"];
1594 std::size_t total_parsed = 0;
1595 std::unordered_set<std::string> cooling_devices_name_parsed;
1596
1597 for (Json::Value::ArrayIndex i = 0; i < cooling_devices.size(); ++i) {
1598 bool apply_powercap = false;
1599 float multiplier = 1.0;
1600 const std::string &name = cooling_devices[i]["Name"].asString();
1601 LOG(INFO) << "CoolingDevice[" << i << "]'s Name: " << name;
1602 if (name.empty()) {
1603 LOG(ERROR) << "Failed to read CoolingDevice[" << i << "]'s Name";
1604 cooling_devices_parsed->clear();
1605 return false;
1606 }
1607
1608 if (cooling_devices[i]["isDisabled"].asBool()) {
1609 LOG(INFO) << "CoolingDevice[" << name << "] is disabled. Skipping parsing";
1610 continue;
1611 }
1612
1613 if (cooling_devices[i]["PowerCap"].asBool() && cooling_devices[i]["PowerCap"].isBool()) {
1614 LOG(INFO) << "CoolingDevice[" << name << "] apply powercap";
1615 apply_powercap = true;
1616 }
1617
1618 if (!cooling_devices[i]["Multiplier"].empty()) {
1619 multiplier = cooling_devices[i]["Multiplier"].asFloat();
1620 if (multiplier <= 0) {
1621 cooling_devices_parsed->clear();
1622 LOG(INFO) << "CoolingDevice[" << name << "]'s Multiplier: " << multiplier
1623 << " is invalid";
1624 return false;
1625 }
1626 }
1627 LOG(INFO) << "CoolingDevice[" << name << "]'s Multiplier: " << multiplier;
1628
1629 auto result = cooling_devices_name_parsed.insert(name.data());
1630 if (!result.second) {
1631 LOG(ERROR) << "Duplicate CoolingDevice[" << i << "]'s Name";
1632 cooling_devices_parsed->clear();
1633 return false;
1634 }
1635
1636 std::string cooling_device_type_str = cooling_devices[i]["Type"].asString();
1637 LOG(INFO) << "CoolingDevice[" << name << "]'s Type: " << cooling_device_type_str;
1638 CoolingType cooling_device_type;
1639
1640 if (!getTypeFromString(cooling_device_type_str, &cooling_device_type)) {
1641 LOG(ERROR) << "Invalid CoolingDevice[" << name
1642 << "]'s Type: " << cooling_device_type_str;
1643 cooling_devices_parsed->clear();
1644 return false;
1645 }
1646
1647 const std::string &read_path = cooling_devices[i]["ReadPath"].asString();
1648 LOG(INFO) << "Cdev Read Path: " << (read_path.empty() ? "default" : read_path);
1649
1650 const std::string &write_path = cooling_devices[i]["WritePath"].asString();
1651 LOG(INFO) << "Cdev Write Path: " << (write_path.empty() ? "default" : write_path);
1652
1653 std::vector<float> state2power;
1654 Json::Value values = cooling_devices[i]["State2Power"];
1655 if (values.size()) {
1656 LOG(INFO) << "Cooling device " << name << " use State2power read from config";
1657 state2power.reserve(values.size());
1658 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
1659 state2power.emplace_back(getFloatFromValue(values[j]));
1660 }
1661 } else {
1662 LOG(INFO) << "CoolingDevice[" << i << "]'s Name: " << name
1663 << " does not support State2Power in thermal config";
1664 }
1665
1666 (*cooling_devices_parsed)[name] = {
1667 .type = cooling_device_type,
1668 .read_path = read_path,
1669 .write_path = write_path,
1670 .state2power = state2power,
1671 .apply_powercap = apply_powercap,
1672 .multiplier = multiplier,
1673 };
1674 ++total_parsed;
1675 }
1676 LOG(INFO) << total_parsed << " CoolingDevices parsed successfully";
1677 return true;
1678 }
1679
ParsePowerRailInfo(const Json::Value & config,std::unordered_map<std::string,PowerRailInfo> * power_rails_parsed,std::unordered_map<std::string,std::vector<std::string>> * power_rail_switch_map)1680 bool ParsePowerRailInfo(
1681 const Json::Value &config,
1682 std::unordered_map<std::string, PowerRailInfo> *power_rails_parsed,
1683 std::unordered_map<std::string, std::vector<std::string>> *power_rail_switch_map) {
1684 Json::Value power_rails = config["PowerRails"];
1685 std::size_t total_parsed = 0;
1686 std::unordered_set<std::string> power_rails_name_parsed;
1687
1688 for (Json::Value::ArrayIndex i = 0; i < power_rails.size(); ++i) {
1689 const std::string &name = power_rails[i]["Name"].asString();
1690 LOG(INFO) << "PowerRail[" << i << "]'s Name: " << name;
1691 if (name.empty()) {
1692 LOG(ERROR) << "Failed to read PowerRail[" << i << "]'s Name";
1693 power_rails_parsed->clear();
1694 return false;
1695 }
1696
1697 std::vector<std::string> linked_power_rails;
1698 std::vector<float> coefficient;
1699 float offset = 0;
1700 FormulaOption formula = FormulaOption::COUNT_THRESHOLD;
1701 bool is_virtual_power_rail = false;
1702 Json::Value values;
1703 int power_sample_count = 0;
1704 std::chrono::milliseconds power_sample_delay;
1705
1706 if (!power_rails[i]["VirtualRails"].empty() && power_rails[i]["VirtualRails"].isBool()) {
1707 is_virtual_power_rail = power_rails[i]["VirtualRails"].asBool();
1708 LOG(INFO) << "PowerRails[" << name << "]'s VirtualRail, set to 'true'";
1709 }
1710
1711 if (is_virtual_power_rail) {
1712 values = power_rails[i]["Combination"];
1713 if (values.size()) {
1714 linked_power_rails.reserve(values.size());
1715 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
1716 linked_power_rails.emplace_back(values[j].asString());
1717 LOG(INFO) << "PowerRail[" << name << "]'s combination[" << j
1718 << "]: " << linked_power_rails[j];
1719 }
1720 } else {
1721 LOG(ERROR) << "PowerRails[" << name << "] has no combination for VirtualRail";
1722 power_rails_parsed->clear();
1723 return false;
1724 }
1725
1726 values = power_rails[i]["Coefficient"];
1727 if (values.size()) {
1728 coefficient.reserve(values.size());
1729 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
1730 coefficient.emplace_back(getFloatFromValue(values[j]));
1731 LOG(INFO) << "PowerRail[" << name << "]'s coefficient[" << j
1732 << "]: " << coefficient[j];
1733 }
1734 } else {
1735 LOG(ERROR) << "PowerRails[" << name << "] has no coefficient for VirtualRail";
1736 power_rails_parsed->clear();
1737 return false;
1738 }
1739
1740 if (linked_power_rails.size() != coefficient.size()) {
1741 LOG(ERROR) << "PowerRails[" << name
1742 << "]'s combination size is not matched with coefficient size";
1743 power_rails_parsed->clear();
1744 return false;
1745 }
1746
1747 if (!power_rails[i]["Offset"].empty()) {
1748 offset = power_rails[i]["Offset"].asFloat();
1749 }
1750
1751 if (linked_power_rails.size() != coefficient.size()) {
1752 LOG(ERROR) << "PowerRails[" << name
1753 << "]'s combination size is not matched with coefficient size";
1754 power_rails_parsed->clear();
1755 return false;
1756 }
1757
1758 if (power_rails[i]["Formula"].asString().compare("COUNT_THRESHOLD") == 0) {
1759 formula = FormulaOption::COUNT_THRESHOLD;
1760 } else if (power_rails[i]["Formula"].asString().compare("WEIGHTED_AVG") == 0) {
1761 formula = FormulaOption::WEIGHTED_AVG;
1762 } else if (power_rails[i]["Formula"].asString().compare("MAXIMUM") == 0) {
1763 formula = FormulaOption::MAXIMUM;
1764 } else if (power_rails[i]["Formula"].asString().compare("MINIMUM") == 0) {
1765 formula = FormulaOption::MINIMUM;
1766 } else {
1767 LOG(ERROR) << "PowerRails[" << name << "]'s Formula is invalid";
1768 power_rails_parsed->clear();
1769 return false;
1770 }
1771 }
1772
1773 std::unique_ptr<VirtualPowerRailInfo> virtual_power_rail_info;
1774 if (is_virtual_power_rail) {
1775 virtual_power_rail_info.reset(
1776 new VirtualPowerRailInfo{linked_power_rails, coefficient, offset, formula});
1777 }
1778
1779 power_sample_count = power_rails[i]["PowerSampleCount"].asInt();
1780 LOG(INFO) << "Power sample Count: " << power_sample_count;
1781
1782 if (!power_rails[i]["PowerSampleDelay"]) {
1783 power_sample_delay = std::chrono::milliseconds::max();
1784 } else {
1785 power_sample_delay =
1786 std::chrono::milliseconds(getIntFromValue(power_rails[i]["PowerSampleDelay"]));
1787 }
1788
1789 std::string trigger_sensor;
1790 values = power_rails[i]["TriggerSensor"];
1791 if (!values.empty()) {
1792 if (values.isString()) {
1793 (*power_rail_switch_map)[values.asString()].emplace_back(name);
1794 LOG(INFO) << "Power rail[" << name << "]'s TriggerSensor: " << values.asString();
1795 }
1796 }
1797
1798 (*power_rails_parsed)[name] = {
1799 .power_sample_count = power_sample_count,
1800 .power_sample_delay = power_sample_delay,
1801 .virtual_power_rail_info = std::move(virtual_power_rail_info),
1802 };
1803 ++total_parsed;
1804 }
1805 LOG(INFO) << total_parsed << " PowerRails parsed successfully";
1806 return true;
1807 }
1808
1809 template <typename T, typename U>
ParseStatsInfo(const Json::Value & stats_config,const std::unordered_map<std::string,U> & entity_info,StatsInfo<T> * stats_info,T min_value)1810 bool ParseStatsInfo(const Json::Value &stats_config,
1811 const std::unordered_map<std::string, U> &entity_info, StatsInfo<T> *stats_info,
1812 T min_value) {
1813 if (stats_config.empty()) {
1814 LOG(INFO) << "No stats config";
1815 return true;
1816 }
1817 std::variant<bool, std::unordered_set<std::string>>
1818 record_by_default_threshold_all_or_name_set_ = false;
1819 if (stats_config["DefaultThresholdEnableAll"].empty() ||
1820 !stats_config["DefaultThresholdEnableAll"].isBool()) {
1821 LOG(INFO) << "Failed to read stats DefaultThresholdEnableAll, set to 'false'";
1822 } else if (stats_config["DefaultThresholdEnableAll"].asBool()) {
1823 record_by_default_threshold_all_or_name_set_ = true;
1824 }
1825 LOG(INFO) << "DefaultThresholdEnableAll " << std::boolalpha
1826 << std::get<bool>(record_by_default_threshold_all_or_name_set_) << std::noboolalpha;
1827
1828 Json::Value values = stats_config["RecordWithDefaultThreshold"];
1829 if (values.size()) {
1830 if (std::get<bool>(record_by_default_threshold_all_or_name_set_)) {
1831 LOG(ERROR) << "Cannot enable record with default threshold when "
1832 "DefaultThresholdEnableAll true.";
1833 return false;
1834 }
1835 record_by_default_threshold_all_or_name_set_ = std::unordered_set<std::string>();
1836 for (Json::Value::ArrayIndex i = 0; i < values.size(); ++i) {
1837 std::string name = values[i].asString();
1838 if (!entity_info.count(name)) {
1839 LOG(ERROR) << "Unknown name [" << name << "] not present in entity_info.";
1840 return false;
1841 }
1842 std::get<std::unordered_set<std::string>>(record_by_default_threshold_all_or_name_set_)
1843 .insert(name);
1844 }
1845 } else {
1846 LOG(INFO) << "No stat by default threshold enabled.";
1847 }
1848
1849 std::unordered_map<std::string, std::vector<ThresholdList<T>>> record_by_threshold;
1850 values = stats_config["RecordWithThreshold"];
1851 if (values.size()) {
1852 Json::Value threshold_values;
1853 for (Json::Value::ArrayIndex i = 0; i < values.size(); i++) {
1854 const std::string &name = values[i]["Name"].asString();
1855 if (!entity_info.count(name)) {
1856 LOG(ERROR) << "Unknown name [" << name << "] not present in entity_info.";
1857 return false;
1858 }
1859
1860 std::optional<std::string> logging_name;
1861 if (!values[i]["LoggingName"].empty()) {
1862 logging_name = values[i]["LoggingName"].asString();
1863 LOG(INFO) << "For [" << name << "]"
1864 << ", stats logging name is [" << logging_name.value() << "]";
1865 }
1866
1867 LOG(INFO) << "Start to parse stats threshold for [" << name << "]";
1868 threshold_values = values[i]["Thresholds"];
1869 if (threshold_values.empty()) {
1870 LOG(ERROR) << "Empty stats threshold not valid.";
1871 return false;
1872 }
1873 const auto &threshold_values_count = threshold_values.size();
1874 if (threshold_values_count > kMaxStatsThresholdCount) {
1875 LOG(ERROR) << "Number of stats threshold " << threshold_values_count
1876 << " greater than max " << kMaxStatsThresholdCount;
1877 return false;
1878 }
1879 std::vector<T> stats_threshold(threshold_values_count);
1880 T prev_value = min_value;
1881 LOG(INFO) << "Thresholds:";
1882 for (Json::Value::ArrayIndex i = 0; i < threshold_values_count; ++i) {
1883 stats_threshold[i] = std::is_floating_point_v<T>
1884 ? getFloatFromValue(threshold_values[i])
1885 : getIntFromValue(threshold_values[i]);
1886 if (stats_threshold[i] <= prev_value) {
1887 LOG(ERROR) << "Invalid array[" << i << "]" << stats_threshold[i]
1888 << " is <=" << prev_value;
1889 return false;
1890 }
1891 prev_value = stats_threshold[i];
1892 LOG(INFO) << "[" << i << "]: " << stats_threshold[i];
1893 }
1894 record_by_threshold[name].emplace_back(logging_name, stats_threshold);
1895 }
1896 } else {
1897 LOG(INFO) << "No stat by threshold enabled.";
1898 }
1899
1900 (*stats_info) = {.record_by_default_threshold_all_or_name_set_ =
1901 record_by_default_threshold_all_or_name_set_,
1902 .record_by_threshold = record_by_threshold};
1903 return true;
1904 }
1905
ParseSensorAbnormalStatsConfig(const Json::Value & abnormal_stats_config,const std::unordered_map<std::string,SensorInfo> & sensor_info_map_,AbnormalStatsInfo * abnormal_stats_info_parsed)1906 bool ParseSensorAbnormalStatsConfig(
1907 const Json::Value &abnormal_stats_config,
1908 const std::unordered_map<std::string, SensorInfo> &sensor_info_map_,
1909 AbnormalStatsInfo *abnormal_stats_info_parsed) {
1910 if (abnormal_stats_config.empty()) {
1911 LOG(INFO) << "No sensors abnormality monitoring info present.";
1912 return true;
1913 }
1914
1915 Json::Value values;
1916
1917 std::optional<TempRangeInfo> default_temp_range_info;
1918 std::vector<AbnormalStatsInfo::SensorsTempRangeInfo> sensors_temp_range_infos;
1919 Json::Value outlier_temp_config = abnormal_stats_config["Outlier"];
1920 if (outlier_temp_config) {
1921 LOG(INFO) << "Start to parse outlier temp config.";
1922
1923 if (outlier_temp_config["Default"]) {
1924 LOG(INFO) << "Start to parse defaultTempRange.";
1925 if (!getTempRangeInfoFromJsonValues(outlier_temp_config["Default"],
1926 &default_temp_range_info.value())) {
1927 LOG(ERROR) << "Failed to parse default temp range config.";
1928 return false;
1929 }
1930 }
1931
1932 Json::Value configs = outlier_temp_config["Configs"];
1933 if (configs) {
1934 std::unordered_set<std::string> sensors_parsed;
1935 for (Json::Value::ArrayIndex i = 0; i < configs.size(); i++) {
1936 LOG(INFO) << "Start to parse temp range config[" << i << "]";
1937 AbnormalStatsInfo::SensorsTempRangeInfo sensors_temp_range_info;
1938 values = configs[i]["Monitor"];
1939 if (!values.size()) {
1940 LOG(ERROR) << "Invalid config no sensor list present for outlier temp "
1941 "config.";
1942 return false;
1943 }
1944 for (Json::Value::ArrayIndex j = 0; j < values.size(); j++) {
1945 const std::string &sensor = values[j].asString();
1946 if (!sensor_info_map_.count(sensor)) {
1947 LOG(ERROR) << "Unknown sensor " << sensor;
1948 return false;
1949 }
1950 auto result = sensors_parsed.insert(sensor);
1951 if (!result.second) {
1952 LOG(ERROR) << "Duplicate Sensor Temp Range Config: " << sensor;
1953 return false;
1954 }
1955 LOG(INFO) << "Monitored sensor [" << j << "]: " << sensor;
1956 sensors_temp_range_info.sensors.push_back(sensor);
1957 }
1958 if (!getTempRangeInfoFromJsonValues(configs[i]["TempRange"],
1959 &sensors_temp_range_info.temp_range_info)) {
1960 LOG(ERROR) << "Failed to parse temp range config.";
1961 return false;
1962 }
1963 sensors_temp_range_infos.push_back(sensors_temp_range_info);
1964 }
1965 }
1966 }
1967 std::optional<TempStuckInfo> default_temp_stuck_info;
1968 std::vector<AbnormalStatsInfo::SensorsTempStuckInfo> sensors_temp_stuck_infos;
1969 Json::Value stuck_temp_config = abnormal_stats_config["Stuck"];
1970 if (stuck_temp_config) {
1971 LOG(INFO) << "Start to parse stuck temp config.";
1972
1973 if (stuck_temp_config["Default"]) {
1974 LOG(INFO) << "Start to parse defaultTempStuck.";
1975 if (!getTempStuckInfoFromJsonValue(stuck_temp_config["Default"],
1976 &default_temp_stuck_info.value())) {
1977 LOG(ERROR) << "Failed to parse default temp stuck config.";
1978 return false;
1979 }
1980 }
1981
1982 Json::Value configs = stuck_temp_config["Configs"];
1983 if (configs) {
1984 std::unordered_set<std::string> sensors_parsed;
1985 for (Json::Value::ArrayIndex i = 0; i < configs.size(); i++) {
1986 LOG(INFO) << "Start to parse temp stuck config[" << i << "]";
1987 AbnormalStatsInfo::SensorsTempStuckInfo sensor_temp_stuck_info;
1988 values = configs[i]["Monitor"];
1989 if (!values.size()) {
1990 LOG(ERROR) << "Invalid config no sensor list present for stuck temp "
1991 "config.";
1992 return false;
1993 }
1994 for (Json::Value::ArrayIndex j = 0; j < values.size(); j++) {
1995 const std::string &sensor = values[j].asString();
1996 if (!sensor_info_map_.count(sensor)) {
1997 LOG(ERROR) << "Unknown sensor " << sensor;
1998 return false;
1999 }
2000 auto result = sensors_parsed.insert(sensor);
2001 if (!result.second) {
2002 LOG(ERROR) << "Duplicate Sensor Temp Stuck Config: " << sensor;
2003 return false;
2004 }
2005 LOG(INFO) << "Monitored sensor [" << j << "]: " << sensor;
2006 sensor_temp_stuck_info.sensors.push_back(sensor);
2007 }
2008 if (!getTempStuckInfoFromJsonValue(configs[i]["TempStuck"],
2009 &sensor_temp_stuck_info.temp_stuck_info)) {
2010 LOG(ERROR) << "Failed to parse temp stuck config.";
2011 return false;
2012 }
2013 sensors_temp_stuck_infos.push_back(sensor_temp_stuck_info);
2014 }
2015 }
2016 }
2017 *abnormal_stats_info_parsed = {
2018 .default_temp_range_info = default_temp_range_info,
2019 .sensors_temp_range_infos = sensors_temp_range_infos,
2020 .default_temp_stuck_info = default_temp_stuck_info,
2021 .sensors_temp_stuck_infos = sensors_temp_stuck_infos,
2022 };
2023 return true;
2024 }
2025
ParseSensorStatsConfig(const Json::Value & config,const std::unordered_map<std::string,SensorInfo> & sensor_info_map_,StatsInfo<float> * sensor_stats_info_parsed,AbnormalStatsInfo * abnormal_stats_info_parsed)2026 bool ParseSensorStatsConfig(const Json::Value &config,
2027 const std::unordered_map<std::string, SensorInfo> &sensor_info_map_,
2028 StatsInfo<float> *sensor_stats_info_parsed,
2029 AbnormalStatsInfo *abnormal_stats_info_parsed) {
2030 Json::Value stats_config = config["Stats"];
2031 if (stats_config.empty()) {
2032 LOG(INFO) << "No Stats Config present.";
2033 return true;
2034 }
2035 // Parse cooling device user vote
2036 Json::Value sensor_config = stats_config["Sensors"];
2037 if (sensor_config.empty()) {
2038 LOG(INFO) << "No Sensor Stats Config present.";
2039 return true;
2040 }
2041 LOG(INFO) << "Parse Stats Config for Sensor Temp.";
2042 // Parse sensor stats config
2043 if (!ParseStatsInfo(stats_config["Sensors"], sensor_info_map_, sensor_stats_info_parsed,
2044 std::numeric_limits<float>::lowest())) {
2045 LOG(ERROR) << "Failed to parse sensor temp stats info.";
2046 sensor_stats_info_parsed->clear();
2047 return false;
2048 }
2049 if (!ParseSensorAbnormalStatsConfig(sensor_config["Abnormality"], sensor_info_map_,
2050 abnormal_stats_info_parsed)) {
2051 LOG(ERROR) << "Failed to parse sensor abnormal stats config.";
2052 return false;
2053 }
2054 return true;
2055 }
2056
ParseCoolingDeviceStatsConfig(const Json::Value & config,const std::unordered_map<std::string,CdevInfo> & cooling_device_info_map_,StatsInfo<int> * cooling_device_request_info_parsed)2057 bool ParseCoolingDeviceStatsConfig(
2058 const Json::Value &config,
2059 const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_,
2060 StatsInfo<int> *cooling_device_request_info_parsed) {
2061 Json::Value stats_config = config["Stats"];
2062 if (stats_config.empty()) {
2063 LOG(INFO) << "No Stats Config present.";
2064 return true;
2065 }
2066 // Parse cooling device user vote
2067 if (stats_config["CoolingDevices"].empty()) {
2068 LOG(INFO) << "No cooling device stats present.";
2069 return true;
2070 }
2071 LOG(INFO) << "Parse Stats Config for Sensor CDev Request.";
2072 if (!ParseStatsInfo(stats_config["CoolingDevices"]["RecordVotePerSensor"],
2073 cooling_device_info_map_, cooling_device_request_info_parsed, -1)) {
2074 LOG(ERROR) << "Failed to parse cooling device user vote stats info.";
2075 cooling_device_request_info_parsed->clear();
2076 return false;
2077 }
2078 return true;
2079 }
2080 } // namespace implementation
2081 } // namespace thermal
2082 } // namespace hardware
2083 } // namespace android
2084 } // namespace aidl
2085