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