* From hardware/google/pixel @ android-14.0.0_r15 Change-Id: I30e35c8e4ef58956f849d64e184aa7e37ec67ef9urubino
parent
7e2c8c3a2b
commit
96d8907514
@ -0,0 +1,78 @@ |
||||
cc_binary { |
||||
name: "android.hardware.thermal-service.pixel", |
||||
srcs: [ |
||||
"service.cpp", |
||||
"Thermal.cpp", |
||||
"thermal-helper.cpp", |
||||
"utils/thermal_throttling.cpp", |
||||
"utils/thermal_info.cpp", |
||||
"utils/thermal_files.cpp", |
||||
"utils/power_files.cpp", |
||||
"utils/powerhal_helper.cpp", |
||||
"utils/thermal_stats_helper.cpp", |
||||
"utils/thermal_watcher.cpp", |
||||
], |
||||
vendor: true, |
||||
relative_install_path: "hw", |
||||
vintf_fragments: [ |
||||
"android.hardware.thermal-service.pixel.xml" |
||||
], |
||||
init_rc: [ |
||||
"android.hardware.thermal-service.pixel.rc", |
||||
], |
||||
shared_libs: [ |
||||
"libbase", |
||||
"libcutils", |
||||
"libjsoncpp", |
||||
"libutils", |
||||
"libnl", |
||||
"libbinder_ndk", |
||||
"android.frameworks.stats-V1-ndk", |
||||
"android.hardware.power-V1-ndk", |
||||
"android.hardware.thermal-V1-ndk", |
||||
"pixel-power-ext-V1-ndk", |
||||
"pixelatoms-cpp", |
||||
], |
||||
static_libs: [ |
||||
"libpixelstats", |
||||
], |
||||
export_shared_lib_headers: [ |
||||
"android.frameworks.stats-V1-ndk", |
||||
"pixelatoms-cpp", |
||||
], |
||||
cflags: [ |
||||
"-Wall", |
||||
"-Werror", |
||||
"-Wextra", |
||||
"-Wunused", |
||||
], |
||||
tidy: true, |
||||
tidy_checks: [ |
||||
"android-*", |
||||
"cert-*", |
||||
"clang-analyzer-security*", |
||||
], |
||||
tidy_checks_as_errors: [ |
||||
"android-*", |
||||
"clang-analyzer-security*", |
||||
"cert-*", |
||||
], |
||||
} |
||||
|
||||
sh_binary { |
||||
name: "thermal_logd", |
||||
src: "init.thermal.logging.sh", |
||||
vendor: true, |
||||
init_rc: [ |
||||
"pixel-thermal-logd.rc", |
||||
], |
||||
} |
||||
|
||||
sh_binary { |
||||
name: "thermal_symlinks", |
||||
src: "init.thermal.symlinks.sh", |
||||
vendor: true, |
||||
init_rc: [ |
||||
"pixel-thermal-symlinks.rc", |
||||
], |
||||
} |
@ -0,0 +1,793 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#define ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL) |
||||
|
||||
#include "Thermal.h" |
||||
|
||||
#include <android-base/file.h> |
||||
#include <android-base/logging.h> |
||||
#include <utils/Trace.h> |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
namespace { |
||||
|
||||
ndk::ScopedAStatus initErrorStatus() { |
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, |
||||
"ThermalHAL not initialized properly."); |
||||
} |
||||
|
||||
ndk::ScopedAStatus readErrorStatus() { |
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage( |
||||
EX_ILLEGAL_STATE, "ThermalHal cannot read any sensor data"); |
||||
} |
||||
|
||||
bool interfacesEqual(const std::shared_ptr<::ndk::ICInterface> left, |
||||
const std::shared_ptr<::ndk::ICInterface> right) { |
||||
if (left == nullptr || right == nullptr || !left->isRemote() || !right->isRemote()) { |
||||
return left == right; |
||||
} |
||||
return left->asBinder() == right->asBinder(); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
Thermal::Thermal() |
||||
: thermal_helper_( |
||||
std::bind(&Thermal::sendThermalChangedCallback, this, std::placeholders::_1)) {} |
||||
|
||||
ndk::ScopedAStatus Thermal::getTemperatures(std::vector<Temperature> *_aidl_return) { |
||||
return getFilteredTemperatures(false, TemperatureType::UNKNOWN, _aidl_return); |
||||
} |
||||
|
||||
ndk::ScopedAStatus Thermal::getTemperaturesWithType(TemperatureType type, |
||||
std::vector<Temperature> *_aidl_return) { |
||||
return getFilteredTemperatures(true, type, _aidl_return); |
||||
} |
||||
|
||||
ndk::ScopedAStatus Thermal::getFilteredTemperatures(bool filterType, TemperatureType type, |
||||
std::vector<Temperature> *_aidl_return) { |
||||
*_aidl_return = {}; |
||||
if (!thermal_helper_.isInitializedOk()) { |
||||
return initErrorStatus(); |
||||
} |
||||
if (!thermal_helper_.fillCurrentTemperatures(filterType, false, type, _aidl_return)) { |
||||
return readErrorStatus(); |
||||
} |
||||
return ndk::ScopedAStatus::ok(); |
||||
} |
||||
|
||||
ndk::ScopedAStatus Thermal::getCoolingDevices(std::vector<CoolingDevice> *_aidl_return) { |
||||
return getFilteredCoolingDevices(false, CoolingType::BATTERY, _aidl_return); |
||||
} |
||||
|
||||
ndk::ScopedAStatus Thermal::getCoolingDevicesWithType(CoolingType type, |
||||
std::vector<CoolingDevice> *_aidl_return) { |
||||
return getFilteredCoolingDevices(true, type, _aidl_return); |
||||
} |
||||
|
||||
ndk::ScopedAStatus Thermal::getFilteredCoolingDevices(bool filterType, CoolingType type, |
||||
std::vector<CoolingDevice> *_aidl_return) { |
||||
*_aidl_return = {}; |
||||
if (!thermal_helper_.isInitializedOk()) { |
||||
return initErrorStatus(); |
||||
} |
||||
if (!thermal_helper_.fillCurrentCoolingDevices(filterType, type, _aidl_return)) { |
||||
return readErrorStatus(); |
||||
} |
||||
return ndk::ScopedAStatus::ok(); |
||||
} |
||||
|
||||
ndk::ScopedAStatus Thermal::getTemperatureThresholds( |
||||
std::vector<TemperatureThreshold> *_aidl_return) { |
||||
*_aidl_return = {}; |
||||
return getFilteredTemperatureThresholds(false, TemperatureType::UNKNOWN, _aidl_return); |
||||
} |
||||
|
||||
ndk::ScopedAStatus Thermal::getTemperatureThresholdsWithType( |
||||
TemperatureType type, std::vector<TemperatureThreshold> *_aidl_return) { |
||||
return getFilteredTemperatureThresholds(true, type, _aidl_return); |
||||
} |
||||
|
||||
ndk::ScopedAStatus Thermal::getFilteredTemperatureThresholds( |
||||
bool filterType, TemperatureType type, std::vector<TemperatureThreshold> *_aidl_return) { |
||||
*_aidl_return = {}; |
||||
if (!thermal_helper_.isInitializedOk()) { |
||||
return initErrorStatus(); |
||||
} |
||||
if (!thermal_helper_.fillTemperatureThresholds(filterType, type, _aidl_return)) { |
||||
return readErrorStatus(); |
||||
} |
||||
return ndk::ScopedAStatus::ok(); |
||||
} |
||||
|
||||
ndk::ScopedAStatus Thermal::registerThermalChangedCallback( |
||||
const std::shared_ptr<IThermalChangedCallback> &callback) { |
||||
ATRACE_CALL(); |
||||
return registerThermalChangedCallback(callback, false, TemperatureType::UNKNOWN); |
||||
} |
||||
|
||||
ndk::ScopedAStatus Thermal::registerThermalChangedCallbackWithType( |
||||
const std::shared_ptr<IThermalChangedCallback> &callback, TemperatureType type) { |
||||
ATRACE_CALL(); |
||||
return registerThermalChangedCallback(callback, true, type); |
||||
} |
||||
|
||||
ndk::ScopedAStatus Thermal::unregisterThermalChangedCallback( |
||||
const std::shared_ptr<IThermalChangedCallback> &callback) { |
||||
if (callback == nullptr) { |
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, |
||||
"Invalid nullptr callback"); |
||||
} |
||||
bool removed = false; |
||||
std::lock_guard<std::mutex> _lock(thermal_callback_mutex_); |
||||
callbacks_.erase( |
||||
std::remove_if( |
||||
callbacks_.begin(), callbacks_.end(), |
||||
[&](const CallbackSetting &c) { |
||||
if (interfacesEqual(c.callback, callback)) { |
||||
LOG(INFO) |
||||
<< "a callback has been unregistered to ThermalHAL, isFilter: " |
||||
<< c.is_filter_type << " Type: " << toString(c.type); |
||||
removed = true; |
||||
return true; |
||||
} |
||||
return false; |
||||
}), |
||||
callbacks_.end()); |
||||
if (!removed) { |
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, |
||||
"Callback wasn't registered"); |
||||
} |
||||
return ndk::ScopedAStatus::ok(); |
||||
} |
||||
|
||||
ndk::ScopedAStatus Thermal::registerThermalChangedCallback( |
||||
const std::shared_ptr<IThermalChangedCallback> &callback, bool filterType, |
||||
TemperatureType type) { |
||||
ATRACE_CALL(); |
||||
if (callback == nullptr) { |
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, |
||||
"Invalid nullptr callback"); |
||||
} |
||||
if (!thermal_helper_.isInitializedOk()) { |
||||
return initErrorStatus(); |
||||
} |
||||
std::lock_guard<std::mutex> _lock(thermal_callback_mutex_); |
||||
if (std::any_of(callbacks_.begin(), callbacks_.end(), [&](const CallbackSetting &c) { |
||||
return interfacesEqual(c.callback, callback); |
||||
})) { |
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, |
||||
"Callback already registered"); |
||||
} |
||||
auto c = callbacks_.emplace_back(callback, filterType, type); |
||||
LOG(INFO) << "a callback has been registered to ThermalHAL, isFilter: " << c.is_filter_type |
||||
<< " Type: " << toString(c.type); |
||||
// Send notification right away after successful thermal callback registration
|
||||
std::function<void()> handler = [this, c, filterType, type]() { |
||||
std::vector<Temperature> temperatures; |
||||
if (thermal_helper_.fillCurrentTemperatures(filterType, true, type, &temperatures)) { |
||||
for (const auto &t : temperatures) { |
||||
if (!filterType || t.type == type) { |
||||
LOG(INFO) << "Sending notification: " |
||||
<< " Type: " << toString(t.type) << " Name: " << t.name |
||||
<< " CurrentValue: " << t.value |
||||
<< " ThrottlingStatus: " << toString(t.throttlingStatus); |
||||
c.callback->notifyThrottling(t); |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
looper_.addEvent(Looper::Event{handler}); |
||||
return ndk::ScopedAStatus::ok(); |
||||
} |
||||
|
||||
void Thermal::sendThermalChangedCallback(const Temperature &t) { |
||||
ATRACE_CALL(); |
||||
std::lock_guard<std::mutex> _lock(thermal_callback_mutex_); |
||||
LOG(VERBOSE) << "Sending notification: " |
||||
<< " Type: " << toString(t.type) << " Name: " << t.name |
||||
<< " CurrentValue: " << t.value |
||||
<< " ThrottlingStatus: " << toString(t.throttlingStatus); |
||||
|
||||
callbacks_.erase(std::remove_if(callbacks_.begin(), callbacks_.end(), |
||||
[&](const CallbackSetting &c) { |
||||
if (!c.is_filter_type || t.type == c.type) { |
||||
::ndk::ScopedAStatus ret = |
||||
c.callback->notifyThrottling(t); |
||||
if (!ret.isOk()) { |
||||
LOG(ERROR) << "a Thermal callback is dead, removed " |
||||
"from callback list."; |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
return false; |
||||
}), |
||||
callbacks_.end()); |
||||
} |
||||
|
||||
void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) { |
||||
*dump_buf << "getVirtualSensorInfo:" << std::endl; |
||||
const auto &map = thermal_helper_.GetSensorInfoMap(); |
||||
for (const auto &sensor_info_pair : map) { |
||||
if (sensor_info_pair.second.virtual_sensor_info != nullptr) { |
||||
*dump_buf << " Name: " << sensor_info_pair.first << std::endl; |
||||
*dump_buf << " LinkedSensorName: ["; |
||||
for (size_t i = 0; |
||||
i < sensor_info_pair.second.virtual_sensor_info->linked_sensors.size(); i++) { |
||||
*dump_buf << sensor_info_pair.second.virtual_sensor_info->linked_sensors[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
*dump_buf << " LinkedSensorCoefficient: ["; |
||||
for (size_t i = 0; i < sensor_info_pair.second.virtual_sensor_info->coefficients.size(); |
||||
i++) { |
||||
*dump_buf << sensor_info_pair.second.virtual_sensor_info->coefficients[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
*dump_buf << " Offset: " << sensor_info_pair.second.virtual_sensor_info->offset |
||||
<< std::endl; |
||||
*dump_buf << " Trigger Sensor: "; |
||||
if (sensor_info_pair.second.virtual_sensor_info->trigger_sensors.empty()) { |
||||
*dump_buf << "N/A" << std::endl; |
||||
} else { |
||||
for (size_t i = 0; |
||||
i < sensor_info_pair.second.virtual_sensor_info->trigger_sensors.size(); i++) { |
||||
*dump_buf << sensor_info_pair.second.virtual_sensor_info->trigger_sensors[i] |
||||
<< " "; |
||||
} |
||||
*dump_buf << std::endl; |
||||
} |
||||
*dump_buf << " Formula: "; |
||||
switch (sensor_info_pair.second.virtual_sensor_info->formula) { |
||||
case FormulaOption::COUNT_THRESHOLD: |
||||
*dump_buf << "COUNT_THRESHOLD"; |
||||
break; |
||||
case FormulaOption::WEIGHTED_AVG: |
||||
*dump_buf << "WEIGHTED_AVG"; |
||||
break; |
||||
case FormulaOption::MAXIMUM: |
||||
*dump_buf << "MAXIMUM"; |
||||
break; |
||||
case FormulaOption::MINIMUM: |
||||
*dump_buf << "MINIMUM"; |
||||
break; |
||||
default: |
||||
*dump_buf << "NONE"; |
||||
break; |
||||
} |
||||
|
||||
*dump_buf << std::endl; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void Thermal::dumpThrottlingInfo(std::ostringstream *dump_buf) { |
||||
*dump_buf << "getThrottlingInfo:" << std::endl; |
||||
const auto &map = thermal_helper_.GetSensorInfoMap(); |
||||
const auto &thermal_throttling_status_map = thermal_helper_.GetThermalThrottlingStatusMap(); |
||||
for (const auto &name_info_pair : map) { |
||||
if (name_info_pair.second.throttling_info == nullptr) { |
||||
continue; |
||||
} |
||||
if (name_info_pair.second.throttling_info->binded_cdev_info_map.size()) { |
||||
if (thermal_throttling_status_map.find(name_info_pair.first) == |
||||
thermal_throttling_status_map.end()) { |
||||
continue; |
||||
} |
||||
*dump_buf << " Name: " << name_info_pair.first << std::endl; |
||||
if (thermal_throttling_status_map.at(name_info_pair.first) |
||||
.pid_power_budget_map.size()) { |
||||
*dump_buf << " PID Info:" << std::endl; |
||||
*dump_buf << " K_po: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << name_info_pair.second.throttling_info->k_po[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
*dump_buf << " K_pu: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << name_info_pair.second.throttling_info->k_pu[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
*dump_buf << " K_i: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << name_info_pair.second.throttling_info->k_i[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
*dump_buf << " K_d: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << name_info_pair.second.throttling_info->k_d[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
*dump_buf << " i_max: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << name_info_pair.second.throttling_info->i_max[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
*dump_buf << " max_alloc_power: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << name_info_pair.second.throttling_info->max_alloc_power[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
*dump_buf << " min_alloc_power: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << name_info_pair.second.throttling_info->min_alloc_power[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
*dump_buf << " s_power: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << name_info_pair.second.throttling_info->s_power[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
*dump_buf << " i_cutoff: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << name_info_pair.second.throttling_info->i_cutoff[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
} |
||||
*dump_buf << " Binded CDEV Info:" << std::endl; |
||||
for (const auto &binded_cdev_info_pair : |
||||
name_info_pair.second.throttling_info->binded_cdev_info_map) { |
||||
*dump_buf << " Cooling device name: " << binded_cdev_info_pair.first << std::endl; |
||||
if (thermal_throttling_status_map.at(name_info_pair.first) |
||||
.pid_power_budget_map.size()) { |
||||
*dump_buf << " WeightForPID: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << binded_cdev_info_pair.second.cdev_weight_for_pid[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
} |
||||
*dump_buf << " Ceiling: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << binded_cdev_info_pair.second.cdev_ceiling[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
*dump_buf << " Hard limit: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << binded_cdev_info_pair.second.limit_info[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
|
||||
if (!binded_cdev_info_pair.second.power_rail.empty()) { |
||||
*dump_buf << " Binded power rail: " |
||||
<< binded_cdev_info_pair.second.power_rail << std::endl; |
||||
*dump_buf << " Power threshold: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << binded_cdev_info_pair.second.power_thresholds[i] << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
*dump_buf << " Floor with PowerLink: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
*dump_buf << binded_cdev_info_pair.second.cdev_floor_with_power_link[i] |
||||
<< " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
*dump_buf << " Release logic: "; |
||||
switch (binded_cdev_info_pair.second.release_logic) { |
||||
case ReleaseLogic::INCREASE: |
||||
*dump_buf << "INCREASE"; |
||||
break; |
||||
case ReleaseLogic::DECREASE: |
||||
*dump_buf << "DECREASE"; |
||||
break; |
||||
case ReleaseLogic::STEPWISE: |
||||
*dump_buf << "STEPWISE"; |
||||
break; |
||||
case ReleaseLogic::RELEASE_TO_FLOOR: |
||||
*dump_buf << "RELEASE_TO_FLOOR"; |
||||
break; |
||||
default: |
||||
*dump_buf << "NONE"; |
||||
break; |
||||
} |
||||
*dump_buf << std::endl; |
||||
*dump_buf << " high_power_check: " << std::boolalpha |
||||
<< binded_cdev_info_pair.second.high_power_check << std::endl; |
||||
*dump_buf << " throttling_with_power_link: " << std::boolalpha |
||||
<< binded_cdev_info_pair.second.throttling_with_power_link |
||||
<< std::endl; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void Thermal::dumpThrottlingRequestStatus(std::ostringstream *dump_buf) { |
||||
const auto &thermal_throttling_status_map = thermal_helper_.GetThermalThrottlingStatusMap(); |
||||
if (!thermal_throttling_status_map.size()) { |
||||
return; |
||||
} |
||||
*dump_buf << "getThrottlingRequestStatus:" << std::endl; |
||||
for (const auto &thermal_throttling_status_pair : thermal_throttling_status_map) { |
||||
*dump_buf << " Name: " << thermal_throttling_status_pair.first << std::endl; |
||||
if (thermal_throttling_status_pair.second.pid_power_budget_map.size()) { |
||||
*dump_buf << " power budget request state" << std::endl; |
||||
for (const auto &request_pair : |
||||
thermal_throttling_status_pair.second.pid_power_budget_map) { |
||||
*dump_buf << " " << request_pair.first << ": " << request_pair.second |
||||
<< std::endl; |
||||
} |
||||
} |
||||
if (thermal_throttling_status_pair.second.pid_cdev_request_map.size()) { |
||||
*dump_buf << " pid cdev request state" << std::endl; |
||||
for (const auto &request_pair : |
||||
thermal_throttling_status_pair.second.pid_cdev_request_map) { |
||||
*dump_buf << " " << request_pair.first << ": " << request_pair.second |
||||
<< std::endl; |
||||
} |
||||
} |
||||
if (thermal_throttling_status_pair.second.hardlimit_cdev_request_map.size()) { |
||||
*dump_buf << " hard limit cdev request state" << std::endl; |
||||
for (const auto &request_pair : |
||||
thermal_throttling_status_pair.second.hardlimit_cdev_request_map) { |
||||
*dump_buf << " " << request_pair.first << ": " << request_pair.second |
||||
<< std::endl; |
||||
} |
||||
} |
||||
if (thermal_throttling_status_pair.second.throttling_release_map.size()) { |
||||
*dump_buf << " cdev release state" << std::endl; |
||||
for (const auto &request_pair : |
||||
thermal_throttling_status_pair.second.throttling_release_map) { |
||||
*dump_buf << " " << request_pair.first << ": " << request_pair.second |
||||
<< std::endl; |
||||
} |
||||
} |
||||
if (thermal_throttling_status_pair.second.cdev_status_map.size()) { |
||||
*dump_buf << " cdev request state" << std::endl; |
||||
for (const auto &request_pair : thermal_throttling_status_pair.second.cdev_status_map) { |
||||
*dump_buf << " " << request_pair.first << ": " << request_pair.second |
||||
<< std::endl; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void Thermal::dumpPowerRailInfo(std::ostringstream *dump_buf) { |
||||
const auto &power_rail_info_map = thermal_helper_.GetPowerRailInfoMap(); |
||||
const auto &power_status_map = thermal_helper_.GetPowerStatusMap(); |
||||
|
||||
*dump_buf << "getPowerRailInfo:" << std::endl; |
||||
for (const auto &power_rail_pair : power_rail_info_map) { |
||||
*dump_buf << " Power Rail: " << power_rail_pair.first << std::endl; |
||||
*dump_buf << " Power Sample Count: " << power_rail_pair.second.power_sample_count |
||||
<< std::endl; |
||||
*dump_buf << " Power Sample Delay: " << power_rail_pair.second.power_sample_delay.count() |
||||
<< std::endl; |
||||
if (power_status_map.count(power_rail_pair.first)) { |
||||
auto power_history = power_status_map.at(power_rail_pair.first).power_history; |
||||
*dump_buf << " Last Updated AVG Power: " |
||||
<< power_status_map.at(power_rail_pair.first).last_updated_avg_power << " mW" |
||||
<< std::endl; |
||||
if (power_rail_pair.second.virtual_power_rail_info != nullptr) { |
||||
*dump_buf << " Formula="; |
||||
switch (power_rail_pair.second.virtual_power_rail_info->formula) { |
||||
case FormulaOption::COUNT_THRESHOLD: |
||||
*dump_buf << "COUNT_THRESHOLD"; |
||||
break; |
||||
case FormulaOption::WEIGHTED_AVG: |
||||
*dump_buf << "WEIGHTED_AVG"; |
||||
break; |
||||
case FormulaOption::MAXIMUM: |
||||
*dump_buf << "MAXIMUM"; |
||||
break; |
||||
case FormulaOption::MINIMUM: |
||||
*dump_buf << "MINIMUM"; |
||||
break; |
||||
default: |
||||
*dump_buf << "NONE"; |
||||
break; |
||||
} |
||||
*dump_buf << std::endl; |
||||
} |
||||
for (size_t i = 0; i < power_history.size(); ++i) { |
||||
if (power_rail_pair.second.virtual_power_rail_info != nullptr) { |
||||
*dump_buf |
||||
<< " Linked power rail " |
||||
<< power_rail_pair.second.virtual_power_rail_info->linked_power_rails[i] |
||||
<< std::endl; |
||||
*dump_buf << " Coefficient=" |
||||
<< power_rail_pair.second.virtual_power_rail_info->coefficients[i] |
||||
<< std::endl; |
||||
*dump_buf << " Power Samples: "; |
||||
} else { |
||||
*dump_buf << " Power Samples: "; |
||||
} |
||||
while (power_history[i].size() > 0) { |
||||
const auto power_sample = power_history[i].front(); |
||||
power_history[i].pop(); |
||||
*dump_buf << "(T=" << power_sample.duration |
||||
<< ", uWs=" << power_sample.energy_counter << ") "; |
||||
} |
||||
*dump_buf << std::endl; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void Thermal::dumpStatsRecord(std::ostringstream *dump_buf, const StatsRecord &stats_record, |
||||
std::string_view line_prefix) { |
||||
const auto now = boot_clock::now(); |
||||
*dump_buf << line_prefix << "Time Since Last Stats Report: " |
||||
<< std::chrono::duration_cast<std::chrono::minutes>( |
||||
now - stats_record.last_stats_report_time) |
||||
.count() |
||||
<< " mins" << std::endl; |
||||
*dump_buf << line_prefix << "Time in State ms: ["; |
||||
for (const auto &time_in_state : stats_record.time_in_state_ms) { |
||||
*dump_buf << time_in_state.count() << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
} |
||||
|
||||
void Thermal::dumpThermalStats(std::ostringstream *dump_buf) { |
||||
*dump_buf << "getThermalStatsInfo:" << std::endl; |
||||
*dump_buf << " Sensor Temp Stats Info:" << std::endl; |
||||
const auto &sensor_temp_stats_map_ = thermal_helper_.GetSensorTempStatsSnapshot(); |
||||
const std::string sensor_temp_stats_line_prefix(" "); |
||||
for (const auto &sensor_temp_stats_pair : sensor_temp_stats_map_) { |
||||
*dump_buf << " Sensor Name: " << sensor_temp_stats_pair.first << std::endl; |
||||
const auto &sensor_temp_stats = sensor_temp_stats_pair.second; |
||||
*dump_buf << " Max Temp: " << sensor_temp_stats.max_temp << ", TimeStamp: " |
||||
<< system_clock::to_time_t(sensor_temp_stats.max_temp_timestamp) << std::endl; |
||||
*dump_buf << " Min Temp: " << sensor_temp_stats.min_temp << ", TimeStamp: " |
||||
<< system_clock::to_time_t(sensor_temp_stats.min_temp_timestamp) << std::endl; |
||||
for (const auto &stats_by_threshold : sensor_temp_stats.stats_by_custom_threshold) { |
||||
*dump_buf << " Record by Threshold: ["; |
||||
for (const auto &threshold : stats_by_threshold.thresholds) { |
||||
*dump_buf << threshold << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
if (stats_by_threshold.logging_name.has_value()) { |
||||
*dump_buf << " Logging Name: " << stats_by_threshold.logging_name.value() |
||||
<< std::endl; |
||||
} |
||||
dumpStatsRecord(dump_buf, stats_by_threshold.stats_record, |
||||
sensor_temp_stats_line_prefix); |
||||
} |
||||
|
||||
if (sensor_temp_stats.stats_by_default_threshold.has_value()) { |
||||
*dump_buf << " Record by Severity:" << std::endl; |
||||
dumpStatsRecord(dump_buf, sensor_temp_stats.stats_by_default_threshold.value(), |
||||
sensor_temp_stats_line_prefix); |
||||
} |
||||
} |
||||
*dump_buf << " Sensor Cdev Request Stats Info:" << std::endl; |
||||
const auto &sensor_cdev_request_stats_map_ = |
||||
thermal_helper_.GetSensorCoolingDeviceRequestStatsSnapshot(); |
||||
const std::string sensor_cdev_request_stats_line_prefix(" "); |
||||
for (const auto &sensor_cdev_request_stats_pair : sensor_cdev_request_stats_map_) { |
||||
*dump_buf << " Sensor Name: " << sensor_cdev_request_stats_pair.first << std::endl; |
||||
for (const auto &cdev_request_stats_pair : sensor_cdev_request_stats_pair.second) { |
||||
*dump_buf << " Cooling Device Name: " << cdev_request_stats_pair.first << std::endl; |
||||
const auto &request_stats = cdev_request_stats_pair.second; |
||||
for (const auto &stats_by_threshold : request_stats.stats_by_custom_threshold) { |
||||
*dump_buf << " Record by Threshold: ["; |
||||
for (const auto &threshold : stats_by_threshold.thresholds) { |
||||
*dump_buf << threshold << " "; |
||||
} |
||||
*dump_buf << "]" << std::endl; |
||||
if (stats_by_threshold.logging_name.has_value()) { |
||||
*dump_buf << " Logging Name: " << stats_by_threshold.logging_name.value() |
||||
<< std::endl; |
||||
} |
||||
dumpStatsRecord(dump_buf, stats_by_threshold.stats_record, |
||||
sensor_cdev_request_stats_line_prefix); |
||||
} |
||||
if (request_stats.stats_by_default_threshold.has_value()) { |
||||
*dump_buf << " Record by All State" << std::endl; |
||||
dumpStatsRecord(dump_buf, request_stats.stats_by_default_threshold.value(), |
||||
sensor_cdev_request_stats_line_prefix); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void Thermal::dumpThermalData(int fd) { |
||||
std::ostringstream dump_buf; |
||||
|
||||
if (!thermal_helper_.isInitializedOk()) { |
||||
dump_buf << "ThermalHAL not initialized properly." << std::endl; |
||||
} else { |
||||
const auto &sensor_status_map = thermal_helper_.GetSensorStatusMap(); |
||||
{ |
||||
dump_buf << "getCachedTemperatures:" << std::endl; |
||||
boot_clock::time_point now = boot_clock::now(); |
||||
for (const auto &sensor_status_pair : sensor_status_map) { |
||||
if ((sensor_status_pair.second.thermal_cached.timestamp) == |
||||
boot_clock::time_point::min()) { |
||||
continue; |
||||
} |
||||
dump_buf << " Name: " << sensor_status_pair.first |
||||
<< " CachedValue: " << sensor_status_pair.second.thermal_cached.temp |
||||
<< " TimeToCache: " |
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>( |
||||
now - sensor_status_pair.second.thermal_cached.timestamp) |
||||
.count() |
||||
<< "ms" << std::endl; |
||||
} |
||||
} |
||||
{ |
||||
dump_buf << "getEmulTemperatures:" << std::endl; |
||||
for (const auto &sensor_status_pair : sensor_status_map) { |
||||
if (sensor_status_pair.second.emul_setting == nullptr) { |
||||
continue; |
||||
} |
||||
dump_buf << " Name: " << sensor_status_pair.first |
||||
<< " EmulTemp: " << sensor_status_pair.second.emul_setting->emul_temp |
||||
<< " EmulSeverity: " |
||||
<< sensor_status_pair.second.emul_setting->emul_severity << std::endl; |
||||
} |
||||
} |
||||
{ |
||||
const auto &map = thermal_helper_.GetSensorInfoMap(); |
||||
dump_buf << "getCurrentTemperatures:" << std::endl; |
||||
Temperature temp_2_0; |
||||
for (const auto &name_info_pair : map) { |
||||
thermal_helper_.readTemperature(name_info_pair.first, &temp_2_0, nullptr, true); |
||||
dump_buf << " Type: " << toString(temp_2_0.type) |
||||
<< " Name: " << name_info_pair.first << " CurrentValue: " << temp_2_0.value |
||||
<< " ThrottlingStatus: " << toString(temp_2_0.throttlingStatus) |
||||
<< std::endl; |
||||
} |
||||
dump_buf << "getTemperatureThresholds:" << std::endl; |
||||
for (const auto &name_info_pair : map) { |
||||
if (!name_info_pair.second.is_watch) { |
||||
continue; |
||||
} |
||||
dump_buf << " Type: " << toString(name_info_pair.second.type) |
||||
<< " Name: " << name_info_pair.first; |
||||
dump_buf << " hotThrottlingThreshold: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
dump_buf << name_info_pair.second.hot_thresholds[i] << " "; |
||||
} |
||||
dump_buf << "] coldThrottlingThreshold: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
dump_buf << name_info_pair.second.cold_thresholds[i] << " "; |
||||
} |
||||
dump_buf << "] vrThrottlingThreshold: " << name_info_pair.second.vr_threshold; |
||||
dump_buf << std::endl; |
||||
} |
||||
dump_buf << "getHysteresis:" << std::endl; |
||||
for (const auto &name_info_pair : map) { |
||||
if (!name_info_pair.second.is_watch) { |
||||
continue; |
||||
} |
||||
dump_buf << " Name: " << name_info_pair.first; |
||||
dump_buf << " hotHysteresis: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
dump_buf << name_info_pair.second.hot_hysteresis[i] << " "; |
||||
} |
||||
dump_buf << "] coldHysteresis: ["; |
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { |
||||
dump_buf << name_info_pair.second.cold_hysteresis[i] << " "; |
||||
} |
||||
dump_buf << "]" << std::endl; |
||||
} |
||||
} |
||||
{ |
||||
dump_buf << "getCurrentCoolingDevices:" << std::endl; |
||||
std::vector<CoolingDevice> cooling_devices; |
||||
if (!thermal_helper_.fillCurrentCoolingDevices(false, CoolingType::CPU, |
||||
&cooling_devices)) { |
||||
dump_buf << " Failed to getCurrentCoolingDevices." << std::endl; |
||||
} |
||||
|
||||
for (const auto &c : cooling_devices) { |
||||
dump_buf << " Type: " << toString(c.type) << " Name: " << c.name |
||||
<< " CurrentValue: " << c.value << std::endl; |
||||
} |
||||
} |
||||
{ |
||||
dump_buf << "getCallbacks:" << std::endl; |
||||
dump_buf << " Total: " << callbacks_.size() << std::endl; |
||||
for (const auto &c : callbacks_) { |
||||
dump_buf << " IsFilter: " << c.is_filter_type << " Type: " << toString(c.type) |
||||
<< std::endl; |
||||
} |
||||
} |
||||
{ |
||||
dump_buf << "sendCallback:" << std::endl; |
||||
dump_buf << " Enabled List: "; |
||||
const auto &map = thermal_helper_.GetSensorInfoMap(); |
||||
for (const auto &name_info_pair : map) { |
||||
if (name_info_pair.second.send_cb) { |
||||
dump_buf << name_info_pair.first << " "; |
||||
} |
||||
} |
||||
dump_buf << std::endl; |
||||
} |
||||
{ |
||||
dump_buf << "sendPowerHint:" << std::endl; |
||||
dump_buf << " Enabled List: "; |
||||
const auto &map = thermal_helper_.GetSensorInfoMap(); |
||||
for (const auto &name_info_pair : map) { |
||||
if (name_info_pair.second.send_powerhint) { |
||||
dump_buf << name_info_pair.first << " "; |
||||
} |
||||
} |
||||
dump_buf << std::endl; |
||||
} |
||||
dumpVirtualSensorInfo(&dump_buf); |
||||
dumpThrottlingInfo(&dump_buf); |
||||
dumpThrottlingRequestStatus(&dump_buf); |
||||
dumpPowerRailInfo(&dump_buf); |
||||
dumpThermalStats(&dump_buf); |
||||
{ |
||||
dump_buf << "getAIDLPowerHalInfo:" << std::endl; |
||||
dump_buf << " Exist: " << std::boolalpha << thermal_helper_.isAidlPowerHalExist() |
||||
<< std::endl; |
||||
dump_buf << " Connected: " << std::boolalpha << thermal_helper_.isPowerHalConnected() |
||||
<< std::endl; |
||||
dump_buf << " Ext connected: " << std::boolalpha |
||||
<< thermal_helper_.isPowerHalExtConnected() << std::endl; |
||||
} |
||||
} |
||||
std::string buf = dump_buf.str(); |
||||
if (!::android::base::WriteStringToFd(buf, fd)) { |
||||
PLOG(ERROR) << "Failed to dump state to fd"; |
||||
} |
||||
fsync(fd); |
||||
} |
||||
|
||||
binder_status_t Thermal::dump(int fd, const char **args, uint32_t numArgs) { |
||||
if (numArgs == 0 || std::string(args[0]) == "-a") { |
||||
dumpThermalData(fd); |
||||
return STATUS_OK; |
||||
} |
||||
|
||||
if (std::string(args[0]) == "emul_temp") { |
||||
return (numArgs != 3 || !thermal_helper_.emulTemp(std::string(args[1]), std::atof(args[2]))) |
||||
? STATUS_BAD_VALUE |
||||
: STATUS_OK; |
||||
} else if (std::string(args[0]) == "emul_severity") { |
||||
return (numArgs != 3 || |
||||
!thermal_helper_.emulSeverity(std::string(args[1]), std::atoi(args[2]))) |
||||
? STATUS_BAD_VALUE |
||||
: STATUS_OK; |
||||
} else if (std::string(args[0]) == "emul_clear") { |
||||
return (numArgs != 2 || !thermal_helper_.emulClear(std::string(args[1]))) ? STATUS_BAD_VALUE |
||||
: STATUS_OK; |
||||
} |
||||
return STATUS_BAD_VALUE; |
||||
} |
||||
|
||||
void Thermal::Looper::addEvent(const Thermal::Looper::Event &e) { |
||||
std::unique_lock<std::mutex> lock(mutex_); |
||||
events_.push(e); |
||||
cv_.notify_all(); |
||||
} |
||||
|
||||
void Thermal::Looper::loop() { |
||||
while (true) { |
||||
std::unique_lock<std::mutex> lock(mutex_); |
||||
cv_.wait(lock, [&] { return !events_.empty(); }); |
||||
Event event = events_.front(); |
||||
events_.pop(); |
||||
lock.unlock(); |
||||
event.handler(); |
||||
} |
||||
} |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,120 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <aidl/android/hardware/thermal/BnThermal.h> |
||||
|
||||
#include <mutex> |
||||
#include <thread> |
||||
|
||||
#include "thermal-helper.h" |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
struct CallbackSetting { |
||||
CallbackSetting(std::shared_ptr<IThermalChangedCallback> callback, bool is_filter_type, |
||||
TemperatureType type) |
||||
: callback(std::move(callback)), is_filter_type(is_filter_type), type(type) {} |
||||
std::shared_ptr<IThermalChangedCallback> callback; |
||||
bool is_filter_type; |
||||
TemperatureType type; |
||||
}; |
||||
|
||||
class Thermal : public BnThermal { |
||||
public: |
||||
Thermal(); |
||||
~Thermal() = default; |
||||
ndk::ScopedAStatus getTemperatures(std::vector<Temperature> *_aidl_return) override; |
||||
ndk::ScopedAStatus getTemperaturesWithType(TemperatureType type, |
||||
std::vector<Temperature> *_aidl_return) override; |
||||
|
||||
ndk::ScopedAStatus getCoolingDevices(std::vector<CoolingDevice> *_aidl_return) override; |
||||
ndk::ScopedAStatus getCoolingDevicesWithType(CoolingType type, |
||||
std::vector<CoolingDevice> *_aidl_return) override; |
||||
|
||||
ndk::ScopedAStatus getTemperatureThresholds( |
||||
std::vector<TemperatureThreshold> *_aidl_return) override; |
||||
ndk::ScopedAStatus getTemperatureThresholdsWithType( |
||||
TemperatureType type, std::vector<TemperatureThreshold> *_aidl_return) override; |
||||
|
||||
ndk::ScopedAStatus registerThermalChangedCallback( |
||||
const std::shared_ptr<IThermalChangedCallback> &callback) override; |
||||
ndk::ScopedAStatus registerThermalChangedCallbackWithType( |
||||
const std::shared_ptr<IThermalChangedCallback> &callback, |
||||
TemperatureType type) override; |
||||
ndk::ScopedAStatus unregisterThermalChangedCallback( |
||||
const std::shared_ptr<IThermalChangedCallback> &callback) override; |
||||
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override; |
||||
|
||||
// Helper function for calling callbacks
|
||||
void sendThermalChangedCallback(const Temperature &t); |
||||
|
||||
private: |
||||
class Looper { |
||||
public: |
||||
struct Event { |
||||
std::function<void()> handler; |
||||
}; |
||||
|
||||
Looper() { |
||||
thread_ = std::thread([&] { loop(); }); |
||||
} |
||||
void addEvent(const Event &e); |
||||
|
||||
private: |
||||
std::condition_variable cv_; |
||||
std::queue<Event> events_; |
||||
std::mutex mutex_; |
||||
std::thread thread_; |
||||
|
||||
void loop(); |
||||
}; |
||||
|
||||
ThermalHelper thermal_helper_; |
||||
std::mutex thermal_callback_mutex_; |
||||
std::vector<CallbackSetting> callbacks_; |
||||
Looper looper_; |
||||
|
||||
ndk::ScopedAStatus getFilteredTemperatures(bool filterType, TemperatureType type, |
||||
std::vector<Temperature> *_aidl_return); |
||||
ndk::ScopedAStatus getFilteredCoolingDevices(bool filterType, CoolingType type, |
||||
std::vector<CoolingDevice> *_aidl_return); |
||||
ndk::ScopedAStatus getFilteredTemperatureThresholds( |
||||
bool filterType, TemperatureType type, std::vector<TemperatureThreshold> *_aidl_return); |
||||
ndk::ScopedAStatus registerThermalChangedCallback( |
||||
const std::shared_ptr<IThermalChangedCallback> &callback, bool filterType, |
||||
TemperatureType type); |
||||
|
||||
void dumpVirtualSensorInfo(std::ostringstream *dump_buf); |
||||
void dumpThrottlingInfo(std::ostringstream *dump_buf); |
||||
void dumpThrottlingRequestStatus(std::ostringstream *dump_buf); |
||||
void dumpPowerRailInfo(std::ostringstream *dump_buf); |
||||
void dumpStatsRecord(std::ostringstream *dump_buf, const StatsRecord &stats_record, |
||||
std::string_view line_prefix); |
||||
void dumpThermalStats(std::ostringstream *dump_buf); |
||||
void dumpThermalData(int fd); |
||||
}; |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,14 @@ |
||||
on property:vendor.thermal.link_ready=1 |
||||
# queue the trigger to start thermal-hal and continue execute |
||||
# per-device thermal setup "on property:vendor.thermal.link_ready=1" |
||||
trigger enable-thermal-hal |
||||
|
||||
on enable-thermal-hal |
||||
restart vendor.thermal-hal |
||||
|
||||
service vendor.thermal-hal /vendor/bin/hw/android.hardware.thermal-service.pixel |
||||
class hal |
||||
user system |
||||
group system |
||||
priority -20 |
||||
disabled |
@ -0,0 +1,7 @@ |
||||
<manifest version="1.0" type="device"> |
||||
<hal format="aidl"> |
||||
<name>android.hardware.thermal</name> |
||||
<version>1</version> |
||||
<fqname>IThermal/default</fqname> |
||||
</hal> |
||||
</manifest> |
@ -0,0 +1,25 @@ |
||||
#!/vendor/bin/sh |
||||
|
||||
if [ $# -eq 1 ]; then |
||||
interval=$1 |
||||
else |
||||
exit 1 |
||||
fi |
||||
|
||||
while true |
||||
do |
||||
logline="tz:" |
||||
for f in /sys/class/thermal/thermal* |
||||
do |
||||
temp=`cat $f/temp` |
||||
logline+="|$temp" |
||||
done |
||||
logline+=" cdev:" |
||||
for f in /sys/class/thermal/cooling_device* |
||||
do |
||||
cur_state=`cat $f/cur_state` |
||||
logline+="|$cur_state" |
||||
done |
||||
log -p w -t THERMAL_LOG $logline |
||||
sleep $interval |
||||
done |
@ -0,0 +1,13 @@ |
||||
#!/vendor/bin/sh |
||||
|
||||
for f in /sys/class/thermal/thermal_zone* |
||||
do |
||||
tz_name=`cat $f/type` |
||||
ln -s $f /dev/thermal/tz-by-name/$tz_name |
||||
done |
||||
for f in /sys/class/thermal/cooling_device* |
||||
do |
||||
cdev_name=`cat $f/type` |
||||
ln -s $f /dev/thermal/cdev-by-name/$cdev_name |
||||
done |
||||
setprop vendor.thermal.link_ready 1 |
@ -0,0 +1,130 @@ |
||||
on property:persist.vendor.log.thermal=1 |
||||
start vendor.thermal.logd |
||||
|
||||
on property:persist.vendor.log.thermal=0 |
||||
stop vendor.thermal.logd |
||||
|
||||
on property:persist.vendor.log.thermal=1 && property:persist.vendor.log.thermal.interval=* |
||||
restart vendor.thermal.logd |
||||
|
||||
service vendor.thermal.logd /vendor/bin/thermal_logd ${persist.vendor.log.thermal.interval:-5} |
||||
class main |
||||
user root |
||||
group root system |
||||
disabled |
||||
|
||||
# Switch thermal protection for Pixels |
||||
on property:persist.vendor.disable.thermal.control=* |
||||
setprop vendor.disable.thermal.control ${persist.vendor.disable.thermal.control} |
||||
|
||||
on property:persist.vendor.disable.thermalhal.control=* |
||||
setprop vendor.disable.thermalhal.control ${persist.vendor.disable.thermalhal.control} |
||||
|
||||
on property:persist.vendor.disable.usb.overheat.mitigation=* |
||||
setprop vendor.disable.usb.overheat.mitigation.control ${persist.vendor.disable.usb.overheat.mitigation} |
||||
|
||||
on property:persist.vendor.disable.bcl.control=* |
||||
setprop vendor.disable.bcl.control ${persist.vendor.disable.bcl.control} |
||||
|
||||
on property:vendor.disable.thermalhal.control=* && property:vendor.thermal.link_ready=1 |
||||
restart vendor.thermal-hal |
||||
|
||||
on property:vendor.disable.thermal.control=1 && property:vendor.thermal.link_ready=1 |
||||
# common |
||||
stop vendor.thermal-engine |
||||
setprop vendor.disable.thermalhal.control 1 |
||||
# sdm845 |
||||
write /dev/thermal/tz-by-name/quiet-therm-adc/mode disabled |
||||
write /dev/thermal/tz-by-name/quiet-therm-monitor/mode disabled |
||||
write /dev/thermal/tz-by-name/fps-therm-adc/mode disabled |
||||
write /dev/thermal/tz-by-name/fps-therm-monitor/mode disabled |
||||
# sdm670 |
||||
write /dev/thermal/tz-by-name/mb-therm-adc/mode disabled |
||||
write /dev/thermal/tz-by-name/mb-therm-monitor/mode disabled |
||||
# sm8150 |
||||
write /dev/thermal/tz-by-name/sdm-therm/mode disabled |
||||
write /dev/thermal/tz-by-name/sdm-therm-monitor/mode disabled |
||||
# sm7150 |
||||
write /dev/thermal/tz-by-name/skin-therm-adc/mode disabled |
||||
write /dev/thermal/tz-by-name/skin-therm-monitor/mode disabled |
||||
# sm7250 |
||||
write /dev/thermal/tz-by-name/skin-therm/emul_temp 25000 |
||||
write /dev/thermal/tz-by-name/skin-therm/mode disabled |
||||
write /dev/thermal/tz-by-name/skin-virt/emul_temp 25000 |
||||
write /dev/thermal/tz-by-name/skin-virt/mode disabled |
||||
write /dev/thermal/tz-by-name/skin-therm-cpu/emul_temp 25000 |
||||
write /dev/thermal/tz-by-name/skin-therm-cpu/mode disabled |
||||
write /dev/thermal/tz-by-name/skin-virt-cpu/emul_temp 25000 |
||||
write /dev/thermal/tz-by-name/skin-virt-cpu/mode disabled |
||||
write /dev/thermal/tz-by-name/skin-therm-monitor/emul_temp 25000 |
||||
write /dev/thermal/tz-by-name/skin-therm-monitor/mode disabled |
||||
write /dev/thermal/tz-by-name/skin-virt-monitor/emul_temp 25000 |
||||
write /dev/thermal/tz-by-name/skin-virt-monitor/mode disabled |
||||
write /dev/thermal/tz-by-name/panel-audio-therm/emul_temp 25000 |
||||
write /dev/thermal/tz-by-name/panel-audio-therm/mode disabled |
||||
write /dev/thermal/tz-by-name/cellular-emergency/emul_temp 25000 |
||||
write /dev/thermal/tz-by-name/cellular-emergency/mode disabled |
||||
write /dev/thermal/tz-by-name/sdm-therm/emul_temp 25000 |
||||
write /dev/thermal/tz-by-name/sdm-therm/mode disabled |
||||
write /dev/thermal/tz-by-name/charger-therm/emul_temp 25000 |
||||
write /dev/thermal/tz-by-name/charger-therm/mode disabled |
||||
# P21 |
||||
write /dev/thermal/tz-by-name/disp_therm/mode disabled |
||||
|
||||
on property:vendor.disable.thermal.control=0 && property:vendor.thermal.link_ready=1 |
||||
# common |
||||
start vendor.thermal-engine |
||||
setprop vendor.disable.thermalhal.control 0 |
||||
# sdm845 |
||||
write /dev/thermal/tz-by-name/quiet-therm-adc/mode enabled |
||||
write /dev/thermal/tz-by-name/quiet-therm-monitor/mode enabled |
||||
write /dev/thermal/tz-by-name/fps-therm-adc/mode enabled |
||||
write /dev/thermal/tz-by-name/fps-therm-monitor/mode enabled |
||||
# sdm670 |
||||
write /dev/thermal/tz-by-name/mb-therm-adc/mode enabled |
||||
write /dev/thermal/tz-by-name/mb-therm-monitor/mode enabled |
||||
# sm8150 |
||||
write /dev/thermal/tz-by-name/sdm-therm/mode enabled |
||||
write /dev/thermal/tz-by-name/sdm-therm-monitor/mode enabled |
||||
# sm7150 |
||||
write /dev/thermal/tz-by-name/skin-therm-adc/mode enabled |
||||
write /dev/thermal/tz-by-name/skin-therm-monitor/mode enabled |
||||
# sm7250 |
||||
write /dev/thermal/tz-by-name/skin-therm/emul_temp 0 |
||||
write /dev/thermal/tz-by-name/skin-therm/mode enabled |
||||
write /dev/thermal/tz-by-name/skin-virt/emul_temp 0 |
||||
write /dev/thermal/tz-by-name/skin-virt/mode enabled |
||||
write /dev/thermal/tz-by-name/skin-therm-cpu/emul_temp 0 |
||||
write /dev/thermal/tz-by-name/skin-therm-cpu/mode enabled |
||||
write /dev/thermal/tz-by-name/skin-virt-cpu/emul_temp 0 |
||||
write /dev/thermal/tz-by-name/skin-virt-cpu/mode enabled |
||||
write /dev/thermal/tz-by-name/skin-therm-monitor/emul_temp 0 |
||||
write /dev/thermal/tz-by-name/skin-therm-monitor/mode enabled |
||||
write /dev/thermal/tz-by-name/skin-virt-monitor/emul_temp 0 |
||||
write /dev/thermal/tz-by-name/skin-virt-monitor/mode enabled |
||||
write /dev/thermal/tz-by-name/panel-audio-therm/emul_temp 0 |
||||
write /dev/thermal/tz-by-name/panel-audio-therm/mode enabled |
||||
write /dev/thermal/tz-by-name/cellular-emergency/emul_temp 0 |
||||
write /dev/thermal/tz-by-name/cellular-emergency/mode enabled |
||||
write /dev/thermal/tz-by-name/sdm-therm/emul_temp 0 |
||||
write /dev/thermal/tz-by-name/sdm-therm/mode enabled |
||||
write /dev/thermal/tz-by-name/charger-therm/emul_temp 0 |
||||
write /dev/thermal/tz-by-name/charger-therm/mode enabled |
||||
# P21 |
||||
write /dev/thermal/tz-by-name/disp_therm/mode enabled |
||||
|
||||
# Toggle BCL control |
||||
on property:vendor.disable.bcl.control=1 |
||||
write /dev/thermal/tz-by-name/soc/mode disabled |
||||
|
||||
on property:vendor.disable.bcl.control=0 |
||||
write /dev/thermal/tz-by-name/soc/mode enabled |
||||
|
||||
# Switch USB port overheat protection |
||||
on property:vendor.disable.usb.overheat.mitigation.control=1 |
||||
write /sys/module/overheat_mitigation/parameters/enable 0 |
||||
write /dev/thermal/tz-by-name/usb_pwr_therm2/emul_temp 25000 |
||||
|
||||
on property:vendor.disable.usb.overheat.mitigation.control=0 |
||||
write /sys/module/overheat_mitigation/parameters/enable 1 |
||||
write /dev/thermal/tz-by-name/usb_pwr_therm2/emul_temp 0 |
@ -0,0 +1,11 @@ |
||||
on boot |
||||
mkdir /dev/thermal 0750 system system |
||||
mkdir /dev/thermal/tz-by-name 0750 system system |
||||
mkdir /dev/thermal/cdev-by-name 0750 system system |
||||
start vendor.thermal.symlinks |
||||
|
||||
service vendor.thermal.symlinks /vendor/bin/thermal_symlinks |
||||
user system |
||||
group system |
||||
oneshot |
||||
disabled |
@ -0,0 +1,51 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
#include <android-base/logging.h> |
||||
#include <android/binder_manager.h> |
||||
#include <android/binder_process.h> |
||||
|
||||
#include "Thermal.h" |
||||
|
||||
constexpr std::string_view kThermalLogTag("pixel-thermal"); |
||||
|
||||
using ::android::OK; |
||||
using ::android::status_t; |
||||
|
||||
// Generated AIDL files:
|
||||
using Thermal = ::aidl::android::hardware::thermal::implementation::Thermal; |
||||
|
||||
#if !defined(THERMAL_INSTANCE_NAME) |
||||
#define THERMAL_INSTANCE_NAME "default" |
||||
#endif |
||||
|
||||
int main(int /* argc */, char ** /* argv */) { |
||||
android::base::SetDefaultTag(kThermalLogTag.data()); |
||||
|
||||
auto svc = ndk::SharedRefBase::make<Thermal>(); |
||||
const auto svcName = std::string() + svc->descriptor + "/" + THERMAL_INSTANCE_NAME; |
||||
LOG(INFO) << "Pixel Thermal AIDL Service starting..." + svcName; |
||||
ABinderProcess_setThreadPoolMaxThreadCount(0); |
||||
|
||||
auto svcBinder = svc->asBinder(); |
||||
binder_status_t status = AServiceManager_addService(svcBinder.get(), svcName.c_str()); |
||||
if (status != STATUS_OK) { |
||||
LOG(ERROR) << "Pixel Thermal AIDL Service failed to start: " << status << "."; |
||||
return EXIT_FAILURE; |
||||
} |
||||
LOG(INFO) << "Pixel Thermal HAL AIDL Service started."; |
||||
ABinderProcess_joinThreadPool(); |
||||
return EXIT_FAILURE; // should not reach
|
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,196 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <aidl/android/hardware/thermal/IThermal.h> |
||||
|
||||
#include <array> |
||||
#include <chrono> |
||||
#include <map> |
||||
#include <mutex> |
||||
#include <shared_mutex> |
||||
#include <string> |
||||
#include <string_view> |
||||
#include <thread> |
||||
#include <unordered_map> |
||||
#include <vector> |
||||
|
||||
#include "utils/power_files.h" |
||||
#include "utils/powerhal_helper.h" |
||||
#include "utils/thermal_files.h" |
||||
#include "utils/thermal_info.h" |
||||
#include "utils/thermal_stats_helper.h" |
||||
#include "utils/thermal_throttling.h" |
||||
#include "utils/thermal_watcher.h" |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
using ::android::sp; |
||||
|
||||
using NotificationCallback = std::function<void(const Temperature &t)>; |
||||
|
||||
// Get thermal_zone type
|
||||
bool getThermalZoneTypeById(int tz_id, std::string *); |
||||
|
||||
struct ThermalSample { |
||||
float temp; |
||||
boot_clock::time_point timestamp; |
||||
}; |
||||
|
||||
struct EmulSetting { |
||||
float emul_temp; |
||||
int emul_severity; |
||||
bool pending_update; |
||||
}; |
||||
|
||||
struct SensorStatus { |
||||
ThrottlingSeverity severity; |
||||
ThrottlingSeverity prev_hot_severity; |
||||
ThrottlingSeverity prev_cold_severity; |
||||
ThrottlingSeverity prev_hint_severity; |
||||
boot_clock::time_point last_update_time; |
||||
ThermalSample thermal_cached; |
||||
std::unique_ptr<EmulSetting> emul_setting; |
||||
}; |
||||
|
||||
class ThermalHelper { |
||||
public: |
||||
explicit ThermalHelper(const NotificationCallback &cb); |
||||
~ThermalHelper() = default; |
||||
|
||||
bool fillCurrentTemperatures(bool filterType, bool filterCallback, TemperatureType type, |
||||
std::vector<Temperature> *temperatures); |
||||
bool fillTemperatureThresholds(bool filterType, TemperatureType type, |
||||
std::vector<TemperatureThreshold> *thresholds) const; |
||||
bool fillCurrentCoolingDevices(bool filterType, CoolingType type, |
||||
std::vector<CoolingDevice> *coolingdevices) const; |
||||
bool emulTemp(std::string_view target_sensor, const float temp); |
||||
bool emulSeverity(std::string_view target_sensor, const int severity); |
||||
bool emulClear(std::string_view target_sensor); |
||||
|
||||
// Disallow copy and assign.
|
||||
ThermalHelper(const ThermalHelper &) = delete; |
||||
void operator=(const ThermalHelper &) = delete; |
||||
|
||||
bool isInitializedOk() const { return is_initialized_; } |
||||
|
||||
// Read the temperature of a single sensor.
|
||||
bool readTemperature(std::string_view sensor_name, Temperature *out); |
||||
bool readTemperature( |
||||
std::string_view sensor_name, Temperature *out, |
||||
std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status = nullptr, |
||||
const bool force_sysfs = false); |
||||
|
||||
bool readTemperatureThreshold(std::string_view sensor_name, TemperatureThreshold *out) const; |
||||
// Read the value of a single cooling device.
|
||||
bool readCoolingDevice(std::string_view cooling_device, CoolingDevice *out) const; |
||||
// Get SensorInfo Map
|
||||
const std::unordered_map<std::string, SensorInfo> &GetSensorInfoMap() const { |
||||
return sensor_info_map_; |
||||
} |
||||
// Get CdevInfo Map
|
||||
const std::unordered_map<std::string, CdevInfo> &GetCdevInfoMap() const { |
||||
return cooling_device_info_map_; |
||||
} |
||||
// Get SensorStatus Map
|
||||
const std::unordered_map<std::string, SensorStatus> &GetSensorStatusMap() const { |
||||
std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_); |
||||
return sensor_status_map_; |
||||
} |
||||
// Get ThermalThrottling Map
|
||||
const std::unordered_map<std::string, ThermalThrottlingStatus> &GetThermalThrottlingStatusMap() |
||||
const { |
||||
return thermal_throttling_.GetThermalThrottlingStatusMap(); |
||||
} |
||||
// Get PowerRailInfo Map
|
||||
const std::unordered_map<std::string, PowerRailInfo> &GetPowerRailInfoMap() const { |
||||
return power_files_.GetPowerRailInfoMap(); |
||||
} |
||||
|
||||
// Get PowerStatus Map
|
||||
const std::unordered_map<std::string, PowerStatus> &GetPowerStatusMap() const { |
||||
return power_files_.GetPowerStatusMap(); |
||||
} |
||||
|
||||
// Get Thermal Stats Sensor Map
|
||||
const std::unordered_map<std::string, SensorTempStats> GetSensorTempStatsSnapshot() { |
||||
return thermal_stats_helper_.GetSensorTempStatsSnapshot(); |
||||
} |
||||
// Get Thermal Stats Sensor, Binded Cdev State Request Map
|
||||
const std::unordered_map<std::string, std::unordered_map<std::string, ThermalStats<int>>> |
||||
GetSensorCoolingDeviceRequestStatsSnapshot() { |
||||
return thermal_stats_helper_.GetSensorCoolingDeviceRequestStatsSnapshot(); |
||||
} |
||||
|
||||
void sendPowerExtHint(const Temperature &t); |
||||
bool isAidlPowerHalExist() { return power_hal_service_.isAidlPowerHalExist(); } |
||||
bool isPowerHalConnected() { return power_hal_service_.isPowerHalConnected(); } |
||||
bool isPowerHalExtConnected() { return power_hal_service_.isPowerHalExtConnected(); } |
||||
|
||||
private: |
||||
bool initializeSensorMap(const std::unordered_map<std::string, std::string> &path_map); |
||||
bool initializeCoolingDevices(const std::unordered_map<std::string, std::string> &path_map); |
||||
bool isSubSensorValid(std::string_view sensor_data, const SensorFusionType sensor_fusion_type); |
||||
void setMinTimeout(SensorInfo *sensor_info); |
||||
void initializeTrip(const std::unordered_map<std::string, std::string> &path_map, |
||||
std::set<std::string> *monitored_sensors, bool thermal_genl_enabled); |
||||
void clearAllThrottling(); |
||||
// For thermal_watcher_'s polling thread, return the sleep interval
|
||||
std::chrono::milliseconds thermalWatcherCallbackFunc( |
||||
const std::set<std::string> &uevent_sensors); |
||||
// Return hot and cold severity status as std::pair
|
||||
std::pair<ThrottlingSeverity, ThrottlingSeverity> getSeverityFromThresholds( |
||||
const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds, |
||||
const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis, |
||||
ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity, |
||||
float value) const; |
||||
// Read sensor data according to the type
|
||||
bool readDataByType(std::string_view sensor_data, float *reading_value, |
||||
const SensorFusionType type, const bool force_no_cache, |
||||
std::map<std::string, float> *sensor_log_map); |
||||
// Read temperature data according to thermal sensor's info
|
||||
bool readThermalSensor(std::string_view sensor_name, float *temp, const bool force_sysfs, |
||||
std::map<std::string, float> *sensor_log_map); |
||||
bool connectToPowerHal(); |
||||
void updateSupportedPowerHints(); |
||||
void updateCoolingDevices(const std::vector<std::string> &cooling_devices_to_update); |
||||
sp<ThermalWatcher> thermal_watcher_; |
||||
PowerFiles power_files_; |
||||
ThermalFiles thermal_sensors_; |
||||
ThermalFiles cooling_devices_; |
||||
ThermalThrottling thermal_throttling_; |
||||
bool is_initialized_; |
||||
const NotificationCallback cb_; |
||||
std::unordered_map<std::string, CdevInfo> cooling_device_info_map_; |
||||
std::unordered_map<std::string, SensorInfo> sensor_info_map_; |
||||
std::unordered_map<std::string, std::unordered_map<ThrottlingSeverity, ThrottlingSeverity>> |
||||
supported_powerhint_map_; |
||||
PowerHalService power_hal_service_; |
||||
ThermalStatsHelper thermal_stats_helper_; |
||||
mutable std::shared_mutex sensor_status_map_mutex_; |
||||
std::unordered_map<std::string, SensorStatus> sensor_status_map_; |
||||
}; |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,219 @@ |
||||
{ |
||||
"definitions":{ |
||||
|
||||
}, |
||||
"$schema":"http://json-schema.org/draft-07/schema#", |
||||
"$id":"http://example.com/root.json", |
||||
"type":"object", |
||||
"title":"The Root Schema", |
||||
"required":[ |
||||
"Sensors" |
||||
], |
||||
"properties":{ |
||||
"Sensors":{ |
||||
"$id":"#/properties/Sensors", |
||||
"type":"array", |
||||
"title":"The Sensors Schema", |
||||
"items":{ |
||||
"$id":"#/properties/Sensors/items", |
||||
"type":"object", |
||||
"title":"The Items Schema", |
||||
"required":[ |
||||
"Name", |
||||
"Type", |
||||
"HotThreshold", |
||||
"VrThreshold", |
||||
"Multiplier" |
||||
], |
||||
"properties":{ |
||||
"Name":{ |
||||
"$id":"#/properties/Sensors/items/properties/Name", |
||||
"type":"string", |
||||
"title":"The Name Schema", |
||||
"default":"", |
||||
"examples":[ |
||||
"cpu0-silver-usr" |
||||
], |
||||
"pattern":"^(.+)$" |
||||
}, |
||||
"Type":{ |
||||
"$id":"#/properties/Sensors/items/properties/Type", |
||||
"type":"string", |
||||
"title":"The Type Schema", |
||||
"default":"", |
||||
"examples":[ |
||||
"CPU" |
||||
], |
||||
"pattern":"^(.+)$" |
||||
}, |
||||
"HotThreshold":{ |
||||
"$id":"#/properties/Sensors/items/properties/HotThreshold", |
||||
"type":"array", |
||||
"title":"The hot threshold Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN", |
||||
"default":"NAN", |
||||
"maxItems":7, |
||||
"minItems":7, |
||||
"items":{ |
||||
"$id":"#/properties/Sensors/items/properties/HotThreshold/items", |
||||
"type":[ |
||||
"string", |
||||
"number" |
||||
], |
||||
"title":"The Items Schema", |
||||
"default":"", |
||||
"examples":[ |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
95, |
||||
"NAN", |
||||
"NAN", |
||||
125 |
||||
], |
||||
"pattern":"^([-+]?[0-9]*\\.?[0-9]+|NAN)$" |
||||
} |
||||
}, |
||||
"HotHysteresis":{ |
||||
"$id":"#/properties/Sensors/items/properties/HotHysteresis", |
||||
"type":"array", |
||||
"title":"The hot hysteresis Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN. Throttling status will be cleared HotThreshold - HotHysteresis.", |
||||
"default":null, |
||||
"maxItems":7, |
||||
"minItems":7, |
||||
"items":{ |
||||
"$id":"#/properties/Sensors/items/properties/HotHysteresis/items", |
||||
"type":[ |
||||
"number" |
||||
], |
||||
"title":"The Items Schema", |
||||
"default":0.0, |
||||
"examples":[ |
||||
0.0, |
||||
0.0, |
||||
0.0, |
||||
1.0, |
||||
1.5, |
||||
1.0, |
||||
2.0 |
||||
] |
||||
} |
||||
}, |
||||
"ColdThreshold":{ |
||||
"$id":"#/properties/Sensors/items/properties/ColdThreshold", |
||||
"type":"array", |
||||
"title":"The cold threshold Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN, default to NAN", |
||||
"default":null, |
||||
"maxItems":7, |
||||
"minItems":7, |
||||
"items":{ |
||||
"$id":"#/properties/Sensors/items/properties/ColdThreshold/items", |
||||
"type":"string", |
||||
"title":"The Items Schema", |
||||
"default":"NAN", |
||||
"examples":[ |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"NAN" |
||||
], |
||||
"pattern":"^([-+]?[0-9]*\\.?[0-9]+|NAN)$" |
||||
} |
||||
}, |
||||
"ColdHysteresis":{ |
||||
"$id":"#/properties/Sensors/items/properties/ColdHysteresis", |
||||
"type":"array", |
||||
"title":"The cold hysteresis Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN. Throttling status will be cleared ColdThreshold + ColdHysteresis.", |
||||
"default":null, |
||||
"maxItems":7, |
||||
"minItems":7, |
||||
"items":{ |
||||
"$id":"#/properties/Sensors/items/properties/ColdHysteresis/items", |
||||
"type":[ |
||||
"number" |
||||
], |
||||
"title":"The Items Schema", |
||||
"default":0.0, |
||||
"examples":[ |
||||
0.0, |
||||
0.0, |
||||
0.0, |
||||
1.0, |
||||
1.5, |
||||
1.0, |
||||
2.0 |
||||
] |
||||
} |
||||
}, |
||||
"VrThreshold":{ |
||||
"$id":"#/properties/Sensors/items/properties/VrThreshold", |
||||
"type":"string", |
||||
"title":"The Vrthreshold Schema", |
||||
"default":"", |
||||
"examples":[ |
||||
"NAN" |
||||
], |
||||
"pattern":"^(.*)$" |
||||
}, |
||||
"Multiplier":{ |
||||
"$id":"#/properties/Sensors/items/properties/Multiplier", |
||||
"type":"number", |
||||
"title":"The Multiplier Schema", |
||||
"default":0.001, |
||||
"examples":[ |
||||
0.001 |
||||
], |
||||
"exclusiveMinimum":0.0 |
||||
}, |
||||
"Monitor":{ |
||||
"$id":"#/properties/Sensors/items/properties/Monitor", |
||||
"type":"boolean", |
||||
"title":"The Monitor Schema, if the sensor will be monitored and used to trigger throttling event", |
||||
"default":false, |
||||
"examples":[ |
||||
true |
||||
] |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"CoolingDevices":{ |
||||
"$id":"#/properties/CoolingDevices", |
||||
"type":"array", |
||||
"title":"The Coolingdevices Schema", |
||||
"items":{ |
||||
"$id":"#/properties/CoolingDevices/items", |
||||
"type":"object", |
||||
"title":"The Items Schema", |
||||
"required":[ |
||||
"Name", |
||||
"Type" |
||||
], |
||||
"properties":{ |
||||
"Name":{ |
||||
"$id":"#/properties/CoolingDevices/items/properties/Name", |
||||
"type":"string", |
||||
"title":"The Name Schema", |
||||
"default":"", |
||||
"examples":[ |
||||
"thermal-cpufreq-0" |
||||
], |
||||
"pattern":"^(.+)$" |
||||
}, |
||||
"Type":{ |
||||
"$id":"#/properties/CoolingDevices/items/properties/Type", |
||||
"type":"string", |
||||
"title":"The Type Schema", |
||||
"default":"", |
||||
"examples":[ |
||||
"CPU" |
||||
], |
||||
"pattern":"^(.+)$" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,342 @@ |
||||
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#define ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL) |
||||
|
||||
#include "power_files.h" |
||||
|
||||
#include <android-base/file.h> |
||||
#include <android-base/logging.h> |
||||
#include <android-base/stringprintf.h> |
||||
#include <android-base/strings.h> |
||||
#include <dirent.h> |
||||
#include <utils/Trace.h> |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
constexpr std::string_view kDeviceType("iio:device"); |
||||
constexpr std::string_view kIioRootDir("/sys/bus/iio/devices"); |
||||
constexpr std::string_view kEnergyValueNode("energy_value"); |
||||
|
||||
using ::android::base::ReadFileToString; |
||||
using ::android::base::StringPrintf; |
||||
|
||||
bool PowerFiles::registerPowerRailsToWatch(const Json::Value &config) { |
||||
if (!ParsePowerRailInfo(config, &power_rail_info_map_)) { |
||||
LOG(ERROR) << "Failed to parse power rail info config"; |
||||
return false; |
||||
} |
||||
|
||||
if (!power_rail_info_map_.size()) { |
||||
LOG(INFO) << " No power rail info config found"; |
||||
return true; |
||||
} |
||||
|
||||
if (!findEnergySourceToWatch()) { |
||||
LOG(ERROR) << "Cannot find energy source"; |
||||
return false; |
||||
} |
||||
|
||||
if (!energy_info_map_.size() && !updateEnergyValues()) { |
||||
LOG(ERROR) << "Faield to update energy info"; |
||||
return false; |
||||
} |
||||
|
||||
for (const auto &power_rail_info_pair : power_rail_info_map_) { |
||||
std::vector<std::queue<PowerSample>> power_history; |
||||
if (!power_rail_info_pair.second.power_sample_count || |
||||
power_rail_info_pair.second.power_sample_delay == std::chrono::milliseconds::max()) { |
||||
continue; |
||||
} |
||||
|
||||
PowerSample power_sample = { |
||||
.energy_counter = 0, |
||||
.duration = 0, |
||||
}; |
||||
|
||||
if (power_rail_info_pair.second.virtual_power_rail_info != nullptr && |
||||
power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size()) { |
||||
for (size_t i = 0; |
||||
i < power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size(); |
||||
++i) { |
||||
if (!energy_info_map_.count(power_rail_info_pair.second.virtual_power_rail_info |
||||
->linked_power_rails[i])) { |
||||
LOG(ERROR) << " Could not find energy source " |
||||
<< power_rail_info_pair.second.virtual_power_rail_info |
||||
->linked_power_rails[i]; |
||||
return false; |
||||
} |
||||
power_history.emplace_back(std::queue<PowerSample>()); |
||||
for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) { |
||||
power_history[i].emplace(power_sample); |
||||
} |
||||
} |
||||
} else { |
||||
if (energy_info_map_.count(power_rail_info_pair.first)) { |
||||
power_history.emplace_back(std::queue<PowerSample>()); |
||||
for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) { |
||||
power_history[0].emplace(power_sample); |
||||
} |
||||
} else { |
||||
LOG(ERROR) << "Could not find energy source " << power_rail_info_pair.first; |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
if (power_history.size()) { |
||||
power_status_map_[power_rail_info_pair.first] = { |
||||
.power_history = power_history, |
||||
.last_update_time = boot_clock::time_point::min(), |
||||
.last_updated_avg_power = NAN, |
||||
}; |
||||
} else { |
||||
LOG(ERROR) << "power history size is zero"; |
||||
return false; |
||||
} |
||||
LOG(INFO) << "Successfully to register power rail " << power_rail_info_pair.first; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
bool PowerFiles::findEnergySourceToWatch(void) { |
||||
std::string devicePath; |
||||
|
||||
if (energy_path_set_.size()) { |
||||
return true; |
||||
} |
||||
|
||||
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kIioRootDir.data()), closedir); |
||||
if (!dir) { |
||||
PLOG(ERROR) << "Error opening directory" << kIioRootDir; |
||||
return false; |
||||
} |
||||
|
||||
// Find any iio:devices that support energy_value
|
||||
while (struct dirent *ent = readdir(dir.get())) { |
||||
std::string devTypeDir = ent->d_name; |
||||
if (devTypeDir.find(kDeviceType) != std::string::npos) { |
||||
devicePath = StringPrintf("%s/%s", kIioRootDir.data(), devTypeDir.data()); |
||||
std::string deviceEnergyContent; |
||||
|
||||
if (!ReadFileToString(StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()), |
||||
&deviceEnergyContent)) { |
||||
} else if (deviceEnergyContent.size()) { |
||||
energy_path_set_.emplace( |
||||
StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data())); |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (!energy_path_set_.size()) { |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool PowerFiles::updateEnergyValues(void) { |
||||
std::string deviceEnergyContent; |
||||
std::string deviceEnergyContents; |
||||
std::string line; |
||||
|
||||
ATRACE_CALL(); |
||||
for (const auto &path : energy_path_set_) { |
||||
if (!::android::base::ReadFileToString(path, &deviceEnergyContent)) { |
||||
LOG(ERROR) << "Failed to read energy content from " << path; |
||||
return false; |
||||
} else { |
||||
deviceEnergyContents.append(deviceEnergyContent); |
||||
} |
||||
} |
||||
|
||||
std::istringstream energyData(deviceEnergyContents); |
||||
|
||||
while (std::getline(energyData, line)) { |
||||
/* Read rail energy */ |
||||
uint64_t energy_counter = 0; |
||||
uint64_t duration = 0; |
||||
|
||||
/* Format example: CH3(T=358356)[S2M_VDD_CPUCL2], 761330 */ |
||||
auto start_pos = line.find("T="); |
||||
auto end_pos = line.find(')'); |
||||
if (start_pos != std::string::npos) { |
||||
duration = |
||||
strtoul(line.substr(start_pos + 2, end_pos - start_pos - 2).c_str(), NULL, 10); |
||||
} else { |
||||
continue; |
||||
} |
||||
|
||||
start_pos = line.find(")["); |
||||
end_pos = line.find(']'); |
||||
std::string railName; |
||||
if (start_pos != std::string::npos) { |
||||
railName = line.substr(start_pos + 2, end_pos - start_pos - 2); |
||||
} else { |
||||
continue; |
||||
} |
||||
|
||||
start_pos = line.find("],"); |
||||
if (start_pos != std::string::npos) { |
||||
energy_counter = strtoul(line.substr(start_pos + 2).c_str(), NULL, 10); |
||||
} else { |
||||
continue; |
||||
} |
||||
|
||||
energy_info_map_[railName] = { |
||||
.energy_counter = energy_counter, |
||||
.duration = duration, |
||||
}; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
float PowerFiles::updateAveragePower(std::string_view power_rail, |
||||
std::queue<PowerSample> *power_history) { |
||||
float avg_power = NAN; |
||||
|
||||
if (!energy_info_map_.count(power_rail.data())) { |
||||
LOG(ERROR) << " Could not find power rail " << power_rail.data(); |
||||
return avg_power; |
||||
} |
||||
const auto last_sample = power_history->front(); |
||||
const auto curr_sample = energy_info_map_.at(power_rail.data()); |
||||
const auto duration = curr_sample.duration - last_sample.duration; |
||||
const auto deltaEnergy = curr_sample.energy_counter - last_sample.energy_counter; |
||||
|
||||
if (!last_sample.duration) { |
||||
LOG(VERBOSE) << "Power rail " << power_rail.data() |
||||
<< ": all power samples have not been collected yet"; |
||||
} else if (duration <= 0 || deltaEnergy < 0) { |
||||
LOG(ERROR) << "Power rail " << power_rail.data() << " is invalid: duration = " << duration |
||||
<< ", deltaEnergy = " << deltaEnergy; |
||||
|
||||
return avg_power; |
||||
} else { |
||||
avg_power = static_cast<float>(deltaEnergy) / static_cast<float>(duration); |
||||
LOG(VERBOSE) << "Power rail " << power_rail.data() << ", avg power = " << avg_power |
||||
<< ", duration = " << duration << ", deltaEnergy = " << deltaEnergy; |
||||
} |
||||
|
||||
power_history->pop(); |
||||
power_history->push(curr_sample); |
||||
|
||||
return avg_power; |
||||
} |
||||
|
||||
float PowerFiles::updatePowerRail(std::string_view power_rail) { |
||||
float avg_power = NAN; |
||||
|
||||
if (!power_rail_info_map_.count(power_rail.data())) { |
||||
return avg_power; |
||||
} |
||||
|
||||
if (!power_status_map_.count(power_rail.data())) { |
||||
return avg_power; |
||||
} |
||||
|
||||
const auto &power_rail_info = power_rail_info_map_.at(power_rail.data()); |
||||
auto &power_status = power_status_map_.at(power_rail.data()); |
||||
|
||||
boot_clock::time_point now = boot_clock::now(); |
||||
auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>( |
||||
now - power_status.last_update_time); |
||||
|
||||
if (power_status.last_update_time != boot_clock::time_point::min() && |
||||
time_elapsed_ms < power_rail_info.power_sample_delay) { |
||||
return power_status.last_updated_avg_power; |
||||
} |
||||
|
||||
if (!energy_info_map_.size() && !updateEnergyValues()) { |
||||
LOG(ERROR) << "Failed to update energy values"; |
||||
return avg_power; |
||||
} |
||||
|
||||
if (power_rail_info.virtual_power_rail_info == nullptr) { |
||||
avg_power = updateAveragePower(power_rail, &power_status.power_history[0]); |
||||
} else { |
||||
const auto offset = power_rail_info.virtual_power_rail_info->offset; |
||||
float avg_power_val = 0.0; |
||||
for (size_t i = 0; i < power_rail_info.virtual_power_rail_info->linked_power_rails.size(); |
||||
i++) { |
||||
float coefficient = power_rail_info.virtual_power_rail_info->coefficients[i]; |
||||
float avg_power_number = updateAveragePower( |
||||
power_rail_info.virtual_power_rail_info->linked_power_rails[i], |
||||
&power_status.power_history[i]); |
||||
|
||||
switch (power_rail_info.virtual_power_rail_info->formula) { |
||||
case FormulaOption::COUNT_THRESHOLD: |
||||
if ((coefficient < 0 && avg_power_number < -coefficient) || |
||||
(coefficient >= 0 && avg_power_number >= coefficient)) |
||||
avg_power_val += 1; |
||||
break; |
||||
case FormulaOption::WEIGHTED_AVG: |
||||
avg_power_val += avg_power_number * coefficient; |
||||
break; |
||||
case FormulaOption::MAXIMUM: |
||||
if (i == 0) |
||||
avg_power_val = std::numeric_limits<float>::lowest(); |
||||
if (avg_power_number * coefficient > avg_power_val) |
||||
avg_power_val = avg_power_number * coefficient; |
||||
break; |
||||
case FormulaOption::MINIMUM: |
||||
if (i == 0) |
||||
avg_power_val = std::numeric_limits<float>::max(); |
||||
if (avg_power_number * coefficient < avg_power_val) |
||||
avg_power_val = avg_power_number * coefficient; |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
if (avg_power_val >= 0) { |
||||
avg_power_val = avg_power_val + offset; |
||||
} |
||||
|
||||
avg_power = avg_power_val; |
||||
} |
||||
|
||||
if (avg_power < 0) { |
||||
avg_power = NAN; |
||||
} |
||||
|
||||
power_status.last_updated_avg_power = avg_power; |
||||
power_status.last_update_time = now; |
||||
return avg_power; |
||||
} |
||||
|
||||
bool PowerFiles::refreshPowerStatus(void) { |
||||
if (!updateEnergyValues()) { |
||||
LOG(ERROR) << "Failed to update energy values"; |
||||
return false; |
||||
} |
||||
|
||||
for (const auto &power_status_pair : power_status_map_) { |
||||
updatePowerRail(power_status_pair.first); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,95 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <android-base/chrono_utils.h> |
||||
|
||||
#include <chrono> |
||||
#include <queue> |
||||
#include <shared_mutex> |
||||
#include <string> |
||||
#include <unordered_map> |
||||
#include <unordered_set> |
||||
|
||||
#include "thermal_info.h" |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
using ::android::base::boot_clock; |
||||
|
||||
struct PowerSample { |
||||
uint64_t energy_counter; |
||||
uint64_t duration; |
||||
}; |
||||
|
||||
struct PowerStatus { |
||||
boot_clock::time_point last_update_time; |
||||
// A vector to record the queues of power sample history.
|
||||
std::vector<std::queue<PowerSample>> power_history; |
||||
float last_updated_avg_power; |
||||
}; |
||||
|
||||
// A helper class for monitoring power rails.
|
||||
class PowerFiles { |
||||
public: |
||||
PowerFiles() = default; |
||||
~PowerFiles() = default; |
||||
// Disallow copy and assign.
|
||||
PowerFiles(const PowerFiles &) = delete; |
||||
void operator=(const PowerFiles &) = delete; |
||||
bool registerPowerRailsToWatch(const Json::Value &config); |
||||
// Update the power data from ODPM sysfs
|
||||
bool refreshPowerStatus(void); |
||||
// Get power status map
|
||||
const std::unordered_map<std::string, PowerStatus> &GetPowerStatusMap() const { |
||||
std::shared_lock<std::shared_mutex> _lock(power_status_map_mutex_); |
||||
return power_status_map_; |
||||
} |
||||
// Get power rail info map
|
||||
const std::unordered_map<std::string, PowerRailInfo> &GetPowerRailInfoMap() const { |
||||
return power_rail_info_map_; |
||||
} |
||||
|
||||
private: |
||||
// Update energy value to energy_info_map_, return false if the value is failed to update.
|
||||
bool updateEnergyValues(void); |
||||
// Compute the average power for physical power rail.
|
||||
float updateAveragePower(std::string_view power_rail, std::queue<PowerSample> *power_history); |
||||
// Update the power data for the target power rail.
|
||||
float updatePowerRail(std::string_view power_rail); |
||||
// Find the energy source path, return false if no energy source found.
|
||||
bool findEnergySourceToWatch(void); |
||||
// The map to record the energy counter for each power rail.
|
||||
std::unordered_map<std::string, PowerSample> energy_info_map_; |
||||
// The map to record the power data for each thermal sensor.
|
||||
std::unordered_map<std::string, PowerStatus> power_status_map_; |
||||
mutable std::shared_mutex power_status_map_mutex_; |
||||
// The map to record the power rail information from thermal config
|
||||
std::unordered_map<std::string, PowerRailInfo> power_rail_info_map_; |
||||
// The set to store the energy source paths
|
||||
std::unordered_set<std::string> energy_path_set_; |
||||
}; |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,138 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#include "powerhal_helper.h" |
||||
|
||||
#include <android-base/file.h> |
||||
#include <android-base/logging.h> |
||||
#include <android-base/properties.h> |
||||
#include <android-base/stringprintf.h> |
||||
#include <android-base/strings.h> |
||||
#include <android/binder_manager.h> |
||||
|
||||
#include <iterator> |
||||
#include <set> |
||||
#include <sstream> |
||||
#include <thread> |
||||
#include <vector> |
||||
|
||||
#include "thermal_info.h" |
||||
#include "thermal_throttling.h" |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
using ::android::base::StringPrintf; |
||||
|
||||
PowerHalService::PowerHalService() |
||||
: power_hal_aidl_exist_(true), power_hal_aidl_(nullptr), power_hal_ext_aidl_(nullptr) { |
||||
connect(); |
||||
} |
||||
|
||||
bool PowerHalService::connect() { |
||||
std::lock_guard<std::mutex> lock(lock_); |
||||
|
||||
if (!power_hal_aidl_exist_) { |
||||
return false; |
||||
} |
||||
|
||||
if (power_hal_aidl_ && power_hal_ext_aidl_) { |
||||
return true; |
||||
} |
||||
|
||||
const std::string kInstance = std::string(IPower::descriptor) + "/default"; |
||||
ndk::SpAIBinder power_binder = ndk::SpAIBinder(AServiceManager_getService(kInstance.c_str())); |
||||
ndk::SpAIBinder ext_power_binder; |
||||
|
||||
if (power_binder.get() == nullptr) { |
||||
LOG(ERROR) << "Cannot get Power Hal Binder"; |
||||
power_hal_aidl_exist_ = false; |
||||
return false; |
||||
} |
||||
|
||||
power_hal_aidl_ = IPower::fromBinder(power_binder); |
||||
|
||||
if (power_hal_aidl_ == nullptr) { |
||||
power_hal_aidl_exist_ = false; |
||||
LOG(ERROR) << "Cannot get Power Hal AIDL" << kInstance.c_str(); |
||||
return false; |
||||
} |
||||
|
||||
if (STATUS_OK != AIBinder_getExtension(power_binder.get(), ext_power_binder.getR()) || |
||||
ext_power_binder.get() == nullptr) { |
||||
LOG(ERROR) << "Cannot get Power Hal Extension Binder"; |
||||
power_hal_aidl_exist_ = false; |
||||
return false; |
||||
} |
||||
|
||||
power_hal_ext_aidl_ = IPowerExt::fromBinder(ext_power_binder); |
||||
if (power_hal_ext_aidl_ == nullptr) { |
||||
LOG(ERROR) << "Cannot get Power Hal Extension AIDL"; |
||||
power_hal_aidl_exist_ = false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool PowerHalService::isModeSupported(const std::string &type, const ThrottlingSeverity &t) { |
||||
bool isSupported = false; |
||||
if (!connect()) { |
||||
return false; |
||||
} |
||||
std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str()); |
||||
lock_.lock(); |
||||
if (!power_hal_ext_aidl_->isModeSupported(power_hint, &isSupported).isOk()) { |
||||
LOG(ERROR) << "Fail to check supported mode, Hint: " << power_hint; |
||||
power_hal_ext_aidl_ = nullptr; |
||||
power_hal_aidl_ = nullptr; |
||||
lock_.unlock(); |
||||
return false; |
||||
} |
||||
lock_.unlock(); |
||||
return isSupported; |
||||
} |
||||
|
||||
void PowerHalService::setMode(const std::string &type, const ThrottlingSeverity &t, |
||||
const bool &enable, const bool error_on_exit) { |
||||
if (!connect()) { |
||||
return; |
||||
} |
||||
|
||||
std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str()); |
||||
LOG(INFO) << (error_on_exit ? "Resend Hint " : "Send Hint ") << power_hint |
||||
<< " Enable: " << std::boolalpha << enable; |
||||
lock_.lock(); |
||||
if (!power_hal_ext_aidl_->setMode(power_hint, enable).isOk()) { |
||||
LOG(ERROR) << "Fail to set mode, Hint: " << power_hint; |
||||
power_hal_ext_aidl_ = nullptr; |
||||
power_hal_aidl_ = nullptr; |
||||
lock_.unlock(); |
||||
if (!error_on_exit) { |
||||
setMode(type, t, enable, true); |
||||
} |
||||
return; |
||||
} |
||||
lock_.unlock(); |
||||
} |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,63 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <aidl/android/hardware/power/IPower.h> |
||||
#include <aidl/android/hardware/thermal/ThrottlingSeverity.h> |
||||
#include <aidl/google/hardware/power/extension/pixel/IPowerExt.h> |
||||
|
||||
#include <queue> |
||||
#include <shared_mutex> |
||||
#include <string> |
||||
#include <unordered_map> |
||||
#include <unordered_set> |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
using ::aidl::android::hardware::power::IPower; |
||||
using ::aidl::google::hardware::power::extension::pixel::IPowerExt; |
||||
|
||||
using CdevRequestStatus = std::unordered_map<std::string, int>; |
||||
|
||||
class PowerHalService { |
||||
public: |
||||
PowerHalService(); |
||||
~PowerHalService() = default; |
||||
bool connect(); |
||||
bool isAidlPowerHalExist() { return power_hal_aidl_exist_; } |
||||
bool isModeSupported(const std::string &type, const ThrottlingSeverity &t); |
||||
bool isPowerHalConnected() { return power_hal_aidl_ != nullptr; } |
||||
bool isPowerHalExtConnected() { return power_hal_ext_aidl_ != nullptr; } |
||||
void setMode(const std::string &type, const ThrottlingSeverity &t, const bool &enable, |
||||
const bool error_on_exit = false); |
||||
|
||||
private: |
||||
bool power_hal_aidl_exist_; |
||||
std::shared_ptr<IPower> power_hal_aidl_; |
||||
std::shared_ptr<IPowerExt> power_hal_ext_aidl_; |
||||
std::mutex lock_; |
||||
}; |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,88 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#define ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL) |
||||
|
||||
#include "thermal_files.h" |
||||
|
||||
#include <android-base/file.h> |
||||
#include <android-base/logging.h> |
||||
#include <android-base/stringprintf.h> |
||||
#include <android-base/strings.h> |
||||
#include <utils/Trace.h> |
||||
|
||||
#include <algorithm> |
||||
#include <string_view> |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
using ::android::base::StringPrintf; |
||||
|
||||
std::string ThermalFiles::getThermalFilePath(std::string_view thermal_name) const { |
||||
auto sensor_itr = thermal_name_to_path_map_.find(thermal_name.data()); |
||||
if (sensor_itr == thermal_name_to_path_map_.end()) { |
||||
return ""; |
||||
} |
||||
return sensor_itr->second; |
||||
} |
||||
|
||||
bool ThermalFiles::addThermalFile(std::string_view thermal_name, std::string_view path) { |
||||
return thermal_name_to_path_map_.emplace(thermal_name, path).second; |
||||
} |
||||
|
||||
bool ThermalFiles::readThermalFile(std::string_view thermal_name, std::string *data) const { |
||||
std::string sensor_reading; |
||||
std::string file_path = getThermalFilePath(std::string_view(thermal_name)); |
||||
*data = ""; |
||||
|
||||
ATRACE_NAME(StringPrintf("ThermalFiles::readThermalFile - %s", thermal_name.data()).c_str()); |
||||
if (file_path.empty()) { |
||||
PLOG(WARNING) << "Failed to find " << thermal_name << "'s path"; |
||||
return false; |
||||
} |
||||
|
||||
if (!::android::base::ReadFileToString(file_path, &sensor_reading)) { |
||||
PLOG(WARNING) << "Failed to read sensor: " << thermal_name; |
||||
return false; |
||||
} |
||||
|
||||
// Strip the newline.
|
||||
*data = ::android::base::Trim(sensor_reading); |
||||
return true; |
||||
} |
||||
|
||||
bool ThermalFiles::writeCdevFile(std::string_view cdev_name, std::string_view data) { |
||||
std::string file_path = |
||||
getThermalFilePath(::android::base::StringPrintf("%s_%s", cdev_name.data(), "w")); |
||||
|
||||
ATRACE_NAME(StringPrintf("ThermalFiles::writeCdevFile - %s", cdev_name.data()).c_str()); |
||||
if (!::android::base::WriteStringToFile(data.data(), file_path)) { |
||||
PLOG(WARNING) << "Failed to write cdev: " << cdev_name << " to " << data.data(); |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,53 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <string> |
||||
#include <unordered_map> |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
class ThermalFiles { |
||||
public: |
||||
ThermalFiles() = default; |
||||
~ThermalFiles() = default; |
||||
ThermalFiles(const ThermalFiles &) = delete; |
||||
void operator=(const ThermalFiles &) = delete; |
||||
|
||||
std::string getThermalFilePath(std::string_view thermal_name) const; |
||||
// Returns true if add was successful, false otherwise.
|
||||
bool addThermalFile(std::string_view thermal_name, std::string_view path); |
||||
// If thermal_name is not found in the thermal names to path map, this will set
|
||||
// data to empty and return false. If the thermal_name is found and its content
|
||||
// is read, this function will fill in data accordingly then return true.
|
||||
bool readThermalFile(std::string_view thermal_name, std::string *data) const; |
||||
bool writeCdevFile(std::string_view thermal_name, std::string_view data); |
||||
size_t getNumThermalFiles() const { return thermal_name_to_path_map_.size(); } |
||||
|
||||
private: |
||||
std::unordered_map<std::string, std::string> thermal_name_to_path_map_; |
||||
}; |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,205 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <aidl/android/hardware/thermal/CoolingType.h> |
||||
#include <aidl/android/hardware/thermal/TemperatureType.h> |
||||
#include <aidl/android/hardware/thermal/ThrottlingSeverity.h> |
||||
#include <json/value.h> |
||||
|
||||
#include <chrono> |
||||
#include <string> |
||||
#include <unordered_map> |
||||
#include <unordered_set> |
||||
#include <variant> |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
constexpr size_t kThrottlingSeverityCount = |
||||
std::distance(::ndk::enum_range<ThrottlingSeverity>().begin(), |
||||
::ndk::enum_range<ThrottlingSeverity>().end()); |
||||
using ThrottlingArray = std::array<float, static_cast<size_t>(kThrottlingSeverityCount)>; |
||||
using CdevArray = std::array<int, static_cast<size_t>(kThrottlingSeverityCount)>; |
||||
constexpr std::chrono::milliseconds kMinPollIntervalMs = std::chrono::milliseconds(2000); |
||||
constexpr std::chrono::milliseconds kUeventPollTimeoutMs = std::chrono::milliseconds(300000); |
||||
// Max number of time_in_state buckets is 20 in atoms
|
||||
// VendorSensorCoolingDeviceStats, VendorTempResidencyStats
|
||||
constexpr int kMaxStatsResidencyCount = 20; |
||||
constexpr int kMaxStatsThresholdCount = kMaxStatsResidencyCount - 1; |
||||
|
||||
enum FormulaOption : uint32_t { |
||||
COUNT_THRESHOLD = 0, |
||||
WEIGHTED_AVG, |
||||
MAXIMUM, |
||||
MINIMUM, |
||||
}; |
||||
|
||||
template <typename T> |
||||
struct ThresholdList { |
||||
std::optional<std::string> logging_name; |
||||
std::vector<T> thresholds; |
||||
explicit ThresholdList(std::optional<std::string> logging_name, std::vector<T> thresholds) |
||||
: logging_name(logging_name), thresholds(thresholds) {} |
||||
|
||||
ThresholdList() = default; |
||||
ThresholdList(const ThresholdList &) = default; |
||||
ThresholdList &operator=(const ThresholdList &) = default; |
||||
ThresholdList(ThresholdList &&) = default; |
||||
ThresholdList &operator=(ThresholdList &&) = default; |
||||
~ThresholdList() = default; |
||||
}; |
||||
|
||||
template <typename T> |
||||
struct StatsInfo { |
||||
// if bool, record all or none depending on flag
|
||||
// if set, check name present in set
|
||||
std::variant<bool, std::unordered_set<std::string> > |
||||
record_by_default_threshold_all_or_name_set_; |
||||
// map name to list of thresholds
|
||||
std::unordered_map<std::string, std::vector<ThresholdList<T> > > record_by_threshold; |
||||
void clear() { |
||||
record_by_default_threshold_all_or_name_set_ = false; |
||||
record_by_threshold.clear(); |
||||
} |
||||
}; |
||||
|
||||
struct StatsConfig { |
||||
StatsInfo<float> sensor_stats_info; |
||||
StatsInfo<int> cooling_device_request_info; |
||||
void clear() { |
||||
sensor_stats_info.clear(); |
||||
cooling_device_request_info.clear(); |
||||
} |
||||
}; |
||||
|
||||
enum SensorFusionType : uint32_t { |
||||
SENSOR = 0, |
||||
ODPM, |
||||
}; |
||||
|
||||
struct VirtualSensorInfo { |
||||
std::vector<std::string> linked_sensors; |
||||
std::vector<SensorFusionType> linked_sensors_type; |
||||
std::vector<float> coefficients; |
||||
float offset; |
||||
std::vector<std::string> trigger_sensors; |
||||
FormulaOption formula; |
||||
}; |
||||
|
||||
struct VirtualPowerRailInfo { |
||||
std::vector<std::string> linked_power_rails; |
||||
std::vector<float> coefficients; |
||||
float offset; |
||||
FormulaOption formula; |
||||
}; |
||||
|
||||
// The method when the ODPM power is lower than threshold
|
||||
enum ReleaseLogic : uint32_t { |
||||
INCREASE = 0, // Increase throttling by step
|
||||
DECREASE, // Decrease throttling by step
|
||||
STEPWISE, // Support both increase and decrease logix
|
||||
RELEASE_TO_FLOOR, // Release throttling to floor directly
|
||||
NONE, |
||||
}; |
||||
|
||||
struct BindedCdevInfo { |
||||
CdevArray limit_info; |
||||
ThrottlingArray power_thresholds; |
||||
ReleaseLogic release_logic; |
||||
ThrottlingArray cdev_weight_for_pid; |
||||
CdevArray cdev_ceiling; |
||||
int max_release_step; |
||||
int max_throttle_step; |
||||
CdevArray cdev_floor_with_power_link; |
||||
std::string power_rail; |
||||
// The flag for activate release logic when power is higher than power threshold
|
||||
bool high_power_check; |
||||
// The flag for only triggering throttling until all power samples are collected
|
||||
bool throttling_with_power_link; |
||||
}; |
||||
|
||||
struct ThrottlingInfo { |
||||
ThrottlingArray k_po; |
||||
ThrottlingArray k_pu; |
||||
ThrottlingArray k_i; |
||||
ThrottlingArray k_d; |
||||
ThrottlingArray i_max; |
||||
ThrottlingArray max_alloc_power; |
||||
ThrottlingArray min_alloc_power; |
||||
ThrottlingArray s_power; |
||||
ThrottlingArray i_cutoff; |
||||
float i_default; |
||||
int tran_cycle; |
||||
std::unordered_map<std::string, ThrottlingArray> excluded_power_info_map; |
||||
std::unordered_map<std::string, BindedCdevInfo> binded_cdev_info_map; |
||||
}; |
||||
|
||||
struct SensorInfo { |
||||
TemperatureType type; |
||||
ThrottlingArray hot_thresholds; |
||||
ThrottlingArray cold_thresholds; |
||||
ThrottlingArray hot_hysteresis; |
||||
ThrottlingArray cold_hysteresis; |
||||
std::string temp_path; |
||||
float vr_threshold; |
||||
float multiplier; |
||||
std::chrono::milliseconds polling_delay; |
||||
std::chrono::milliseconds passive_delay; |
||||
std::chrono::milliseconds time_resolution; |
||||
bool send_cb; |
||||
bool send_powerhint; |
||||
bool is_watch; |
||||
bool is_hidden; |
||||
std::unique_ptr<VirtualSensorInfo> virtual_sensor_info; |
||||
std::shared_ptr<ThrottlingInfo> throttling_info; |
||||
}; |
||||
|
||||
struct CdevInfo { |
||||
CoolingType type; |
||||
std::string read_path; |
||||
std::string write_path; |
||||
std::vector<float> state2power; |
||||
int max_state; |
||||
}; |
||||
|
||||
struct PowerRailInfo { |
||||
std::string rail; |
||||
int power_sample_count; |
||||
std::chrono::milliseconds power_sample_delay; |
||||
std::unique_ptr<VirtualPowerRailInfo> virtual_power_rail_info; |
||||
}; |
||||
|
||||
bool ParseThermalConfig(std::string_view config_path, Json::Value *config); |
||||
bool ParseSensorInfo(const Json::Value &config, |
||||
std::unordered_map<std::string, SensorInfo> *sensors_parsed); |
||||
bool ParseCoolingDevice(const Json::Value &config, |
||||
std::unordered_map<std::string, CdevInfo> *cooling_device_parsed); |
||||
bool ParsePowerRailInfo(const Json::Value &config, |
||||
std::unordered_map<std::string, PowerRailInfo> *power_rail_parsed); |
||||
bool ParseStatsConfig(const Json::Value &config, |
||||
const std::unordered_map<std::string, SensorInfo> &sensor_info_map_, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_, |
||||
StatsConfig *stats_config); |
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,509 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#include "thermal_stats_helper.h" |
||||
|
||||
#include <android-base/logging.h> |
||||
#include <android/binder_manager.h> |
||||
#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h> |
||||
|
||||
#include <algorithm> |
||||
#include <numeric> |
||||
#include <string_view> |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
constexpr std::string_view kCustomThresholdSetSuffix("-TH-"); |
||||
constexpr std::string_view kCompressedThresholdSuffix("-CMBN-TH"); |
||||
|
||||
using aidl::android::frameworks::stats::VendorAtom; |
||||
namespace PixelAtoms = ::android::hardware::google::pixel::PixelAtoms; |
||||
|
||||
namespace { |
||||
static std::shared_ptr<IStats> stats_client = nullptr; |
||||
std::shared_ptr<IStats> getStatsService() { |
||||
static std::once_flag statsServiceFlag; |
||||
std::call_once(statsServiceFlag, []() { |
||||
const std::string instance = std::string() + IStats::descriptor + "/default"; |
||||
bool isStatsDeclared = AServiceManager_isDeclared(instance.c_str()); |
||||
if (!isStatsDeclared) { |
||||
LOG(ERROR) << "Stats service is not registered."; |
||||
return; |
||||
} |
||||
stats_client = IStats::fromBinder( |
||||
ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str()))); |
||||
}); |
||||
return stats_client; |
||||
} |
||||
|
||||
bool isRecordByDefaultThreshold(const std::variant<bool, std::unordered_set<std::string>> |
||||
&record_by_default_threshold_all_or_name_set_, |
||||
std::string_view name) { |
||||
if (std::holds_alternative<bool>(record_by_default_threshold_all_or_name_set_)) { |
||||
return std::get<bool>(record_by_default_threshold_all_or_name_set_); |
||||
} |
||||
return std::get<std::unordered_set<std::string>>(record_by_default_threshold_all_or_name_set_) |
||||
.count(name.data()); |
||||
} |
||||
|
||||
template <typename T> |
||||
int calculateThresholdBucket(const std::vector<T> &thresholds, T value) { |
||||
if (thresholds.empty()) { |
||||
LOG(VERBOSE) << "No threshold present, so bucket is " << value << " as int."; |
||||
return static_cast<int>(value); |
||||
} |
||||
auto threshold_idx = std::upper_bound(thresholds.begin(), thresholds.end(), value); |
||||
int bucket = (threshold_idx - thresholds.begin()); |
||||
LOG(VERBOSE) << "For value: " << value << " bucket is: " << bucket; |
||||
return bucket; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
bool ThermalStatsHelper::initializeStats( |
||||
const Json::Value &config, |
||||
const std::unordered_map<std::string, SensorInfo> &sensor_info_map_, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_) { |
||||
StatsConfig stats_config; |
||||
if (!ParseStatsConfig(config, sensor_info_map_, cooling_device_info_map_, &stats_config)) { |
||||
LOG(ERROR) << "Failed to parse stats config"; |
||||
return false; |
||||
} |
||||
bool is_initialized_ = |
||||
initializeSensorTempStats(stats_config.sensor_stats_info, sensor_info_map_) && |
||||
initializeSensorCdevRequestStats(stats_config.cooling_device_request_info, |
||||
sensor_info_map_, cooling_device_info_map_); |
||||
if (is_initialized_) { |
||||
last_total_stats_report_time = boot_clock::now(); |
||||
LOG(INFO) << "Thermal Stats Initialized Successfully"; |
||||
} |
||||
return is_initialized_; |
||||
} |
||||
|
||||
bool ThermalStatsHelper::initializeSensorCdevRequestStats( |
||||
const StatsInfo<int> &request_stats_info, |
||||
const std::unordered_map<std::string, SensorInfo> &sensor_info_map_, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_) { |
||||
std::unique_lock<std::shared_mutex> _lock(sensor_cdev_request_stats_map_mutex_); |
||||
for (const auto &[sensor, sensor_info] : sensor_info_map_) { |
||||
for (const auto &binded_cdev_info_pair : |
||||
sensor_info.throttling_info->binded_cdev_info_map) { |
||||
const auto &cdev = binded_cdev_info_pair.first; |
||||
const auto &max_state = |
||||
cooling_device_info_map_.at(binded_cdev_info_pair.first).max_state; |
||||
// Record by all state
|
||||
if (isRecordByDefaultThreshold( |
||||
request_stats_info.record_by_default_threshold_all_or_name_set_, cdev)) { |
||||
// if the number of states is greater / equal(as state starts from 0) than
|
||||
// residency_buckets in atom combine the initial states
|
||||
if (max_state >= kMaxStatsResidencyCount) { |
||||
// buckets = [max_state -kMaxStatsResidencyCount + 1, ...max_state]
|
||||
// idx = [1, .. max_state - (max_state - kMaxStatsResidencyCount + 1) + 1]
|
||||
// idx = [1, .. kMaxStatsResidencyCount]
|
||||
const auto starting_state = max_state - kMaxStatsResidencyCount + 1; |
||||
std::vector<int> thresholds(kMaxStatsResidencyCount); |
||||
std::iota(thresholds.begin(), thresholds.end(), starting_state); |
||||
const auto logging_name = cdev + kCompressedThresholdSuffix.data(); |
||||
ThresholdList<int> threshold_list(logging_name, thresholds); |
||||
sensor_cdev_request_stats_map_[sensor][cdev] |
||||
.stats_by_custom_threshold.emplace_back(threshold_list); |
||||
} else { |
||||
// buckets = [0, 1, 2, 3, ...max_state]
|
||||
const auto default_threshold_time_in_state_size = max_state + 1; |
||||
sensor_cdev_request_stats_map_[sensor][cdev].stats_by_default_threshold = |
||||
StatsRecord(default_threshold_time_in_state_size); |
||||
} |
||||
LOG(INFO) << "Sensor Cdev user vote stats on basis of all state initialized for [" |
||||
<< sensor << "-" << cdev << "]"; |
||||
} |
||||
|
||||
// Record by custom threshold
|
||||
if (request_stats_info.record_by_threshold.count(cdev)) { |
||||
for (const auto &threshold_list : request_stats_info.record_by_threshold.at(cdev)) { |
||||
// check last threshold value(which is >= number of buckets as numbers in
|
||||
// threshold are strictly increasing from 0) is less than max_state
|
||||
if (threshold_list.thresholds.back() >= max_state) { |
||||
LOG(ERROR) << "For sensor " << sensor << " bindedCdev: " << cdev |
||||
<< "Invalid bindedCdev stats threshold: " |
||||
<< threshold_list.thresholds.back() << " >= " << max_state; |
||||
sensor_cdev_request_stats_map_.clear(); |
||||
return false; |
||||
} |
||||
sensor_cdev_request_stats_map_[sensor][cdev] |
||||
.stats_by_custom_threshold.emplace_back(threshold_list); |
||||
LOG(INFO) |
||||
<< "Sensor Cdev user vote stats on basis of threshold initialized for [" |
||||
<< sensor << "-" << cdev << "]"; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
bool ThermalStatsHelper::initializeSensorTempStats( |
||||
const StatsInfo<float> &sensor_stats_info, |
||||
const std::unordered_map<std::string, SensorInfo> &sensor_info_map_) { |
||||
std::unique_lock<std::shared_mutex> _lock(sensor_temp_stats_map_mutex_); |
||||
const int severity_time_in_state_size = kThrottlingSeverityCount; |
||||
for (const auto &[sensor, sensor_info] : sensor_info_map_) { |
||||
// Record by severity
|
||||
if (sensor_info.is_watch && |
||||
isRecordByDefaultThreshold( |
||||
sensor_stats_info.record_by_default_threshold_all_or_name_set_, sensor)) { |
||||
// number of buckets = number of severity
|
||||
sensor_temp_stats_map_[sensor].stats_by_default_threshold = |
||||
StatsRecord(severity_time_in_state_size); |
||||
LOG(INFO) << "Sensor temp stats on basis of severity initialized for [" << sensor |
||||
<< "]"; |
||||
} |
||||
|
||||
// Record by custom threshold
|
||||
if (sensor_stats_info.record_by_threshold.count(sensor)) { |
||||
for (const auto &threshold_list : sensor_stats_info.record_by_threshold.at(sensor)) { |
||||
sensor_temp_stats_map_[sensor].stats_by_custom_threshold.emplace_back( |
||||
threshold_list); |
||||
LOG(INFO) << "Sensor temp stats on basis of threshold initialized for [" << sensor |
||||
<< "]"; |
||||
} |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
void ThermalStatsHelper::updateStatsRecord(StatsRecord *stats_record, int new_state) { |
||||
const auto now = boot_clock::now(); |
||||
const auto cur_state_duration = std::chrono::duration_cast<std::chrono::milliseconds>( |
||||
now - stats_record->cur_state_start_time); |
||||
LOG(VERBOSE) << "Adding duration " << cur_state_duration.count() |
||||
<< " for cur_state: " << stats_record->cur_state << " with value: " |
||||
<< stats_record->time_in_state_ms[stats_record->cur_state].count(); |
||||
// Update last record end time
|
||||
stats_record->time_in_state_ms[stats_record->cur_state] += cur_state_duration; |
||||
stats_record->cur_state_start_time = now; |
||||
stats_record->cur_state = new_state; |
||||
} |
||||
|
||||
void ThermalStatsHelper::updateSensorCdevRequestStats(std::string_view sensor, |
||||
std::string_view cdev, int new_value) { |
||||
std::unique_lock<std::shared_mutex> _lock(sensor_cdev_request_stats_map_mutex_); |
||||
if (!sensor_cdev_request_stats_map_.count(sensor.data()) || |
||||
!sensor_cdev_request_stats_map_[sensor.data()].count(cdev.data())) { |
||||
return; |
||||
} |
||||
auto &request_stats = sensor_cdev_request_stats_map_[sensor.data()][cdev.data()]; |
||||
for (auto &stats_by_threshold : request_stats.stats_by_custom_threshold) { |
||||
int value = calculateThresholdBucket(stats_by_threshold.thresholds, new_value); |
||||
if (value != stats_by_threshold.stats_record.cur_state) { |
||||
LOG(VERBOSE) << "Updating bindedCdev stats for sensor: " << sensor.data() |
||||
<< " , cooling_device: " << cdev.data() << " with new value: " << value; |
||||
updateStatsRecord(&stats_by_threshold.stats_record, value); |
||||
} |
||||
} |
||||
|
||||
if (request_stats.stats_by_default_threshold.has_value()) { |
||||
auto &stats_record = request_stats.stats_by_default_threshold.value(); |
||||
if (new_value != stats_record.cur_state) { |
||||
LOG(VERBOSE) << "Updating bindedCdev stats for sensor: " << sensor.data() |
||||
<< " , cooling_device: " << cdev.data() |
||||
<< " with new value: " << new_value; |
||||
updateStatsRecord(&stats_record, new_value); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void ThermalStatsHelper::updateSensorTempStatsByThreshold(std::string_view sensor, |
||||
float temperature) { |
||||
std::unique_lock<std::shared_mutex> _lock(sensor_temp_stats_map_mutex_); |
||||
if (!sensor_temp_stats_map_.count(sensor.data())) { |
||||
return; |
||||
} |
||||
auto &sensor_temp_stats = sensor_temp_stats_map_[sensor.data()]; |
||||
for (auto &stats_by_threshold : sensor_temp_stats.stats_by_custom_threshold) { |
||||
int value = calculateThresholdBucket(stats_by_threshold.thresholds, temperature); |
||||
if (value != stats_by_threshold.stats_record.cur_state) { |
||||
LOG(VERBOSE) << "Updating sensor stats for sensor: " << sensor.data() |
||||
<< " with value: " << value; |
||||
updateStatsRecord(&stats_by_threshold.stats_record, value); |
||||
} |
||||
} |
||||
if (temperature > sensor_temp_stats.max_temp) { |
||||
sensor_temp_stats.max_temp = temperature; |
||||
sensor_temp_stats.max_temp_timestamp = system_clock::now(); |
||||
} |
||||
if (temperature < sensor_temp_stats.min_temp) { |
||||
sensor_temp_stats.min_temp = temperature; |
||||
sensor_temp_stats.min_temp_timestamp = system_clock::now(); |
||||
} |
||||
} |
||||
|
||||
void ThermalStatsHelper::updateSensorTempStatsBySeverity(std::string_view sensor, |
||||
const ThrottlingSeverity &severity) { |
||||
std::unique_lock<std::shared_mutex> _lock(sensor_temp_stats_map_mutex_); |
||||
if (sensor_temp_stats_map_.count(sensor.data()) && |
||||
sensor_temp_stats_map_[sensor.data()].stats_by_default_threshold.has_value()) { |
||||
auto &stats_record = |
||||
sensor_temp_stats_map_[sensor.data()].stats_by_default_threshold.value(); |
||||
int value = static_cast<int>(severity); |
||||
if (value != stats_record.cur_state) { |
||||
LOG(VERBOSE) << "Updating sensor stats for sensor: " << sensor.data() |
||||
<< " with value: " << value; |
||||
updateStatsRecord(&stats_record, value); |
||||
} |
||||
} |
||||
} |
||||
|
||||
int ThermalStatsHelper::reportStats() { |
||||
const auto curTime = boot_clock::now(); |
||||
const auto since_last_total_stats_update_ms = |
||||
std::chrono::duration_cast<std::chrono::milliseconds>(curTime - |
||||
last_total_stats_report_time); |
||||
LOG(VERBOSE) << "Duration from last total stats update is: " |
||||
<< since_last_total_stats_update_ms.count(); |
||||
if (since_last_total_stats_update_ms < kUpdateIntervalMs) { |
||||
LOG(VERBOSE) << "Time elapsed since last update less than " << kUpdateIntervalMs.count(); |
||||
return 0; |
||||
} |
||||
|
||||
const std::shared_ptr<IStats> stats_client = getStatsService(); |
||||
if (!stats_client) { |
||||
LOG(ERROR) << "Unable to get AIDL Stats service"; |
||||
return -1; |
||||
} |
||||
int count_failed_reporting = |
||||
reportAllSensorTempStats(stats_client) + reportAllSensorCdevRequestStats(stats_client); |
||||
last_total_stats_report_time = curTime; |
||||
return count_failed_reporting; |
||||
} |
||||
|
||||
int ThermalStatsHelper::reportAllSensorTempStats(const std::shared_ptr<IStats> &stats_client) { |
||||
int count_failed_reporting = 0; |
||||
std::unique_lock<std::shared_mutex> _lock(sensor_temp_stats_map_mutex_); |
||||
for (auto &[sensor, temp_stats] : sensor_temp_stats_map_) { |
||||
for (size_t threshold_set_idx = 0; |
||||
threshold_set_idx < temp_stats.stats_by_custom_threshold.size(); threshold_set_idx++) { |
||||
auto &stats_by_threshold = temp_stats.stats_by_custom_threshold[threshold_set_idx]; |
||||
std::string sensor_name = stats_by_threshold.logging_name.value_or( |
||||
sensor + kCustomThresholdSetSuffix.data() + std::to_string(threshold_set_idx)); |
||||
if (!reportSensorTempStats(stats_client, sensor_name, temp_stats, |
||||
&stats_by_threshold.stats_record)) { |
||||
count_failed_reporting++; |
||||
} |
||||
} |
||||
if (temp_stats.stats_by_default_threshold.has_value()) { |
||||
if (!reportSensorTempStats(stats_client, sensor, temp_stats, |
||||
&temp_stats.stats_by_default_threshold.value())) { |
||||
count_failed_reporting++; |
||||
} |
||||
} |
||||
} |
||||
return count_failed_reporting; |
||||
} |
||||
|
||||
bool ThermalStatsHelper::reportSensorTempStats(const std::shared_ptr<IStats> &stats_client, |
||||
std::string_view sensor, |
||||
const SensorTempStats &sensor_temp_stats, |
||||
StatsRecord *stats_record) { |
||||
LOG(VERBOSE) << "Reporting sensor stats for " << sensor; |
||||
// maintain a copy in case reporting fails
|
||||
StatsRecord thermal_stats_before_reporting = *stats_record; |
||||
std::vector<VendorAtomValue> values(2); |
||||
values[0].set<VendorAtomValue::stringValue>(sensor); |
||||
std::vector<int64_t> time_in_state_ms = processStatsRecordForReporting(stats_record); |
||||
const auto since_last_update_ms = std::chrono::duration_cast<std::chrono::milliseconds>( |
||||
stats_record->cur_state_start_time - stats_record->last_stats_report_time); |
||||
values[1].set<VendorAtomValue::longValue>(since_last_update_ms.count()); |
||||
VendorAtomValue tmp; |
||||
for (auto &time_in_state : time_in_state_ms) { |
||||
tmp.set<VendorAtomValue::longValue>(time_in_state); |
||||
values.push_back(tmp); |
||||
} |
||||
auto remaining_residency_buckets_count = kMaxStatsResidencyCount - time_in_state_ms.size(); |
||||
if (remaining_residency_buckets_count > 0) { |
||||
tmp.set<VendorAtomValue::longValue>(0); |
||||
values.insert(values.end(), remaining_residency_buckets_count, tmp); |
||||
} |
||||
tmp.set<VendorAtomValue::floatValue>(sensor_temp_stats.max_temp); |
||||
values.push_back(tmp); |
||||
tmp.set<VendorAtomValue::longValue>( |
||||
system_clock::to_time_t(sensor_temp_stats.max_temp_timestamp)); |
||||
values.push_back(tmp); |
||||
tmp.set<VendorAtomValue::floatValue>(sensor_temp_stats.min_temp); |
||||
values.push_back(tmp); |
||||
tmp.set<VendorAtomValue::longValue>( |
||||
system_clock::to_time_t(sensor_temp_stats.min_temp_timestamp)); |
||||
values.push_back(tmp); |
||||
|
||||
if (!reportAtom(stats_client, PixelAtoms::Atom::kVendorTempResidencyStats, std::move(values))) { |
||||
LOG(ERROR) << "Unable to report VendorTempResidencyStats to Stats service for " |
||||
"sensor: " |
||||
<< sensor; |
||||
*stats_record = restoreStatsRecordOnFailure(std::move(thermal_stats_before_reporting)); |
||||
return false; |
||||
} |
||||
// Update last time of stats reporting
|
||||
stats_record->last_stats_report_time = boot_clock::now(); |
||||
return true; |
||||
} |
||||
|
||||
int ThermalStatsHelper::reportAllSensorCdevRequestStats( |
||||
const std::shared_ptr<IStats> &stats_client) { |
||||
int count_failed_reporting = 0; |
||||
std::unique_lock<std::shared_mutex> _lock(sensor_cdev_request_stats_map_mutex_); |
||||
for (auto &[sensor, cdev_request_stats_map] : sensor_cdev_request_stats_map_) { |
||||
for (auto &[cdev, request_stats] : cdev_request_stats_map) { |
||||
for (size_t threshold_set_idx = 0; |
||||
threshold_set_idx < request_stats.stats_by_custom_threshold.size(); |
||||
threshold_set_idx++) { |
||||
auto &stats_by_threshold = |
||||
request_stats.stats_by_custom_threshold[threshold_set_idx]; |
||||
std::string cdev_name = stats_by_threshold.logging_name.value_or( |
||||
cdev + kCustomThresholdSetSuffix.data() + |
||||
std::to_string(threshold_set_idx)); |
||||
if (!reportSensorCdevRequestStats(stats_client, sensor, cdev_name, |
||||
&stats_by_threshold.stats_record)) { |
||||
count_failed_reporting++; |
||||
} |
||||
} |
||||
|
||||
if (request_stats.stats_by_default_threshold.has_value()) { |
||||
if (!reportSensorCdevRequestStats( |
||||
stats_client, sensor, cdev, |
||||
&request_stats.stats_by_default_threshold.value())) { |
||||
count_failed_reporting++; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return count_failed_reporting; |
||||
} |
||||
|
||||
bool ThermalStatsHelper::reportSensorCdevRequestStats(const std::shared_ptr<IStats> &stats_client, |
||||
std::string_view sensor, |
||||
std::string_view cdev, |
||||
StatsRecord *stats_record) { |
||||
LOG(VERBOSE) << "Reporting bindedCdev stats for sensor: " << sensor |
||||
<< " cooling_device: " << cdev; |
||||
// maintain a copy in case reporting fails
|
||||
StatsRecord thermal_stats_before_reporting = *stats_record; |
||||
std::vector<VendorAtomValue> values(3); |
||||
values[0].set<VendorAtomValue::stringValue>(sensor); |
||||
values[1].set<VendorAtomValue::stringValue>(cdev); |
||||
std::vector<int64_t> time_in_state_ms = processStatsRecordForReporting(stats_record); |
||||
const auto since_last_update_ms = std::chrono::duration_cast<std::chrono::milliseconds>( |
||||
stats_record->cur_state_start_time - stats_record->last_stats_report_time); |
||||
values[2].set<VendorAtomValue::longValue>(since_last_update_ms.count()); |
||||
VendorAtomValue tmp; |
||||
for (auto &time_in_state : time_in_state_ms) { |
||||
tmp.set<VendorAtomValue::longValue>(time_in_state); |
||||
values.push_back(tmp); |
||||
} |
||||
|
||||
if (!reportAtom(stats_client, PixelAtoms::Atom::kVendorSensorCoolingDeviceStats, |
||||
std::move(values))) { |
||||
LOG(ERROR) << "Unable to report VendorSensorCoolingDeviceStats to Stats " |
||||
"service for sensor: " |
||||
<< sensor << " cooling_device: " << cdev; |
||||
*stats_record = restoreStatsRecordOnFailure(std::move(thermal_stats_before_reporting)); |
||||
return false; |
||||
} |
||||
// Update last time of stats reporting
|
||||
stats_record->last_stats_report_time = boot_clock::now(); |
||||
return true; |
||||
} |
||||
|
||||
std::vector<int64_t> ThermalStatsHelper::processStatsRecordForReporting(StatsRecord *stats_record) { |
||||
// update the last unclosed entry and start new record with same state
|
||||
updateStatsRecord(stats_record, stats_record->cur_state); |
||||
std::vector<std::chrono::milliseconds> &time_in_state_ms = stats_record->time_in_state_ms; |
||||
// convert std::chrono::milliseconds time_in_state to int64_t vector for reporting
|
||||
std::vector<int64_t> stats_residency(time_in_state_ms.size()); |
||||
std::transform(time_in_state_ms.begin(), time_in_state_ms.end(), stats_residency.begin(), |
||||
[](std::chrono::milliseconds time_ms) { return time_ms.count(); }); |
||||
// clear previous stats
|
||||
std::fill(time_in_state_ms.begin(), time_in_state_ms.end(), std::chrono::milliseconds::zero()); |
||||
return stats_residency; |
||||
} |
||||
|
||||
bool ThermalStatsHelper::reportAtom(const std::shared_ptr<IStats> &stats_client, |
||||
const int32_t &atom_id, std::vector<VendorAtomValue> &&values) { |
||||
LOG(VERBOSE) << "Reporting thermal stats for atom_id " << atom_id; |
||||
// Send vendor atom to IStats HAL
|
||||
VendorAtom event = {.reverseDomainName = "", .atomId = atom_id, .values = std::move(values)}; |
||||
const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event); |
||||
return ret.isOk(); |
||||
} |
||||
|
||||
StatsRecord ThermalStatsHelper::restoreStatsRecordOnFailure( |
||||
StatsRecord &&stats_record_before_failure) { |
||||
stats_record_before_failure.report_fail_count += 1; |
||||
// If consecutive count of failure is high, reset stat to avoid overflow
|
||||
if (stats_record_before_failure.report_fail_count >= kMaxStatsReportingFailCount) { |
||||
return StatsRecord(stats_record_before_failure.time_in_state_ms.size(), |
||||
stats_record_before_failure.cur_state); |
||||
} else { |
||||
return stats_record_before_failure; |
||||
} |
||||
} |
||||
|
||||
std::unordered_map<std::string, SensorTempStats> ThermalStatsHelper::GetSensorTempStatsSnapshot() { |
||||
auto sensor_temp_stats_snapshot = sensor_temp_stats_map_; |
||||
for (auto &sensor_temp_stats_pair : sensor_temp_stats_snapshot) { |
||||
for (auto &temp_stats : sensor_temp_stats_pair.second.stats_by_custom_threshold) { |
||||
// update the last unclosed entry and start new record with same state
|
||||
updateStatsRecord(&temp_stats.stats_record, temp_stats.stats_record.cur_state); |
||||
} |
||||
if (sensor_temp_stats_pair.second.stats_by_default_threshold.has_value()) { |
||||
auto &stats_by_default_threshold = |
||||
sensor_temp_stats_pair.second.stats_by_default_threshold.value(); |
||||
// update the last unclosed entry and start new record with same state
|
||||
updateStatsRecord(&stats_by_default_threshold, stats_by_default_threshold.cur_state); |
||||
} |
||||
} |
||||
return sensor_temp_stats_snapshot; |
||||
} |
||||
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, ThermalStats<int>>> |
||||
ThermalStatsHelper::GetSensorCoolingDeviceRequestStatsSnapshot() { |
||||
auto sensor_cdev_request_stats_snapshot = sensor_cdev_request_stats_map_; |
||||
for (auto &sensor_cdev_request_stats_pair : sensor_cdev_request_stats_snapshot) { |
||||
for (auto &cdev_request_stats_pair : sensor_cdev_request_stats_pair.second) { |
||||
for (auto &request_stats : cdev_request_stats_pair.second.stats_by_custom_threshold) { |
||||
// update the last unclosed entry and start new record with same state
|
||||
updateStatsRecord(&request_stats.stats_record, |
||||
request_stats.stats_record.cur_state); |
||||
} |
||||
if (cdev_request_stats_pair.second.stats_by_default_threshold.has_value()) { |
||||
auto &stats_by_default_threshold = |
||||
cdev_request_stats_pair.second.stats_by_default_threshold.value(); |
||||
// update the last unclosed entry and start new record with same state
|
||||
updateStatsRecord(&stats_by_default_threshold, |
||||
stats_by_default_threshold.cur_state); |
||||
} |
||||
} |
||||
} |
||||
return sensor_cdev_request_stats_snapshot; |
||||
} |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,170 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <aidl/android/frameworks/stats/IStats.h> |
||||
#include <aidl/android/hardware/thermal/Temperature.h> |
||||
#include <android-base/chrono_utils.h> |
||||
|
||||
#include <chrono> |
||||
#include <shared_mutex> |
||||
#include <string_view> |
||||
#include <unordered_map> |
||||
#include <vector> |
||||
|
||||
#include "thermal_info.h" |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
using aidl::android::frameworks::stats::IStats; |
||||
using aidl::android::frameworks::stats::VendorAtomValue; |
||||
using ::android::base::boot_clock; |
||||
using std::chrono::system_clock; |
||||
using SystemTimePoint = std::chrono::time_point<std::chrono::system_clock>; |
||||
|
||||
constexpr int kMaxStatsReportingFailCount = 3; |
||||
|
||||
struct StatsRecord { |
||||
int cur_state; /* temperature / cdev state at current time */ |
||||
boot_clock::time_point cur_state_start_time; |
||||
boot_clock::time_point last_stats_report_time = boot_clock::time_point::min(); |
||||
std::vector<std::chrono::milliseconds> time_in_state_ms; /* stats array */ |
||||
int report_fail_count = 0; /* Number of times failed to report stats */ |
||||
explicit StatsRecord(const size_t &time_in_state_size, int state = 0) |
||||
: cur_state(state), |
||||
cur_state_start_time(boot_clock::now()), |
||||
last_stats_report_time(boot_clock::now()), |
||||
report_fail_count(0) { |
||||
time_in_state_ms = std::vector<std::chrono::milliseconds>( |
||||
time_in_state_size, std::chrono::milliseconds::zero()); |
||||
} |
||||
StatsRecord() = default; |
||||
StatsRecord(const StatsRecord &) = default; |
||||
StatsRecord &operator=(const StatsRecord &) = default; |
||||
StatsRecord(StatsRecord &&) = default; |
||||
StatsRecord &operator=(StatsRecord &&) = default; |
||||
~StatsRecord() = default; |
||||
}; |
||||
|
||||
template <typename ValueType> |
||||
struct StatsByThreshold { |
||||
std::vector<ValueType> thresholds; |
||||
std::optional<std::string> logging_name; |
||||
StatsRecord stats_record; |
||||
explicit StatsByThreshold(ThresholdList<ValueType> threshold_list) |
||||
: thresholds(threshold_list.thresholds), logging_name(threshold_list.logging_name) { |
||||
// number of states = number of thresholds + 1
|
||||
// e.g. threshold: [30, 50, 60]
|
||||
// buckets: [MIN - 30, 30 - 50, 50-60, 60-MAX]
|
||||
int time_in_state_size = threshold_list.thresholds.size() + 1; |
||||
stats_record = StatsRecord(time_in_state_size); |
||||
} |
||||
StatsByThreshold() = default; |
||||
StatsByThreshold(const StatsByThreshold &) = default; |
||||
StatsByThreshold &operator=(const StatsByThreshold &) = default; |
||||
StatsByThreshold(StatsByThreshold &&) = default; |
||||
StatsByThreshold &operator=(StatsByThreshold &&) = default; |
||||
~StatsByThreshold() = default; |
||||
}; |
||||
|
||||
template <typename ValueType> |
||||
struct ThermalStats { |
||||
std::vector<StatsByThreshold<ValueType>> stats_by_custom_threshold; |
||||
std::optional<StatsRecord> stats_by_default_threshold; |
||||
}; |
||||
|
||||
struct SensorTempStats : ThermalStats<float> { |
||||
float max_temp = std::numeric_limits<float>::min(); |
||||
SystemTimePoint max_temp_timestamp = SystemTimePoint::min(); |
||||
float min_temp = std::numeric_limits<float>::max(); |
||||
SystemTimePoint min_temp_timestamp = SystemTimePoint::min(); |
||||
}; |
||||
|
||||
class ThermalStatsHelper { |
||||
public: |
||||
ThermalStatsHelper() = default; |
||||
~ThermalStatsHelper() = default; |
||||
// Disallow copy and assign
|
||||
ThermalStatsHelper(const ThermalStatsHelper &) = delete; |
||||
void operator=(const ThermalStatsHelper &) = delete; |
||||
|
||||
bool initializeStats(const Json::Value &config, |
||||
const std::unordered_map<std::string, SensorInfo> &sensor_info_map_, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_); |
||||
void updateSensorCdevRequestStats(std::string_view trigger_sensor, std::string_view cdev, |
||||
int new_state); |
||||
void updateSensorTempStatsBySeverity(std::string_view sensor, |
||||
const ThrottlingSeverity &severity); |
||||
void updateSensorTempStatsByThreshold(std::string_view sensor, float temperature); |
||||
/*
|
||||
* Function to report all the stats by calling all specific stats reporting function. |
||||
* Returns: |
||||
* 0, if time_elapsed < kUpdateIntervalMs or if no failure in reporting |
||||
* -1, if failed to get AIDL stats services |
||||
* >0, count represents the number of stats failed to report. |
||||
*/ |
||||
int reportStats(); |
||||
// Get a snapshot of Thermal Stats Sensor Map till that point in time
|
||||
std::unordered_map<std::string, SensorTempStats> GetSensorTempStatsSnapshot(); |
||||
// Get a snapshot of Thermal Stats Sensor Map till that point in time
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, ThermalStats<int>>> |
||||
GetSensorCoolingDeviceRequestStatsSnapshot(); |
||||
|
||||
private: |
||||
static constexpr std::chrono::milliseconds kUpdateIntervalMs = |
||||
std::chrono::duration_cast<std::chrono::milliseconds>(24h); |
||||
boot_clock::time_point last_total_stats_report_time = boot_clock::time_point::min(); |
||||
|
||||
mutable std::shared_mutex sensor_temp_stats_map_mutex_; |
||||
// Temperature stats for each sensor being watched
|
||||
std::unordered_map<std::string, SensorTempStats> sensor_temp_stats_map_; |
||||
mutable std::shared_mutex sensor_cdev_request_stats_map_mutex_; |
||||
// userVote request stat for the sensor to the corresponding cdev (sensor -> cdev ->
|
||||
// StatsRecord)
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, ThermalStats<int>>> |
||||
sensor_cdev_request_stats_map_; |
||||
|
||||
bool initializeSensorTempStats( |
||||
const StatsInfo<float> &sensor_stats_info, |
||||
const std::unordered_map<std::string, SensorInfo> &sensor_info_map_); |
||||
bool initializeSensorCdevRequestStats( |
||||
const StatsInfo<int> &request_stats_info, |
||||
const std::unordered_map<std::string, SensorInfo> &sensor_info_map_, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map_); |
||||
void updateStatsRecord(StatsRecord *stats_record, int new_state); |
||||
int reportAllSensorTempStats(const std::shared_ptr<IStats> &stats_client); |
||||
bool reportSensorTempStats(const std::shared_ptr<IStats> &stats_client, std::string_view sensor, |
||||
const SensorTempStats &sensor_temp_stats, StatsRecord *stats_record); |
||||
int reportAllSensorCdevRequestStats(const std::shared_ptr<IStats> &stats_client); |
||||
bool reportSensorCdevRequestStats(const std::shared_ptr<IStats> &stats_client, |
||||
std::string_view sensor, std::string_view cdev, |
||||
StatsRecord *stats_record); |
||||
bool reportAtom(const std::shared_ptr<IStats> &stats_client, const int32_t &atom_id, |
||||
std::vector<VendorAtomValue> &&values); |
||||
std::vector<int64_t> processStatsRecordForReporting(StatsRecord *stats_record); |
||||
StatsRecord restoreStatsRecordOnFailure(StatsRecord &&stats_record_before_failure); |
||||
}; |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,767 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
#define ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL) |
||||
|
||||
#include "thermal_throttling.h" |
||||
|
||||
#include <android-base/file.h> |
||||
#include <android-base/logging.h> |
||||
#include <android-base/properties.h> |
||||
#include <android-base/stringprintf.h> |
||||
#include <android-base/strings.h> |
||||
#include <utils/Trace.h> |
||||
|
||||
#include <iterator> |
||||
#include <set> |
||||
#include <sstream> |
||||
#include <thread> |
||||
#include <vector> |
||||
|
||||
#include "power_files.h" |
||||
#include "thermal_info.h" |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
using ::android::base::StringPrintf; |
||||
|
||||
// To find the next PID target state according to the current thermal severity
|
||||
size_t getTargetStateOfPID(const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity) { |
||||
size_t target_state = 0; |
||||
|
||||
for (const auto &severity : ::ndk::enum_range<ThrottlingSeverity>()) { |
||||
size_t state = static_cast<size_t>(severity); |
||||
if (std::isnan(sensor_info.throttling_info->s_power[state])) { |
||||
continue; |
||||
} |
||||
target_state = state; |
||||
if (severity > curr_severity) { |
||||
break; |
||||
} |
||||
} |
||||
LOG(VERBOSE) << "PID target state = " << target_state; |
||||
return target_state; |
||||
} |
||||
|
||||
void ThermalThrottling::clearThrottlingData(std::string_view sensor_name, |
||||
const SensorInfo &sensor_info) { |
||||
if (!thermal_throttling_status_map_.count(sensor_name.data())) { |
||||
return; |
||||
} |
||||
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_); |
||||
|
||||
for (auto &pid_power_budget_pair : |
||||
thermal_throttling_status_map_.at(sensor_name.data()).pid_power_budget_map) { |
||||
pid_power_budget_pair.second = std::numeric_limits<int>::max(); |
||||
} |
||||
|
||||
for (auto &pid_cdev_request_pair : |
||||
thermal_throttling_status_map_.at(sensor_name.data()).pid_cdev_request_map) { |
||||
pid_cdev_request_pair.second = 0; |
||||
} |
||||
|
||||
for (auto &hardlimit_cdev_request_pair : |
||||
thermal_throttling_status_map_.at(sensor_name.data()).hardlimit_cdev_request_map) { |
||||
hardlimit_cdev_request_pair.second = 0; |
||||
} |
||||
|
||||
for (auto &throttling_release_pair : |
||||
thermal_throttling_status_map_.at(sensor_name.data()).throttling_release_map) { |
||||
throttling_release_pair.second = 0; |
||||
} |
||||
|
||||
thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN; |
||||
thermal_throttling_status_map_[sensor_name.data()].i_budget = |
||||
sensor_info.throttling_info->i_default; |
||||
thermal_throttling_status_map_[sensor_name.data()].prev_target = |
||||
static_cast<size_t>(ThrottlingSeverity::NONE); |
||||
thermal_throttling_status_map_[sensor_name.data()].prev_power_budget = NAN; |
||||
thermal_throttling_status_map_[sensor_name.data()].tran_cycle = 0; |
||||
|
||||
return; |
||||
} |
||||
|
||||
bool ThermalThrottling::registerThermalThrottling( |
||||
std::string_view sensor_name, const std::shared_ptr<ThrottlingInfo> &throttling_info, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) { |
||||
if (thermal_throttling_status_map_.count(sensor_name.data())) { |
||||
LOG(ERROR) << "Sensor " << sensor_name.data() << " throttling map has been registered"; |
||||
return false; |
||||
} |
||||
|
||||
if (throttling_info == nullptr) { |
||||
LOG(ERROR) << "Sensor " << sensor_name.data() << " has no throttling info"; |
||||
return false; |
||||
} |
||||
|
||||
thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN; |
||||
thermal_throttling_status_map_[sensor_name.data()].i_budget = throttling_info->i_default; |
||||
thermal_throttling_status_map_[sensor_name.data()].prev_target = |
||||
static_cast<size_t>(ThrottlingSeverity::NONE); |
||||
thermal_throttling_status_map_[sensor_name.data()].prev_power_budget = NAN; |
||||
thermal_throttling_status_map_[sensor_name.data()].tran_cycle = 0; |
||||
|
||||
for (auto &binded_cdev_pair : throttling_info->binded_cdev_info_map) { |
||||
if (!cooling_device_info_map.count(binded_cdev_pair.first)) { |
||||
LOG(ERROR) << "Could not find " << sensor_name.data() << "'s binded CDEV " |
||||
<< binded_cdev_pair.first; |
||||
return false; |
||||
} |
||||
// Register PID throttling map
|
||||
for (const auto &cdev_weight : binded_cdev_pair.second.cdev_weight_for_pid) { |
||||
if (!std::isnan(cdev_weight)) { |
||||
thermal_throttling_status_map_[sensor_name.data()] |
||||
.pid_power_budget_map[binded_cdev_pair.first] = |
||||
std::numeric_limits<int>::max(); |
||||
thermal_throttling_status_map_[sensor_name.data()] |
||||
.pid_cdev_request_map[binded_cdev_pair.first] = 0; |
||||
thermal_throttling_status_map_[sensor_name.data()] |
||||
.cdev_status_map[binded_cdev_pair.first] = 0; |
||||
cdev_all_request_map_[binded_cdev_pair.first].insert(0); |
||||
break; |
||||
} |
||||
} |
||||
// Register hard limit throttling map
|
||||
for (const auto &limit_info : binded_cdev_pair.second.limit_info) { |
||||
if (limit_info > 0) { |
||||
thermal_throttling_status_map_[sensor_name.data()] |
||||
.hardlimit_cdev_request_map[binded_cdev_pair.first] = 0; |
||||
thermal_throttling_status_map_[sensor_name.data()] |
||||
.cdev_status_map[binded_cdev_pair.first] = 0; |
||||
cdev_all_request_map_[binded_cdev_pair.first].insert(0); |
||||
break; |
||||
} |
||||
} |
||||
// Register throttling release map if power threshold exists
|
||||
if (!binded_cdev_pair.second.power_rail.empty()) { |
||||
for (const auto &power_threshold : binded_cdev_pair.second.power_thresholds) { |
||||
if (!std::isnan(power_threshold)) { |
||||
thermal_throttling_status_map_[sensor_name.data()] |
||||
.throttling_release_map[binded_cdev_pair.first] = 0; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
// return power budget based on PID algo
|
||||
float ThermalThrottling::updatePowerBudget(const Temperature &temp, const SensorInfo &sensor_info, |
||||
std::chrono::milliseconds time_elapsed_ms, |
||||
ThrottlingSeverity curr_severity) { |
||||
float p = 0, d = 0; |
||||
float power_budget = std::numeric_limits<float>::max(); |
||||
bool target_changed = false; |
||||
float budget_transient = 0.0; |
||||
auto &throttling_status = thermal_throttling_status_map_.at(temp.name); |
||||
std::string sensor_name = temp.name; |
||||
|
||||
if (curr_severity == ThrottlingSeverity::NONE) { |
||||
return power_budget; |
||||
} |
||||
|
||||
const auto target_state = getTargetStateOfPID(sensor_info, curr_severity); |
||||
if (throttling_status.prev_target != static_cast<size_t>(ThrottlingSeverity::NONE) && |
||||
target_state != throttling_status.prev_target && |
||||
sensor_info.throttling_info->tran_cycle > 0) { |
||||
throttling_status.tran_cycle = sensor_info.throttling_info->tran_cycle - 1; |
||||
target_changed = true; |
||||
} |
||||
throttling_status.prev_target = target_state; |
||||
|
||||
// Compute PID
|
||||
float err = sensor_info.hot_thresholds[target_state] - temp.value; |
||||
p = err * (err < 0 ? sensor_info.throttling_info->k_po[target_state] |
||||
: sensor_info.throttling_info->k_pu[target_state]); |
||||
|
||||
if (err < sensor_info.throttling_info->i_cutoff[target_state]) { |
||||
throttling_status.i_budget += err * sensor_info.throttling_info->k_i[target_state]; |
||||
} |
||||
|
||||
if (fabsf(throttling_status.i_budget) > sensor_info.throttling_info->i_max[target_state]) { |
||||
throttling_status.i_budget = sensor_info.throttling_info->i_max[target_state] * |
||||
(throttling_status.i_budget > 0 ? 1 : -1); |
||||
} |
||||
|
||||
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) / |
||||
time_elapsed_ms.count(); |
||||
} |
||||
|
||||
throttling_status.prev_err = err; |
||||
// Calculate power budget
|
||||
power_budget = |
||||
sensor_info.throttling_info->s_power[target_state] + p + throttling_status.i_budget + d; |
||||
if (power_budget < sensor_info.throttling_info->min_alloc_power[target_state]) { |
||||
power_budget = sensor_info.throttling_info->min_alloc_power[target_state]; |
||||
} |
||||
if (power_budget > sensor_info.throttling_info->max_alloc_power[target_state]) { |
||||
power_budget = sensor_info.throttling_info->max_alloc_power[target_state]; |
||||
} |
||||
|
||||
if (target_changed) { |
||||
throttling_status.budget_transient = throttling_status.prev_power_budget - power_budget; |
||||
} |
||||
|
||||
if (throttling_status.tran_cycle) { |
||||
budget_transient = throttling_status.budget_transient * |
||||
((static_cast<float>(throttling_status.tran_cycle) / |
||||
static_cast<float>(sensor_info.throttling_info->tran_cycle))); |
||||
power_budget += budget_transient; |
||||
throttling_status.tran_cycle--; |
||||
} |
||||
|
||||
LOG(INFO) << temp.name << " power_budget=" << power_budget << " err=" << err |
||||
<< " s_power=" << sensor_info.throttling_info->s_power[target_state] |
||||
<< " time_elapsed_ms=" << time_elapsed_ms.count() << " p=" << p |
||||
<< " i=" << throttling_status.i_budget << " d=" << d |
||||
<< " budget transient=" << budget_transient << " control target=" << target_state; |
||||
|
||||
ATRACE_INT((sensor_name + std::string("-power_budget")).c_str(), |
||||
static_cast<int>(power_budget)); |
||||
ATRACE_INT((sensor_name + std::string("-s_power")).c_str(), |
||||
static_cast<int>(sensor_info.throttling_info->s_power[target_state])); |
||||
ATRACE_INT((sensor_name + std::string("-time_elapsed_ms")).c_str(), |
||||
static_cast<int>(time_elapsed_ms.count())); |
||||
ATRACE_INT((sensor_name + std::string("-budget_transient")).c_str(), |
||||
static_cast<int>(budget_transient)); |
||||
ATRACE_INT((sensor_name + std::string("-i")).c_str(), |
||||
static_cast<int>(throttling_status.i_budget)); |
||||
ATRACE_INT((sensor_name + std::string("-target_state")).c_str(), |
||||
static_cast<int>(target_state)); |
||||
|
||||
ATRACE_INT((sensor_name + std::string("-err")).c_str(), static_cast<int>(err / sensor_info.multiplier)); |
||||
ATRACE_INT((sensor_name + std::string("-p")).c_str(), static_cast<int>(p)); |
||||
ATRACE_INT((sensor_name + std::string("-d")).c_str(), static_cast<int>(d)); |
||||
ATRACE_INT((sensor_name + std::string("-temp")).c_str(), static_cast<int>(temp.value / sensor_info.multiplier)); |
||||
|
||||
throttling_status.prev_power_budget = power_budget; |
||||
|
||||
return power_budget; |
||||
} |
||||
|
||||
float ThermalThrottling::computeExcludedPower( |
||||
const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity, |
||||
const std::unordered_map<std::string, PowerStatus> &power_status_map, std::string *log_buf, |
||||
std::string_view sensor_name) { |
||||
float excluded_power = 0.0; |
||||
|
||||
for (const auto &excluded_power_info_pair : |
||||
sensor_info.throttling_info->excluded_power_info_map) { |
||||
const auto last_updated_avg_power = |
||||
power_status_map.at(excluded_power_info_pair.first).last_updated_avg_power; |
||||
if (!std::isnan(last_updated_avg_power)) { |
||||
excluded_power += last_updated_avg_power * |
||||
excluded_power_info_pair.second[static_cast<size_t>(curr_severity)]; |
||||
log_buf->append(StringPrintf( |
||||
"(%s: %0.2f mW, cdev_weight: %f)", excluded_power_info_pair.first.c_str(), |
||||
last_updated_avg_power, |
||||
excluded_power_info_pair.second[static_cast<size_t>(curr_severity)])); |
||||
|
||||
ATRACE_INT((std::string(sensor_name) + std::string("-") + |
||||
excluded_power_info_pair.first + std::string("-avg_power")) |
||||
.c_str(), |
||||
static_cast<int>(last_updated_avg_power)); |
||||
} |
||||
} |
||||
|
||||
ATRACE_INT((std::string(sensor_name) + std::string("-excluded_power")).c_str(), |
||||
static_cast<int>(excluded_power)); |
||||
return excluded_power; |
||||
} |
||||
|
||||
// Allocate power budget to binded cooling devices base on the real ODPM power data
|
||||
bool ThermalThrottling::allocatePowerToCdev( |
||||
const Temperature &temp, const SensorInfo &sensor_info, |
||||
const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, |
||||
const std::unordered_map<std::string, PowerStatus> &power_status_map, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) { |
||||
float total_weight = 0; |
||||
float last_updated_avg_power = NAN; |
||||
float allocated_power = 0; |
||||
float allocated_weight = 0; |
||||
bool low_power_device_check = true; |
||||
bool is_budget_allocated = false; |
||||
bool power_data_invalid = false; |
||||
std::set<std::string> allocated_cdev; |
||||
std::string log_buf; |
||||
|
||||
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_); |
||||
auto total_power_budget = updatePowerBudget(temp, sensor_info, time_elapsed_ms, curr_severity); |
||||
|
||||
if (sensor_info.throttling_info->excluded_power_info_map.size()) { |
||||
total_power_budget -= computeExcludedPower(sensor_info, curr_severity, power_status_map, |
||||
&log_buf, temp.name); |
||||
total_power_budget = std::max(total_power_budget, 0.0f); |
||||
if (!log_buf.empty()) { |
||||
LOG(INFO) << temp.name << " power budget=" << total_power_budget << " after " << log_buf |
||||
<< " is excluded"; |
||||
} |
||||
} |
||||
|
||||
// Compute total cdev weight
|
||||
for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) { |
||||
const auto cdev_weight = binded_cdev_info_pair.second |
||||
.cdev_weight_for_pid[static_cast<size_t>(curr_severity)]; |
||||
if (std::isnan(cdev_weight) || cdev_weight == 0) { |
||||
allocated_cdev.insert(binded_cdev_info_pair.first); |
||||
continue; |
||||
} |
||||
total_weight += cdev_weight; |
||||
} |
||||
|
||||
while (!is_budget_allocated) { |
||||
for (const auto &binded_cdev_info_pair : |
||||
sensor_info.throttling_info->binded_cdev_info_map) { |
||||
float cdev_power_adjustment = 0; |
||||
const auto cdev_weight = |
||||
binded_cdev_info_pair.second |
||||
.cdev_weight_for_pid[static_cast<size_t>(curr_severity)]; |
||||
|
||||
if (allocated_cdev.count(binded_cdev_info_pair.first)) { |
||||
continue; |
||||
} |
||||
if (std::isnan(cdev_weight) || !cdev_weight) { |
||||
allocated_cdev.insert(binded_cdev_info_pair.first); |
||||
continue; |
||||
} |
||||
|
||||
// Get the power data
|
||||
if (!power_data_invalid) { |
||||
if (!binded_cdev_info_pair.second.power_rail.empty()) { |
||||
last_updated_avg_power = |
||||
power_status_map.at(binded_cdev_info_pair.second.power_rail) |
||||
.last_updated_avg_power; |
||||
if (std::isnan(last_updated_avg_power)) { |
||||
LOG(VERBOSE) << "power data is under collecting"; |
||||
power_data_invalid = true; |
||||
break; |
||||
} |
||||
|
||||
ATRACE_INT((temp.name + std::string("-") + |
||||
binded_cdev_info_pair.second.power_rail + std::string("-avg_power")) |
||||
.c_str(), |
||||
static_cast<int>(last_updated_avg_power)); |
||||
} else { |
||||
power_data_invalid = true; |
||||
break; |
||||
} |
||||
if (binded_cdev_info_pair.second.throttling_with_power_link) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
auto cdev_power_budget = total_power_budget * (cdev_weight / total_weight); |
||||
cdev_power_adjustment = cdev_power_budget - last_updated_avg_power; |
||||
|
||||
if (low_power_device_check) { |
||||
// Share the budget for the CDEV which power is lower than target
|
||||
if (cdev_power_adjustment > 0 && |
||||
thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at( |
||||
binded_cdev_info_pair.first) == 0) { |
||||
allocated_power += last_updated_avg_power; |
||||
allocated_weight += cdev_weight; |
||||
allocated_cdev.insert(binded_cdev_info_pair.first); |
||||
if (!binded_cdev_info_pair.second.power_rail.empty()) { |
||||
log_buf.append(StringPrintf("(%s: %0.2f mW)", |
||||
binded_cdev_info_pair.second.power_rail.c_str(), |
||||
last_updated_avg_power)); |
||||
} |
||||
LOG(VERBOSE) << temp.name << " binded " << binded_cdev_info_pair.first |
||||
<< " has been already at min state 0"; |
||||
} |
||||
} else { |
||||
const CdevInfo &cdev_info = cooling_device_info_map.at(binded_cdev_info_pair.first); |
||||
if (!binded_cdev_info_pair.second.power_rail.empty()) { |
||||
log_buf.append(StringPrintf("(%s: %0.2f mW)", |
||||
binded_cdev_info_pair.second.power_rail.c_str(), |
||||
last_updated_avg_power)); |
||||
} |
||||
// Ignore the power distribution if the CDEV has no space to reduce power
|
||||
if ((cdev_power_adjustment < 0 && |
||||
thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at( |
||||
binded_cdev_info_pair.first) == cdev_info.max_state)) { |
||||
LOG(VERBOSE) << temp.name << " binded " << binded_cdev_info_pair.first |
||||
<< " has been already at max state " << cdev_info.max_state; |
||||
continue; |
||||
} |
||||
|
||||
if (!power_data_invalid && binded_cdev_info_pair.second.power_rail != "") { |
||||
auto cdev_curr_power_budget = |
||||
thermal_throttling_status_map_[temp.name].pid_power_budget_map.at( |
||||
binded_cdev_info_pair.first); |
||||
|
||||
if (last_updated_avg_power > cdev_curr_power_budget) { |
||||
cdev_power_budget = cdev_curr_power_budget += |
||||
(cdev_power_adjustment * |
||||
(cdev_curr_power_budget / last_updated_avg_power)); |
||||
} else { |
||||
cdev_power_budget = cdev_curr_power_budget += cdev_power_adjustment; |
||||
} |
||||
} else { |
||||
cdev_power_budget = total_power_budget * (cdev_weight / total_weight); |
||||
} |
||||
|
||||
if (!std::isnan(cdev_info.state2power[0]) && |
||||
cdev_power_budget > cdev_info.state2power[0]) { |
||||
cdev_power_budget = cdev_info.state2power[0]; |
||||
} else if (cdev_power_budget < 0) { |
||||
cdev_power_budget = 0; |
||||
} |
||||
|
||||
int max_cdev_vote; |
||||
if (!getCdevMaxRequest(binded_cdev_info_pair.first, &max_cdev_vote)) { |
||||
return false; |
||||
} |
||||
|
||||
const auto curr_cdev_vote = |
||||
thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at( |
||||
binded_cdev_info_pair.first); |
||||
|
||||
if (binded_cdev_info_pair.second.max_release_step != |
||||
std::numeric_limits<int>::max() && |
||||
(power_data_invalid || cdev_power_adjustment > 0)) { |
||||
if (!power_data_invalid && curr_cdev_vote < max_cdev_vote) { |
||||
cdev_power_budget = cdev_info.state2power[curr_cdev_vote]; |
||||
LOG(VERBOSE) << temp.name << "'s " << binded_cdev_info_pair.first |
||||
<< " vote: " << curr_cdev_vote |
||||
<< " is lower than max cdev vote: " << max_cdev_vote; |
||||
} else { |
||||
const auto target_state = std::max( |
||||
curr_cdev_vote - binded_cdev_info_pair.second.max_release_step, 0); |
||||
cdev_power_budget = |
||||
std::min(cdev_power_budget, cdev_info.state2power[target_state]); |
||||
} |
||||
} |
||||
|
||||
if (binded_cdev_info_pair.second.max_throttle_step != |
||||
std::numeric_limits<int>::max() && |
||||
(power_data_invalid || cdev_power_adjustment < 0)) { |
||||
const auto target_state = std::min( |
||||
curr_cdev_vote + binded_cdev_info_pair.second.max_throttle_step, |
||||
cdev_info.max_state); |
||||
cdev_power_budget = |
||||
std::max(cdev_power_budget, cdev_info.state2power[target_state]); |
||||
} |
||||
|
||||
thermal_throttling_status_map_[temp.name].pid_power_budget_map.at( |
||||
binded_cdev_info_pair.first) = cdev_power_budget; |
||||
LOG(VERBOSE) << temp.name << " allocate " |
||||
<< thermal_throttling_status_map_[temp.name].pid_power_budget_map.at( |
||||
binded_cdev_info_pair.first) |
||||
<< "mW to " << binded_cdev_info_pair.first |
||||
<< "(cdev_weight=" << cdev_weight << ")"; |
||||
} |
||||
} |
||||
|
||||
if (!power_data_invalid) { |
||||
total_power_budget -= allocated_power; |
||||
total_weight -= allocated_weight; |
||||
} |
||||
allocated_power = 0; |
||||
allocated_weight = 0; |
||||
|
||||
if (low_power_device_check) { |
||||
low_power_device_check = false; |
||||
} else { |
||||
is_budget_allocated = true; |
||||
} |
||||
} |
||||
if (log_buf.size()) { |
||||
LOG(INFO) << temp.name << " binded power rails: " << log_buf; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
void ThermalThrottling::updateCdevRequestByPower( |
||||
std::string sensor_name, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) { |
||||
size_t i; |
||||
|
||||
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_); |
||||
for (auto &pid_power_budget_pair : |
||||
thermal_throttling_status_map_[sensor_name.data()].pid_power_budget_map) { |
||||
const CdevInfo &cdev_info = cooling_device_info_map.at(pid_power_budget_pair.first); |
||||
|
||||
for (i = 0; i < cdev_info.state2power.size() - 1; ++i) { |
||||
if (pid_power_budget_pair.second >= cdev_info.state2power[i]) { |
||||
break; |
||||
} |
||||
} |
||||
thermal_throttling_status_map_[sensor_name.data()].pid_cdev_request_map.at( |
||||
pid_power_budget_pair.first) = static_cast<int>(i); |
||||
} |
||||
|
||||
return; |
||||
} |
||||
|
||||
void ThermalThrottling::updateCdevRequestBySeverity(std::string_view sensor_name, |
||||
const SensorInfo &sensor_info, |
||||
ThrottlingSeverity curr_severity) { |
||||
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_); |
||||
for (auto const &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) { |
||||
thermal_throttling_status_map_[sensor_name.data()].hardlimit_cdev_request_map.at( |
||||
binded_cdev_info_pair.first) = |
||||
binded_cdev_info_pair.second.limit_info[static_cast<size_t>(curr_severity)]; |
||||
LOG(VERBOSE) << "Hard Limit: Sensor " << sensor_name.data() << " update cdev " |
||||
<< binded_cdev_info_pair.first << " to " |
||||
<< thermal_throttling_status_map_[sensor_name.data()] |
||||
.hardlimit_cdev_request_map.at(binded_cdev_info_pair.first); |
||||
} |
||||
} |
||||
|
||||
bool ThermalThrottling::throttlingReleaseUpdate( |
||||
std::string_view sensor_name, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map, |
||||
const std::unordered_map<std::string, PowerStatus> &power_status_map, |
||||
const ThrottlingSeverity severity, const SensorInfo &sensor_info) { |
||||
ATRACE_CALL(); |
||||
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_); |
||||
if (!thermal_throttling_status_map_.count(sensor_name.data())) { |
||||
return false; |
||||
} |
||||
auto &thermal_throttling_status = thermal_throttling_status_map_.at(sensor_name.data()); |
||||
for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) { |
||||
float avg_power = -1; |
||||
|
||||
if (!thermal_throttling_status.throttling_release_map.count(binded_cdev_info_pair.first) || |
||||
!power_status_map.count(binded_cdev_info_pair.second.power_rail)) { |
||||
return false; |
||||
} |
||||
|
||||
const auto max_state = cooling_device_info_map.at(binded_cdev_info_pair.first).max_state; |
||||
|
||||
auto &release_step = |
||||
thermal_throttling_status.throttling_release_map.at(binded_cdev_info_pair.first); |
||||
avg_power = |
||||
power_status_map.at(binded_cdev_info_pair.second.power_rail).last_updated_avg_power; |
||||
|
||||
if (std::isnan(avg_power) || avg_power < 0) { |
||||
release_step = binded_cdev_info_pair.second.throttling_with_power_link ? max_state : 0; |
||||
continue; |
||||
} |
||||
|
||||
bool is_over_budget = true; |
||||
if (!binded_cdev_info_pair.second.high_power_check) { |
||||
if (avg_power < |
||||
binded_cdev_info_pair.second.power_thresholds[static_cast<int>(severity)]) { |
||||
is_over_budget = false; |
||||
} |
||||
} else { |
||||
if (avg_power > |
||||
binded_cdev_info_pair.second.power_thresholds[static_cast<int>(severity)]) { |
||||
is_over_budget = false; |
||||
} |
||||
} |
||||
LOG(INFO) << sensor_name.data() << "'s " << binded_cdev_info_pair.first |
||||
<< " binded power rail " << binded_cdev_info_pair.second.power_rail |
||||
<< ": power threshold = " |
||||
<< binded_cdev_info_pair.second.power_thresholds[static_cast<int>(severity)] |
||||
<< ", avg power = " << avg_power; |
||||
std::string atrace_prefix = ::android::base::StringPrintf( |
||||
"%s-%s", sensor_name.data(), binded_cdev_info_pair.second.power_rail.data()); |
||||
ATRACE_INT( |
||||
(atrace_prefix + std::string("-power_threshold")).c_str(), |
||||
static_cast<int>( |
||||
binded_cdev_info_pair.second.power_thresholds[static_cast<int>(severity)])); |
||||
ATRACE_INT((atrace_prefix + std::string("-avg_power")).c_str(), avg_power); |
||||
|
||||
switch (binded_cdev_info_pair.second.release_logic) { |
||||
case ReleaseLogic::INCREASE: |
||||
if (!is_over_budget) { |
||||
if (std::abs(release_step) < static_cast<int>(max_state)) { |
||||
release_step--; |
||||
} |
||||
} else { |
||||
release_step = 0; |
||||
} |
||||
break; |
||||
case ReleaseLogic::DECREASE: |
||||
if (!is_over_budget) { |
||||
if (release_step < static_cast<int>(max_state)) { |
||||
release_step++; |
||||
} |
||||
} else { |
||||
release_step = 0; |
||||
} |
||||
break; |
||||
case ReleaseLogic::STEPWISE: |
||||
if (!is_over_budget) { |
||||
if (release_step < static_cast<int>(max_state)) { |
||||
release_step++; |
||||
} |
||||
} else { |
||||
if (std::abs(release_step) < static_cast<int>(max_state)) { |
||||
release_step--; |
||||
} |
||||
} |
||||
break; |
||||
case ReleaseLogic::RELEASE_TO_FLOOR: |
||||
release_step = is_over_budget ? 0 : max_state; |
||||
break; |
||||
case ReleaseLogic::NONE: |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
void ThermalThrottling::thermalThrottlingUpdate( |
||||
const Temperature &temp, const SensorInfo &sensor_info, |
||||
const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, |
||||
const std::unordered_map<std::string, PowerStatus> &power_status_map, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) { |
||||
if (!thermal_throttling_status_map_.count(temp.name)) { |
||||
return; |
||||
} |
||||
|
||||
if (thermal_throttling_status_map_[temp.name].pid_power_budget_map.size()) { |
||||
if (!allocatePowerToCdev(temp, sensor_info, curr_severity, time_elapsed_ms, |
||||
power_status_map, cooling_device_info_map)) { |
||||
LOG(ERROR) << "Sensor " << temp.name << " PID request cdev failed"; |
||||
// Clear the CDEV request if the power budget is failed to be allocated
|
||||
for (auto &pid_cdev_request_pair : |
||||
thermal_throttling_status_map_[temp.name].pid_cdev_request_map) { |
||||
pid_cdev_request_pair.second = 0; |
||||
} |
||||
} |
||||
updateCdevRequestByPower(temp.name, cooling_device_info_map); |
||||
} |
||||
|
||||
if (thermal_throttling_status_map_[temp.name].hardlimit_cdev_request_map.size()) { |
||||
updateCdevRequestBySeverity(temp.name.c_str(), sensor_info, curr_severity); |
||||
} |
||||
|
||||
if (thermal_throttling_status_map_[temp.name].throttling_release_map.size()) { |
||||
throttlingReleaseUpdate(temp.name.c_str(), cooling_device_info_map, power_status_map, |
||||
curr_severity, sensor_info); |
||||
} |
||||
} |
||||
|
||||
void ThermalThrottling::computeCoolingDevicesRequest( |
||||
std::string_view sensor_name, const SensorInfo &sensor_info, |
||||
const ThrottlingSeverity curr_severity, std::vector<std::string> *cooling_devices_to_update, |
||||
ThermalStatsHelper *thermal_stats_helper) { |
||||
int release_step = 0; |
||||
std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_); |
||||
|
||||
if (!thermal_throttling_status_map_.count(sensor_name.data())) { |
||||
return; |
||||
} |
||||
|
||||
auto &thermal_throttling_status = thermal_throttling_status_map_.at(sensor_name.data()); |
||||
const auto &cdev_release_map = thermal_throttling_status.throttling_release_map; |
||||
|
||||
for (auto &cdev_request_pair : thermal_throttling_status.cdev_status_map) { |
||||
int pid_cdev_request = 0; |
||||
int hardlimit_cdev_request = 0; |
||||
const auto &cdev_name = cdev_request_pair.first; |
||||
const auto &binded_cdev_info = |
||||
sensor_info.throttling_info->binded_cdev_info_map.at(cdev_name); |
||||
const auto cdev_ceiling = binded_cdev_info.cdev_ceiling[static_cast<size_t>(curr_severity)]; |
||||
const auto cdev_floor = |
||||
binded_cdev_info.cdev_floor_with_power_link[static_cast<size_t>(curr_severity)]; |
||||
release_step = 0; |
||||
|
||||
if (thermal_throttling_status.pid_cdev_request_map.count(cdev_name)) { |
||||
pid_cdev_request = thermal_throttling_status.pid_cdev_request_map.at(cdev_name); |
||||
} |
||||
|
||||
if (thermal_throttling_status.hardlimit_cdev_request_map.count(cdev_name)) { |
||||
hardlimit_cdev_request = |
||||
thermal_throttling_status.hardlimit_cdev_request_map.at(cdev_name); |
||||
} |
||||
|
||||
if (cdev_release_map.count(cdev_name)) { |
||||
release_step = cdev_release_map.at(cdev_name); |
||||
} |
||||
|
||||
LOG(VERBOSE) << sensor_name.data() << " binded cooling device " << cdev_name |
||||
<< "'s pid_request=" << pid_cdev_request |
||||
<< " hardlimit_cdev_request=" << hardlimit_cdev_request |
||||
<< " release_step=" << release_step |
||||
<< " cdev_floor_with_power_link=" << cdev_floor |
||||
<< " cdev_ceiling=" << cdev_ceiling; |
||||
std::string atrace_prefix = |
||||
::android::base::StringPrintf("%s-%s", sensor_name.data(), cdev_name.data()); |
||||
ATRACE_INT((atrace_prefix + std::string("-pid_request")).c_str(), pid_cdev_request); |
||||
ATRACE_INT((atrace_prefix + std::string("-hardlimit_request")).c_str(), |
||||
hardlimit_cdev_request); |
||||
ATRACE_INT((atrace_prefix + std::string("-release_step")).c_str(), release_step); |
||||
ATRACE_INT((atrace_prefix + std::string("-cdev_floor")).c_str(), cdev_floor); |
||||
ATRACE_INT((atrace_prefix + std::string("-cdev_ceiling")).c_str(), cdev_ceiling); |
||||
|
||||
auto request_state = std::max(pid_cdev_request, hardlimit_cdev_request); |
||||
if (release_step) { |
||||
if (release_step >= request_state) { |
||||
request_state = 0; |
||||
} else { |
||||
request_state = request_state - release_step; |
||||
} |
||||
// Only check the cdev_floor when release step is non zero
|
||||
request_state = std::max(request_state, cdev_floor); |
||||
} |
||||
request_state = std::min(request_state, cdev_ceiling); |
||||
if (cdev_request_pair.second != request_state) { |
||||
if (updateCdevMaxRequestAndNotifyIfChange(cdev_name, cdev_request_pair.second, |
||||
request_state)) { |
||||
cooling_devices_to_update->emplace_back(cdev_name); |
||||
} |
||||
cdev_request_pair.second = request_state; |
||||
// Update sensor cdev request time in state
|
||||
thermal_stats_helper->updateSensorCdevRequestStats(sensor_name, cdev_name, |
||||
cdev_request_pair.second); |
||||
} |
||||
} |
||||
} |
||||
|
||||
bool ThermalThrottling::updateCdevMaxRequestAndNotifyIfChange(std::string_view cdev_name, |
||||
int cur_request, int new_request) { |
||||
std::unique_lock<std::shared_mutex> _lock(cdev_all_request_map_mutex_); |
||||
auto &request_set = cdev_all_request_map_.at(cdev_name.data()); |
||||
int cur_max_request = (*request_set.begin()); |
||||
// Remove old cdev request and add the new one.
|
||||
request_set.erase(request_set.find(cur_request)); |
||||
request_set.insert(new_request); |
||||
// Check if there is any change in aggregated max cdev request.
|
||||
int new_max_request = (*request_set.begin()); |
||||
LOG(VERBOSE) << "For cooling device [" << cdev_name.data() |
||||
<< "] cur_max_request is: " << cur_max_request |
||||
<< " new_max_request is: " << new_max_request; |
||||
return new_max_request != cur_max_request; |
||||
} |
||||
|
||||
bool ThermalThrottling::getCdevMaxRequest(std::string_view cdev_name, int *max_state) { |
||||
std::shared_lock<std::shared_mutex> _lock(cdev_all_request_map_mutex_); |
||||
if (!cdev_all_request_map_.count(cdev_name.data())) { |
||||
LOG(ERROR) << "Cooling device [" << cdev_name.data() |
||||
<< "] not present in cooling device request map"; |
||||
return false; |
||||
} |
||||
*max_state = *cdev_all_request_map_.at(cdev_name.data()).begin(); |
||||
return true; |
||||
} |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,142 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <aidl/android/hardware/thermal/Temperature.h> |
||||
|
||||
#include <queue> |
||||
#include <set> |
||||
#include <shared_mutex> |
||||
#include <string> |
||||
#include <unordered_map> |
||||
#include <unordered_set> |
||||
|
||||
#include "power_files.h" |
||||
#include "thermal_info.h" |
||||
#include "thermal_stats_helper.h" |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
struct ThermalThrottlingStatus { |
||||
std::unordered_map<std::string, int> pid_power_budget_map; |
||||
std::unordered_map<std::string, int> pid_cdev_request_map; |
||||
std::unordered_map<std::string, int> hardlimit_cdev_request_map; |
||||
std::unordered_map<std::string, int> throttling_release_map; |
||||
std::unordered_map<std::string, int> cdev_status_map; |
||||
float prev_err; |
||||
float i_budget; |
||||
float prev_target; |
||||
float prev_power_budget; |
||||
float budget_transient; |
||||
int tran_cycle; |
||||
}; |
||||
|
||||
// Return the control temp target of PID algorithm
|
||||
size_t getTargetStateOfPID(const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity); |
||||
|
||||
// A helper class for conducting thermal throttling
|
||||
class ThermalThrottling { |
||||
public: |
||||
ThermalThrottling() = default; |
||||
~ThermalThrottling() = default; |
||||
// Disallow copy and assign.
|
||||
ThermalThrottling(const ThermalThrottling &) = delete; |
||||
void operator=(const ThermalThrottling &) = delete; |
||||
|
||||
// Clear throttling data
|
||||
void clearThrottlingData(std::string_view sensor_name, const SensorInfo &sensor_info); |
||||
// Register map for throttling algo
|
||||
bool registerThermalThrottling( |
||||
std::string_view sensor_name, const std::shared_ptr<ThrottlingInfo> &throttling_info, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map); |
||||
// Register map for throttling release algo
|
||||
bool registerThrottlingReleaseToWatch(std::string_view sensor_name, std::string_view cdev_name, |
||||
const BindedCdevInfo &binded_cdev_info); |
||||
// Get throttling status map
|
||||
const std::unordered_map<std::string, ThermalThrottlingStatus> &GetThermalThrottlingStatusMap() |
||||
const { |
||||
std::shared_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_); |
||||
return thermal_throttling_status_map_; |
||||
} |
||||
// Update thermal throttling request for the specific sensor
|
||||
void thermalThrottlingUpdate( |
||||
const Temperature &temp, const SensorInfo &sensor_info, |
||||
const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, |
||||
const std::unordered_map<std::string, PowerStatus> &power_status_map, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map); |
||||
|
||||
// Compute the throttling target from all the sensors' request
|
||||
void computeCoolingDevicesRequest(std::string_view sensor_name, const SensorInfo &sensor_info, |
||||
const ThrottlingSeverity curr_severity, |
||||
std::vector<std::string> *cooling_devices_to_update, |
||||
ThermalStatsHelper *thermal_stats_helper); |
||||
// Get the aggregated (from all sensor) max request for a cooling device
|
||||
bool getCdevMaxRequest(std::string_view cdev_name, int *max_state); |
||||
|
||||
private: |
||||
// PID algo - get the total power budget
|
||||
float updatePowerBudget(const Temperature &temp, const SensorInfo &sensor_info, |
||||
std::chrono::milliseconds time_elapsed_ms, |
||||
ThrottlingSeverity curr_severity); |
||||
|
||||
// PID algo - return the power number from excluded power rail list
|
||||
float computeExcludedPower(const SensorInfo &sensor_info, |
||||
const ThrottlingSeverity curr_severity, |
||||
const std::unordered_map<std::string, PowerStatus> &power_status_map, |
||||
std::string *log_buf, std::string_view sensor_name); |
||||
|
||||
// PID algo - allocate the power to target CDEV according to the ODPM
|
||||
bool allocatePowerToCdev( |
||||
const Temperature &temp, const SensorInfo &sensor_info, |
||||
const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, |
||||
const std::unordered_map<std::string, PowerStatus> &power_status_map, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map); |
||||
// PID algo - map the target throttling state according to the power budget
|
||||
void updateCdevRequestByPower( |
||||
std::string sensor_name, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map); |
||||
// Hard limit algo - assign the throttling state according to the severity
|
||||
void updateCdevRequestBySeverity(std::string_view sensor_name, const SensorInfo &sensor_info, |
||||
ThrottlingSeverity curr_severity); |
||||
// Throttling release algo - decide release step according to the predefined power threshold,
|
||||
// return false if the throttling release is not registered in thermal config
|
||||
bool throttlingReleaseUpdate( |
||||
std::string_view sensor_name, |
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map, |
||||
const std::unordered_map<std::string, PowerStatus> &power_status_map, |
||||
const ThrottlingSeverity severity, const SensorInfo &sensor_info); |
||||
// Update the cooling device request set for new request and notify the caller if there is
|
||||
// change in max_request for the cooling device.
|
||||
bool updateCdevMaxRequestAndNotifyIfChange(std::string_view cdev_name, int cur_request, |
||||
int new_request); |
||||
mutable std::shared_mutex thermal_throttling_status_map_mutex_; |
||||
// Thermal throttling status from each sensor
|
||||
std::unordered_map<std::string, ThermalThrottlingStatus> thermal_throttling_status_map_; |
||||
std::shared_mutex cdev_all_request_map_mutex_; |
||||
// Set of all request for a cooling device from each sensor
|
||||
std::unordered_map<std::string, std::multiset<int, std::greater<int>>> cdev_all_request_map_; |
||||
}; |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,533 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#define ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL) |
||||
|
||||
#include "thermal_watcher.h" |
||||
|
||||
#include <android-base/file.h> |
||||
#include <android-base/logging.h> |
||||
#include <android-base/stringprintf.h> |
||||
#include <android-base/strings.h> |
||||
#include <cutils/uevent.h> |
||||
#include <dirent.h> |
||||
#include <linux/netlink.h> |
||||
#include <linux/thermal.h> |
||||
#include <sys/inotify.h> |
||||
#include <sys/resource.h> |
||||
#include <sys/types.h> |
||||
#include <utils/Trace.h> |
||||
|
||||
#include <chrono> |
||||
#include <fstream> |
||||
|
||||
#include "../thermal-helper.h" |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
namespace { |
||||
|
||||
static int nlErrorHandle(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { |
||||
int *ret = reinterpret_cast<int *>(arg); |
||||
*ret = err->error; |
||||
LOG(ERROR) << __func__ << "nl_groups: " << nla->nl_groups << ", nl_pid: " << nla->nl_pid; |
||||
|
||||
return NL_STOP; |
||||
} |
||||
|
||||
static int nlFinishHandle(struct nl_msg *msg, void *arg) { |
||||
int *ret = reinterpret_cast<int *>(arg); |
||||
*ret = 1; |
||||
struct nlmsghdr *nlh = nlmsg_hdr(msg); |
||||
|
||||
LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type; |
||||
|
||||
return NL_OK; |
||||
} |
||||
|
||||
static int nlAckHandle(struct nl_msg *msg, void *arg) { |
||||
int *ret = reinterpret_cast<int *>(arg); |
||||
*ret = 1; |
||||
struct nlmsghdr *nlh = nlmsg_hdr(msg); |
||||
|
||||
LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type; |
||||
|
||||
return NL_OK; |
||||
} |
||||
|
||||
static int nlSeqCheckHandle(struct nl_msg *msg, void *arg) { |
||||
int *ret = reinterpret_cast<int *>(arg); |
||||
*ret = 1; |
||||
struct nlmsghdr *nlh = nlmsg_hdr(msg); |
||||
|
||||
LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type; |
||||
|
||||
return NL_OK; |
||||
} |
||||
|
||||
struct HandlerArgs { |
||||
const char *group; |
||||
int id; |
||||
}; |
||||
|
||||
static int nlSendMsg(struct nl_sock *sock, struct nl_msg *msg, |
||||
int (*rx_handler)(struct nl_msg *, void *), void *data) { |
||||
int err, done = 0; |
||||
|
||||
std::unique_ptr<nl_cb, decltype(&nl_cb_put)> cb(nl_cb_alloc(NL_CB_DEFAULT), nl_cb_put); |
||||
|
||||
err = nl_send_auto_complete(sock, msg); |
||||
if (err < 0) |
||||
return err; |
||||
|
||||
err = 0; |
||||
nl_cb_err(cb.get(), NL_CB_CUSTOM, nlErrorHandle, &err); |
||||
nl_cb_set(cb.get(), NL_CB_FINISH, NL_CB_CUSTOM, nlFinishHandle, &done); |
||||
nl_cb_set(cb.get(), NL_CB_ACK, NL_CB_CUSTOM, nlAckHandle, &done); |
||||
|
||||
if (rx_handler != NULL) |
||||
nl_cb_set(cb.get(), NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data); |
||||
|
||||
while (err == 0 && done == 0) nl_recvmsgs(sock, cb.get()); |
||||
|
||||
return err; |
||||
} |
||||
|
||||
static int nlFamilyHandle(struct nl_msg *msg, void *arg) { |
||||
struct HandlerArgs *grp = reinterpret_cast<struct HandlerArgs *>(arg); |
||||
struct nlattr *tb[CTRL_ATTR_MAX + 1]; |
||||
struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); |
||||
struct nlattr *mcgrp; |
||||
int rem_mcgrp; |
||||
|
||||
nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); |
||||
|
||||
if (!tb[CTRL_ATTR_MCAST_GROUPS]) { |
||||
LOG(ERROR) << __func__ << "Multicast group not found"; |
||||
return -1; |
||||
} |
||||
|
||||
nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) { |
||||
struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1]; |
||||
|
||||
nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, reinterpret_cast<nlattr *>(nla_data(mcgrp)), |
||||
nla_len(mcgrp), NULL); |
||||
|
||||
if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]) |
||||
continue; |
||||
|
||||
if (strncmp(reinterpret_cast<char *>(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])), |
||||
grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])) != 0) |
||||
continue; |
||||
|
||||
grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]); |
||||
|
||||
break; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int nlGetMulticastId(struct nl_sock *sock, const char *family, const char *group) { |
||||
int err = 0, ctrlid; |
||||
struct HandlerArgs grp = { |
||||
.group = group, |
||||
.id = -ENOENT, |
||||
}; |
||||
|
||||
std::unique_ptr<nl_msg, decltype(&nlmsg_free)> msg(nlmsg_alloc(), nlmsg_free); |
||||
|
||||
ctrlid = genl_ctrl_resolve(sock, "nlctrl"); |
||||
|
||||
genlmsg_put(msg.get(), 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0); |
||||
|
||||
nla_put_string(msg.get(), CTRL_ATTR_FAMILY_NAME, family); |
||||
|
||||
err = nlSendMsg(sock, msg.get(), nlFamilyHandle, &grp); |
||||
if (err) |
||||
return err; |
||||
|
||||
err = grp.id; |
||||
LOG(INFO) << group << " multicast_id: " << grp.id; |
||||
|
||||
return err; |
||||
} |
||||
|
||||
static bool socketAddMembership(struct nl_sock *sock, const char *group) { |
||||
int mcid = nlGetMulticastId(sock, THERMAL_GENL_FAMILY_NAME, group); |
||||
if (mcid < 0) { |
||||
LOG(ERROR) << "Failed to get multicast id: " << group; |
||||
return false; |
||||
} |
||||
|
||||
if (nl_socket_add_membership(sock, mcid)) { |
||||
LOG(ERROR) << "Failed to add netlink socket membership: " << group; |
||||
return false; |
||||
} |
||||
|
||||
LOG(INFO) << "Added netlink socket membership: " << group; |
||||
return true; |
||||
} |
||||
|
||||
static int handleEvent(struct nl_msg *n, void *arg) { |
||||
struct nlmsghdr *nlh = nlmsg_hdr(n); |
||||
struct genlmsghdr *glh = genlmsg_hdr(nlh); |
||||
struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1]; |
||||
int *tz_id = reinterpret_cast<int *>(arg); |
||||
|
||||
genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); |
||||
|
||||
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_UP) { |
||||
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_UP"; |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { |
||||
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
} |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]) |
||||
LOG(INFO) << "Thermal zone trip id: " |
||||
<< nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]); |
||||
} |
||||
|
||||
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_DOWN) { |
||||
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_DOWN"; |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { |
||||
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
} |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]) |
||||
LOG(INFO) << "Thermal zone trip id: " |
||||
<< nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]); |
||||
} |
||||
|
||||
if (glh->cmd == THERMAL_GENL_EVENT_TZ_GOV_CHANGE) { |
||||
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_GOV_CHANGE"; |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { |
||||
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
} |
||||
if (attrs[THERMAL_GENL_ATTR_GOV_NAME]) |
||||
LOG(INFO) << "Governor name: " << nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]); |
||||
} |
||||
|
||||
if (glh->cmd == THERMAL_GENL_EVENT_TZ_CREATE) { |
||||
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_CREATE"; |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { |
||||
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
} |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_NAME]) |
||||
LOG(INFO) << "Thermal zone name: " << nla_get_string(attrs[THERMAL_GENL_ATTR_TZ_NAME]); |
||||
} |
||||
|
||||
if (glh->cmd == THERMAL_GENL_EVENT_TZ_DELETE) { |
||||
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_DELETE"; |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { |
||||
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
} |
||||
} |
||||
|
||||
if (glh->cmd == THERMAL_GENL_EVENT_TZ_DISABLE) { |
||||
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_DISABLE"; |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { |
||||
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
} |
||||
} |
||||
|
||||
if (glh->cmd == THERMAL_GENL_EVENT_TZ_ENABLE) { |
||||
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_ENABLE"; |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { |
||||
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
} |
||||
} |
||||
|
||||
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_CHANGE) { |
||||
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_CHANGE"; |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { |
||||
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
} |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]) |
||||
LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]); |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]) |
||||
LOG(INFO) << "Trip type: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]); |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]) |
||||
LOG(INFO) << "Trip temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]); |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]) |
||||
LOG(INFO) << "Trip hyst: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]); |
||||
} |
||||
|
||||
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_ADD) { |
||||
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_ADD"; |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) |
||||
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]) |
||||
LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]); |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]) |
||||
LOG(INFO) << "Trip type: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]); |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]) |
||||
LOG(INFO) << "Trip temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]); |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]) |
||||
LOG(INFO) << "Trip hyst: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]); |
||||
} |
||||
|
||||
if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_DELETE) { |
||||
LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_DELETE"; |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { |
||||
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
} |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]) |
||||
LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]); |
||||
} |
||||
|
||||
if (glh->cmd == THERMAL_GENL_EVENT_CDEV_STATE_UPDATE) { |
||||
LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_STATE_UPDATE"; |
||||
if (attrs[THERMAL_GENL_ATTR_CDEV_ID]) |
||||
LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]); |
||||
if (attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE]) |
||||
LOG(INFO) << "Cooling device current state: " |
||||
<< nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE]); |
||||
} |
||||
|
||||
if (glh->cmd == THERMAL_GENL_EVENT_CDEV_ADD) { |
||||
LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_ADD"; |
||||
if (attrs[THERMAL_GENL_ATTR_CDEV_NAME]) |
||||
LOG(INFO) << "Cooling device name: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_NAME]); |
||||
if (attrs[THERMAL_GENL_ATTR_CDEV_ID]) |
||||
LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]); |
||||
if (attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE]) |
||||
LOG(INFO) << "Cooling device max state: " |
||||
<< nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE]); |
||||
} |
||||
|
||||
if (glh->cmd == THERMAL_GENL_EVENT_CDEV_DELETE) { |
||||
LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_DELETE"; |
||||
if (attrs[THERMAL_GENL_ATTR_CDEV_ID]) |
||||
LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]); |
||||
} |
||||
|
||||
if (glh->cmd == THERMAL_GENL_SAMPLING_TEMP) { |
||||
LOG(INFO) << "THERMAL_GENL_SAMPLING_TEMP"; |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { |
||||
LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
*tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); |
||||
} |
||||
if (attrs[THERMAL_GENL_ATTR_TZ_TEMP]) |
||||
LOG(INFO) << "Thermal zone temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
void ThermalWatcher::registerFilesToWatch(const std::set<std::string> &sensors_to_watch) { |
||||
LOG(INFO) << "Uevent register file to watch..."; |
||||
monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end()); |
||||
|
||||
uevent_fd_.reset((TEMP_FAILURE_RETRY(uevent_open_socket(64 * 1024, true)))); |
||||
if (uevent_fd_.get() < 0) { |
||||
LOG(ERROR) << "failed to open uevent socket"; |
||||
return; |
||||
} |
||||
|
||||
fcntl(uevent_fd_, F_SETFL, O_NONBLOCK); |
||||
|
||||
looper_->addFd(uevent_fd_.get(), 0, ::android::Looper::EVENT_INPUT, nullptr, nullptr); |
||||
sleep_ms_ = std::chrono::milliseconds(0); |
||||
last_update_time_ = boot_clock::now(); |
||||
} |
||||
|
||||
void ThermalWatcher::registerFilesToWatchNl(const std::set<std::string> &sensors_to_watch) { |
||||
LOG(INFO) << "Thermal genl register file to watch..."; |
||||
monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end()); |
||||
|
||||
sk_thermal = nl_socket_alloc(); |
||||
if (!sk_thermal) { |
||||
LOG(ERROR) << "nl_socket_alloc failed"; |
||||
return; |
||||
} |
||||
|
||||
if (genl_connect(sk_thermal)) { |
||||
LOG(ERROR) << "genl_connect failed: sk_thermal"; |
||||
return; |
||||
} |
||||
|
||||
thermal_genl_fd_.reset(nl_socket_get_fd(sk_thermal)); |
||||
if (thermal_genl_fd_.get() < 0) { |
||||
LOG(ERROR) << "Failed to create thermal netlink socket"; |
||||
return; |
||||
} |
||||
|
||||
if (!socketAddMembership(sk_thermal, THERMAL_GENL_EVENT_GROUP_NAME)) { |
||||
return; |
||||
} |
||||
|
||||
/*
|
||||
* Currently, only the update_temperature() will send thermal genl samlping events |
||||
* from kernel. To avoid thermal-hal busy because samlping events are sent |
||||
* too frequently, ignore thermal genl samlping events until we figure out how to use it. |
||||
* |
||||
if (!socketAddMembership(sk_thermal, THERMAL_GENL_SAMPLING_GROUP_NAME)) { |
||||
return; |
||||
} |
||||
*/ |
||||
|
||||
fcntl(thermal_genl_fd_, F_SETFL, O_NONBLOCK); |
||||
looper_->addFd(thermal_genl_fd_.get(), 0, ::android::Looper::EVENT_INPUT, nullptr, nullptr); |
||||
sleep_ms_ = std::chrono::milliseconds(0); |
||||
last_update_time_ = boot_clock::now(); |
||||
} |
||||
|
||||
bool ThermalWatcher::startWatchingDeviceFiles() { |
||||
if (cb_) { |
||||
auto ret = this->run("FileWatcherThread", ::android::PRIORITY_HIGHEST); |
||||
if (ret != ::android::NO_ERROR) { |
||||
LOG(ERROR) << "ThermalWatcherThread start fail"; |
||||
return false; |
||||
} else { |
||||
LOG(INFO) << "ThermalWatcherThread started"; |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
void ThermalWatcher::parseUevent(std::set<std::string> *sensors_set) { |
||||
bool thermal_event = false; |
||||
constexpr int kUeventMsgLen = 2048; |
||||
char msg[kUeventMsgLen + 2]; |
||||
char *cp; |
||||
|
||||
while (true) { |
||||
int n = uevent_kernel_multicast_recv(uevent_fd_.get(), msg, kUeventMsgLen); |
||||
if (n <= 0) { |
||||
if (errno != EAGAIN && errno != EWOULDBLOCK) { |
||||
LOG(ERROR) << "Error reading from Uevent Fd"; |
||||
} |
||||
break; |
||||
} |
||||
|
||||
if (n >= kUeventMsgLen) { |
||||
LOG(ERROR) << "Uevent overflowed buffer, discarding"; |
||||
continue; |
||||
} |
||||
|
||||
msg[n] = '\0'; |
||||
msg[n + 1] = '\0'; |
||||
|
||||
cp = msg; |
||||
while (*cp) { |
||||
std::string uevent = cp; |
||||
auto findSubSystemThermal = uevent.find("SUBSYSTEM=thermal"); |
||||
if (!thermal_event) { |
||||
if (::android::base::StartsWith(uevent, "SUBSYSTEM=")) { |
||||
if (findSubSystemThermal != std::string::npos) { |
||||
thermal_event = true; |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
} else { |
||||
auto start_pos = uevent.find("NAME="); |
||||
if (start_pos != std::string::npos) { |
||||
start_pos += 5; |
||||
std::string name = uevent.substr(start_pos); |
||||
if (monitored_sensors_.find(name) != monitored_sensors_.end()) { |
||||
sensors_set->insert(name); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
while (*cp++) { |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// TODO(b/175367921): Consider for potentially adding more type of event in the function
|
||||
// instead of just add the sensors to the list.
|
||||
void ThermalWatcher::parseGenlink(std::set<std::string> *sensors_set) { |
||||
int err = 0, done = 0, tz_id = -1; |
||||
|
||||
std::unique_ptr<nl_cb, decltype(&nl_cb_put)> cb(nl_cb_alloc(NL_CB_DEFAULT), nl_cb_put); |
||||
|
||||
nl_cb_err(cb.get(), NL_CB_CUSTOM, nlErrorHandle, &err); |
||||
nl_cb_set(cb.get(), NL_CB_FINISH, NL_CB_CUSTOM, nlFinishHandle, &done); |
||||
nl_cb_set(cb.get(), NL_CB_ACK, NL_CB_CUSTOM, nlAckHandle, &done); |
||||
nl_cb_set(cb.get(), NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nlSeqCheckHandle, &done); |
||||
nl_cb_set(cb.get(), NL_CB_VALID, NL_CB_CUSTOM, handleEvent, &tz_id); |
||||
|
||||
while (!done && !err) { |
||||
nl_recvmsgs(sk_thermal, cb.get()); |
||||
|
||||
if (tz_id < 0) { |
||||
break; |
||||
} |
||||
|
||||
std::string name; |
||||
if (getThermalZoneTypeById(tz_id, &name) && |
||||
monitored_sensors_.find(name) != monitored_sensors_.end()) { |
||||
sensors_set->insert(name); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void ThermalWatcher::wake() { |
||||
looper_->wake(); |
||||
} |
||||
|
||||
bool ThermalWatcher::threadLoop() { |
||||
LOG(VERBOSE) << "ThermalWatcher polling..."; |
||||
|
||||
int fd; |
||||
std::set<std::string> sensors; |
||||
|
||||
auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - |
||||
last_update_time_); |
||||
|
||||
if (time_elapsed_ms < sleep_ms_ && |
||||
looper_->pollOnce(sleep_ms_.count(), &fd, nullptr, nullptr) >= 0) { |
||||
ATRACE_NAME("ThermalWatcher::threadLoop - receive event"); |
||||
if (fd != uevent_fd_.get() && fd != thermal_genl_fd_.get()) { |
||||
return true; |
||||
} else if (fd == thermal_genl_fd_.get()) { |
||||
parseGenlink(&sensors); |
||||
} else if (fd == uevent_fd_.get()) { |
||||
parseUevent(&sensors); |
||||
} |
||||
// Ignore cb_ if uevent is not from monitored sensors
|
||||
if (sensors.size() == 0) { |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
sleep_ms_ = cb_(sensors); |
||||
last_update_time_ = boot_clock::now(); |
||||
return true; |
||||
} |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
@ -0,0 +1,114 @@ |
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <android-base/chrono_utils.h> |
||||
#include <android-base/unique_fd.h> |
||||
#include <linux/genetlink.h> |
||||
#include <netlink/genl/ctrl.h> |
||||
#include <netlink/genl/genl.h> |
||||
#include <utils/Looper.h> |
||||
#include <utils/Thread.h> |
||||
|
||||
#include <chrono> |
||||
#include <condition_variable> |
||||
#include <future> |
||||
#include <list> |
||||
#include <mutex> |
||||
#include <set> |
||||
#include <string> |
||||
#include <thread> |
||||
#include <unordered_map> |
||||
#include <vector> |
||||
|
||||
namespace aidl { |
||||
namespace android { |
||||
namespace hardware { |
||||
namespace thermal { |
||||
namespace implementation { |
||||
|
||||
using ::android::base::boot_clock; |
||||
using ::android::base::unique_fd; |
||||
using WatcherCallback = std::function<std::chrono::milliseconds(const std::set<std::string> &name)>; |
||||
|
||||
// A helper class for monitoring thermal files changes.
|
||||
class ThermalWatcher : public ::android::Thread { |
||||
public: |
||||
explicit ThermalWatcher(const WatcherCallback &cb) |
||||
: Thread(false), cb_(cb), looper_(new ::android::Looper(true)) {} |
||||
~ThermalWatcher() = default; |
||||
|
||||
// Disallow copy and assign.
|
||||
ThermalWatcher(const ThermalWatcher &) = delete; |
||||
void operator=(const ThermalWatcher &) = delete; |
||||
|
||||
// Start the thread and return true if it succeeds.
|
||||
bool startWatchingDeviceFiles(); |
||||
// Give the file watcher a list of files to start watching. This helper
|
||||
// class will by default wait for modifications to the file with a looper.
|
||||
// This should be called before starting watcher thread.
|
||||
// For monitoring uevents.
|
||||
void registerFilesToWatch(const std::set<std::string> &sensors_to_watch); |
||||
// For monitoring thermal genl events.
|
||||
void registerFilesToWatchNl(const std::set<std::string> &sensors_to_watch); |
||||
// Wake up the looper thus the worker thread, immediately. This can be called
|
||||
// in any thread.
|
||||
void wake(); |
||||
|
||||
private: |
||||
// The work done by the watcher thread. This will use inotify to check for
|
||||
// modifications to the files to watch. If any modification is seen this
|
||||
// will callback the registered function with the new data read from the
|
||||
// modified file.
|
||||
bool threadLoop() override; |
||||
|
||||
// Parse uevent message
|
||||
void parseUevent(std::set<std::string> *sensor_name); |
||||
|
||||
// Parse thermal netlink message
|
||||
void parseGenlink(std::set<std::string> *sensor_name); |
||||
|
||||
// Maps watcher filer descriptor to watched file path.
|
||||
std::unordered_map<int, std::string> watch_to_file_path_map_; |
||||
|
||||
// The callback function. Called whenever thermal uevent is seen.
|
||||
// The function passed in should expect a string in the form (type).
|
||||
// Where type is the name of the thermal zone that trigger a uevent notification.
|
||||
// Callback will return thermal trigger status for next polling decision.
|
||||
const WatcherCallback cb_; |
||||
|
||||
::android::sp<::android::Looper> looper_; |
||||
|
||||
// For uevent socket registration.
|
||||
::android::base::unique_fd uevent_fd_; |
||||
// For thermal genl socket registration.
|
||||
::android::base::unique_fd thermal_genl_fd_; |
||||
// Sensor list which monitor flag is enabled.
|
||||
std::set<std::string> monitored_sensors_; |
||||
// Sleep interval voting result
|
||||
std::chrono::milliseconds sleep_ms_; |
||||
// Timestamp for last thermal update
|
||||
boot_clock::time_point last_update_time_; |
||||
// For thermal genl socket object.
|
||||
struct nl_sock *sk_thermal; |
||||
}; |
||||
|
||||
} // namespace implementation
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
Loading…
Reference in new issue