From e3935df35113acb061585431698a598547bf08fd Mon Sep 17 00:00:00 2001 From: Giovanni Ricca Date: Wed, 12 Mar 2025 15:14:16 +0100 Subject: [PATCH] aidl: thermal: Update AIDL Thermal HAL from `android-15.0.0_r20` HEAD to 387cefaab6d1e4ac30a859f512387cffa640abc1 ("Merge changes from topic "p25_thermal_config_checker" into main") Change-Id: I63223eb7562c6218104b774c0ffaae8e8baeb02c --- aidl/thermal/Android.bp | 3 +- aidl/thermal/Thermal.cpp | 4 + aidl/thermal/Thermal.h | 2 + ...roid.hardware.thermal-service.mediatek.xml | 2 +- aidl/thermal/thermal-helper.cpp | 134 +++++++----- aidl/thermal/thermal-helper.h | 15 +- aidl/thermal/utils/thermal_info.cpp | 116 +++++++--- aidl/thermal/utils/thermal_info.h | 16 +- .../utils/thermal_predictions_helper.cpp | 205 ++++++++++++++++++ .../utils/thermal_predictions_helper.h | 81 +++++++ aidl/thermal/utils/thermal_throttling.cpp | 43 +++- 11 files changed, 512 insertions(+), 109 deletions(-) create mode 100644 aidl/thermal/utils/thermal_predictions_helper.cpp create mode 100644 aidl/thermal/utils/thermal_predictions_helper.h diff --git a/aidl/thermal/Android.bp b/aidl/thermal/Android.bp index 0ac248f..40c4b18 100644 --- a/aidl/thermal/Android.bp +++ b/aidl/thermal/Android.bp @@ -10,6 +10,7 @@ cc_binary { "utils/power_files.cpp", "utils/powerhal_helper.cpp", "utils/thermal_stats_helper.cpp", + "utils/thermal_predictions_helper.cpp", "utils/thermal_watcher.cpp", "virtualtemp_estimator/virtualtemp_estimator.cpp", ], @@ -33,7 +34,7 @@ cc_binary { "libbinder_ndk", "android.frameworks.stats-V2-ndk", "android.hardware.power-V1-ndk", - "android.hardware.thermal-V2-ndk", + "android.hardware.thermal-V3-ndk", "pixel-power-ext-V1-ndk", "pixelatoms-cpp", ], diff --git a/aidl/thermal/Thermal.cpp b/aidl/thermal/Thermal.cpp index a1386ef..55bd50e 100644 --- a/aidl/thermal/Thermal.cpp +++ b/aidl/thermal/Thermal.cpp @@ -297,6 +297,10 @@ ndk::ScopedAStatus Thermal::unregisterCoolingDeviceChangedCallback( return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus Thermal::forecastSkinTemperature(int32_t, float *) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) { *dump_buf << "getVirtualSensorInfo:" << std::endl; const auto &map = thermal_helper_->GetSensorInfoMap(); diff --git a/aidl/thermal/Thermal.h b/aidl/thermal/Thermal.h index 9f16ded..d77d9b3 100644 --- a/aidl/thermal/Thermal.h +++ b/aidl/thermal/Thermal.h @@ -68,6 +68,8 @@ class Thermal : public BnThermal { CoolingType type) override; ndk::ScopedAStatus unregisterCoolingDeviceChangedCallback( const std::shared_ptr &callback) override; + ndk::ScopedAStatus forecastSkinTemperature(int32_t forecastSeconds, + float *_aidl_return) override; binder_status_t dump(int fd, const char **args, uint32_t numArgs) override; diff --git a/aidl/thermal/android.hardware.thermal-service.mediatek.xml b/aidl/thermal/android.hardware.thermal-service.mediatek.xml index 08dc68c..148ddbf 100644 --- a/aidl/thermal/android.hardware.thermal-service.mediatek.xml +++ b/aidl/thermal/android.hardware.thermal-service.mediatek.xml @@ -1,7 +1,7 @@ android.hardware.thermal - 2 + 3 IThermal/default diff --git a/aidl/thermal/thermal-helper.cpp b/aidl/thermal/thermal-helper.cpp index 72cc2a3..4be004d 100644 --- a/aidl/thermal/thermal-helper.cpp +++ b/aidl/thermal/thermal-helper.cpp @@ -179,6 +179,11 @@ ThermalHelperImpl::ThermalHelperImpl(const NotificationCallback &cb) ret = false; } + if (!thermal_predictions_helper_.initializePredictionSensors(sensor_info_map_)) { + LOG(ERROR) << "Failed to initialize prediction sensors"; + ret = false; + } + if (ret) { if (!thermal_stats_helper_.initializeStats(config, sensor_info_map_, cooling_device_info_map_, this)) { @@ -279,7 +284,8 @@ ThermalHelperImpl::ThermalHelperImpl(const NotificationCallback &cb) } } // Check predictor info config - if (name_status_pair.second.predictor_info != nullptr) { + if ((name_status_pair.second.predictor_info != nullptr) && + name_status_pair.second.predictor_info->support_pid_compensation) { std::string predict_sensor_name = name_status_pair.second.predictor_info->sensor; if (!(sensor_info_map_.count(predict_sensor_name))) { LOG(ERROR) << name_status_pair.first << "'s predictor " << predict_sensor_name @@ -297,31 +303,29 @@ ThermalHelperImpl::ThermalHelperImpl(const NotificationCallback &cb) break; } - if (name_status_pair.second.predictor_info->support_pid_compensation) { - std::vector output_template; - size_t prediction_weight_count = - name_status_pair.second.predictor_info->prediction_weights.size(); - // read predictor out to get the size of output vector - ::thermal::vtestimator::VtEstimatorStatus predict_check = - predictor_sensor_info.virtual_sensor_info->vt_estimator->GetAllPredictions( - &output_template); + std::vector output_template; + size_t prediction_weight_count = + name_status_pair.second.predictor_info->prediction_weights.size(); + // read predictor out to get the size of output vector + ::thermal::vtestimator::VtEstimatorStatus predict_check = + predictor_sensor_info.virtual_sensor_info->vt_estimator->GetAllPredictions( + &output_template); - if (predict_check != ::thermal::vtestimator::kVtEstimatorOk) { - LOG(ERROR) << "Failed to get output size of " << name_status_pair.first - << "'s predictor " << predict_sensor_name - << " GetAllPredictions ret: " << ret << ")"; - ret = false; - break; - } + if (predict_check != ::thermal::vtestimator::kVtEstimatorOk) { + LOG(ERROR) << "Failed to get output size of " << name_status_pair.first + << "'s predictor " << predict_sensor_name + << " GetAllPredictions ret: " << ret << ")"; + ret = false; + break; + } - if (prediction_weight_count != output_template.size()) { - LOG(ERROR) << "Sensor [" << name_status_pair.first << "]: " - << "prediction weights size (" << prediction_weight_count - << ") doesn't match predictor [" << predict_sensor_name - << "]'s output size (" << output_template.size() << ")"; - ret = false; - break; - } + if (prediction_weight_count != output_template.size()) { + LOG(ERROR) << "Sensor [" << name_status_pair.first + << "]: " << "prediction weights size (" << prediction_weight_count + << ") doesn't match predictor [" << predict_sensor_name + << "]'s output size (" << output_template.size() << ")"; + ret = false; + break; } } } @@ -507,23 +511,29 @@ bool ThermalHelperImpl::readCoolingDevice(std::string_view cooling_device, return true; } -bool ThermalHelperImpl::readTemperature(std::string_view sensor_name, Temperature *out, - const bool force_no_cache) { +SensorReadStatus ThermalHelperImpl::readTemperature(std::string_view sensor_name, Temperature *out, + const bool force_no_cache) { // Return fail if the thermal sensor cannot be read. float temp = NAN; std::map sensor_log_map; auto &sensor_status = sensor_status_map_.at(sensor_name.data()); - if (!readThermalSensor(sensor_name, &temp, force_no_cache, &sensor_log_map)) { + const auto ret = readThermalSensor(sensor_name, &temp, force_no_cache, &sensor_log_map); + if (ret == SensorReadStatus::ERROR) { LOG(ERROR) << "Failed to read thermal sensor " << sensor_name.data(); thermal_stats_helper_.reportThermalAbnormality( ThermalSensorAbnormalityDetected::TEMP_READ_FAIL, sensor_name, std::nullopt); - return false; + return SensorReadStatus::ERROR; + } + + if (ret == SensorReadStatus::UNDER_COLLECTING) { + LOG(INFO) << "Thermal sensor " << sensor_name.data() << " is under collecting"; + return SensorReadStatus::UNDER_COLLECTING; } if (std::isnan(temp)) { LOG(INFO) << "Sensor " << sensor_name.data() << " temperature is nan."; - return false; + return SensorReadStatus::ERROR; } const auto severity_reference = getSeverityReference(sensor_name.data()); @@ -537,7 +547,7 @@ bool ThermalHelperImpl::readTemperature(std::string_view sensor_name, Temperatur // Only update status if the thermal sensor is being monitored if (!sensor_info.is_watch) { - return true; + return SensorReadStatus::OKAY; } ThrottlingSeverity prev_hot_severity, prev_cold_severity; { @@ -589,7 +599,7 @@ bool ThermalHelperImpl::readTemperature(std::string_view sensor_name, Temperatur ATRACE_INT((sensor_name.data() + std::string("-severity")).c_str(), static_cast(out->throttlingStatus)); - return true; + return SensorReadStatus::OKAY; } bool ThermalHelperImpl::readTemperatureThreshold(std::string_view sensor_name, @@ -822,7 +832,6 @@ bool ThermalHelperImpl::initializeCoolingDevices( << cooling_device_info_pair.second.state2power.size() << ", number should be " << cooling_device_info_pair.second.max_state + 1 << " (max_state + 1)"; - return false; } } @@ -941,9 +950,11 @@ bool ThermalHelperImpl::fillCurrentTemperatures(bool filterType, bool filterCall if (filterCallback && !name_info_pair.second.send_cb) { continue; } - if (readTemperature(name_info_pair.first, &temp, false)) { + + const auto status = readTemperature(name_info_pair.first, &temp, false); + if (status == SensorReadStatus::OKAY) { ret.emplace_back(std::move(temp)); - } else { + } else if (status == SensorReadStatus::ERROR) { LOG(ERROR) << __func__ << ": error reading temperature for sensor: " << name_info_pair.first; } @@ -1006,7 +1017,7 @@ ThrottlingSeverity ThermalHelperImpl::getSeverityReference(std::string_view sens } Temperature temp; - if (!readTemperature(severity_reference, &temp, false)) { + if (readTemperature(severity_reference, &temp, false) != SensorReadStatus::OKAY) { return ThrottlingSeverity::NONE; } LOG(VERBOSE) << sensor_name << "'s severity reference " << severity_reference @@ -1019,8 +1030,8 @@ bool ThermalHelperImpl::readDataByType(std::string_view sensor_data, float *read std::map *sensor_log_map) { switch (type) { case SensorFusionType::SENSOR: - if (!readThermalSensor(sensor_data.data(), reading_value, force_no_cache, - sensor_log_map)) { + if (readThermalSensor(sensor_data.data(), reading_value, force_no_cache, + sensor_log_map) == SensorReadStatus::ERROR) { LOG(ERROR) << "Failed to get " << sensor_data.data() << " data"; return false; } @@ -1096,6 +1107,9 @@ bool ThermalHelperImpl::runVirtualTempEstimator(std::string_view sensor_name, sensor_info.virtual_sensor_info->vt_estimator->Estimate(model_inputs, &model_outputs); if (ret == ::thermal::vtestimator::kVtEstimatorOk) { + if (sensor_info.predictor_info && sensor_info.predictor_info->supports_predictions) { + thermal_predictions_helper_.updateSensor(sensor_name, model_outputs); + } *outputs = model_outputs; return true; } else if (ret == ::thermal::vtestimator::kVtEstimatorLowConfidence || @@ -1241,16 +1255,16 @@ bool ThermalHelperImpl::readTemperaturePredictions(std::string_view sensor_name, constexpr int kTranTimeoutParam = 2; -bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *temp, - const bool force_no_cache, - std::map *sensor_log_map) { +SensorReadStatus ThermalHelperImpl::readThermalSensor( + std::string_view sensor_name, float *temp, const bool force_no_cache, + std::map *sensor_log_map) { std::string file_reading; boot_clock::time_point now = boot_clock::now(); ATRACE_NAME(StringPrintf("ThermalHelper::readThermalSensor - %s", sensor_name.data()).c_str()); if (!(sensor_info_map_.count(sensor_name.data()) && sensor_status_map_.count(sensor_name.data()))) { - return false; + return SensorReadStatus::ERROR; } const auto &sensor_info = sensor_info_map_.at(sensor_name.data()); @@ -1261,7 +1275,7 @@ bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *t if (sensor_status.override_status.emul_temp != nullptr) { *temp = sensor_status.override_status.emul_temp->temp; (*sensor_log_map)[sensor_name.data()] = *temp; - return true; + return SensorReadStatus::OKAY; } } @@ -1276,7 +1290,7 @@ bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *t *temp = sensor_status.thermal_cached.temp; (*sensor_log_map)[sensor_name.data()] = *temp; ATRACE_INT((sensor_name.data() + std::string("-cached")).c_str(), static_cast(*temp)); - return true; + return SensorReadStatus::OKAY; } // Reading thermal sensor according to it's composition @@ -1284,7 +1298,7 @@ bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *t if (!thermal_sensors_.readThermalFile(sensor_name.data(), &file_reading) || file_reading.empty()) { LOG(ERROR) << "failed to read sensor: " << sensor_name << " zone: " << sensor_info.zone_name; - return false; + return SensorReadStatus::ERROR; } *temp = std::stof(::android::base::Trim(file_reading)); } else { @@ -1299,21 +1313,26 @@ bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *t force_no_cache, sensor_log_map)) { LOG(ERROR) << "Failed to read " << sensor_name.data() << "'s linked sensor " << sensor_info.virtual_sensor_info->linked_sensors[i]; - return false; + return SensorReadStatus::ERROR; } if (std::isnan(sensor_readings[i])) { LOG(INFO) << sensor_name << " data is under collecting"; - return true; + return SensorReadStatus::UNDER_COLLECTING; } } - if ((sensor_info.virtual_sensor_info->formula == FormulaOption::USE_ML_MODEL) || - (sensor_info.virtual_sensor_info->formula == FormulaOption::USE_LINEAR_MODEL)) { + if (sensor_info.virtual_sensor_info->formula == FormulaOption::PREVIOUSLY_PREDICTED) { + const auto ret = thermal_predictions_helper_.readSensor(sensor_name, temp); + if (ret != SensorReadStatus::OKAY) { + return ret; + } + } else if ((sensor_info.virtual_sensor_info->formula == FormulaOption::USE_ML_MODEL) || + (sensor_info.virtual_sensor_info->formula == FormulaOption::USE_LINEAR_MODEL)) { std::vector vt_estimator_out; if (!runVirtualTempEstimator(sensor_name, sensor_log_map, force_no_cache, &vt_estimator_out)) { LOG(ERROR) << "Failed running VirtualEstimator for " << sensor_name; - return false; + return SensorReadStatus::ERROR; } *temp = vt_estimator_out[0]; } else { @@ -1325,11 +1344,11 @@ bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *t force_no_cache, sensor_log_map)) { LOG(ERROR) << "Failed to read " << sensor_name.data() << "'s coefficient " << sensor_info.virtual_sensor_info->coefficients[i]; - return false; + return SensorReadStatus::ERROR; } if (std::isnan(coefficient)) { LOG(INFO) << sensor_name << " data is under collecting"; - return true; + return SensorReadStatus::UNDER_COLLECTING; } switch (sensor_info.virtual_sensor_info->formula) { case FormulaOption::COUNT_THRESHOLD: @@ -1354,7 +1373,7 @@ bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *t break; default: LOG(ERROR) << "Unknown formula type for sensor " << sensor_name.data(); - return false; + return SensorReadStatus::ERROR; } } *temp = (temp_val + sensor_info.virtual_sensor_info->offset); @@ -1377,7 +1396,7 @@ bool ThermalHelperImpl::readThermalSensor(std::string_view sensor_name, float *t } auto real_temp = (*temp) * sensor_info.multiplier; thermal_stats_helper_.updateSensorTempStatsByThreshold(sensor_name, real_temp); - return true; + return SensorReadStatus::OKAY; } // This is called in the different thread context and will update sensor_status @@ -1493,12 +1512,19 @@ std::chrono::milliseconds ThermalHelperImpl::thermalWatcherCallbackFunc( } std::pair throttling_status; - if (!readTemperature(name_status_pair.first, &temp, force_no_cache)) { + const auto ret = readTemperature(name_status_pair.first, &temp, force_no_cache); + if (ret == SensorReadStatus::ERROR) { LOG(ERROR) << __func__ << ": error reading temperature for sensor: " << name_status_pair.first; continue; } + if (ret == SensorReadStatus::UNDER_COLLECTING) { + LOG(INFO) << __func__ + << ": data under collecting for sensor: " << name_status_pair.first; + continue; + } + { std::unique_lock _lock(sensor_status_map_mutex_); if (sensor_status.pending_notification) { diff --git a/aidl/thermal/thermal-helper.h b/aidl/thermal/thermal-helper.h index 675d157..06cbd54 100644 --- a/aidl/thermal/thermal-helper.h +++ b/aidl/thermal/thermal-helper.h @@ -23,6 +23,7 @@ #include "utils/powerhal_helper.h" #include "utils/thermal_files.h" #include "utils/thermal_info.h" +#include "utils/thermal_predictions_helper.h" #include "utils/thermal_stats_helper.h" #include "utils/thermal_throttling.h" #include "utils/thermal_watcher.h" @@ -81,8 +82,8 @@ class ThermalHelper { const bool max_throttling) = 0; virtual bool emulClear(std::string_view target_sensor) = 0; virtual bool isInitializedOk() const = 0; - virtual bool readTemperature(std::string_view sensor_name, Temperature *out, - const bool force_sysfs = false) = 0; + virtual SensorReadStatus readTemperature(std::string_view sensor_name, Temperature *out, + const bool force_sysfs = false) = 0; virtual bool readTemperatureThreshold(std::string_view sensor_name, TemperatureThreshold *out) const = 0; virtual bool readCoolingDevice(std::string_view cooling_device, CoolingDevice *out) const = 0; @@ -130,8 +131,8 @@ class ThermalHelperImpl : public ThermalHelper { bool isInitializedOk() const override { return is_initialized_; } // Read the temperature of a single sensor. - bool readTemperature(std::string_view sensor_name, Temperature *out, - const bool force_sysfs = false) override; + SensorReadStatus readTemperature(std::string_view sensor_name, Temperature *out, + const bool force_sysfs = false) override; bool readTemperatureThreshold(std::string_view sensor_name, TemperatureThreshold *out) const override; @@ -203,8 +204,9 @@ class ThermalHelperImpl : public ThermalHelper { bool readDataByType(std::string_view sensor_data, float *reading_value, const SensorFusionType type, const bool force_no_cache, std::map *sensor_log_map); - bool readThermalSensor(std::string_view sensor_name, float *temp, const bool force_sysfs, - std::map *sensor_log_map); + SensorReadStatus readThermalSensor(std::string_view sensor_name, float *temp, + const bool force_sysfs, + std::map *sensor_log_map); bool runVirtualTempEstimator(std::string_view sensor_name, std::map *sensor_log_map, const bool force_no_cache, std::vector *outputs); @@ -231,6 +233,7 @@ class ThermalHelperImpl : public ThermalHelper { supported_powerhint_map_; PowerHalService power_hal_service_; ThermalStatsHelper thermal_stats_helper_; + ThermalPredictionsHelper thermal_predictions_helper_; mutable std::shared_mutex sensor_status_map_mutex_; std::unordered_map sensor_status_map_; }; diff --git a/aidl/thermal/utils/thermal_info.cpp b/aidl/thermal/utils/thermal_info.cpp index 2ac30e6..4a14d40 100644 --- a/aidl/thermal/utils/thermal_info.cpp +++ b/aidl/thermal/utils/thermal_info.cpp @@ -348,8 +348,11 @@ bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sens formula = FormulaOption::USE_ML_MODEL; } else if (sensor["Formula"].asString().compare("USE_LINEAR_MODEL") == 0) { formula = FormulaOption::USE_LINEAR_MODEL; + } else if (sensor["Formula"].asString().compare("PREVIOUSLY_PREDICTED") == 0) { + formula = FormulaOption::PREVIOUSLY_PREDICTED; } else { - LOG(ERROR) << "Sensor[" << name << "]'s Formula is invalid"; + LOG(ERROR) << "Sensor[" << name << "]'s Formula: " << sensor["Formula"].asString() + << " is invalid"; return false; } @@ -389,12 +392,14 @@ bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sens coefficients.emplace_back(values[j].asString()); LOG(INFO) << "Sensor[" << name << "]'s coefficient[" << j << "]: " << coefficients[j]; } - } else if ((formula != FormulaOption::USE_ML_MODEL)) { + } else if ((formula != FormulaOption::USE_ML_MODEL) && + (formula != FormulaOption::PREVIOUSLY_PREDICTED)) { LOG(ERROR) << "Sensor[" << name << "] has no Coefficient setting"; return false; } if ((linked_sensors.size() != coefficients.size()) && - (formula != FormulaOption::USE_ML_MODEL) && (formula != FormulaOption::USE_LINEAR_MODEL)) { + (formula != FormulaOption::USE_ML_MODEL) && (formula != FormulaOption::USE_LINEAR_MODEL) && + (formula != FormulaOption::PREVIOUSLY_PREDICTED)) { LOG(ERROR) << "Sensor[" << name << "] has invalid Coefficient size"; return false; } @@ -594,47 +599,88 @@ bool ParseVirtualSensorInfo(const std::string_view name, const Json::Value &sens bool ParsePredictorInfo(const std::string_view name, const Json::Value &sensor, std::unique_ptr *predictor_info) { Json::Value predictor = sensor["PredictorInfo"]; - if (predictor.empty()) { - return true; - } - - LOG(INFO) << "Start to parse Sensor[" << name << "]'s PredictorInfo"; - if (predictor["Sensor"].empty()) { - LOG(ERROR) << "Failed to parse Sensor [" << name << "]'s PredictorInfo"; - return false; - } - std::string predict_sensor; bool support_pid_compensation = false; std::vector prediction_weights; ThrottlingArray k_p_compensate; - predict_sensor = predictor["Sensor"].asString(); - LOG(INFO) << "Sensor [" << name << "]'s predictor name is " << predict_sensor; - // parse pid compensation configuration - if ((!predictor["PredictionWeight"].empty()) && (!predictor["KPCompensate"].empty())) { - support_pid_compensation = true; - if (!predictor["PredictionWeight"].size()) { - LOG(ERROR) << "Failed to parse PredictionWeight"; + + bool supports_predictions = false; + int prediction_sample_interval = 0; + int num_prediction_samples = 0; + int prediction_duration = 0; + bool set_predictor_info = false; + + if (!predictor.empty()) { + set_predictor_info = true; + LOG(INFO) << "Start to parse Sensor[" << name << "]'s PredictorInfo"; + if (predictor["Sensor"].empty()) { + LOG(ERROR) << "Failed to parse Sensor [" << name << "]'s PredictorInfo"; return false; } - prediction_weights.reserve(predictor["PredictionWeight"].size()); - for (Json::Value::ArrayIndex i = 0; i < predictor["PredictionWeight"].size(); ++i) { - float weight = predictor["PredictionWeight"][i].asFloat(); - if (std::isnan(weight)) { - LOG(ERROR) << "Unexpected NAN prediction weight for sensor [" << name << "]"; + + predict_sensor = predictor["Sensor"].asString(); + LOG(INFO) << "Sensor [" << name << "]'s predictor name is " << predict_sensor; + // parse pid compensation configuration + if ((!predictor["PredictionWeight"].empty()) && (!predictor["KPCompensate"].empty())) { + support_pid_compensation = true; + if (!predictor["PredictionWeight"].size()) { + LOG(ERROR) << "Failed to parse PredictionWeight"; + return false; + } + prediction_weights.reserve(predictor["PredictionWeight"].size()); + for (Json::Value::ArrayIndex i = 0; i < predictor["PredictionWeight"].size(); ++i) { + float weight = predictor["PredictionWeight"][i].asFloat(); + if (std::isnan(weight)) { + LOG(ERROR) << "Unexpected NAN prediction weight for sensor [" << name << "]"; + } + prediction_weights.emplace_back(weight); + LOG(INFO) << "Sensor[" << name << "]'s prediction weights [" << i + << "]: " << weight; + } + if (!getFloatFromJsonValues(predictor["KPCompensate"], &k_p_compensate, false, false)) { + LOG(ERROR) << "Failed to parse KPCompensate"; + return false; } - prediction_weights.emplace_back(weight); - LOG(INFO) << "Sensor[" << name << "]'s prediction weights [" << i << "]: " << weight; - } - if (!getFloatFromJsonValues(predictor["KPCompensate"], &k_p_compensate, false, false)) { - LOG(ERROR) << "Failed to parse KPCompensate"; - return false; } } - LOG(INFO) << "Successfully created PredictorInfo for Sensor[" << name << "]"; - predictor_info->reset(new PredictorInfo{predict_sensor, support_pid_compensation, - prediction_weights, k_p_compensate}); + if (sensor["SupportPrediction"].asBool()) { + set_predictor_info = true; + supports_predictions = true; + LOG(INFO) << "Sensor[" << name << "] supports predictions."; + + if (sensor["SampleDuration"].empty()) { + LOG(ERROR) << "SampleDuration is empty for predictor sensor: " << name; + return false; + } + + if (sensor["OutputLabelCount"].empty()) { + LOG(ERROR) << "OutputLabelCount is empty for predictor sensor: " << name; + return false; + } + + prediction_sample_interval = sensor["SampleDuration"].asInt(); + num_prediction_samples = sensor["OutputLabelCount"].asInt(); + } + + if (sensor["Formula"].asString().compare("PREVIOUSLY_PREDICTED") == 0) { + set_predictor_info = true; + if (sensor["PredictionDuration"].empty()) { + LOG(ERROR) << "Sensor[" << name + << "] is a PREVIOUSLY_PREDICTED sensor and has no PredictionDuration"; + return false; + } + + prediction_duration = sensor["PredictionDuration"].asInt(); + } + + if (set_predictor_info) { + LOG(INFO) << "Successfully created PredictorInfo for Sensor[" << name << "]"; + predictor_info->reset(new PredictorInfo{predict_sensor, support_pid_compensation, + prediction_weights, k_p_compensate, + supports_predictions, prediction_sample_interval, + num_prediction_samples, prediction_duration}); + } return true; } @@ -1477,8 +1523,8 @@ bool ParseSensorInfo(const Json::Value &config, .hot_hysteresis = hot_hysteresis, .cold_hysteresis = cold_hysteresis, .temp_path = temp_path, - .zone_name = zone_name, .severity_reference = severity_reference, + .zone_name = zone_name, .vr_threshold = vr_threshold, .multiplier = multiplier, .polling_delay = polling_delay, diff --git a/aidl/thermal/utils/thermal_info.h b/aidl/thermal/utils/thermal_info.h index d33a881..ac95178 100644 --- a/aidl/thermal/utils/thermal_info.h +++ b/aidl/thermal/utils/thermal_info.h @@ -46,7 +46,8 @@ enum class FormulaOption : uint32_t { MAXIMUM, MINIMUM, USE_ML_MODEL, - USE_LINEAR_MODEL + USE_LINEAR_MODEL, + PREVIOUSLY_PREDICTED }; template @@ -120,6 +121,12 @@ enum class SensorFusionType : uint32_t { CDEV, }; +enum class SensorReadStatus : uint32_t { + OKAY = 0, + UNDER_COLLECTING, + ERROR, +}; + std::ostream &operator<<(std::ostream &os, const SensorFusionType &sensor_fusion_type); struct VirtualSensorInfo { @@ -141,6 +148,11 @@ struct PredictorInfo { bool support_pid_compensation; std::vector prediction_weights; ThrottlingArray k_p_compensate; + + bool supports_predictions; // Does this sensor support predictions + int prediction_sample_interval; // Interval between each predicted sample + int num_prediction_samples; // How many samples are predicted for each iteration + int prediction_duration; // Prediction duration for a PREDICTED sensor }; struct VirtualPowerRailInfo { @@ -205,8 +217,8 @@ struct SensorInfo { ThrottlingArray hot_hysteresis; ThrottlingArray cold_hysteresis; std::string temp_path; - std::string zone_name; std::string severity_reference; + std::string zone_name; float vr_threshold; float multiplier; std::chrono::milliseconds polling_delay; diff --git a/aidl/thermal/utils/thermal_predictions_helper.cpp b/aidl/thermal/utils/thermal_predictions_helper.cpp new file mode 100644 index 0000000..1f7c5b7 --- /dev/null +++ b/aidl/thermal/utils/thermal_predictions_helper.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "thermal_predictions_helper.h" + +#include +#include + +#include +#include +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace thermal { +namespace implementation { + +bool ThermalPredictionsHelper::registerPredictorSensor(std::string_view sensor_name, + int sample_duration, int num_out_samples) { + if (sample_duration <= 0 || num_out_samples <= 0) { + LOG(ERROR) << "Invalid sample_duration: " << sample_duration + << " or num_out_samples: " << num_out_samples << " for sensor: " << sensor_name; + return false; + } + + if (predictor_sensors_.count(sensor_name.data())) { + LOG(ERROR) << "sensor_name " << sensor_name << " is already registered as predictor"; + return false; + } + + predictor_sensors_[sensor_name.data()] = PredictorSensorInfo( + {std::string(sensor_name), sample_duration, num_out_samples, + std::vector(num_out_samples, PredictionSample(num_out_samples)), 0}); + return true; +} + +bool ThermalPredictionsHelper::registerPredictedSensor(std::string_view sensor_name, + std::string_view linked_sensor, + int duration) { + if (duration < 0) { + LOG(ERROR) << "Invalid duration: " << duration << " for sensor: " << sensor_name; + return false; + } + + if (predicted_sensors_.count(sensor_name.data())) { + LOG(ERROR) << "sensor_name " << sensor_name << " is already registered as predicted sensor"; + return false; + } + + if (predictor_sensors_.count(linked_sensor.data()) == 0) { + LOG(ERROR) << "linked_sensor_name " << linked_sensor << " is not registered as predictor"; + return false; + } + + PredictorSensorInfo &predictor_sensor_info = predictor_sensors_[linked_sensor.data()]; + const int max_prediction_duration = + (predictor_sensor_info.num_out_samples - 1) * predictor_sensor_info.sample_duration; + + if (duration > max_prediction_duration) { + LOG(ERROR) << "Predicted sensor " << sensor_name + << " duration is greater than max prediction duration of predictor " + << linked_sensor << " which is " << max_prediction_duration; + return false; + } + + // round up to nearest lower index + const int prediction_index = duration / predictor_sensor_info.sample_duration; + if (duration % predictor_sensor_info.sample_duration != 0) { + LOG(INFO) << "Predicted sensor " << sensor_name << " duration " << duration + << " is not a multiple of " << linked_sensor << " sample duration " + << predictor_sensor_info.sample_duration << " and hence updated to " + << prediction_index * predictor_sensor_info.sample_duration; + } + + predicted_sensors_[sensor_name.data()] = PredictedSensorInfo( + {std::string(sensor_name), std::string(linked_sensor), duration, prediction_index}); + return true; +} + +bool ThermalPredictionsHelper::updateSensor(std::string_view sensor_name, + std::vector &values) { + std::unique_lock _lock(sensor_predictions_mutex_); + const auto sensor_itr = predictor_sensors_.find(sensor_name.data()); + if (sensor_itr == predictor_sensors_.end()) { + LOG(ERROR) << "sensor_name " << sensor_name << " is not registered as predictor"; + return false; + } + + PredictorSensorInfo &predictor_sensor_info = predictor_sensors_[sensor_name.data()]; + if (values.size() != static_cast(predictor_sensor_info.num_out_samples)) { + LOG(ERROR) << "Invalid number of values: " << values.size() + << " for sensor: " << sensor_name + << ", expected: " << predictor_sensor_info.num_out_samples; + return false; + } + + predictor_sensor_info.samples[predictor_sensor_info.cur_index].timestamp = boot_clock::now(); + predictor_sensor_info.samples[predictor_sensor_info.cur_index].values = values; + predictor_sensor_info.cur_index++; + predictor_sensor_info.cur_index %= predictor_sensor_info.num_out_samples; + + return true; +} + +SensorReadStatus ThermalPredictionsHelper::readSensor(std::string_view sensor_name, float *temp) { + std::shared_lock _lock(sensor_predictions_mutex_); + const auto sensor_itr = predicted_sensors_.find(sensor_name.data()); + if (sensor_itr == predicted_sensors_.end()) { + LOG(ERROR) << "sensor_name " << sensor_name << " is not registered as predicted sensor"; + return SensorReadStatus::ERROR; + } + + PredictedSensorInfo &predicted_sensor_info = predicted_sensors_[sensor_name.data()]; + const int prediction_index = predicted_sensor_info.prediction_index; + + const auto linked_sensor_itr = predictor_sensors_.find(predicted_sensor_info.linked_sensor); + if (linked_sensor_itr == predictor_sensors_.end()) { + LOG(ERROR) << "linked_sensor_name " << predicted_sensor_info.linked_sensor + << " is not registered as predictor for sensor" << sensor_name; + return SensorReadStatus::ERROR; + } + + PredictorSensorInfo predictor_sensor_info = linked_sensor_itr->second; + boot_clock::time_point now = boot_clock::now(); + const auto min_time_elapsed_ms = predicted_sensor_info.duration - kToleranceIntervalMs; + const auto max_time_elapsed_ms = predicted_sensor_info.duration + kToleranceIntervalMs; + int loop_count = 0; + do { + int index = predictor_sensor_info.cur_index - loop_count - 1; + if (index < 0) { + index += predictor_sensor_info.num_out_samples; + } + + const auto time_elapsed = std::chrono::duration_cast( + now - predictor_sensor_info.samples[index].timestamp); + if (time_elapsed.count() <= max_time_elapsed_ms && + time_elapsed.count() >= min_time_elapsed_ms) { + *temp = predictor_sensor_info.samples[index].values[prediction_index]; + return SensorReadStatus::OKAY; + } + + loop_count++; + } while (loop_count < predictor_sensor_info.num_out_samples); + + LOG(INFO) << "sensor_name: " << sensor_name << " no valid prediction samples found"; + return SensorReadStatus::UNDER_COLLECTING; +} + +bool ThermalPredictionsHelper::initializePredictionSensors( + const std::unordered_map &sensor_info_map) { + std::unique_lock _lock(sensor_predictions_mutex_); + + for (auto it = sensor_info_map.begin(); it != sensor_info_map.end(); ++it) { + const std::string_view sensor_name = it->first; + const SensorInfo &sensor_info = it->second; + + if (!sensor_info.predictor_info || !sensor_info.virtual_sensor_info || + (!sensor_info.predictor_info->supports_predictions)) { + continue; + } + + if (!registerPredictorSensor(sensor_name, + sensor_info.predictor_info->prediction_sample_interval, + sensor_info.predictor_info->num_prediction_samples)) { + LOG(ERROR) << "Failed to register predictor sensor: " << sensor_name; + return false; + } + } + + for (auto it = sensor_info_map.begin(); it != sensor_info_map.end(); ++it) { + const std::string_view sensor_name = it->first; + const SensorInfo &sensor_info = it->second; + + if (!sensor_info.predictor_info || !sensor_info.virtual_sensor_info || + (sensor_info.virtual_sensor_info->formula != FormulaOption::PREVIOUSLY_PREDICTED)) { + continue; + } + + if (sensor_info.virtual_sensor_info->linked_sensors.size() != 1) { + LOG(ERROR) << "Invalid number of linked sensors: " + << sensor_info.virtual_sensor_info->linked_sensors.size() + << " for sensor: " << sensor_name; + return false; + } + + if (!registerPredictedSensor(sensor_name, + sensor_info.virtual_sensor_info->linked_sensors[0], + sensor_info.predictor_info->prediction_duration)) { + LOG(ERROR) << "Failed to register predicted sensor: " << sensor_name; + return false; + } + } + + return true; +} + +} // namespace implementation +} // namespace thermal +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/thermal/utils/thermal_predictions_helper.h b/aidl/thermal/utils/thermal_predictions_helper.h new file mode 100644 index 0000000..d26e6be --- /dev/null +++ b/aidl/thermal/utils/thermal_predictions_helper.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +#include "thermal_info.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace thermal { +namespace implementation { + +using ::android::base::boot_clock; +constexpr int kToleranceIntervalMs = 1000; + +struct PredictionSample { + PredictionSample(int num_out_samples) { + timestamp = boot_clock::time_point::min(); + values = std::vector(num_out_samples, NAN); + } + boot_clock::time_point timestamp; + std::vector values; +}; + +struct PredictorSensorInfo { + std::string sensor_name; + int sample_duration; + int num_out_samples; + std::vector samples; + int cur_index; +}; + +struct PredictedSensorInfo { + std::string sensor_name; + std::string linked_sensor; + int duration; + int prediction_index; +}; + +class ThermalPredictionsHelper { + public: + ThermalPredictionsHelper() = default; + ~ThermalPredictionsHelper() = default; + // Disallow copy and assign + ThermalPredictionsHelper(const ThermalPredictionsHelper &) = delete; + void operator=(const ThermalPredictionsHelper &) = delete; + + bool initializePredictionSensors( + const std::unordered_map &sensor_info_map); + bool updateSensor(std::string_view sensor_name, std::vector &values); + SensorReadStatus readSensor(std::string_view sensor_name, float *temp); + + private: + std::unordered_map predictor_sensors_; + std::unordered_map predicted_sensors_; + mutable std::shared_mutex sensor_predictions_mutex_; + + bool registerPredictedSensor(std::string_view sensor_name, std::string_view linked_sensor, + int duration); + bool registerPredictorSensor(std::string_view sensor_name, int sample_duration, + int num_out_samples); +}; + +} // namespace implementation +} // namespace thermal +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/thermal/utils/thermal_throttling.cpp b/aidl/thermal/utils/thermal_throttling.cpp index e868d8c..e329c14 100644 --- a/aidl/thermal/utils/thermal_throttling.cpp +++ b/aidl/thermal/utils/thermal_throttling.cpp @@ -181,14 +181,33 @@ float ThermalThrottling::updatePowerBudget( float p = 0, d = 0; float power_budget = std::numeric_limits::max(); bool target_changed = false; + bool is_fully_throttle = true; + bool is_fully_release = true; float budget_transient = 0.0; auto &throttling_status = thermal_throttling_status_map_.at(temp.name); + const auto &profile = throttling_status.profile; std::string sensor_name = temp.name; if (curr_severity == ThrottlingSeverity::NONE) { return power_budget; } + // Go through the binded cdev, check current throttle status + for (const auto &binded_cdev_info_pair : + ((sensor_info.throttling_info->profile_map.empty() || + !sensor_info.throttling_info->profile_map.contains(profile)) + ? sensor_info.throttling_info->binded_cdev_info_map + : sensor_info.throttling_info->profile_map.at(profile))) { + if (throttling_status.pid_cdev_request_map.at(binded_cdev_info_pair.first) > + binded_cdev_info_pair.second.limit_info[static_cast(curr_severity)]) { + is_fully_release = false; + } + if (throttling_status.pid_cdev_request_map.at(binded_cdev_info_pair.first) < + binded_cdev_info_pair.second.cdev_ceiling[static_cast(curr_severity)]) { + is_fully_throttle = false; + } + } + const auto target_state = getTargetStateOfPID(sensor_info, curr_severity); if (throttling_status.prev_target != static_cast(ThrottlingSeverity::NONE) && target_state != throttling_status.prev_target && @@ -206,9 +225,11 @@ float ThermalThrottling::updatePowerBudget( return sensor_info.throttling_info->min_alloc_power[target_state]; } + // Calculate P budget p = err * (err < 0 ? sensor_info.throttling_info->k_po[target_state] : sensor_info.throttling_info->k_pu[target_state]); + // Calculate I budget if (std::isnan(throttling_status.i_budget)) { if (std::isnan(sensor_info.throttling_info->i_default_pct)) { throttling_status.i_budget = sensor_info.throttling_info->i_default; @@ -227,15 +248,16 @@ float ThermalThrottling::updatePowerBudget( } if (err < sensor_info.throttling_info->i_cutoff[target_state]) { - if (!(throttling_status.prev_power_budget <= - sensor_info.throttling_info->min_alloc_power[target_state] && - err < 0) && - !(throttling_status.prev_power_budget >= - sensor_info.throttling_info->max_alloc_power[target_state] && - err > 0)) { - throttling_status.i_budget += - err * (err < 0 ? sensor_info.throttling_info->k_io[target_state] - : sensor_info.throttling_info->k_iu[target_state]); + if (err < 0 && + throttling_status.prev_power_budget > + sensor_info.throttling_info->min_alloc_power[target_state] && + !is_fully_throttle) { + throttling_status.i_budget += err * sensor_info.throttling_info->k_io[target_state]; + } else if (err > 0 && + throttling_status.prev_power_budget < + sensor_info.throttling_info->max_alloc_power[target_state] && + !is_fully_release) { + throttling_status.i_budget += err * sensor_info.throttling_info->k_iu[target_state]; } } @@ -244,6 +266,7 @@ float ThermalThrottling::updatePowerBudget( (throttling_status.i_budget > 0 ? 1 : -1); } + // Calculate D budget if (!std::isnan(throttling_status.prev_err) && time_elapsed_ms != std::chrono::milliseconds::zero()) { d = sensor_info.throttling_info->k_d[target_state] * (err - throttling_status.prev_err) / @@ -382,7 +405,7 @@ bool ThermalThrottling::allocatePowerToCdev( } } - // Compute total cdev weight + // Go through binded cdev, compute total cdev weight for (const auto &binded_cdev_info_pair : (sensor_info.throttling_info->profile_map.count(profile) ? sensor_info.throttling_info->profile_map.at(profile)