mirror of
https://github.com/PixelExperience-Devices/device_xiaomi_sm6250-common.git
synced 2025-04-29 02:27:21 +09:00
sm6250-common: Update thermal HAL
* From hardware/google/pixel at d774cbb949e98627e4172bf8fc11e8d954599aa7. Change-Id: I3a3a0c29575d0595e71a30f1e64e33ca34d2eb27
This commit is contained in:
parent
27f575c973
commit
7f99605ccc
3
atoll.mk
3
atoll.mk
@ -386,7 +386,8 @@ PRODUCT_BOOT_JARS += \
|
|||||||
|
|
||||||
# Thermal HAL
|
# Thermal HAL
|
||||||
PRODUCT_PACKAGES += \
|
PRODUCT_PACKAGES += \
|
||||||
android.hardware.thermal@2.0-service.xiaomi_atoll
|
android.hardware.thermal@2.0-service.xiaomi_atoll \
|
||||||
|
thermal_symlinks
|
||||||
|
|
||||||
PRODUCT_COPY_FILES += \
|
PRODUCT_COPY_FILES += \
|
||||||
$(LOCAL_PATH)/configs/thermal_info_config.json:$(TARGET_COPY_OUT_VENDOR)/etc/thermal_info_config.json
|
$(LOCAL_PATH)/configs/thermal_info_config.json:$(TARGET_COPY_OUT_VENDOR)/etc/thermal_info_config.json
|
||||||
|
2
sepolicy/vendor/file_contexts
vendored
2
sepolicy/vendor/file_contexts
vendored
@ -44,3 +44,5 @@
|
|||||||
|
|
||||||
# Thermal
|
# Thermal
|
||||||
/vendor/bin/hw/android\.hardware\.thermal@2\.0-service\.xiaomi_atoll u:object_r:hal_thermal_default_exec:s0
|
/vendor/bin/hw/android\.hardware\.thermal@2\.0-service\.xiaomi_atoll u:object_r:hal_thermal_default_exec:s0
|
||||||
|
/vendor/bin/thermal_symlinks u:object_r:init-thermal-symlinks-sh_exec:s0
|
||||||
|
/dev/thermal(/.*)? u:object_r:thermal_link_device:s0
|
||||||
|
12
sepolicy/vendor/init-thermal-symlinks.sh.te
vendored
Normal file
12
sepolicy/vendor/init-thermal-symlinks.sh.te
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
type init-thermal-symlinks-sh, domain;
|
||||||
|
type init-thermal-symlinks-sh_exec, exec_type, vendor_file_type, file_type;
|
||||||
|
|
||||||
|
init_daemon_domain(init-thermal-symlinks-sh)
|
||||||
|
|
||||||
|
allow init-thermal-symlinks-sh vendor_toolbox_exec:file rx_file_perms;
|
||||||
|
allow init-thermal-symlinks-sh thermal_link_device:dir rw_dir_perms;
|
||||||
|
allow init-thermal-symlinks-sh thermal_link_device:lnk_file create_file_perms;
|
||||||
|
allow init-thermal-symlinks-sh sysfs_thermal:dir r_dir_perms;
|
||||||
|
allow init-thermal-symlinks-sh sysfs_thermal:file r_file_perms;
|
||||||
|
allow init-thermal-symlinks-sh sysfs_thermal:lnk_file r_file_perms;
|
||||||
|
set_prop(init-thermal-symlinks-sh, vendor_thermal_prop)
|
@ -16,6 +16,7 @@ cc_binary {
|
|||||||
"utils/config_parser.cpp",
|
"utils/config_parser.cpp",
|
||||||
"utils/thermal_files.cpp",
|
"utils/thermal_files.cpp",
|
||||||
"utils/thermal_watcher.cpp",
|
"utils/thermal_watcher.cpp",
|
||||||
|
"utils/power_files.cpp",
|
||||||
],
|
],
|
||||||
shared_libs: [
|
shared_libs: [
|
||||||
"libbase",
|
"libbase",
|
||||||
@ -23,11 +24,12 @@ cc_binary {
|
|||||||
"libhidlbase",
|
"libhidlbase",
|
||||||
"libjsoncpp",
|
"libjsoncpp",
|
||||||
"libutils",
|
"libutils",
|
||||||
|
"libnl",
|
||||||
"libbinder_ndk",
|
"libbinder_ndk",
|
||||||
"android.hardware.thermal@1.0",
|
"android.hardware.thermal@1.0",
|
||||||
"android.hardware.thermal@2.0",
|
"android.hardware.thermal@2.0",
|
||||||
"android.hardware.power-ndk_platform",
|
"android.hardware.power-V1-ndk_platform",
|
||||||
"pixel-power-ext-ndk_platform"
|
"pixel-power-ext-V1-ndk_platform"
|
||||||
],
|
],
|
||||||
cflags: [
|
cflags: [
|
||||||
"-Wall",
|
"-Wall",
|
||||||
@ -45,3 +47,12 @@ cc_binary {
|
|||||||
"-warnings-as-errors=android-*,clang-analyzer-security*,cert-*"
|
"-warnings-as-errors=android-*,clang-analyzer-security*,cert-*"
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sh_binary {
|
||||||
|
name: "xiaomi_atoll_thermal_symlinks",
|
||||||
|
src: "init.thermal.symlinks.sh",
|
||||||
|
vendor: true,
|
||||||
|
init_rc: [
|
||||||
|
"xiaomi_atoll-thermal-symlinks.rc",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
@ -36,7 +36,6 @@ namespace {
|
|||||||
using ::android::hardware::interfacesEqual;
|
using ::android::hardware::interfacesEqual;
|
||||||
using ::android::hardware::thermal::V1_0::ThermalStatus;
|
using ::android::hardware::thermal::V1_0::ThermalStatus;
|
||||||
using ::android::hardware::thermal::V1_0::ThermalStatusCode;
|
using ::android::hardware::thermal::V1_0::ThermalStatusCode;
|
||||||
using ::android::hidl::base::V1_0::IBase;
|
|
||||||
|
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
Return<void> setFailureAndCallback(T _hidl_cb, hidl_vec<U> data, std::string_view debug_msg) {
|
Return<void> setFailureAndCallback(T _hidl_cb, hidl_vec<U> data, std::string_view debug_msg) {
|
||||||
@ -234,16 +233,15 @@ Return<void> Thermal::unregisterThermalChangedCallback(
|
|||||||
return Void();
|
return Void();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thermal::sendThermalChangedCallback(const std::vector<Temperature_2_0> &temps) {
|
void Thermal::sendThermalChangedCallback(const Temperature_2_0 &t) {
|
||||||
std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
|
std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
|
||||||
for (auto &t : temps) {
|
|
||||||
LOG(INFO) << "Sending notification: "
|
|
||||||
<< " Type: " << android::hardware::thermal::V2_0::toString(t.type)
|
|
||||||
<< " Name: " << t.name << " CurrentValue: " << t.value << " ThrottlingStatus: "
|
|
||||||
<< android::hardware::thermal::V2_0::toString(t.throttlingStatus);
|
|
||||||
|
|
||||||
thermal_helper_.sendPowerExtHint(t);
|
LOG(VERBOSE) << "Sending notification: "
|
||||||
callbacks_.erase(
|
<< " Type: " << android::hardware::thermal::V2_0::toString(t.type)
|
||||||
|
<< " Name: " << t.name << " CurrentValue: " << t.value << " ThrottlingStatus: "
|
||||||
|
<< android::hardware::thermal::V2_0::toString(t.throttlingStatus);
|
||||||
|
|
||||||
|
callbacks_.erase(
|
||||||
std::remove_if(callbacks_.begin(), callbacks_.end(),
|
std::remove_if(callbacks_.begin(), callbacks_.end(),
|
||||||
[&](const CallbackSetting &c) {
|
[&](const CallbackSetting &c) {
|
||||||
if (!c.is_filter_type || t.type == c.type) {
|
if (!c.is_filter_type || t.type == c.type) {
|
||||||
@ -255,6 +253,276 @@ void Thermal::sendThermalChangedCallback(const std::vector<Temperature_2_0> &tem
|
|||||||
return false;
|
return false;
|
||||||
}),
|
}),
|
||||||
callbacks_.end());
|
callbacks_.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) {
|
||||||
|
*dump_buf << "VirtualSensorInfo:" << 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: "
|
||||||
|
<< (sensor_info_pair.second.virtual_sensor_info->trigger_sensor.empty()
|
||||||
|
? "N/A"
|
||||||
|
: sensor_info_pair.second.virtual_sensor_info->trigger_sensor)
|
||||||
|
<< 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 << "Throttling Info:" << std::endl;
|
||||||
|
const auto &map = thermal_helper_.GetSensorInfoMap();
|
||||||
|
for (const auto &name_info_pair : map) {
|
||||||
|
if (name_info_pair.second.throttling_info->binded_cdev_info_map.size()) {
|
||||||
|
*dump_buf << " Name: " << name_info_pair.first << std::endl;
|
||||||
|
*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;
|
||||||
|
if (name_info_pair.second.throttling_info->binded_cdev_info_map.size()) {
|
||||||
|
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;
|
||||||
|
*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 << " 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 << " 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 << " 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 &sensor_status_map = thermal_helper_.GetSensorStatusMap();
|
||||||
|
const auto &cdev_status_map = thermal_helper_.GetCdevStatusMap();
|
||||||
|
const auto &release_map = thermal_helper_.GetThrottlingReleaseMap();
|
||||||
|
*dump_buf << "Throttling Request Status " << std::endl;
|
||||||
|
for (const auto &cdev_status_pair : cdev_status_map) {
|
||||||
|
*dump_buf << " Name: " << cdev_status_pair.first << std::endl;
|
||||||
|
for (const auto &request_pair : cdev_status_pair.second) {
|
||||||
|
*dump_buf << " Request Sensor: " << request_pair.first << std::endl;
|
||||||
|
*dump_buf << " Request Throttling State: " << request_pair.second << std::endl;
|
||||||
|
if (sensor_status_map.at(request_pair.first).pid_request_map.size() &&
|
||||||
|
sensor_status_map.at(request_pair.first)
|
||||||
|
.pid_request_map.count(cdev_status_pair.first)) {
|
||||||
|
*dump_buf << " PID Request State: "
|
||||||
|
<< sensor_status_map.at(request_pair.first)
|
||||||
|
.pid_request_map.at(cdev_status_pair.first)
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
if (sensor_status_map.at(request_pair.first).hard_limit_request_map.size() &&
|
||||||
|
sensor_status_map.at(request_pair.first)
|
||||||
|
.hard_limit_request_map.count(cdev_status_pair.first)) {
|
||||||
|
*dump_buf << " Hard Limit Request State: "
|
||||||
|
<< sensor_status_map.at(request_pair.first)
|
||||||
|
.hard_limit_request_map.at(cdev_status_pair.first)
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
if (release_map.count(request_pair.first) &&
|
||||||
|
release_map.at(request_pair.first).count(cdev_status_pair.first)) {
|
||||||
|
const auto &cdev_release_info =
|
||||||
|
release_map.at(request_pair.first).at(cdev_status_pair.first);
|
||||||
|
*dump_buf << " Release Step: " << cdev_release_info.release_step << 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 << "Power Rail Info " << 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;
|
||||||
|
for (const auto &power_status_pair : power_status_map) {
|
||||||
|
if (power_status_pair.second.count(power_rail_pair.first)) {
|
||||||
|
auto power_history =
|
||||||
|
power_status_pair.second.at(power_rail_pair.first).power_history;
|
||||||
|
*dump_buf << " Request Sensor: " << power_status_pair.first << std::endl;
|
||||||
|
*dump_buf
|
||||||
|
<< " Last Updated AVG Power: "
|
||||||
|
<< power_status_pair.second.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,24 +639,31 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
dump_buf << "Monitor:" << std::endl;
|
dump_buf << "SendCallback" << std::endl;
|
||||||
|
dump_buf << " Enabled List: ";
|
||||||
const auto &map = thermal_helper_.GetSensorInfoMap();
|
const auto &map = thermal_helper_.GetSensorInfoMap();
|
||||||
for (const auto &name_info_pair : map) {
|
for (const auto &name_info_pair : map) {
|
||||||
dump_buf << " Name: " << name_info_pair.first;
|
if (name_info_pair.second.send_cb) {
|
||||||
dump_buf << " Monitor: " << std::boolalpha << name_info_pair.second.is_monitor
|
dump_buf << name_info_pair.first << " ";
|
||||||
<< std::noboolalpha << std::endl;
|
}
|
||||||
}
|
}
|
||||||
|
dump_buf << std::endl;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
dump_buf << "SendPowerHint:" << std::endl;
|
dump_buf << "SendPowerHint" << std::endl;
|
||||||
|
dump_buf << " Enabled List: ";
|
||||||
const auto &map = thermal_helper_.GetSensorInfoMap();
|
const auto &map = thermal_helper_.GetSensorInfoMap();
|
||||||
for (const auto &name_info_pair : map) {
|
for (const auto &name_info_pair : map) {
|
||||||
dump_buf << " Name: " << name_info_pair.first;
|
if (name_info_pair.second.send_powerhint) {
|
||||||
dump_buf << " SendPowerHint: " << std::boolalpha
|
dump_buf << name_info_pair.first << " ";
|
||||||
<< name_info_pair.second.send_powerhint << std::noboolalpha
|
}
|
||||||
<< std::endl;
|
|
||||||
}
|
}
|
||||||
|
dump_buf << std::endl;
|
||||||
}
|
}
|
||||||
|
dumpVirtualSensorInfo(&dump_buf);
|
||||||
|
dumpThrottlingInfo(&dump_buf);
|
||||||
|
dumpThrottlingRequestStatus(&dump_buf);
|
||||||
|
dumpPowerRailInfo(&dump_buf);
|
||||||
{
|
{
|
||||||
dump_buf << "AIDL Power Hal exist: " << std::boolalpha
|
dump_buf << "AIDL Power Hal exist: " << std::boolalpha
|
||||||
<< thermal_helper_.isAidlPowerHalExist() << std::endl;
|
<< thermal_helper_.isAidlPowerHalExist() << std::endl;
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
#ifndef ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
|
|
||||||
#define ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
|
#pragma once
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -40,7 +40,7 @@ using ::android::hardware::thermal::V2_0::IThermalChangedCallback;
|
|||||||
struct CallbackSetting {
|
struct CallbackSetting {
|
||||||
CallbackSetting(sp<IThermalChangedCallback> callback, bool is_filter_type,
|
CallbackSetting(sp<IThermalChangedCallback> callback, bool is_filter_type,
|
||||||
TemperatureType_2_0 type)
|
TemperatureType_2_0 type)
|
||||||
: callback(callback), is_filter_type(is_filter_type), type(type) {}
|
: callback(std::move(callback)), is_filter_type(is_filter_type), type(type) {}
|
||||||
sp<IThermalChangedCallback> callback;
|
sp<IThermalChangedCallback> callback;
|
||||||
bool is_filter_type;
|
bool is_filter_type;
|
||||||
TemperatureType_2_0 type;
|
TemperatureType_2_0 type;
|
||||||
@ -65,9 +65,9 @@ class Thermal : public IThermal {
|
|||||||
getCurrentTemperatures_cb _hidl_cb) override;
|
getCurrentTemperatures_cb _hidl_cb) override;
|
||||||
Return<void> getTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
|
Return<void> getTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
|
||||||
getTemperatureThresholds_cb _hidl_cb) override;
|
getTemperatureThresholds_cb _hidl_cb) override;
|
||||||
Return<void> registerThermalChangedCallback(const sp<IThermalChangedCallback> &callback,
|
Return<void> registerThermalChangedCallback(
|
||||||
bool filterType, TemperatureType_2_0 type,
|
const sp<IThermalChangedCallback> &callback, bool filterType, TemperatureType_2_0 type,
|
||||||
registerThermalChangedCallback_cb _hidl_cb) override;
|
registerThermalChangedCallback_cb _hidl_cb) override;
|
||||||
Return<void> unregisterThermalChangedCallback(
|
Return<void> unregisterThermalChangedCallback(
|
||||||
const sp<IThermalChangedCallback> &callback,
|
const sp<IThermalChangedCallback> &callback,
|
||||||
unregisterThermalChangedCallback_cb _hidl_cb) override;
|
unregisterThermalChangedCallback_cb _hidl_cb) override;
|
||||||
@ -78,10 +78,14 @@ class Thermal : public IThermal {
|
|||||||
Return<void> debug(const hidl_handle &fd, const hidl_vec<hidl_string> &args) override;
|
Return<void> debug(const hidl_handle &fd, const hidl_vec<hidl_string> &args) override;
|
||||||
|
|
||||||
// Helper function for calling callbacks
|
// Helper function for calling callbacks
|
||||||
void sendThermalChangedCallback(const std::vector<Temperature_2_0> &temps);
|
void sendThermalChangedCallback(const Temperature_2_0 &t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ThermalHelper thermal_helper_;
|
ThermalHelper thermal_helper_;
|
||||||
|
void dumpVirtualSensorInfo(std::ostringstream *dump_buf);
|
||||||
|
void dumpThrottlingInfo(std::ostringstream *dump_buf);
|
||||||
|
void dumpThrottlingRequestStatus(std::ostringstream *dump_buf);
|
||||||
|
void dumpPowerRailInfo(std::ostringstream *dump_buf);
|
||||||
std::mutex thermal_callback_mutex_;
|
std::mutex thermal_callback_mutex_;
|
||||||
std::vector<CallbackSetting> callbacks_;
|
std::vector<CallbackSetting> callbacks_;
|
||||||
};
|
};
|
||||||
@ -91,5 +95,3 @@ class Thermal : public IThermal {
|
|||||||
} // namespace thermal
|
} // namespace thermal
|
||||||
} // namespace hardware
|
} // namespace hardware
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
||||||
#endif // ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
|
|
||||||
|
13
thermal/init.thermal.symlinks.sh
Executable file
13
thermal/init.thermal.symlinks.sh
Executable file
@ -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
|
@ -17,6 +17,8 @@
|
|||||||
#include <hidl/HidlTransportSupport.h>
|
#include <hidl/HidlTransportSupport.h>
|
||||||
#include "Thermal.h"
|
#include "Thermal.h"
|
||||||
|
|
||||||
|
constexpr std::string_view kThermalLogTag("pixel-thermal");
|
||||||
|
|
||||||
using ::android::OK;
|
using ::android::OK;
|
||||||
using ::android::status_t;
|
using ::android::status_t;
|
||||||
|
|
||||||
@ -34,6 +36,7 @@ static int shutdown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int /* argc */, char ** /* argv */) {
|
int main(int /* argc */, char ** /* argv */) {
|
||||||
|
android::base::SetDefaultTag(kThermalLogTag.data());
|
||||||
status_t status;
|
status_t status;
|
||||||
android::sp<IThermal> service = nullptr;
|
android::sp<IThermal> service = nullptr;
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -27,8 +27,7 @@
|
|||||||
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef THERMAL_THERMAL_HELPER_H__
|
#pragma once
|
||||||
#define THERMAL_THERMAL_HELPER_H__
|
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@ -45,6 +44,7 @@
|
|||||||
#include <android/hardware/thermal/2.0/IThermal.h>
|
#include <android/hardware/thermal/2.0/IThermal.h>
|
||||||
|
|
||||||
#include "utils/config_parser.h"
|
#include "utils/config_parser.h"
|
||||||
|
#include "utils/power_files.h"
|
||||||
#include "utils/thermal_files.h"
|
#include "utils/thermal_files.h"
|
||||||
#include "utils/thermal_watcher.h"
|
#include "utils/thermal_watcher.h"
|
||||||
|
|
||||||
@ -69,14 +69,23 @@ using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
|
|||||||
using ::android::hardware::thermal::V2_0::TemperatureThreshold;
|
using ::android::hardware::thermal::V2_0::TemperatureThreshold;
|
||||||
using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
|
using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
|
||||||
|
|
||||||
using NotificationCallback = std::function<void(const std::vector<Temperature_2_0> &temps)>;
|
using NotificationCallback = std::function<void(const Temperature_2_0 &t)>;
|
||||||
using NotificationTime = std::chrono::time_point<std::chrono::steady_clock>;
|
using NotificationTime = std::chrono::time_point<std::chrono::steady_clock>;
|
||||||
|
using CdevRequestStatus = std::unordered_map<std::string, int>;
|
||||||
|
|
||||||
|
// Get thermal_zone type
|
||||||
|
bool getThermalZoneTypeById(int tz_id, std::string *);
|
||||||
|
|
||||||
struct SensorStatus {
|
struct SensorStatus {
|
||||||
ThrottlingSeverity severity;
|
ThrottlingSeverity severity;
|
||||||
ThrottlingSeverity prev_hot_severity;
|
ThrottlingSeverity prev_hot_severity;
|
||||||
ThrottlingSeverity prev_cold_severity;
|
ThrottlingSeverity prev_cold_severity;
|
||||||
ThrottlingSeverity prev_hint_severity;
|
ThrottlingSeverity prev_hint_severity;
|
||||||
|
boot_clock::time_point last_update_time;
|
||||||
|
std::unordered_map<std::string, int> pid_request_map;
|
||||||
|
std::unordered_map<std::string, int> hard_limit_request_map;
|
||||||
|
float err_integral;
|
||||||
|
float prev_err;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PowerHalService {
|
class PowerHalService {
|
||||||
@ -99,7 +108,7 @@ class PowerHalService {
|
|||||||
|
|
||||||
class ThermalHelper {
|
class ThermalHelper {
|
||||||
public:
|
public:
|
||||||
ThermalHelper(const NotificationCallback &cb);
|
explicit ThermalHelper(const NotificationCallback &cb);
|
||||||
~ThermalHelper() = default;
|
~ThermalHelper() = default;
|
||||||
|
|
||||||
bool fillTemperatures(hidl_vec<Temperature_1_0> *temperatures) const;
|
bool fillTemperatures(hidl_vec<Temperature_1_0> *temperatures) const;
|
||||||
@ -118,52 +127,104 @@ class ThermalHelper {
|
|||||||
bool isInitializedOk() const { return is_initialized_; }
|
bool isInitializedOk() const { return is_initialized_; }
|
||||||
|
|
||||||
// Read the temperature of a single sensor.
|
// Read the temperature of a single sensor.
|
||||||
bool readTemperature(std::string_view sensor_name, Temperature_1_0 *out) const;
|
bool readTemperature(std::string_view sensor_name, Temperature_1_0 *out,
|
||||||
|
bool is_virtual_sensor = false) const;
|
||||||
bool readTemperature(
|
bool readTemperature(
|
||||||
std::string_view sensor_name, Temperature_2_0 *out,
|
std::string_view sensor_name, Temperature_2_0 *out,
|
||||||
std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status = nullptr) const;
|
std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status = nullptr,
|
||||||
|
bool is_virtual_sensor = false) const;
|
||||||
bool readTemperatureThreshold(std::string_view sensor_name, TemperatureThreshold *out) const;
|
bool readTemperatureThreshold(std::string_view sensor_name, TemperatureThreshold *out) const;
|
||||||
// Read the value of a single cooling device.
|
// Read the value of a single cooling device.
|
||||||
bool readCoolingDevice(std::string_view cooling_device, CoolingDevice_2_0 *out) const;
|
bool readCoolingDevice(std::string_view cooling_device, CoolingDevice_2_0 *out) const;
|
||||||
// Get SensorInfo Map
|
// Get SensorInfo Map
|
||||||
const std::map<std::string, SensorInfo> &GetSensorInfoMap() const { return sensor_info_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 PowerRailInfo Map
|
||||||
|
const std::unordered_map<std::string, PowerRailInfo> &GetPowerRailInfoMap() const {
|
||||||
|
return power_rail_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 CdevStatus Map
|
||||||
|
const std::unordered_map<std::string, CdevRequestStatus> &GetCdevStatusMap() const {
|
||||||
|
std::shared_lock<std::shared_mutex> _lock(cdev_status_map_mutex_);
|
||||||
|
return cdev_status_map_;
|
||||||
|
}
|
||||||
|
// Get ThrottlingRelease Map
|
||||||
|
const std::unordered_map<std::string, CdevReleaseStatus> &GetThrottlingReleaseMap() const {
|
||||||
|
return power_files_.GetThrottlingReleaseMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get PowerStatus Map
|
||||||
|
const std::unordered_map<std::string, PowerStatusMap> &GetPowerStatusMap() const {
|
||||||
|
return power_files_.GetPowerStatusMap();
|
||||||
|
}
|
||||||
|
|
||||||
void sendPowerExtHint(const Temperature_2_0 &t);
|
void sendPowerExtHint(const Temperature_2_0 &t);
|
||||||
|
|
||||||
bool isAidlPowerHalExist() { return power_hal_service_.isAidlPowerHalExist(); }
|
bool isAidlPowerHalExist() { return power_hal_service_.isAidlPowerHalExist(); }
|
||||||
bool isPowerHalConnected() { return power_hal_service_.isPowerHalConnected(); }
|
bool isPowerHalConnected() { return power_hal_service_.isPowerHalConnected(); }
|
||||||
bool isPowerHalExtConnected() { return power_hal_service_.isPowerHalExtConnected(); }
|
bool isPowerHalExtConnected() { return power_hal_service_.isPowerHalExtConnected(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool initializeSensorMap(const std::map<std::string, std::string> &path_map);
|
bool initializeSensorMap(const std::unordered_map<std::string, std::string> &path_map);
|
||||||
bool initializeCoolingDevices(const std::map<std::string, std::string> &path_map);
|
bool initializeCoolingDevices(const std::unordered_map<std::string, std::string> &path_map);
|
||||||
bool initializeTrip(const std::map<std::string, std::string> &path_map);
|
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);
|
||||||
|
|
||||||
// For thermal_watcher_'s polling thread
|
// For thermal_watcher_'s polling thread, return the sleep interval
|
||||||
bool thermalWatcherCallbackFunc(const std::set<std::string> &uevent_sensors);
|
std::chrono::milliseconds thermalWatcherCallbackFunc(
|
||||||
|
const std::set<std::string> &uevent_sensors);
|
||||||
// Return hot and cold severity status as std::pair
|
// Return hot and cold severity status as std::pair
|
||||||
std::pair<ThrottlingSeverity, ThrottlingSeverity> getSeverityFromThresholds(
|
std::pair<ThrottlingSeverity, ThrottlingSeverity> getSeverityFromThresholds(
|
||||||
const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds,
|
const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds,
|
||||||
const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis,
|
const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis,
|
||||||
ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity,
|
ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity,
|
||||||
float value) const;
|
float value) const;
|
||||||
|
bool checkVirtualSensor(std::string_view sensor_name, std::string *temp) const;
|
||||||
|
|
||||||
|
// Return the target state of PID algorithm
|
||||||
|
size_t getTargetStateOfPID(const SensorInfo &sensor_info, const SensorStatus &sensor_status);
|
||||||
|
// Return the power budget which is computed by PID algorithm
|
||||||
|
float pidPowerCalculator(const Temperature_2_0 &temp, const SensorInfo &sensor_info,
|
||||||
|
SensorStatus *sensor_status,
|
||||||
|
const std::chrono::milliseconds time_elapsed_ms, size_t target_state);
|
||||||
bool connectToPowerHal();
|
bool connectToPowerHal();
|
||||||
void updateSupportedPowerHints();
|
void updateSupportedPowerHints();
|
||||||
|
bool requestCdevByPower(std::string_view sensor_name, SensorStatus *sensor_status,
|
||||||
|
const SensorInfo &sensor_info, float total_power_budget,
|
||||||
|
size_t target_state);
|
||||||
|
void requestCdevBySeverity(std::string_view sensor_name, SensorStatus *sensor_status,
|
||||||
|
const SensorInfo &sensor_info);
|
||||||
|
void computeCoolingDevicesRequest(std::string_view sensor_name, const SensorInfo &sensor_info,
|
||||||
|
const SensorStatus &sensor_status,
|
||||||
|
std::vector<std::string> *cooling_devices_to_update);
|
||||||
|
void updateCoolingDevices(const std::vector<std::string> &cooling_devices_to_update);
|
||||||
sp<ThermalWatcher> thermal_watcher_;
|
sp<ThermalWatcher> thermal_watcher_;
|
||||||
|
PowerFiles power_files_;
|
||||||
ThermalFiles thermal_sensors_;
|
ThermalFiles thermal_sensors_;
|
||||||
ThermalFiles cooling_devices_;
|
ThermalFiles cooling_devices_;
|
||||||
bool is_initialized_;
|
bool is_initialized_;
|
||||||
const NotificationCallback cb_;
|
const NotificationCallback cb_;
|
||||||
const std::map<std::string, CoolingType> cooling_device_info_map_;
|
std::unordered_map<std::string, CdevInfo> cooling_device_info_map_;
|
||||||
const std::map<std::string, SensorInfo> sensor_info_map_;
|
std::unordered_map<std::string, SensorInfo> sensor_info_map_;
|
||||||
std::map<std::string, std::map<ThrottlingSeverity, ThrottlingSeverity>>
|
std::unordered_map<std::string, PowerRailInfo> power_rail_info_map_;
|
||||||
|
std::unordered_map<std::string, std::map<ThrottlingSeverity, ThrottlingSeverity>>
|
||||||
supported_powerhint_map_;
|
supported_powerhint_map_;
|
||||||
PowerHalService power_hal_service_;
|
PowerHalService power_hal_service_;
|
||||||
|
|
||||||
mutable std::shared_mutex sensor_status_map_mutex_;
|
mutable std::shared_mutex sensor_status_map_mutex_;
|
||||||
std::map<std::string, SensorStatus> sensor_status_map_;
|
std::unordered_map<std::string, SensorStatus> sensor_status_map_;
|
||||||
|
mutable std::shared_mutex cdev_status_map_mutex_;
|
||||||
|
std::unordered_map<std::string, CdevRequestStatus> cdev_status_map_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace implementation
|
} // namespace implementation
|
||||||
@ -171,5 +232,3 @@ class ThermalHelper {
|
|||||||
} // namespace thermal
|
} // namespace thermal
|
||||||
} // namespace hardware
|
} // namespace hardware
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
||||||
#endif // THERMAL_THERMAL_HELPER_H__
|
|
||||||
|
@ -15,9 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
#include <android-base/file.h>
|
#include <android-base/file.h>
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
|
#include <android-base/properties.h>
|
||||||
#include <android-base/strings.h>
|
#include <android-base/strings.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <set>
|
#include <unordered_set>
|
||||||
|
|
||||||
#include <json/reader.h>
|
#include <json/reader.h>
|
||||||
#include <json/value.h>
|
#include <json/value.h>
|
||||||
@ -30,6 +31,8 @@ namespace thermal {
|
|||||||
namespace V2_0 {
|
namespace V2_0 {
|
||||||
namespace implementation {
|
namespace implementation {
|
||||||
|
|
||||||
|
constexpr std::string_view kPowerLinkDisabledProperty("vendor.disable.thermal.powerlink");
|
||||||
|
|
||||||
using ::android::hardware::hidl_enum_range;
|
using ::android::hardware::hidl_enum_range;
|
||||||
using ::android::hardware::thermal::V2_0::toString;
|
using ::android::hardware::thermal::V2_0::toString;
|
||||||
using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
|
using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
|
||||||
@ -57,27 +60,103 @@ float getFloatFromValue(const Json::Value &value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getIntFromValue(const Json::Value &value) {
|
||||||
|
if (value.isString()) {
|
||||||
|
return (value.asString() == "max") ? std::numeric_limits<int>::max()
|
||||||
|
: std::stoul(value.asString());
|
||||||
|
} else {
|
||||||
|
return value.asInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getIntFromJsonValues(const Json::Value &values, CdevArray *out, bool inc_check,
|
||||||
|
bool dec_check) {
|
||||||
|
CdevArray ret;
|
||||||
|
|
||||||
|
if (inc_check && dec_check) {
|
||||||
|
LOG(ERROR) << "Cannot enable inc_check and dec_check at the same time";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.size() != kThrottlingSeverityCount) {
|
||||||
|
LOG(ERROR) << "Values size is invalid";
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
int last;
|
||||||
|
for (Json::Value::ArrayIndex i = 0; i < kThrottlingSeverityCount; ++i) {
|
||||||
|
ret[i] = getIntFromValue(values[i]);
|
||||||
|
if (inc_check && ret[i] < last) {
|
||||||
|
LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " min=" << last;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dec_check && ret[i] > last) {
|
||||||
|
LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " max=" << last;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
last = ret[i];
|
||||||
|
LOG(INFO) << "[" << i << "]: " << ret[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = ret;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getFloatFromJsonValues(const Json::Value &values, ThrottlingArray *out, bool inc_check,
|
||||||
|
bool dec_check) {
|
||||||
|
ThrottlingArray ret;
|
||||||
|
|
||||||
|
if (inc_check && dec_check) {
|
||||||
|
LOG(ERROR) << "Cannot enable inc_check and dec_check at the same time";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.size() != kThrottlingSeverityCount) {
|
||||||
|
LOG(ERROR) << "Values size is invalid";
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
float last = std::nanf("");
|
||||||
|
for (Json::Value::ArrayIndex i = 0; i < kThrottlingSeverityCount; ++i) {
|
||||||
|
ret[i] = getFloatFromValue(values[i]);
|
||||||
|
if (inc_check && !std::isnan(last) && !std::isnan(ret[i]) && ret[i] < last) {
|
||||||
|
LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " min=" << last;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dec_check && !std::isnan(last) && !std::isnan(ret[i]) && ret[i] > last) {
|
||||||
|
LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " max=" << last;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
last = std::isnan(ret[i]) ? last : ret[i];
|
||||||
|
LOG(INFO) << "[" << i << "]: " << ret[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = ret;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path) {
|
std::unordered_map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path) {
|
||||||
std::string json_doc;
|
std::string json_doc;
|
||||||
std::map<std::string, SensorInfo> sensors_parsed;
|
std::unordered_map<std::string, SensorInfo> sensors_parsed;
|
||||||
if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
|
if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
|
||||||
LOG(ERROR) << "Failed to read JSON config from " << config_path;
|
LOG(ERROR) << "Failed to read JSON config from " << config_path;
|
||||||
return sensors_parsed;
|
return sensors_parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value root;
|
Json::Value root;
|
||||||
Json::Reader reader;
|
Json::CharReaderBuilder builder;
|
||||||
|
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
|
||||||
|
std::string errorMessage;
|
||||||
|
|
||||||
if (!reader.parse(json_doc, root)) {
|
if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
|
||||||
LOG(ERROR) << "Failed to parse JSON config";
|
LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
|
||||||
return sensors_parsed;
|
return sensors_parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value sensors = root["Sensors"];
|
Json::Value sensors = root["Sensors"];
|
||||||
std::size_t total_parsed = 0;
|
std::size_t total_parsed = 0;
|
||||||
std::set<std::string> sensors_name_parsed;
|
std::unordered_set<std::string> sensors_name_parsed;
|
||||||
|
|
||||||
for (Json::Value::ArrayIndex i = 0; i < sensors.size(); ++i) {
|
for (Json::Value::ArrayIndex i = 0; i < sensors.size(); ++i) {
|
||||||
const std::string &name = sensors[i]["Name"].asString();
|
const std::string &name = sensors[i]["Name"].asString();
|
||||||
@ -107,6 +186,24 @@ std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
|
|||||||
return sensors_parsed;
|
return sensors_parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool send_cb = false;
|
||||||
|
if (sensors[i]["Monitor"].empty() || !sensors[i]["Monitor"].isBool()) {
|
||||||
|
LOG(INFO) << "Failed to read Sensor[" << name << "]'s Monitor, set to 'false'";
|
||||||
|
} else if (sensors[i]["Monitor"].asBool()) {
|
||||||
|
send_cb = true;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Sensor[" << name << "]'s SendCallback: " << std::boolalpha << send_cb
|
||||||
|
<< std::noboolalpha;
|
||||||
|
|
||||||
|
bool send_powerhint = false;
|
||||||
|
if (sensors[i]["SendPowerHint"].empty() || !sensors[i]["SendPowerHint"].isBool()) {
|
||||||
|
LOG(INFO) << "Failed to read Sensor[" << name << "]'s SendPowerHint, set to 'false'";
|
||||||
|
} else if (sensors[i]["SendPowerHint"].asBool()) {
|
||||||
|
send_powerhint = true;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Sensor[" << name << "]'s SendPowerHint: " << std::boolalpha << send_powerhint
|
||||||
|
<< std::noboolalpha;
|
||||||
|
|
||||||
std::array<float, kThrottlingSeverityCount> hot_thresholds;
|
std::array<float, kThrottlingSeverityCount> hot_thresholds;
|
||||||
hot_thresholds.fill(NAN);
|
hot_thresholds.fill(NAN);
|
||||||
std::array<float, kThrottlingSeverityCount> cold_thresholds;
|
std::array<float, kThrottlingSeverityCount> cold_thresholds;
|
||||||
@ -115,7 +212,18 @@ std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
|
|||||||
hot_hysteresis.fill(0.0);
|
hot_hysteresis.fill(0.0);
|
||||||
std::array<float, kThrottlingSeverityCount> cold_hysteresis;
|
std::array<float, kThrottlingSeverityCount> cold_hysteresis;
|
||||||
cold_hysteresis.fill(0.0);
|
cold_hysteresis.fill(0.0);
|
||||||
|
std::vector<std::string> linked_sensors;
|
||||||
|
std::vector<float> coefficients;
|
||||||
|
float offset = 0;
|
||||||
|
std::string trigger_sensor;
|
||||||
|
|
||||||
|
FormulaOption formula = FormulaOption::COUNT_THRESHOLD;
|
||||||
|
bool is_virtual_sensor = false;
|
||||||
|
if (sensors[i]["VirtualSensor"].empty() || !sensors[i]["VirtualSensor"].isBool()) {
|
||||||
|
LOG(INFO) << "Failed to read Sensor[" << name << "]'s VirtualSensor, set to 'false'";
|
||||||
|
} else {
|
||||||
|
is_virtual_sensor = sensors[i]["VirtualSensor"].asBool();
|
||||||
|
}
|
||||||
Json::Value values = sensors[i]["HotThreshold"];
|
Json::Value values = sensors[i]["HotThreshold"];
|
||||||
if (values.size() != kThrottlingSeverityCount) {
|
if (values.size() != kThrottlingSeverityCount) {
|
||||||
LOG(ERROR) << "Invalid "
|
LOG(ERROR) << "Invalid "
|
||||||
@ -201,6 +309,63 @@ std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_virtual_sensor) {
|
||||||
|
values = sensors[i]["Combination"];
|
||||||
|
if (values.size()) {
|
||||||
|
linked_sensors.reserve(values.size());
|
||||||
|
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
|
||||||
|
linked_sensors.emplace_back(values[j].asString());
|
||||||
|
LOG(INFO) << "Sensor[" << name << "]'s combination[" << j
|
||||||
|
<< "]: " << linked_sensors[j];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
values = sensors[i]["Coefficient"];
|
||||||
|
if (values.size()) {
|
||||||
|
coefficients.reserve(values.size());
|
||||||
|
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
|
||||||
|
coefficients.emplace_back(getFloatFromValue(values[j]));
|
||||||
|
LOG(INFO) << "Sensor[" << name << "]'s coefficient[" << j
|
||||||
|
<< "]: " << coefficients[j];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sensors[i]["Offset"].empty()) {
|
||||||
|
offset = sensors[i]["Offset"].asFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linked_sensors.size() != coefficients.size()) {
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
trigger_sensor = sensors[i]["TriggerSensor"].asString();
|
||||||
|
if (sensors[i]["Formula"].asString().compare("COUNT_THRESHOLD") == 0) {
|
||||||
|
formula = FormulaOption::COUNT_THRESHOLD;
|
||||||
|
} else if (sensors[i]["Formula"].asString().compare("WEIGHTED_AVG") == 0) {
|
||||||
|
formula = FormulaOption::WEIGHTED_AVG;
|
||||||
|
} else if (sensors[i]["Formula"].asString().compare("MAXIMUM") == 0) {
|
||||||
|
formula = FormulaOption::MAXIMUM;
|
||||||
|
} else if (sensors[i]["Formula"].asString().compare("MINIMUM") == 0) {
|
||||||
|
formula = FormulaOption::MINIMUM;
|
||||||
|
} else {
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string temp_path;
|
||||||
|
if (!sensors[i]["TempPath"].empty()) {
|
||||||
|
temp_path = sensors[i]["TempPath"].asString();
|
||||||
|
LOG(INFO) << "Sensor[" << name << "]'s TempPath: " << temp_path;
|
||||||
|
}
|
||||||
|
|
||||||
float vr_threshold = NAN;
|
float vr_threshold = NAN;
|
||||||
vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]);
|
vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]);
|
||||||
LOG(INFO) << "Sensor[" << name << "]'s VrThreshold: " << vr_threshold;
|
LOG(INFO) << "Sensor[" << name << "]'s VrThreshold: " << vr_threshold;
|
||||||
@ -208,23 +373,287 @@ std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
|
|||||||
float multiplier = sensors[i]["Multiplier"].asFloat();
|
float multiplier = sensors[i]["Multiplier"].asFloat();
|
||||||
LOG(INFO) << "Sensor[" << name << "]'s Multiplier: " << multiplier;
|
LOG(INFO) << "Sensor[" << name << "]'s Multiplier: " << multiplier;
|
||||||
|
|
||||||
bool is_monitor = false;
|
std::chrono::milliseconds polling_delay;
|
||||||
if (sensors[i]["Monitor"].empty() || !sensors[i]["Monitor"].isBool()) {
|
if (sensors[i]["PollingDelay"].empty()) {
|
||||||
LOG(INFO) << "Failed to read Sensor[" << name << "]'s Monitor, set to 'false'";
|
polling_delay = kUeventPollTimeoutMs;
|
||||||
} else {
|
} else {
|
||||||
is_monitor = sensors[i]["Monitor"].asBool();
|
polling_delay = std::chrono::milliseconds(getIntFromValue(sensors[i]["PollingDelay"]));
|
||||||
}
|
}
|
||||||
LOG(INFO) << "Sensor[" << name << "]'s Monitor: " << std::boolalpha << is_monitor
|
LOG(INFO) << "Sensor[" << name << "]'s Polling delay: " << polling_delay.count();
|
||||||
<< std::noboolalpha;
|
|
||||||
|
|
||||||
bool send_powerhint = false;
|
std::chrono::milliseconds passive_delay;
|
||||||
if (sensors[i]["SendPowerHint"].empty() || !sensors[i]["SendPowerHint"].isBool()) {
|
if (sensors[i]["PassiveDelay"].empty()) {
|
||||||
LOG(INFO) << "Failed to read Sensor[" << name << "]'s SendPowerHint, set to 'false'";
|
passive_delay = kMinPollIntervalMs;
|
||||||
} else {
|
} else {
|
||||||
send_powerhint = sensors[i]["SendPowerHint"].asBool();
|
passive_delay = std::chrono::milliseconds(getIntFromValue(sensors[i]["PassiveDelay"]));
|
||||||
}
|
}
|
||||||
LOG(INFO) << "Sensor[" << name << "]'s SendPowerHint: " << std::boolalpha << send_powerhint
|
LOG(INFO) << "Sensor[" << name << "]'s Passive delay: " << passive_delay.count();
|
||||||
<< std::noboolalpha;
|
|
||||||
|
bool support_pid = false;
|
||||||
|
std::array<float, kThrottlingSeverityCount> k_po;
|
||||||
|
k_po.fill(0.0);
|
||||||
|
std::array<float, kThrottlingSeverityCount> k_pu;
|
||||||
|
k_pu.fill(0.0);
|
||||||
|
std::array<float, kThrottlingSeverityCount> k_i;
|
||||||
|
k_i.fill(0.0);
|
||||||
|
std::array<float, kThrottlingSeverityCount> k_d;
|
||||||
|
k_d.fill(0.0);
|
||||||
|
std::array<float, kThrottlingSeverityCount> i_max;
|
||||||
|
i_max.fill(NAN);
|
||||||
|
std::array<float, kThrottlingSeverityCount> max_alloc_power;
|
||||||
|
max_alloc_power.fill(NAN);
|
||||||
|
std::array<float, kThrottlingSeverityCount> min_alloc_power;
|
||||||
|
min_alloc_power.fill(NAN);
|
||||||
|
std::array<float, kThrottlingSeverityCount> s_power;
|
||||||
|
s_power.fill(NAN);
|
||||||
|
std::array<float, kThrottlingSeverityCount> i_cutoff;
|
||||||
|
i_cutoff.fill(NAN);
|
||||||
|
|
||||||
|
// Parse PID parameters
|
||||||
|
if (!sensors[i]["PIDInfo"].empty()) {
|
||||||
|
LOG(INFO) << "Start to parse"
|
||||||
|
<< " Sensor[" << name << "]'s K_Po";
|
||||||
|
if (sensors[i]["PIDInfo"]["K_Po"].empty() ||
|
||||||
|
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_Po"], &k_po, false, false)) {
|
||||||
|
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Po";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Start to parse"
|
||||||
|
<< " Sensor[" << name << "]'s K_Pu";
|
||||||
|
if (sensors[i]["PIDInfo"]["K_Pu"].empty() ||
|
||||||
|
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_Pu"], &k_pu, false, false)) {
|
||||||
|
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Pu";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Start to parse"
|
||||||
|
<< " Sensor[" << name << "]'s K_I";
|
||||||
|
if (sensors[i]["PIDInfo"]["K_I"].empty() ||
|
||||||
|
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_I"], &k_i, false, false)) {
|
||||||
|
LOG(INFO) << "Sensor[" << name << "]: Failed to parse K_I";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Start to parse"
|
||||||
|
<< " Sensor[" << name << "]'s K_D";
|
||||||
|
if (sensors[i]["PIDInfo"]["K_D"].empty() ||
|
||||||
|
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_D"], &k_d, false, false)) {
|
||||||
|
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_D";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Start to parse"
|
||||||
|
<< " Sensor[" << name << "]'s I_Max";
|
||||||
|
if (sensors[i]["PIDInfo"]["I_Max"].empty() ||
|
||||||
|
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["I_Max"], &i_max, false, false)) {
|
||||||
|
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse I_Max";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Start to parse"
|
||||||
|
<< " Sensor[" << name << "]'s MaxAllocPower";
|
||||||
|
if (sensors[i]["PIDInfo"]["MaxAllocPower"].empty() ||
|
||||||
|
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["MaxAllocPower"], &max_alloc_power,
|
||||||
|
false, true)) {
|
||||||
|
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse MaxAllocPower";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Start to parse"
|
||||||
|
<< " Sensor[" << name << "]'s MinAllocPower";
|
||||||
|
if (sensors[i]["PIDInfo"]["MinAllocPower"].empty() ||
|
||||||
|
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["MinAllocPower"], &min_alloc_power,
|
||||||
|
false, true)) {
|
||||||
|
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse MinAllocPower";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Start to parse"
|
||||||
|
<< " Sensor[" << name << "]'s S_Power";
|
||||||
|
if (sensors[i]["PIDInfo"]["S_Power"].empty() ||
|
||||||
|
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["S_Power"], &s_power, false, true)) {
|
||||||
|
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse S_Power";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Start to parse"
|
||||||
|
<< " Sensor[" << name << "]'s I_Cutoff";
|
||||||
|
if (sensors[i]["PIDInfo"]["I_Cutoff"].empty() ||
|
||||||
|
!getFloatFromJsonValues(sensors[i]["PIDInfo"]["I_Cutoff"], &i_cutoff, false,
|
||||||
|
false)) {
|
||||||
|
LOG(ERROR) << "Sensor[" << name << "]: Failed to parse I_Cutoff";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
// Confirm we have at least one valid PID combination
|
||||||
|
bool valid_pid_combination = false;
|
||||||
|
for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
|
||||||
|
if (!std::isnan(s_power[j])) {
|
||||||
|
if (std::isnan(k_po[j]) || std::isnan(k_pu[j]) || std::isnan(k_i[j]) ||
|
||||||
|
std::isnan(k_d[j]) || std::isnan(i_max[j]) ||
|
||||||
|
std::isnan(max_alloc_power[j]) || std::isnan(min_alloc_power[j]) ||
|
||||||
|
std::isnan(i_cutoff[j])) {
|
||||||
|
valid_pid_combination = false;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
valid_pid_combination = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!valid_pid_combination) {
|
||||||
|
LOG(ERROR) << "Sensor[" << name << "]: Invalid PID parameters combinations";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
} else {
|
||||||
|
support_pid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse binded cooling device
|
||||||
|
bool support_hard_limit = false;
|
||||||
|
std::unordered_map<std::string, BindedCdevInfo> binded_cdev_info_map;
|
||||||
|
values = sensors[i]["BindedCdevInfo"];
|
||||||
|
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
|
||||||
|
Json::Value sub_values;
|
||||||
|
const std::string &cdev_name = values[j]["CdevRequest"].asString();
|
||||||
|
ThrottlingArray cdev_weight_for_pid;
|
||||||
|
cdev_weight_for_pid.fill(NAN);
|
||||||
|
CdevArray cdev_ceiling;
|
||||||
|
cdev_ceiling.fill(std::numeric_limits<int>::max());
|
||||||
|
if (support_pid) {
|
||||||
|
if (!values[j]["CdevWeightForPID"].empty()) {
|
||||||
|
LOG(INFO) << "Sensor[" << name << "]: Star to parse " << cdev_name
|
||||||
|
<< "'s CdevWeightForPID";
|
||||||
|
if (!getFloatFromJsonValues(values[j]["CdevWeightForPID"], &cdev_weight_for_pid,
|
||||||
|
false, false)) {
|
||||||
|
LOG(ERROR) << "Failed to parse CdevWeightForPID";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!values[j]["CdevCeiling"].empty()) {
|
||||||
|
LOG(INFO) << "Sensor[" << name
|
||||||
|
<< "]: Start to parse CdevCeiling: " << cdev_name;
|
||||||
|
if (!getIntFromJsonValues(values[j]["CdevCeiling"], &cdev_ceiling, false,
|
||||||
|
false)) {
|
||||||
|
LOG(ERROR) << "Failed to parse CdevCeiling";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CdevArray limit_info;
|
||||||
|
limit_info.fill(0);
|
||||||
|
ThrottlingArray power_thresholds;
|
||||||
|
power_thresholds.fill(NAN);
|
||||||
|
|
||||||
|
ReleaseLogic release_logic = ReleaseLogic::NONE;
|
||||||
|
|
||||||
|
sub_values = values[j]["LimitInfo"];
|
||||||
|
if (sub_values.size()) {
|
||||||
|
LOG(INFO) << "Sensor[" << name << "]: Start to parse LimitInfo: " << cdev_name;
|
||||||
|
if (!getIntFromJsonValues(sub_values, &limit_info, false, false)) {
|
||||||
|
LOG(ERROR) << "Failed to parse LimitInfo";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
support_hard_limit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse linked power info
|
||||||
|
bool is_power_data_invalid = false;
|
||||||
|
std::string power_rail;
|
||||||
|
bool high_power_check = false;
|
||||||
|
bool throttling_with_power_link = false;
|
||||||
|
CdevArray cdev_floor_with_power_link;
|
||||||
|
cdev_floor_with_power_link.fill(0);
|
||||||
|
|
||||||
|
const bool power_link_disabled =
|
||||||
|
android::base::GetBoolProperty(kPowerLinkDisabledProperty.data(), false);
|
||||||
|
if (!power_link_disabled) {
|
||||||
|
power_rail = values[j]["BindedPowerRail"].asString();
|
||||||
|
|
||||||
|
if (values[j]["HighPowerCheck"].asBool()) {
|
||||||
|
high_power_check = true;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Highpowercheck: " << std::boolalpha << high_power_check;
|
||||||
|
|
||||||
|
if (values[j]["ThrottlingWithPowerLink"].asBool()) {
|
||||||
|
throttling_with_power_link = true;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "ThrottlingwithPowerLink: " << std::boolalpha
|
||||||
|
<< throttling_with_power_link;
|
||||||
|
|
||||||
|
sub_values = values[j]["CdevFloorWithPowerLink"];
|
||||||
|
if (sub_values.size()) {
|
||||||
|
LOG(INFO) << "Sensor[" << name << "]: Start to parse " << cdev_name
|
||||||
|
<< "'s CdevFloorWithPowerLink";
|
||||||
|
if (!getIntFromJsonValues(sub_values, &cdev_floor_with_power_link, false,
|
||||||
|
false)) {
|
||||||
|
LOG(ERROR) << "Failed to parse CdevFloor";
|
||||||
|
is_power_data_invalid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sub_values = values[j]["PowerThreshold"];
|
||||||
|
if (sub_values.size()) {
|
||||||
|
LOG(INFO) << "Sensor[" << name << "]: Start to parse " << cdev_name
|
||||||
|
<< "'s PowerThreshold";
|
||||||
|
if (!getFloatFromJsonValues(sub_values, &power_thresholds, false, false)) {
|
||||||
|
LOG(ERROR) << "Failed to parse power thresholds";
|
||||||
|
is_power_data_invalid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values[j]["ReleaseLogic"].asString() == "INCREASE") {
|
||||||
|
release_logic = ReleaseLogic::INCREASE;
|
||||||
|
LOG(INFO) << "Release logic: INCREASE";
|
||||||
|
} else if (values[j]["ReleaseLogic"].asString() == "DECREASE") {
|
||||||
|
release_logic = ReleaseLogic::DECREASE;
|
||||||
|
LOG(INFO) << "Release logic: DECREASE";
|
||||||
|
} else if (values[j]["ReleaseLogic"].asString() == "STEPWISE") {
|
||||||
|
release_logic = ReleaseLogic::STEPWISE;
|
||||||
|
LOG(INFO) << "Release logic: STEPWISE";
|
||||||
|
} else if (values[j]["ReleaseLogic"].asString() == "RELEASE_TO_FLOOR") {
|
||||||
|
release_logic = ReleaseLogic::RELEASE_TO_FLOOR;
|
||||||
|
LOG(INFO) << "Release logic: RELEASE_TO_FLOOR";
|
||||||
|
} else {
|
||||||
|
LOG(ERROR) << "Release logic is invalid";
|
||||||
|
is_power_data_invalid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_power_data_invalid) {
|
||||||
|
LOG(ERROR) << cdev_name << "'s power rail " << power_rail << " is invalid";
|
||||||
|
sensors_parsed.clear();
|
||||||
|
return sensors_parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binded_cdev_info_map[cdev_name] = {
|
||||||
|
.limit_info = limit_info,
|
||||||
|
.power_thresholds = power_thresholds,
|
||||||
|
.release_logic = release_logic,
|
||||||
|
.high_power_check = high_power_check,
|
||||||
|
.throttling_with_power_link = throttling_with_power_link,
|
||||||
|
.cdev_weight_for_pid = cdev_weight_for_pid,
|
||||||
|
.cdev_ceiling = cdev_ceiling,
|
||||||
|
.cdev_floor_with_power_link = cdev_floor_with_power_link,
|
||||||
|
.power_rail = power_rail,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_monitor = (send_cb | send_powerhint | support_pid | support_hard_limit);
|
||||||
|
LOG(INFO) << "Sensor[" << name << "]'s Monitor: " << std::boolalpha << is_monitor;
|
||||||
|
|
||||||
|
std::unique_ptr<VirtualSensorInfo> virtual_sensor_info;
|
||||||
|
if (is_virtual_sensor) {
|
||||||
|
virtual_sensor_info.reset(new VirtualSensorInfo{linked_sensors, coefficients, offset,
|
||||||
|
trigger_sensor, formula});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ThrottlingInfo> throttling_info(
|
||||||
|
new ThrottlingInfo{k_po, k_pu, k_i, k_d, i_max, max_alloc_power, min_alloc_power,
|
||||||
|
s_power, i_cutoff, binded_cdev_info_map});
|
||||||
|
|
||||||
sensors_parsed[name] = {
|
sensors_parsed[name] = {
|
||||||
.type = sensor_type,
|
.type = sensor_type,
|
||||||
@ -232,11 +661,18 @@ std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
|
|||||||
.cold_thresholds = cold_thresholds,
|
.cold_thresholds = cold_thresholds,
|
||||||
.hot_hysteresis = hot_hysteresis,
|
.hot_hysteresis = hot_hysteresis,
|
||||||
.cold_hysteresis = cold_hysteresis,
|
.cold_hysteresis = cold_hysteresis,
|
||||||
|
.temp_path = temp_path,
|
||||||
.vr_threshold = vr_threshold,
|
.vr_threshold = vr_threshold,
|
||||||
.multiplier = multiplier,
|
.multiplier = multiplier,
|
||||||
.is_monitor = is_monitor,
|
.polling_delay = polling_delay,
|
||||||
|
.passive_delay = passive_delay,
|
||||||
|
.send_cb = send_cb,
|
||||||
.send_powerhint = send_powerhint,
|
.send_powerhint = send_powerhint,
|
||||||
|
.is_monitor = is_monitor,
|
||||||
|
.virtual_sensor_info = std::move(virtual_sensor_info),
|
||||||
|
.throttling_info = std::move(throttling_info),
|
||||||
};
|
};
|
||||||
|
|
||||||
++total_parsed;
|
++total_parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,25 +680,27 @@ std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
|
|||||||
return sensors_parsed;
|
return sensors_parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_path) {
|
std::unordered_map<std::string, CdevInfo> ParseCoolingDevice(std::string_view config_path) {
|
||||||
std::string json_doc;
|
std::string json_doc;
|
||||||
std::map<std::string, CoolingType> cooling_devices_parsed;
|
std::unordered_map<std::string, CdevInfo> cooling_devices_parsed;
|
||||||
if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
|
if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
|
||||||
LOG(ERROR) << "Failed to read JSON config from " << config_path;
|
LOG(ERROR) << "Failed to read JSON config from " << config_path;
|
||||||
return cooling_devices_parsed;
|
return cooling_devices_parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value root;
|
Json::Value root;
|
||||||
Json::Reader reader;
|
Json::CharReaderBuilder builder;
|
||||||
|
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
|
||||||
|
std::string errorMessage;
|
||||||
|
|
||||||
if (!reader.parse(json_doc, root)) {
|
if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
|
||||||
LOG(ERROR) << "Failed to parse JSON config";
|
LOG(ERROR) << "Failed to parse JSON config";
|
||||||
return cooling_devices_parsed;
|
return cooling_devices_parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value cooling_devices = root["CoolingDevices"];
|
Json::Value cooling_devices = root["CoolingDevices"];
|
||||||
std::size_t total_parsed = 0;
|
std::size_t total_parsed = 0;
|
||||||
std::set<std::string> cooling_devices_name_parsed;
|
std::unordered_set<std::string> cooling_devices_name_parsed;
|
||||||
|
|
||||||
for (Json::Value::ArrayIndex i = 0; i < cooling_devices.size(); ++i) {
|
for (Json::Value::ArrayIndex i = 0; i < cooling_devices.size(); ++i) {
|
||||||
const std::string &name = cooling_devices[i]["Name"].asString();
|
const std::string &name = cooling_devices[i]["Name"].asString();
|
||||||
@ -292,8 +730,35 @@ std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_pa
|
|||||||
return cooling_devices_parsed;
|
return cooling_devices_parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
cooling_devices_parsed[name] = cooling_device_type;
|
const std::string &read_path = cooling_devices[i]["ReadPath"].asString();
|
||||||
|
LOG(INFO) << "Cdev Read Path: " << (read_path.empty() ? "default" : read_path);
|
||||||
|
|
||||||
|
const std::string &write_path = cooling_devices[i]["WritePath"].asString();
|
||||||
|
LOG(INFO) << "Cdev Write Path: " << (write_path.empty() ? "default" : write_path);
|
||||||
|
|
||||||
|
std::vector<float> state2power;
|
||||||
|
Json::Value values = cooling_devices[i]["State2Power"];
|
||||||
|
if (values.size()) {
|
||||||
|
state2power.reserve(values.size());
|
||||||
|
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
|
||||||
|
state2power.emplace_back(getFloatFromValue(values[j]));
|
||||||
|
LOG(INFO) << "Cooling device[" << name << "]'s Power2State[" << j
|
||||||
|
<< "]: " << state2power[j];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG(INFO) << "CoolingDevice[" << i << "]'s Name: " << name
|
||||||
|
<< " does not support Power2State";
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &power_rail = cooling_devices[i]["PowerRail"].asString();
|
||||||
|
LOG(INFO) << "Cooling device power rail : " << power_rail;
|
||||||
|
|
||||||
|
cooling_devices_parsed[name] = {
|
||||||
|
.type = cooling_device_type,
|
||||||
|
.read_path = read_path,
|
||||||
|
.write_path = write_path,
|
||||||
|
.state2power = state2power,
|
||||||
|
};
|
||||||
++total_parsed;
|
++total_parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,6 +766,139 @@ std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_pa
|
|||||||
return cooling_devices_parsed;
|
return cooling_devices_parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::string, PowerRailInfo> ParsePowerRailInfo(std::string_view config_path) {
|
||||||
|
std::string json_doc;
|
||||||
|
std::unordered_map<std::string, PowerRailInfo> power_rails_parsed;
|
||||||
|
if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
|
||||||
|
LOG(ERROR) << "Failed to read JSON config from " << config_path;
|
||||||
|
return power_rails_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value root;
|
||||||
|
Json::CharReaderBuilder builder;
|
||||||
|
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
|
||||||
|
std::string errorMessage;
|
||||||
|
|
||||||
|
if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
|
||||||
|
LOG(ERROR) << "Failed to parse JSON config";
|
||||||
|
return power_rails_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value power_rails = root["PowerRails"];
|
||||||
|
std::size_t total_parsed = 0;
|
||||||
|
std::unordered_set<std::string> power_rails_name_parsed;
|
||||||
|
|
||||||
|
for (Json::Value::ArrayIndex i = 0; i < power_rails.size(); ++i) {
|
||||||
|
const std::string &name = power_rails[i]["Name"].asString();
|
||||||
|
LOG(INFO) << "PowerRail[" << i << "]'s Name: " << name;
|
||||||
|
if (name.empty()) {
|
||||||
|
LOG(ERROR) << "Failed to read "
|
||||||
|
<< "PowerRail[" << i << "]'s Name";
|
||||||
|
power_rails_parsed.clear();
|
||||||
|
return power_rails_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string rail;
|
||||||
|
if (power_rails[i]["Rail"].empty()) {
|
||||||
|
rail = name;
|
||||||
|
} else {
|
||||||
|
rail = power_rails[i]["Rail"].asString();
|
||||||
|
}
|
||||||
|
LOG(INFO) << "PowerRail[" << i << "]'s Rail: " << rail;
|
||||||
|
|
||||||
|
std::vector<std::string> linked_power_rails;
|
||||||
|
std::vector<float> coefficients;
|
||||||
|
float offset = 0;
|
||||||
|
FormulaOption formula = FormulaOption::COUNT_THRESHOLD;
|
||||||
|
bool is_virtual_power_rail = false;
|
||||||
|
Json::Value values;
|
||||||
|
int power_sample_count = 0;
|
||||||
|
std::chrono::milliseconds power_sample_delay;
|
||||||
|
|
||||||
|
if (!power_rails[i]["VirtualRails"].empty() && power_rails[i]["VirtualRails"].isBool()) {
|
||||||
|
is_virtual_power_rail = power_rails[i]["VirtualRails"].asBool();
|
||||||
|
LOG(INFO) << "PowerRails[" << name << "]'s VirtualRail, set to 'true'";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_virtual_power_rail) {
|
||||||
|
values = power_rails[i]["Combination"];
|
||||||
|
if (values.size()) {
|
||||||
|
linked_power_rails.reserve(values.size());
|
||||||
|
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
|
||||||
|
linked_power_rails.emplace_back(values[j].asString());
|
||||||
|
LOG(INFO) << "PowerRail[" << name << "]'s combination[" << j
|
||||||
|
<< "]: " << linked_power_rails[j];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
power_rails_parsed.clear();
|
||||||
|
return power_rails_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
values = power_rails[i]["Coefficient"];
|
||||||
|
if (values.size()) {
|
||||||
|
coefficients.reserve(values.size());
|
||||||
|
for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
|
||||||
|
coefficients.emplace_back(getFloatFromValue(values[j]));
|
||||||
|
LOG(INFO) << "PowerRail[" << name << "]'s coefficient[" << j
|
||||||
|
<< "]: " << coefficients[j];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
power_rails_parsed.clear();
|
||||||
|
return power_rails_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!power_rails[i]["Offset"].empty()) {
|
||||||
|
offset = power_rails[i]["Offset"].asFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linked_power_rails.size() != coefficients.size()) {
|
||||||
|
power_rails_parsed.clear();
|
||||||
|
return power_rails_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (power_rails[i]["Formula"].asString().compare("COUNT_THRESHOLD") == 0) {
|
||||||
|
formula = FormulaOption::COUNT_THRESHOLD;
|
||||||
|
} else if (power_rails[i]["Formula"].asString().compare("WEIGHTED_AVG") == 0) {
|
||||||
|
formula = FormulaOption::WEIGHTED_AVG;
|
||||||
|
} else if (power_rails[i]["Formula"].asString().compare("MAXIMUM") == 0) {
|
||||||
|
formula = FormulaOption::MAXIMUM;
|
||||||
|
} else if (power_rails[i]["Formula"].asString().compare("MINIMUM") == 0) {
|
||||||
|
formula = FormulaOption::MINIMUM;
|
||||||
|
} else {
|
||||||
|
power_rails_parsed.clear();
|
||||||
|
return power_rails_parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<VirtualPowerRailInfo> virtual_power_rail_info;
|
||||||
|
if (is_virtual_power_rail) {
|
||||||
|
virtual_power_rail_info.reset(
|
||||||
|
new VirtualPowerRailInfo{linked_power_rails, coefficients, offset, formula});
|
||||||
|
}
|
||||||
|
|
||||||
|
power_sample_count = power_rails[i]["PowerSampleCount"].asInt();
|
||||||
|
LOG(INFO) << "Power sample Count: " << power_sample_count;
|
||||||
|
|
||||||
|
if (!power_rails[i]["PowerSampleDelay"]) {
|
||||||
|
power_sample_delay = std::chrono::milliseconds::max();
|
||||||
|
} else {
|
||||||
|
power_sample_delay =
|
||||||
|
std::chrono::milliseconds(getIntFromValue(power_rails[i]["PowerSampleDelay"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
power_rails_parsed[name] = {
|
||||||
|
.rail = rail,
|
||||||
|
.power_sample_count = power_sample_count,
|
||||||
|
.power_sample_delay = power_sample_delay,
|
||||||
|
.virtual_power_rail_info = std::move(virtual_power_rail_info),
|
||||||
|
};
|
||||||
|
++total_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(INFO) << total_parsed << " PowerRails parsed successfully";
|
||||||
|
return power_rails_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace implementation
|
} // namespace implementation
|
||||||
} // namespace V2_0
|
} // namespace V2_0
|
||||||
} // namespace thermal
|
} // namespace thermal
|
||||||
|
@ -14,11 +14,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef THERMAL_UTILS_CONFIG_PARSER_H__
|
#pragma once
|
||||||
#define THERMAL_UTILS_CONFIG_PARSER_H__
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <android/hardware/thermal/2.0/IThermal.h>
|
#include <android/hardware/thermal/2.0/IThermal.h>
|
||||||
|
|
||||||
@ -29,12 +28,73 @@ namespace V2_0 {
|
|||||||
namespace implementation {
|
namespace implementation {
|
||||||
|
|
||||||
using ::android::hardware::hidl_enum_range;
|
using ::android::hardware::hidl_enum_range;
|
||||||
using ::android::hardware::thermal::V2_0::CoolingType;
|
using CoolingType_2_0 = ::android::hardware::thermal::V2_0::CoolingType;
|
||||||
using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
|
using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
|
||||||
using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
|
using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
|
||||||
constexpr size_t kThrottlingSeverityCount = std::distance(
|
constexpr size_t kThrottlingSeverityCount = std::distance(
|
||||||
hidl_enum_range<ThrottlingSeverity>().begin(), hidl_enum_range<ThrottlingSeverity>().end());
|
hidl_enum_range<ThrottlingSeverity>().begin(), hidl_enum_range<ThrottlingSeverity>().end());
|
||||||
using ThrottlingArray = std::array<float, static_cast<size_t>(kThrottlingSeverityCount)>;
|
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);
|
||||||
|
|
||||||
|
enum FormulaOption : uint32_t {
|
||||||
|
COUNT_THRESHOLD = 0,
|
||||||
|
WEIGHTED_AVG,
|
||||||
|
MAXIMUM,
|
||||||
|
MINIMUM,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VirtualSensorInfo {
|
||||||
|
std::vector<std::string> linked_sensors;
|
||||||
|
std::vector<float> coefficients;
|
||||||
|
float offset;
|
||||||
|
std::string trigger_sensor;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
std::unordered_map<std::string, BindedCdevInfo> binded_cdev_info_map;
|
||||||
|
};
|
||||||
|
|
||||||
struct SensorInfo {
|
struct SensorInfo {
|
||||||
TemperatureType_2_0 type;
|
TemperatureType_2_0 type;
|
||||||
@ -42,19 +102,38 @@ struct SensorInfo {
|
|||||||
ThrottlingArray cold_thresholds;
|
ThrottlingArray cold_thresholds;
|
||||||
ThrottlingArray hot_hysteresis;
|
ThrottlingArray hot_hysteresis;
|
||||||
ThrottlingArray cold_hysteresis;
|
ThrottlingArray cold_hysteresis;
|
||||||
|
std::string temp_path;
|
||||||
float vr_threshold;
|
float vr_threshold;
|
||||||
float multiplier;
|
float multiplier;
|
||||||
bool is_monitor;
|
std::chrono::milliseconds polling_delay;
|
||||||
|
std::chrono::milliseconds passive_delay;
|
||||||
|
bool send_cb;
|
||||||
bool send_powerhint;
|
bool send_powerhint;
|
||||||
|
bool is_monitor;
|
||||||
|
std::unique_ptr<VirtualSensorInfo> virtual_sensor_info;
|
||||||
|
std::unique_ptr<ThrottlingInfo> throttling_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path);
|
struct CdevInfo {
|
||||||
std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_path);
|
CoolingType_2_0 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path);
|
||||||
|
std::unordered_map<std::string, CdevInfo> ParseCoolingDevice(std::string_view config_path);
|
||||||
|
std::unordered_map<std::string, PowerRailInfo> ParsePowerRailInfo(std::string_view config_path);
|
||||||
|
|
||||||
} // namespace implementation
|
} // namespace implementation
|
||||||
} // namespace V2_0
|
} // namespace V2_0
|
||||||
} // namespace thermal
|
} // namespace thermal
|
||||||
} // namespace hardware
|
} // namespace hardware
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
||||||
#endif // THERMAL_UTILS_CONFIG_PARSER_H__
|
|
||||||
|
444
thermal/utils/power_files.cpp
Normal file
444
thermal/utils/power_files.cpp
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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 <dirent.h>
|
||||||
|
|
||||||
|
#include <android-base/file.h>
|
||||||
|
#include <android-base/logging.h>
|
||||||
|
#include <android-base/stringprintf.h>
|
||||||
|
#include <android-base/strings.h>
|
||||||
|
|
||||||
|
#include "power_files.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace hardware {
|
||||||
|
namespace thermal {
|
||||||
|
namespace V2_0 {
|
||||||
|
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;
|
||||||
|
|
||||||
|
void PowerFiles::setPowerDataToDefault(std::string_view sensor_name) {
|
||||||
|
std::unique_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
|
||||||
|
if (!throttling_release_map_.count(sensor_name.data()) ||
|
||||||
|
!power_status_map_.count(sensor_name.data())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &cdev_release_map = throttling_release_map_.at(sensor_name.data());
|
||||||
|
PowerSample power_sample = {};
|
||||||
|
|
||||||
|
for (auto &power_status_pair : power_status_map_.at(sensor_name.data())) {
|
||||||
|
for (size_t i = 0; i < power_status_pair.second.power_history.size(); ++i) {
|
||||||
|
for (size_t j = 0; j < power_status_pair.second.power_history[i].size(); ++j) {
|
||||||
|
power_status_pair.second.power_history[i].pop();
|
||||||
|
power_status_pair.second.power_history[i].emplace(power_sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
power_status_pair.second.last_updated_avg_power = NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &cdev_release_pair : cdev_release_map) {
|
||||||
|
cdev_release_pair.second.release_step = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int PowerFiles::getReleaseStep(std::string_view sensor_name, std::string_view cdev_name) {
|
||||||
|
int release_step = 0;
|
||||||
|
std::shared_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
|
||||||
|
|
||||||
|
if (throttling_release_map_.count(sensor_name.data()) &&
|
||||||
|
throttling_release_map_[sensor_name.data()].count(cdev_name.data())) {
|
||||||
|
release_step = throttling_release_map_[sensor_name.data()][cdev_name.data()].release_step;
|
||||||
|
}
|
||||||
|
|
||||||
|
return release_step;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PowerFiles::registerPowerRailsToWatch(std::string_view sensor_name, std::string_view cdev_name,
|
||||||
|
const BindedCdevInfo &binded_cdev_info,
|
||||||
|
const CdevInfo &cdev_info,
|
||||||
|
const PowerRailInfo &power_rail_info) {
|
||||||
|
std::vector<std::queue<PowerSample>> power_history;
|
||||||
|
PowerSample power_sample = {
|
||||||
|
.energy_counter = 0,
|
||||||
|
.duration = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (throttling_release_map_.count(sensor_name.data()) &&
|
||||||
|
throttling_release_map_[sensor_name.data()].count(binded_cdev_info.power_rail)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!energy_info_map_.size() && !updateEnergyValues()) {
|
||||||
|
LOG(ERROR) << "Faield to update energy info";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (power_rail_info.virtual_power_rail_info != nullptr &&
|
||||||
|
power_rail_info.virtual_power_rail_info->linked_power_rails.size()) {
|
||||||
|
for (size_t i = 0; i < power_rail_info.virtual_power_rail_info->linked_power_rails.size();
|
||||||
|
++i) {
|
||||||
|
if (energy_info_map_.count(
|
||||||
|
power_rail_info.virtual_power_rail_info->linked_power_rails[i])) {
|
||||||
|
power_history.emplace_back(std::queue<PowerSample>());
|
||||||
|
for (int j = 0; j < power_rail_info.power_sample_count; j++) {
|
||||||
|
power_history[i].emplace(power_sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (energy_info_map_.count(power_rail_info.rail)) {
|
||||||
|
power_history.emplace_back(std::queue<PowerSample>());
|
||||||
|
for (int j = 0; j < power_rail_info.power_sample_count; j++) {
|
||||||
|
power_history[0].emplace(power_sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (power_history.size()) {
|
||||||
|
throttling_release_map_[sensor_name.data()][cdev_name.data()] = {
|
||||||
|
.release_step = 0,
|
||||||
|
.max_release_step = cdev_info.max_state,
|
||||||
|
};
|
||||||
|
power_status_map_[sensor_name.data()][binded_cdev_info.power_rail] = {
|
||||||
|
.power_history = power_history,
|
||||||
|
.time_remaining = power_rail_info.power_sample_delay,
|
||||||
|
.last_updated_avg_power = NAN,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(INFO) << "Sensor " << sensor_name.data() << " successfully registers power rail "
|
||||||
|
<< binded_cdev_info.power_rail << " for cooling device " << cdev_name.data();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerFiles::clearEnergyInfoMap(void) {
|
||||||
|
energy_info_map_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PowerFiles::updateEnergyValues(void) {
|
||||||
|
std::string deviceEnergyContent;
|
||||||
|
std::string deviceEnergyContents;
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
clearEnergyInfoMap();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PowerFiles::getAveragePower(std::string_view power_rail,
|
||||||
|
std::queue<PowerSample> *power_history, bool power_sample_update,
|
||||||
|
float *avg_power) {
|
||||||
|
const auto curr_sample = energy_info_map_.at(power_rail.data());
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
|
const auto last_sample = power_history->front();
|
||||||
|
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() << ": the last energy timestamp is zero";
|
||||||
|
} else if (duration <= 0 || deltaEnergy < 0) {
|
||||||
|
LOG(ERROR) << "Power rail " << power_rail.data() << " is invalid: duration = " << duration
|
||||||
|
<< ", deltaEnergy = " << deltaEnergy;
|
||||||
|
|
||||||
|
ret = false;
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (power_sample_update) {
|
||||||
|
power_history->pop();
|
||||||
|
power_history->push(curr_sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PowerFiles::computeAveragePower(const PowerRailInfo &power_rail_info,
|
||||||
|
PowerStatus *power_status, bool power_sample_update,
|
||||||
|
float *avg_power) {
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
|
float avg_power_val = -1;
|
||||||
|
float offset = power_rail_info.virtual_power_rail_info->offset;
|
||||||
|
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 = -1;
|
||||||
|
if (!getAveragePower(power_rail_info.virtual_power_rail_info->linked_power_rails[i],
|
||||||
|
&power_status->power_history[i], power_sample_update,
|
||||||
|
&avg_power_number)) {
|
||||||
|
ret = false;
|
||||||
|
continue;
|
||||||
|
} else if (avg_power_number < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PowerFiles::throttlingReleaseUpdate(std::string_view sensor_name, std::string_view cdev_name,
|
||||||
|
const ThrottlingSeverity severity,
|
||||||
|
const std::chrono::milliseconds time_elapsed_ms,
|
||||||
|
const BindedCdevInfo &binded_cdev_info,
|
||||||
|
const PowerRailInfo &power_rail_info,
|
||||||
|
bool power_sample_update, bool severity_changed) {
|
||||||
|
std::unique_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
|
||||||
|
float avg_power = -1;
|
||||||
|
|
||||||
|
if (!throttling_release_map_.count(sensor_name.data()) ||
|
||||||
|
!throttling_release_map_[sensor_name.data()].count(cdev_name.data()) ||
|
||||||
|
!power_status_map_.count(sensor_name.data()) ||
|
||||||
|
!power_status_map_[sensor_name.data()].count(binded_cdev_info.power_rail)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &release_status = throttling_release_map_[sensor_name.data()].at(cdev_name.data());
|
||||||
|
auto &power_status = power_status_map_[sensor_name.data()].at(binded_cdev_info.power_rail);
|
||||||
|
|
||||||
|
if (power_sample_update) {
|
||||||
|
if (time_elapsed_ms > power_status.time_remaining) {
|
||||||
|
power_status.time_remaining = power_rail_info.power_sample_delay;
|
||||||
|
} else {
|
||||||
|
power_status.time_remaining = power_status.time_remaining - time_elapsed_ms;
|
||||||
|
LOG(VERBOSE) << "Power rail " << binded_cdev_info.power_rail
|
||||||
|
<< " : timeout remaining = " << power_status.time_remaining.count();
|
||||||
|
if (!severity_changed) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// get the cached average power when thermal severity is changed
|
||||||
|
power_sample_update = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!severity_changed &&
|
||||||
|
power_status.time_remaining != power_rail_info.power_sample_delay) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!energy_info_map_.size() && !updateEnergyValues()) {
|
||||||
|
LOG(ERROR) << "Failed to update energy values";
|
||||||
|
release_status.release_step = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!power_sample_update && !std::isnan(power_status.last_updated_avg_power)) {
|
||||||
|
avg_power = power_status.last_updated_avg_power;
|
||||||
|
} else {
|
||||||
|
// Return false if we cannot get the average power of the target power rail
|
||||||
|
if (!((power_rail_info.virtual_power_rail_info == nullptr)
|
||||||
|
? getAveragePower(binded_cdev_info.power_rail, &power_status.power_history[0],
|
||||||
|
power_sample_update, &avg_power)
|
||||||
|
: computeAveragePower(power_rail_info, &power_status, power_sample_update,
|
||||||
|
&avg_power))) {
|
||||||
|
release_status.release_step = 0;
|
||||||
|
if (binded_cdev_info.throttling_with_power_link) {
|
||||||
|
release_status.release_step = release_status.max_release_step;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else if (avg_power < 0) {
|
||||||
|
if (binded_cdev_info.throttling_with_power_link) {
|
||||||
|
release_status.release_step = release_status.max_release_step;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
power_status.last_updated_avg_power = avg_power;
|
||||||
|
bool is_over_budget = true;
|
||||||
|
if (!binded_cdev_info.high_power_check) {
|
||||||
|
if (avg_power < binded_cdev_info.power_thresholds[static_cast<int>(severity)]) {
|
||||||
|
is_over_budget = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (avg_power > binded_cdev_info.power_thresholds[static_cast<int>(severity)]) {
|
||||||
|
is_over_budget = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Power rail " << binded_cdev_info.power_rail << ": power threshold = "
|
||||||
|
<< binded_cdev_info.power_thresholds[static_cast<int>(severity)]
|
||||||
|
<< ", avg power = " << avg_power;
|
||||||
|
|
||||||
|
switch (binded_cdev_info.release_logic) {
|
||||||
|
case ReleaseLogic::INCREASE:
|
||||||
|
if (!is_over_budget) {
|
||||||
|
if (std::abs(release_status.release_step) <
|
||||||
|
static_cast<int>(release_status.max_release_step)) {
|
||||||
|
release_status.release_step--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
release_status.release_step = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ReleaseLogic::DECREASE:
|
||||||
|
if (!is_over_budget) {
|
||||||
|
if (release_status.release_step <
|
||||||
|
static_cast<int>(release_status.max_release_step)) {
|
||||||
|
release_status.release_step++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
release_status.release_step = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ReleaseLogic::STEPWISE:
|
||||||
|
if (!is_over_budget) {
|
||||||
|
if (release_status.release_step <
|
||||||
|
static_cast<int>(release_status.max_release_step)) {
|
||||||
|
release_status.release_step++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (std::abs(release_status.release_step) <
|
||||||
|
static_cast<int>(release_status.max_release_step)) {
|
||||||
|
release_status.release_step--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ReleaseLogic::RELEASE_TO_FLOOR:
|
||||||
|
release_status.release_step = is_over_budget ? 0 : release_status.max_release_step;
|
||||||
|
break;
|
||||||
|
case ReleaseLogic::NONE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace implementation
|
||||||
|
} // namespace V2_0
|
||||||
|
} // namespace thermal
|
||||||
|
} // namespace hardware
|
||||||
|
} // namespace android
|
127
thermal/utils/power_files.h
Normal file
127
thermal/utils/power_files.h
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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 <queue>
|
||||||
|
#include <shared_mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include "config_parser.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace hardware {
|
||||||
|
namespace thermal {
|
||||||
|
namespace V2_0 {
|
||||||
|
namespace implementation {
|
||||||
|
|
||||||
|
struct PowerSample {
|
||||||
|
uint64_t energy_counter;
|
||||||
|
uint64_t duration;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReleaseStatus {
|
||||||
|
int release_step;
|
||||||
|
int max_release_step;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PowerStatus {
|
||||||
|
std::chrono::milliseconds time_remaining;
|
||||||
|
// A vector to record the queues of power sample history.
|
||||||
|
std::vector<std::queue<PowerSample>> power_history;
|
||||||
|
float last_updated_avg_power;
|
||||||
|
};
|
||||||
|
|
||||||
|
using CdevReleaseStatus = std::unordered_map<std::string, ReleaseStatus>;
|
||||||
|
using PowerStatusMap = std::unordered_map<std::string, PowerStatus>;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Register a map for the throttling release decision of target power rail
|
||||||
|
// Return false if the power_rail is not supported.
|
||||||
|
bool registerPowerRailsToWatch(std::string_view sensor_name, std::string_view cdev_name,
|
||||||
|
const BindedCdevInfo &binded_cdev_info,
|
||||||
|
const CdevInfo &cdev_info, const PowerRailInfo &power_rail_info);
|
||||||
|
|
||||||
|
// Find the energy source path, return false if no energy source found.
|
||||||
|
bool findEnergySourceToWatch(void);
|
||||||
|
|
||||||
|
// Clear the data of energy_info_map_.
|
||||||
|
void clearEnergyInfoMap(void);
|
||||||
|
|
||||||
|
// Update energy value to energy_info_map_, return false if the value is failed to update.
|
||||||
|
bool updateEnergyValues(void);
|
||||||
|
|
||||||
|
bool getAveragePower(std::string_view power_rail, std::queue<PowerSample> *power_history,
|
||||||
|
bool power_sample_update, float *avg_power);
|
||||||
|
bool computeAveragePower(const PowerRailInfo &power_rail_info, PowerStatus *power_status,
|
||||||
|
bool power_sample_update, float *avg_power);
|
||||||
|
|
||||||
|
// Update the throttling release status according to the average power, return true if power
|
||||||
|
// rail is updated.
|
||||||
|
bool throttlingReleaseUpdate(std::string_view sensor_name, std::string_view cdev_name,
|
||||||
|
const ThrottlingSeverity severity,
|
||||||
|
const std::chrono::milliseconds time_elapsed_ms,
|
||||||
|
const BindedCdevInfo &binded_cdev_info,
|
||||||
|
const PowerRailInfo &power_rail_info, bool power_sample_update,
|
||||||
|
bool severity_changed);
|
||||||
|
|
||||||
|
// Get the throttling release status for the targer power rail which is binded in specific
|
||||||
|
// sensor.
|
||||||
|
int getReleaseStep(std::string_view sensor_name, std::string_view cdev_name);
|
||||||
|
|
||||||
|
// Clear the data of throttling_release_map_.
|
||||||
|
void setPowerDataToDefault(std::string_view sensor_name);
|
||||||
|
|
||||||
|
// Get throttling release status map
|
||||||
|
const std::unordered_map<std::string, CdevReleaseStatus> &GetThrottlingReleaseMap() const {
|
||||||
|
std::shared_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
|
||||||
|
return throttling_release_map_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Power status map
|
||||||
|
const std::unordered_map<std::string, PowerStatusMap> &GetPowerStatusMap() const {
|
||||||
|
std::shared_lock<std::shared_mutex> _lock(power_status_map_mutex_);
|
||||||
|
return power_status_map_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The map to record the energy info for each power rail.
|
||||||
|
std::unordered_map<std::string, PowerSample> energy_info_map_;
|
||||||
|
// The map to record the throttling release status for each thermal sensor.
|
||||||
|
std::unordered_map<std::string, CdevReleaseStatus> throttling_release_map_;
|
||||||
|
mutable std::shared_mutex throttling_release_map_mutex_;
|
||||||
|
// The map to record the power data for each thermal sensor.
|
||||||
|
std::unordered_map<std::string, PowerStatusMap> power_status_map_;
|
||||||
|
mutable std::shared_mutex power_status_map_mutex_;
|
||||||
|
// The set to store the energy source paths
|
||||||
|
std::unordered_set<std::string> energy_path_set_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace implementation
|
||||||
|
} // namespace V2_0
|
||||||
|
} // namespace thermal
|
||||||
|
} // namespace hardware
|
||||||
|
} // namespace android
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <android-base/file.h>
|
#include <android-base/file.h>
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
|
#include <android-base/stringprintf.h>
|
||||||
#include <android-base/strings.h>
|
#include <android-base/strings.h>
|
||||||
#include "thermal_files.h"
|
#include "thermal_files.h"
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ bool ThermalFiles::readThermalFile(std::string_view thermal_name, std::string *d
|
|||||||
std::string file_path = getThermalFilePath(std::string_view(thermal_name));
|
std::string file_path = getThermalFilePath(std::string_view(thermal_name));
|
||||||
*data = "";
|
*data = "";
|
||||||
if (file_path.empty()) {
|
if (file_path.empty()) {
|
||||||
|
PLOG(WARNING) << "Failed to find " << thermal_name << "'s path";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +60,18 @@ bool ThermalFiles::readThermalFile(std::string_view thermal_name, std::string *d
|
|||||||
return true;
|
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"));
|
||||||
|
|
||||||
|
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 implementation
|
||||||
} // namespace V2_0
|
} // namespace V2_0
|
||||||
} // namespace thermal
|
} // namespace thermal
|
||||||
|
@ -14,8 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef THERMAL_UTILS_THERMAL_FILES_H_
|
#pragma once
|
||||||
#define THERMAL_UTILS_THERMAL_FILES_H_
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@ -40,6 +39,7 @@ class ThermalFiles {
|
|||||||
// data to empty and return false. If the thermal_name is found and its content
|
// 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.
|
// is read, this function will fill in data accordingly then return true.
|
||||||
bool readThermalFile(std::string_view thermal_name, std::string *data) const;
|
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(); }
|
size_t getNumThermalFiles() const { return thermal_name_to_path_map_.size(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -51,5 +51,3 @@ class ThermalFiles {
|
|||||||
} // namespace thermal
|
} // namespace thermal
|
||||||
} // namespace hardware
|
} // namespace hardware
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
||||||
#endif // THERMAL_UTILS_THERMAL_FILES_H_
|
|
||||||
|
@ -15,6 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
#include <cutils/uevent.h>
|
#include <cutils/uevent.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <linux/genetlink.h>
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/thermal.h>
|
||||||
|
#include <netlink/genl/ctrl.h>
|
||||||
|
#include <netlink/genl/genl.h>
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@ -23,8 +28,10 @@
|
|||||||
|
|
||||||
#include <android-base/file.h>
|
#include <android-base/file.h>
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
|
#include <android-base/stringprintf.h>
|
||||||
#include <android-base/strings.h>
|
#include <android-base/strings.h>
|
||||||
|
|
||||||
|
#include "thermal-helper.h"
|
||||||
#include "thermal_watcher.h"
|
#include "thermal_watcher.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
@ -33,27 +40,360 @@ namespace thermal {
|
|||||||
namespace V2_0 {
|
namespace V2_0 {
|
||||||
namespace implementation {
|
namespace implementation {
|
||||||
|
|
||||||
using std::chrono_literals::operator""ms;
|
namespace {
|
||||||
|
|
||||||
void ThermalWatcher::registerFilesToWatch(const std::set<std::string> &sensors_to_watch,
|
static int nlErrorHandle(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) {
|
||||||
bool uevent_monitor) {
|
int *ret = reinterpret_cast<int *>(arg);
|
||||||
monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end());
|
*ret = err->error;
|
||||||
if (!uevent_monitor) {
|
LOG(ERROR) << __func__ << "nl_groups: " << nla->nl_groups << ", nl_pid: " << nla->nl_pid;
|
||||||
is_polling_ = true;
|
|
||||||
return;
|
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))));
|
uevent_fd_.reset((TEMP_FAILURE_RETRY(uevent_open_socket(64 * 1024, true))));
|
||||||
if (uevent_fd_.get() < 0) {
|
if (uevent_fd_.get() < 0) {
|
||||||
LOG(ERROR) << "failed to open uevent socket";
|
LOG(ERROR) << "failed to open uevent socket";
|
||||||
is_polling_ = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
|
fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
looper_->addFd(uevent_fd_.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr);
|
looper_->addFd(uevent_fd_.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr);
|
||||||
is_polling_ = false;
|
sleep_ms_ = std::chrono::milliseconds(0);
|
||||||
thermal_triggered_ = true;
|
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, Looper::EVENT_INPUT, nullptr, nullptr);
|
||||||
|
sleep_ms_ = std::chrono::milliseconds(0);
|
||||||
last_update_time_ = boot_clock::now();
|
last_update_time_ = boot_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,9 +436,10 @@ void ThermalWatcher::parseUevent(std::set<std::string> *sensors_set) {
|
|||||||
cp = msg;
|
cp = msg;
|
||||||
while (*cp) {
|
while (*cp) {
|
||||||
std::string uevent = cp;
|
std::string uevent = cp;
|
||||||
|
auto findSubSystemThermal = uevent.find("SUBSYSTEM=thermal");
|
||||||
if (!thermal_event) {
|
if (!thermal_event) {
|
||||||
if (uevent.find("SUBSYSTEM=") == 0) {
|
if (!uevent.find("SUBSYSTEM=")) {
|
||||||
if (uevent.find("SUBSYSTEM=thermal") != std::string::npos) {
|
if (findSubSystemThermal != std::string::npos) {
|
||||||
thermal_event = true;
|
thermal_event = true;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@ -122,34 +463,64 @@ void ThermalWatcher::parseUevent(std::set<std::string> *sensors_set) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) &&
|
||||||
|
std::find(monitored_sensors_.begin(), monitored_sensors_.end(), name) !=
|
||||||
|
monitored_sensors_.end()) {
|
||||||
|
sensors_set->insert(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ThermalWatcher::wake() {
|
void ThermalWatcher::wake() {
|
||||||
looper_->wake();
|
looper_->wake();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ThermalWatcher::threadLoop() {
|
bool ThermalWatcher::threadLoop() {
|
||||||
LOG(VERBOSE) << "ThermalWatcher polling...";
|
LOG(VERBOSE) << "ThermalWatcher polling...";
|
||||||
// Polling interval 2s
|
|
||||||
static constexpr int kMinPollIntervalMs = 2000;
|
|
||||||
// Max uevent timeout 5mins
|
|
||||||
static constexpr int kUeventPollTimeoutMs = 300000;
|
|
||||||
int fd;
|
int fd;
|
||||||
std::set<std::string> sensors;
|
std::set<std::string> sensors;
|
||||||
|
|
||||||
auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() -
|
auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() -
|
||||||
last_update_time_)
|
last_update_time_);
|
||||||
.count();
|
|
||||||
int timeout = (thermal_triggered_ || is_polling_) ? kMinPollIntervalMs : kUeventPollTimeoutMs;
|
if (time_elapsed_ms < sleep_ms_ &&
|
||||||
if (time_elapsed_ms < timeout && looper_->pollOnce(timeout, &fd, nullptr, nullptr) >= 0) {
|
looper_->pollOnce(sleep_ms_.count(), &fd, nullptr, nullptr) >= 0) {
|
||||||
if (fd != uevent_fd_.get()) {
|
if (fd != uevent_fd_.get() && fd != thermal_genl_fd_.get()) {
|
||||||
return true;
|
return true;
|
||||||
|
} else if (fd == thermal_genl_fd_.get()) {
|
||||||
|
parseGenlink(&sensors);
|
||||||
|
} else if (fd == uevent_fd_.get()) {
|
||||||
|
parseUevent(&sensors);
|
||||||
}
|
}
|
||||||
parseUevent(&sensors);
|
|
||||||
// Ignore cb_ if uevent is not from monitored sensors
|
// Ignore cb_ if uevent is not from monitored sensors
|
||||||
if (sensors.size() == 0) {
|
if (sensors.size() == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thermal_triggered_ = cb_(sensors);
|
|
||||||
|
sleep_ms_ = cb_(sensors);
|
||||||
last_update_time_ = boot_clock::now();
|
last_update_time_ = boot_clock::now();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
#ifndef THERMAL_UTILS_THERMAL_WATCHER_H_
|
|
||||||
#define THERMAL_UTILS_THERMAL_WATCHER_H_
|
#pragma once
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@ -40,12 +40,12 @@ namespace implementation {
|
|||||||
|
|
||||||
using android::base::boot_clock;
|
using android::base::boot_clock;
|
||||||
using android::base::unique_fd;
|
using android::base::unique_fd;
|
||||||
using WatcherCallback = std::function<bool(const std::set<std::string> &name)>;
|
using WatcherCallback = std::function<std::chrono::milliseconds(const std::set<std::string> &name)>;
|
||||||
|
|
||||||
// A helper class for monitoring thermal files changes.
|
// A helper class for monitoring thermal files changes.
|
||||||
class ThermalWatcher : public ::android::Thread {
|
class ThermalWatcher : public ::android::Thread {
|
||||||
public:
|
public:
|
||||||
ThermalWatcher(const WatcherCallback &cb)
|
explicit ThermalWatcher(const WatcherCallback &cb)
|
||||||
: Thread(false), cb_(cb), looper_(new Looper(true)) {}
|
: Thread(false), cb_(cb), looper_(new Looper(true)) {}
|
||||||
~ThermalWatcher() = default;
|
~ThermalWatcher() = default;
|
||||||
|
|
||||||
@ -58,7 +58,10 @@ class ThermalWatcher : public ::android::Thread {
|
|||||||
// Give the file watcher a list of files to start watching. This helper
|
// 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.
|
// class will by default wait for modifications to the file with a looper.
|
||||||
// This should be called before starting watcher thread.
|
// This should be called before starting watcher thread.
|
||||||
void registerFilesToWatch(const std::set<std::string> &sensors_to_watch, bool uevent_monitor);
|
// 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
|
// Wake up the looper thus the worker thread, immediately. This can be called
|
||||||
// in any thread.
|
// in any thread.
|
||||||
void wake();
|
void wake();
|
||||||
@ -73,6 +76,9 @@ class ThermalWatcher : public ::android::Thread {
|
|||||||
// Parse uevent message
|
// Parse uevent message
|
||||||
void parseUevent(std::set<std::string> *sensor_name);
|
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.
|
// Maps watcher filer descriptor to watched file path.
|
||||||
std::unordered_map<int, std::string> watch_to_file_path_map_;
|
std::unordered_map<int, std::string> watch_to_file_path_map_;
|
||||||
|
|
||||||
@ -86,14 +92,16 @@ class ThermalWatcher : public ::android::Thread {
|
|||||||
|
|
||||||
// For uevent socket registration.
|
// For uevent socket registration.
|
||||||
android::base::unique_fd uevent_fd_;
|
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.
|
// Sensor list which monitor flag is enabled.
|
||||||
std::set<std::string> monitored_sensors_;
|
std::set<std::string> monitored_sensors_;
|
||||||
// Flag to point out if any sensor across the first threshold.
|
// Sleep interval voting result
|
||||||
bool thermal_triggered_;
|
std::chrono::milliseconds sleep_ms_;
|
||||||
// Flag to point out if device can support uevent notify.
|
|
||||||
bool is_polling_;
|
|
||||||
// Timestamp for last thermal update
|
// Timestamp for last thermal update
|
||||||
boot_clock::time_point last_update_time_;
|
boot_clock::time_point last_update_time_;
|
||||||
|
// For thermal genl socket object.
|
||||||
|
struct nl_sock *sk_thermal;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace implementation
|
} // namespace implementation
|
||||||
@ -101,5 +109,3 @@ class ThermalWatcher : public ::android::Thread {
|
|||||||
} // namespace thermal
|
} // namespace thermal
|
||||||
} // namespace hardware
|
} // namespace hardware
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
||||||
#endif // THERMAL_UTILS_THERMAL_WATCHER_H_
|
|
||||||
|
11
thermal/xiaomi_atoll-thermal-symlinks.rc
Normal file
11
thermal/xiaomi_atoll-thermal-symlinks.rc
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
on early-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
|
||||||
|
class hal
|
||||||
|
user system
|
||||||
|
group system
|
||||||
|
oneshot
|
Loading…
x
Reference in New Issue
Block a user