mirror of
https://github.com/PixelExperience-Devices/device_xiaomi_sm6250-common.git
synced 2025-05-03 04:27:16 +09:00
sm6250-common: Import Pixel thermal HAL
* From hardware/google/pixel at 728fb99bbb910be05711421310efa6827aaaa4fa. Change-Id: I763b4dbef65084cfee337065b2c5ab465f69bca8
This commit is contained in:
parent
ea179f21b0
commit
c03d9330bc
3
Android.bp
Normal file
3
Android.bp
Normal file
@ -0,0 +1,3 @@
|
||||
soong_namespace {
|
||||
imports: ["hardware/google/interfaces"],
|
||||
}
|
4
atoll.mk
4
atoll.mk
@ -351,5 +351,9 @@ PRODUCT_PACKAGES += \
|
||||
PRODUCT_BOOT_JARS += \
|
||||
telephony-ext
|
||||
|
||||
# Thermal HAL
|
||||
PRODUCT_PACKAGES += \
|
||||
android.hardware.thermal@2.0-service.pixel
|
||||
|
||||
# Inherit proprietary targets
|
||||
$(call inherit-product, vendor/xiaomi/sm6250-common/sm6250-common-vendor.mk)
|
||||
|
1
sepolicy/vendor/file.te
vendored
Normal file
1
sepolicy/vendor/file.te
vendored
Normal file
@ -0,0 +1 @@
|
||||
type thermal_link_device, dev_type;
|
3
sepolicy/vendor/file_contexts
vendored
3
sepolicy/vendor/file_contexts
vendored
@ -6,3 +6,6 @@
|
||||
|
||||
# Power
|
||||
/vendor/bin/hw/android\.hardware\.power-service\.xiaomi-libperfmgr u:object_r:hal_power_default_exec:s0
|
||||
|
||||
# Thermal
|
||||
/vendor/bin/hw/android\.hardware\.thermal@2\.0-service\.pixel u:object_r:hal_thermal_default_exec:s0
|
||||
|
11
sepolicy/vendor/hal_thermal_default.te
vendored
Normal file
11
sepolicy/vendor/hal_thermal_default.te
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
allow hal_thermal_default sysfs_thermal:dir r_dir_perms;
|
||||
allow hal_thermal_default sysfs_thermal:file rw_file_perms;
|
||||
allow hal_thermal_default sysfs_thermal:lnk_file r_file_perms;
|
||||
allow hal_thermal_default thermal_link_device:dir r_dir_perms;
|
||||
allow hal_thermal_default proc_stat:file r_file_perms;
|
||||
|
||||
allow hal_thermal_default self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl;
|
||||
|
||||
hal_client_domain(hal_thermal_default, hal_power);
|
||||
|
||||
get_prop(hal_thermal_default, vendor_thermal_prop)
|
1
sepolicy/vendor/property.te
vendored
Normal file
1
sepolicy/vendor/property.te
vendored
Normal file
@ -0,0 +1 @@
|
||||
type vendor_thermal_prop, property_type;
|
2
sepolicy/vendor/property_contexts
vendored
Normal file
2
sepolicy/vendor/property_contexts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Thermal
|
||||
vendor.thermal. u:object_r:vendor_thermal_prop:s0
|
2
sepolicy/vendor/vendor_init.te
vendored
Normal file
2
sepolicy/vendor/vendor_init.te
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
allow vendor_init thermal_link_device:dir r_dir_perms;
|
||||
allow vendor_init thermal_link_device:lnk_file r_file_perms;
|
56
thermal/Android.bp
Normal file
56
thermal/Android.bp
Normal file
@ -0,0 +1,56 @@
|
||||
cc_binary {
|
||||
name: "android.hardware.thermal@2.0-service.pixel",
|
||||
defaults: [
|
||||
"hidl_defaults",
|
||||
],
|
||||
vendor: true,
|
||||
relative_install_path: "hw",
|
||||
vintf_fragments: ["android.hardware.thermal@2.0-service.pixel.xml"],
|
||||
init_rc: [
|
||||
"android.hardware.thermal@2.0-service.pixel.rc",
|
||||
],
|
||||
srcs: [
|
||||
"service.cpp",
|
||||
"Thermal.cpp",
|
||||
"thermal-helper.cpp",
|
||||
"utils/config_parser.cpp",
|
||||
"utils/thermal_files.cpp",
|
||||
"utils/thermal_watcher.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libhidlbase",
|
||||
"libjsoncpp",
|
||||
"libutils",
|
||||
"libbinder_ndk",
|
||||
"android.hardware.thermal@1.0",
|
||||
"android.hardware.thermal@2.0",
|
||||
"android.hardware.power-ndk_platform",
|
||||
"pixel-power-ext-ndk_platform"
|
||||
],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-Wextra",
|
||||
"-Wunused",
|
||||
],
|
||||
tidy: true,
|
||||
tidy_checks: [
|
||||
"android-*",
|
||||
"cert-*",
|
||||
"clang-analyzer-security*",
|
||||
],
|
||||
tidy_flags: [
|
||||
"-warnings-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",
|
||||
],
|
||||
}
|
3
thermal/OWNERS
Normal file
3
thermal/OWNERS
Normal file
@ -0,0 +1,3 @@
|
||||
wvw@google.com
|
||||
paillon@google.com
|
||||
jychen@google.com
|
414
thermal/Thermal.cpp
Normal file
414
thermal/Thermal.cpp
Normal file
@ -0,0 +1,414 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 <cerrno>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
|
||||
#include "Thermal.h"
|
||||
#include "thermal-helper.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace thermal {
|
||||
namespace V2_0 {
|
||||
namespace implementation {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::android::hardware::interfacesEqual;
|
||||
using ::android::hardware::thermal::V1_0::ThermalStatus;
|
||||
using ::android::hardware::thermal::V1_0::ThermalStatusCode;
|
||||
using ::android::hidl::base::V1_0::IBase;
|
||||
|
||||
template <typename T, typename U>
|
||||
Return<void> setFailureAndCallback(T _hidl_cb, hidl_vec<U> data, std::string_view debug_msg) {
|
||||
ThermalStatus status;
|
||||
status.code = ThermalStatusCode::FAILURE;
|
||||
status.debugMessage = debug_msg.data();
|
||||
_hidl_cb(status, data);
|
||||
return Void();
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
Return<void> setInitFailureAndCallback(T _hidl_cb, hidl_vec<U> data) {
|
||||
return setFailureAndCallback(_hidl_cb, data, "Failure initializing thermal HAL");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// On init we will spawn a thread which will continually watch for
|
||||
// throttling. When throttling is seen, if we have a callback registered
|
||||
// the thread will call notifyThrottling() else it will log the dropped
|
||||
// throttling event and do nothing. The thread is only killed when
|
||||
// Thermal() is killed.
|
||||
Thermal::Thermal()
|
||||
: thermal_helper_(
|
||||
std::bind(&Thermal::sendThermalChangedCallback, this, std::placeholders::_1)) {}
|
||||
|
||||
// Methods from ::android::hardware::thermal::V1_0::IThermal.
|
||||
Return<void> Thermal::getTemperatures(getTemperatures_cb _hidl_cb) {
|
||||
ThermalStatus status;
|
||||
status.code = ThermalStatusCode::SUCCESS;
|
||||
hidl_vec<Temperature_1_0> temperatures;
|
||||
|
||||
if (!thermal_helper_.isInitializedOk()) {
|
||||
LOG(ERROR) << "ThermalHAL not initialized properly.";
|
||||
return setInitFailureAndCallback(_hidl_cb, temperatures);
|
||||
}
|
||||
|
||||
if (!thermal_helper_.fillTemperatures(&temperatures)) {
|
||||
return setFailureAndCallback(_hidl_cb, temperatures, "Failed to read thermal sensors.");
|
||||
}
|
||||
|
||||
_hidl_cb(status, temperatures);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Thermal::getCpuUsages(getCpuUsages_cb _hidl_cb) {
|
||||
ThermalStatus status;
|
||||
status.code = ThermalStatusCode::SUCCESS;
|
||||
hidl_vec<CpuUsage> cpu_usages;
|
||||
|
||||
if (!thermal_helper_.isInitializedOk()) {
|
||||
return setInitFailureAndCallback(_hidl_cb, cpu_usages);
|
||||
}
|
||||
|
||||
if (!thermal_helper_.fillCpuUsages(&cpu_usages)) {
|
||||
return setFailureAndCallback(_hidl_cb, cpu_usages, "Failed to get CPU usages.");
|
||||
}
|
||||
|
||||
_hidl_cb(status, cpu_usages);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Thermal::getCoolingDevices(getCoolingDevices_cb _hidl_cb) {
|
||||
ThermalStatus status;
|
||||
status.code = ThermalStatusCode::SUCCESS;
|
||||
hidl_vec<CoolingDevice_1_0> cooling_devices;
|
||||
|
||||
if (!thermal_helper_.isInitializedOk()) {
|
||||
return setInitFailureAndCallback(_hidl_cb, cooling_devices);
|
||||
}
|
||||
_hidl_cb(status, cooling_devices);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Thermal::getCurrentTemperatures(bool filterType, TemperatureType_2_0 type,
|
||||
getCurrentTemperatures_cb _hidl_cb) {
|
||||
ThermalStatus status;
|
||||
status.code = ThermalStatusCode::SUCCESS;
|
||||
hidl_vec<Temperature_2_0> temperatures;
|
||||
|
||||
if (!thermal_helper_.isInitializedOk()) {
|
||||
LOG(ERROR) << "ThermalHAL not initialized properly.";
|
||||
return setInitFailureAndCallback(_hidl_cb, temperatures);
|
||||
}
|
||||
|
||||
if (!thermal_helper_.fillCurrentTemperatures(filterType, type, &temperatures)) {
|
||||
return setFailureAndCallback(_hidl_cb, temperatures, "Failed to read thermal sensors.");
|
||||
}
|
||||
|
||||
_hidl_cb(status, temperatures);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Thermal::getTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
|
||||
getTemperatureThresholds_cb _hidl_cb) {
|
||||
ThermalStatus status;
|
||||
status.code = ThermalStatusCode::SUCCESS;
|
||||
hidl_vec<TemperatureThreshold> temperatures;
|
||||
|
||||
if (!thermal_helper_.isInitializedOk()) {
|
||||
LOG(ERROR) << "ThermalHAL not initialized properly.";
|
||||
return setInitFailureAndCallback(_hidl_cb, temperatures);
|
||||
}
|
||||
|
||||
if (!thermal_helper_.fillTemperatureThresholds(filterType, type, &temperatures)) {
|
||||
return setFailureAndCallback(_hidl_cb, temperatures, "Failed to read thermal sensors.");
|
||||
}
|
||||
|
||||
_hidl_cb(status, temperatures);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Thermal::getCurrentCoolingDevices(bool filterType, CoolingType type,
|
||||
getCurrentCoolingDevices_cb _hidl_cb) {
|
||||
ThermalStatus status;
|
||||
status.code = ThermalStatusCode::SUCCESS;
|
||||
hidl_vec<CoolingDevice_2_0> cooling_devices;
|
||||
|
||||
if (!thermal_helper_.isInitializedOk()) {
|
||||
LOG(ERROR) << "ThermalHAL not initialized properly.";
|
||||
return setInitFailureAndCallback(_hidl_cb, cooling_devices);
|
||||
}
|
||||
|
||||
if (!thermal_helper_.fillCurrentCoolingDevices(filterType, type, &cooling_devices)) {
|
||||
return setFailureAndCallback(_hidl_cb, cooling_devices, "Failed to read thermal sensors.");
|
||||
}
|
||||
|
||||
_hidl_cb(status, cooling_devices);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Thermal::registerThermalChangedCallback(const sp<IThermalChangedCallback> &callback,
|
||||
bool filterType, TemperatureType_2_0 type,
|
||||
registerThermalChangedCallback_cb _hidl_cb) {
|
||||
ThermalStatus status;
|
||||
if (callback == nullptr) {
|
||||
status.code = ThermalStatusCode::FAILURE;
|
||||
status.debugMessage = "Invalid nullptr callback";
|
||||
LOG(ERROR) << status.debugMessage;
|
||||
_hidl_cb(status);
|
||||
return Void();
|
||||
} else {
|
||||
status.code = ThermalStatusCode::SUCCESS;
|
||||
}
|
||||
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);
|
||||
})) {
|
||||
status.code = ThermalStatusCode::FAILURE;
|
||||
status.debugMessage = "Same callback registered already";
|
||||
LOG(ERROR) << status.debugMessage;
|
||||
} else {
|
||||
callbacks_.emplace_back(callback, filterType, type);
|
||||
LOG(INFO) << "a callback has been registered to ThermalHAL, isFilter: " << filterType
|
||||
<< " Type: " << android::hardware::thermal::V2_0::toString(type);
|
||||
}
|
||||
_hidl_cb(status);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Thermal::unregisterThermalChangedCallback(
|
||||
const sp<IThermalChangedCallback> &callback, unregisterThermalChangedCallback_cb _hidl_cb) {
|
||||
ThermalStatus status;
|
||||
if (callback == nullptr) {
|
||||
status.code = ThermalStatusCode::FAILURE;
|
||||
status.debugMessage = "Invalid nullptr callback";
|
||||
LOG(ERROR) << status.debugMessage;
|
||||
_hidl_cb(status);
|
||||
return Void();
|
||||
} else {
|
||||
status.code = ThermalStatusCode::SUCCESS;
|
||||
}
|
||||
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: "
|
||||
<< android::hardware::thermal::V2_0::toString(c.type);
|
||||
removed = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
callbacks_.end());
|
||||
if (!removed) {
|
||||
status.code = ThermalStatusCode::FAILURE;
|
||||
status.debugMessage = "The callback was not registered before";
|
||||
LOG(ERROR) << status.debugMessage;
|
||||
}
|
||||
_hidl_cb(status);
|
||||
return Void();
|
||||
}
|
||||
|
||||
void Thermal::sendThermalChangedCallback(const std::vector<Temperature_2_0> &temps) {
|
||||
std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
|
||||
for (auto &t : temps) {
|
||||
LOG(INFO) << "Sending notification: "
|
||||
<< " Type: " << android::hardware::thermal::V2_0::toString(t.type)
|
||||
<< " Name: " << t.name << " CurrentValue: " << t.value << " ThrottlingStatus: "
|
||||
<< android::hardware::thermal::V2_0::toString(t.throttlingStatus);
|
||||
|
||||
thermal_helper_.sendPowerExtHint(t);
|
||||
callbacks_.erase(
|
||||
std::remove_if(callbacks_.begin(), callbacks_.end(),
|
||||
[&](const CallbackSetting &c) {
|
||||
if (!c.is_filter_type || t.type == c.type) {
|
||||
Return<void> ret = c.callback->notifyThrottling(t);
|
||||
return !ret.isOk();
|
||||
}
|
||||
LOG(ERROR)
|
||||
<< "a Thermal callback is dead, removed from callback list.";
|
||||
return false;
|
||||
}),
|
||||
callbacks_.end());
|
||||
}
|
||||
}
|
||||
|
||||
Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_string> &) {
|
||||
if (handle != nullptr && handle->numFds >= 1) {
|
||||
int fd = handle->data[0];
|
||||
std::ostringstream dump_buf;
|
||||
|
||||
if (!thermal_helper_.isInitializedOk()) {
|
||||
dump_buf << "ThermalHAL not initialized properly." << std::endl;
|
||||
} else {
|
||||
{
|
||||
hidl_vec<Temperature_1_0> temperatures;
|
||||
dump_buf << "getTemperatures:" << std::endl;
|
||||
if (!thermal_helper_.fillTemperatures(&temperatures)) {
|
||||
dump_buf << "Failed to read thermal sensors." << std::endl;
|
||||
}
|
||||
|
||||
for (const auto &t : temperatures) {
|
||||
dump_buf << " Type: " << android::hardware::thermal::V1_0::toString(t.type)
|
||||
<< " Name: " << t.name << " CurrentValue: " << t.currentValue
|
||||
<< " ThrottlingThreshold: " << t.throttlingThreshold
|
||||
<< " ShutdownThreshold: " << t.shutdownThreshold
|
||||
<< " VrThrottlingThreshold: " << t.vrThrottlingThreshold << std::endl;
|
||||
}
|
||||
}
|
||||
{
|
||||
hidl_vec<CpuUsage> cpu_usages;
|
||||
dump_buf << "getCpuUsages:" << std::endl;
|
||||
if (!thermal_helper_.fillCpuUsages(&cpu_usages)) {
|
||||
dump_buf << "Failed to get CPU usages." << std::endl;
|
||||
}
|
||||
|
||||
for (const auto &usage : cpu_usages) {
|
||||
dump_buf << " Name: " << usage.name << " Active: " << usage.active
|
||||
<< " Total: " << usage.total << " IsOnline: " << usage.isOnline
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
{
|
||||
dump_buf << "getCurrentTemperatures:" << std::endl;
|
||||
hidl_vec<Temperature_2_0> temperatures;
|
||||
if (!thermal_helper_.fillCurrentTemperatures(false, TemperatureType_2_0::SKIN,
|
||||
&temperatures)) {
|
||||
dump_buf << "Failed to getCurrentTemperatures." << std::endl;
|
||||
}
|
||||
|
||||
for (const auto &t : temperatures) {
|
||||
dump_buf << " Type: " << android::hardware::thermal::V2_0::toString(t.type)
|
||||
<< " Name: " << t.name << " CurrentValue: " << t.value
|
||||
<< " ThrottlingStatus: "
|
||||
<< android::hardware::thermal::V2_0::toString(t.throttlingStatus)
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
{
|
||||
dump_buf << "getTemperatureThresholds:" << std::endl;
|
||||
hidl_vec<TemperatureThreshold> temperatures;
|
||||
if (!thermal_helper_.fillTemperatureThresholds(false, TemperatureType_2_0::SKIN,
|
||||
&temperatures)) {
|
||||
dump_buf << "Failed to getTemperatureThresholds." << std::endl;
|
||||
}
|
||||
|
||||
for (const auto &t : temperatures) {
|
||||
dump_buf << " Type: " << android::hardware::thermal::V2_0::toString(t.type)
|
||||
<< " Name: " << t.name;
|
||||
dump_buf << " hotThrottlingThreshold: [";
|
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
|
||||
dump_buf << t.hotThrottlingThresholds[i] << " ";
|
||||
}
|
||||
dump_buf << "] coldThrottlingThreshold: [";
|
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
|
||||
dump_buf << t.coldThrottlingThresholds[i] << " ";
|
||||
}
|
||||
dump_buf << "] vrThrottlingThreshold: " << t.vrThrottlingThreshold;
|
||||
dump_buf << std::endl;
|
||||
}
|
||||
}
|
||||
{
|
||||
dump_buf << "getCurrentCoolingDevices:" << std::endl;
|
||||
hidl_vec<CoolingDevice_2_0> 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: " << android::hardware::thermal::V2_0::toString(c.type)
|
||||
<< " Name: " << c.name << " CurrentValue: " << c.value << std::endl;
|
||||
}
|
||||
}
|
||||
{
|
||||
dump_buf << "Callbacks: Total " << callbacks_.size() << std::endl;
|
||||
for (const auto &c : callbacks_) {
|
||||
dump_buf << " IsFilter: " << c.is_filter_type
|
||||
<< " Type: " << android::hardware::thermal::V2_0::toString(c.type)
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
{
|
||||
dump_buf << "getHysteresis:" << std::endl;
|
||||
const auto &map = thermal_helper_.GetSensorInfoMap();
|
||||
for (const auto &name_info_pair : map) {
|
||||
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 << "Monitor:" << std::endl;
|
||||
const auto &map = thermal_helper_.GetSensorInfoMap();
|
||||
for (const auto &name_info_pair : map) {
|
||||
dump_buf << " Name: " << name_info_pair.first;
|
||||
dump_buf << " Monitor: " << std::boolalpha << name_info_pair.second.is_monitor
|
||||
<< std::noboolalpha << std::endl;
|
||||
}
|
||||
}
|
||||
{
|
||||
dump_buf << "SendPowerHint:" << std::endl;
|
||||
const auto &map = thermal_helper_.GetSensorInfoMap();
|
||||
for (const auto &name_info_pair : map) {
|
||||
dump_buf << " Name: " << name_info_pair.first;
|
||||
dump_buf << " SendPowerHint: " << std::boolalpha
|
||||
<< name_info_pair.second.send_powerhint << std::noboolalpha
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
{
|
||||
dump_buf << "AIDL Power Hal exist: " << std::boolalpha
|
||||
<< thermal_helper_.isAidlPowerHalExist() << std::endl;
|
||||
dump_buf << "AIDL Power Hal connected: " << std::boolalpha
|
||||
<< thermal_helper_.isPowerHalConnected() << std::endl;
|
||||
dump_buf << "AIDL Power Hal 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);
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
95
thermal/Thermal.h
Normal file
95
thermal/Thermal.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
#ifndef ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
|
||||
#define ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <android/hardware/thermal/2.0/IThermal.h>
|
||||
#include <android/hardware/thermal/2.0/IThermalChangedCallback.h>
|
||||
#include <hidl/Status.h>
|
||||
|
||||
#include "thermal-helper.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace thermal {
|
||||
namespace V2_0 {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::Return;
|
||||
using ::android::hardware::thermal::V2_0::IThermal;
|
||||
using ::android::hardware::thermal::V2_0::IThermalChangedCallback;
|
||||
|
||||
struct CallbackSetting {
|
||||
CallbackSetting(sp<IThermalChangedCallback> callback, bool is_filter_type,
|
||||
TemperatureType_2_0 type)
|
||||
: callback(callback), is_filter_type(is_filter_type), type(type) {}
|
||||
sp<IThermalChangedCallback> callback;
|
||||
bool is_filter_type;
|
||||
TemperatureType_2_0 type;
|
||||
};
|
||||
|
||||
class Thermal : public IThermal {
|
||||
public:
|
||||
Thermal();
|
||||
~Thermal() = default;
|
||||
|
||||
// Disallow copy and assign.
|
||||
Thermal(const Thermal &) = delete;
|
||||
void operator=(const Thermal &) = delete;
|
||||
|
||||
// Methods from ::android::hardware::thermal::V1_0::IThermal.
|
||||
Return<void> getTemperatures(getTemperatures_cb _hidl_cb) override;
|
||||
Return<void> getCpuUsages(getCpuUsages_cb _hidl_cb) override;
|
||||
Return<void> getCoolingDevices(getCoolingDevices_cb _hidl_cb) override;
|
||||
|
||||
// Methods from ::android::hardware::thermal::V2_0::IThermal follow.
|
||||
Return<void> getCurrentTemperatures(bool filterType, TemperatureType_2_0 type,
|
||||
getCurrentTemperatures_cb _hidl_cb) override;
|
||||
Return<void> getTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
|
||||
getTemperatureThresholds_cb _hidl_cb) override;
|
||||
Return<void> registerThermalChangedCallback(const sp<IThermalChangedCallback> &callback,
|
||||
bool filterType, TemperatureType_2_0 type,
|
||||
registerThermalChangedCallback_cb _hidl_cb) override;
|
||||
Return<void> unregisterThermalChangedCallback(
|
||||
const sp<IThermalChangedCallback> &callback,
|
||||
unregisterThermalChangedCallback_cb _hidl_cb) override;
|
||||
Return<void> getCurrentCoolingDevices(bool filterType, CoolingType type,
|
||||
getCurrentCoolingDevices_cb _hidl_cb) override;
|
||||
|
||||
// Methods from ::android::hidl::base::V1_0::IBase follow.
|
||||
Return<void> debug(const hidl_handle &fd, const hidl_vec<hidl_string> &args) override;
|
||||
|
||||
// Helper function for calling callbacks
|
||||
void sendThermalChangedCallback(const std::vector<Temperature_2_0> &temps);
|
||||
|
||||
private:
|
||||
ThermalHelper thermal_helper_;
|
||||
std::mutex thermal_callback_mutex_;
|
||||
std::vector<CallbackSetting> callbacks_;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
|
6
thermal/android.hardware.thermal@2.0-service.pixel.rc
Normal file
6
thermal/android.hardware.thermal@2.0-service.pixel.rc
Normal file
@ -0,0 +1,6 @@
|
||||
service vendor.thermal-hal-2-0 /vendor/bin/hw/android.hardware.thermal@2.0-service.pixel
|
||||
interface android.hardware.thermal@1.0::IThermal default
|
||||
interface android.hardware.thermal@2.0::IThermal default
|
||||
class hal
|
||||
user system
|
||||
group system
|
12
thermal/android.hardware.thermal@2.0-service.pixel.xml
Normal file
12
thermal/android.hardware.thermal@2.0-service.pixel.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="hidl">
|
||||
<name>android.hardware.thermal</name>
|
||||
<transport>hwbinder</transport>
|
||||
<version>1.0</version>
|
||||
<version>2.0</version>
|
||||
<interface>
|
||||
<name>IThermal</name>
|
||||
<instance>default</instance>
|
||||
</interface>
|
||||
</hal>
|
||||
</manifest>
|
25
thermal/init.thermal.logging.sh
Executable file
25
thermal/init.thermal.logging.sh
Executable file
@ -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
|
14
thermal/pixel-thermal-logd.rc
Normal file
14
thermal/pixel-thermal-logd.rc
Normal file
@ -0,0 +1,14 @@
|
||||
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
|
60
thermal/service.cpp
Normal file
60
thermal/service.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 <hidl/HidlTransportSupport.h>
|
||||
#include "Thermal.h"
|
||||
|
||||
using ::android::OK;
|
||||
using ::android::status_t;
|
||||
|
||||
// libhwbinder:
|
||||
using ::android::hardware::configureRpcThreadpool;
|
||||
using ::android::hardware::joinRpcThreadpool;
|
||||
|
||||
// Generated HIDL files:
|
||||
using ::android::hardware::thermal::V2_0::IThermal;
|
||||
using ::android::hardware::thermal::V2_0::implementation::Thermal;
|
||||
|
||||
static int shutdown() {
|
||||
LOG(ERROR) << "Pixel Thermal HAL Service is shutting down.";
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int /* argc */, char ** /* argv */) {
|
||||
status_t status;
|
||||
android::sp<IThermal> service = nullptr;
|
||||
|
||||
LOG(INFO) << "Pixel Thermal HAL Service 2.0 starting...";
|
||||
|
||||
service = new Thermal();
|
||||
if (service == nullptr) {
|
||||
LOG(ERROR) << "Error creating an instance of Thermal HAL. Exiting...";
|
||||
return shutdown();
|
||||
}
|
||||
|
||||
configureRpcThreadpool(1, true /* callerWillJoin */);
|
||||
|
||||
status = service->registerAsService();
|
||||
if (status != OK) {
|
||||
LOG(ERROR) << "Could not register service for ThermalHAL (" << status << ")";
|
||||
return shutdown();
|
||||
}
|
||||
|
||||
LOG(INFO) << "Pixel Thermal HAL Service 2.0 started successfully.";
|
||||
joinRpcThreadpool();
|
||||
// We should not get past the joinRpcThreadpool().
|
||||
return shutdown();
|
||||
}
|
773
thermal/thermal-helper.cpp
Normal file
773
thermal/thermal-helper.cpp
Normal file
@ -0,0 +1,773 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 <iterator>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#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 <hidl/HidlTransportSupport.h>
|
||||
|
||||
#include "thermal-helper.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace thermal {
|
||||
namespace V2_0 {
|
||||
namespace implementation {
|
||||
|
||||
constexpr std::string_view kCpuOnlineRoot("/sys/devices/system/cpu");
|
||||
constexpr std::string_view kThermalSensorsRoot("/sys/devices/virtual/thermal");
|
||||
constexpr std::string_view kCpuUsageFile("/proc/stat");
|
||||
constexpr std::string_view kCpuOnlineFileSuffix("online");
|
||||
constexpr std::string_view kCpuPresentFile("/sys/devices/system/cpu/present");
|
||||
constexpr std::string_view kSensorPrefix("thermal_zone");
|
||||
constexpr std::string_view kCoolingDevicePrefix("cooling_device");
|
||||
constexpr std::string_view kThermalNameFile("type");
|
||||
constexpr std::string_view kSensorPolicyFile("policy");
|
||||
constexpr std::string_view kSensorTempSuffix("temp");
|
||||
constexpr std::string_view kSensorTripPointTempZeroFile("trip_point_0_temp");
|
||||
constexpr std::string_view kSensorTripPointHystZeroFile("trip_point_0_hyst");
|
||||
constexpr std::string_view kUserSpaceSuffix("user_space");
|
||||
constexpr std::string_view kCoolingDeviceCurStateSuffix("cur_state");
|
||||
constexpr std::string_view kConfigProperty("vendor.thermal.config");
|
||||
constexpr std::string_view kConfigDefaultFileName("thermal_info_config.json");
|
||||
|
||||
namespace {
|
||||
using android::base::StringPrintf;
|
||||
using android::hardware::thermal::V2_0::toString;
|
||||
|
||||
/*
|
||||
* Pixel don't offline CPU, so std::thread::hardware_concurrency(); should work.
|
||||
* However /sys/devices/system/cpu/present is preferred.
|
||||
* The file is expected to contain single text line with two numbers %d-%d,
|
||||
* which is a range of available cpu numbers, e.g. 0-7 would mean there
|
||||
* are 8 cores number from 0 to 7.
|
||||
* For Android systems this approach is safer than using cpufeatures, see bug
|
||||
* b/36941727.
|
||||
*/
|
||||
std::size_t getNumberOfCores() {
|
||||
std::string file;
|
||||
if (!android::base::ReadFileToString(kCpuPresentFile.data(), &file)) {
|
||||
LOG(ERROR) << "Error reading Cpu present file: " << kCpuPresentFile;
|
||||
return 0;
|
||||
}
|
||||
std::vector<std::string> pieces = android::base::Split(file, "-");
|
||||
if (pieces.size() != 2) {
|
||||
LOG(ERROR) << "Error parsing Cpu present file content: " << file;
|
||||
return 0;
|
||||
}
|
||||
auto min_core = std::stoul(pieces[0]);
|
||||
auto max_core = std::stoul(pieces[1]);
|
||||
if (max_core < min_core) {
|
||||
LOG(ERROR) << "Error parsing Cpu present min and max: " << min_core << " - " << max_core;
|
||||
return 0;
|
||||
}
|
||||
return static_cast<std::size_t>(max_core - min_core + 1);
|
||||
}
|
||||
const std::size_t kMaxCpus = getNumberOfCores();
|
||||
|
||||
void parseCpuUsagesFileAndAssignUsages(hidl_vec<CpuUsage> *cpu_usages) {
|
||||
uint64_t cpu_num, user, nice, system, idle;
|
||||
std::string cpu_name;
|
||||
std::string data;
|
||||
if (!android::base::ReadFileToString(kCpuUsageFile.data(), &data)) {
|
||||
LOG(ERROR) << "Error reading Cpu usage file: " << kCpuUsageFile;
|
||||
return;
|
||||
}
|
||||
|
||||
std::istringstream stat_data(data);
|
||||
std::string line;
|
||||
while (std::getline(stat_data, line)) {
|
||||
if (line.find("cpu") == 0 && isdigit(line[3])) {
|
||||
// Split the string using spaces.
|
||||
std::vector<std::string> words = android::base::Split(line, " ");
|
||||
cpu_name = words[0];
|
||||
cpu_num = std::stoi(cpu_name.substr(3));
|
||||
|
||||
if (cpu_num < kMaxCpus) {
|
||||
user = std::stoi(words[1]);
|
||||
nice = std::stoi(words[2]);
|
||||
system = std::stoi(words[3]);
|
||||
idle = std::stoi(words[4]);
|
||||
|
||||
// Check if the CPU is online by reading the online file.
|
||||
std::string cpu_online_path =
|
||||
StringPrintf("%s/%s/%s", kCpuOnlineRoot.data(), cpu_name.c_str(),
|
||||
kCpuOnlineFileSuffix.data());
|
||||
std::string is_online;
|
||||
if (!android::base::ReadFileToString(cpu_online_path, &is_online)) {
|
||||
LOG(ERROR) << "Could not open Cpu online file: " << cpu_online_path;
|
||||
return;
|
||||
}
|
||||
is_online = android::base::Trim(is_online);
|
||||
|
||||
(*cpu_usages)[cpu_num].name = cpu_name;
|
||||
(*cpu_usages)[cpu_num].active = user + nice + system;
|
||||
(*cpu_usages)[cpu_num].total = user + nice + system + idle;
|
||||
(*cpu_usages)[cpu_num].isOnline = (is_online == "1") ? true : false;
|
||||
} else {
|
||||
LOG(ERROR) << "Unexpected cpu number: " << words[0];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> parseThermalPathMap(std::string_view prefix) {
|
||||
std::map<std::string, std::string> path_map;
|
||||
std::unique_ptr<DIR, int (*)(DIR *)> dir(opendir(kThermalSensorsRoot.data()), closedir);
|
||||
if (!dir) {
|
||||
return path_map;
|
||||
}
|
||||
|
||||
// std::filesystem is not available for vendor yet
|
||||
// see discussion: aosp/894015
|
||||
while (struct dirent *dp = readdir(dir.get())) {
|
||||
if (dp->d_type != DT_DIR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!android::base::StartsWith(dp->d_name, prefix.data())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string path = android::base::StringPrintf("%s/%s/%s", kThermalSensorsRoot.data(),
|
||||
dp->d_name, kThermalNameFile.data());
|
||||
std::string name;
|
||||
if (!android::base::ReadFileToString(path, &name)) {
|
||||
PLOG(ERROR) << "Failed to read from " << path;
|
||||
continue;
|
||||
}
|
||||
|
||||
path_map.emplace(
|
||||
android::base::Trim(name),
|
||||
android::base::StringPrintf("%s/%s", kThermalSensorsRoot.data(), dp->d_name));
|
||||
}
|
||||
|
||||
return path_map;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
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_ != nullptr)
|
||||
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 (!isPowerHalConnected()) {
|
||||
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_aidl_exist_ = false;
|
||||
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) {
|
||||
if (!isPowerHalConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str());
|
||||
LOG(INFO) << "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_aidl_exist_ = false;
|
||||
power_hal_ext_aidl_ = nullptr;
|
||||
power_hal_aidl_ = nullptr;
|
||||
lock_.unlock();
|
||||
return;
|
||||
}
|
||||
lock_.unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
* Populate the sensor_name_to_file_map_ map by walking through the file tree,
|
||||
* reading the type file and assigning the temp file path to the map. If we do
|
||||
* not succeed, abort.
|
||||
*/
|
||||
ThermalHelper::ThermalHelper(const NotificationCallback &cb)
|
||||
: thermal_watcher_(new ThermalWatcher(
|
||||
std::bind(&ThermalHelper::thermalWatcherCallbackFunc, this, std::placeholders::_1))),
|
||||
cb_(cb),
|
||||
cooling_device_info_map_(ParseCoolingDevice(
|
||||
"/vendor/etc/" +
|
||||
android::base::GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data()))),
|
||||
sensor_info_map_(ParseSensorInfo(
|
||||
"/vendor/etc/" +
|
||||
android::base::GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data()))) {
|
||||
for (auto const &name_status_pair : sensor_info_map_) {
|
||||
sensor_status_map_[name_status_pair.first] = {
|
||||
.severity = ThrottlingSeverity::NONE,
|
||||
.prev_hot_severity = ThrottlingSeverity::NONE,
|
||||
.prev_cold_severity = ThrottlingSeverity::NONE,
|
||||
.prev_hint_severity = ThrottlingSeverity::NONE,
|
||||
};
|
||||
}
|
||||
|
||||
auto tz_map = parseThermalPathMap(kSensorPrefix.data());
|
||||
auto cdev_map = parseThermalPathMap(kCoolingDevicePrefix.data());
|
||||
|
||||
is_initialized_ = initializeSensorMap(tz_map) && initializeCoolingDevices(cdev_map);
|
||||
if (!is_initialized_) {
|
||||
LOG(FATAL) << "ThermalHAL could not be initialized properly.";
|
||||
}
|
||||
std::set<std::string> monitored_sensors;
|
||||
std::transform(sensor_info_map_.cbegin(), sensor_info_map_.cend(),
|
||||
std::inserter(monitored_sensors, monitored_sensors.begin()),
|
||||
[](std::pair<std::string, SensorInfo> const &sensor) {
|
||||
if (sensor.second.is_monitor)
|
||||
return sensor.first;
|
||||
else
|
||||
return std::string();
|
||||
});
|
||||
|
||||
thermal_watcher_->registerFilesToWatch(monitored_sensors, initializeTrip(tz_map));
|
||||
|
||||
// Need start watching after status map initialized
|
||||
is_initialized_ = thermal_watcher_->startWatchingDeviceFiles();
|
||||
if (!is_initialized_) {
|
||||
LOG(FATAL) << "ThermalHAL could not start watching thread properly.";
|
||||
}
|
||||
|
||||
if (!connectToPowerHal()) {
|
||||
LOG(ERROR) << "Fail to connect to Power Hal";
|
||||
} else {
|
||||
updateSupportedPowerHints();
|
||||
}
|
||||
}
|
||||
|
||||
bool ThermalHelper::readCoolingDevice(std::string_view cooling_device,
|
||||
CoolingDevice_2_0 *out) const {
|
||||
// Read the file. If the file can't be read temp will be empty string.
|
||||
std::string data;
|
||||
|
||||
if (!cooling_devices_.readThermalFile(cooling_device, &data)) {
|
||||
LOG(ERROR) << "readCoolingDevice: failed to read cooling_device: " << cooling_device;
|
||||
return false;
|
||||
}
|
||||
|
||||
const CoolingType &type = cooling_device_info_map_.at(cooling_device.data());
|
||||
|
||||
out->type = type;
|
||||
out->name = cooling_device.data();
|
||||
out->value = std::stoi(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThermalHelper::readTemperature(std::string_view sensor_name, Temperature_1_0 *out) const {
|
||||
// Read the file. If the file can't be read temp will be empty string.
|
||||
std::string temp;
|
||||
|
||||
if (!thermal_sensors_.readThermalFile(sensor_name, &temp)) {
|
||||
LOG(ERROR) << "readTemperature: sensor not found: " << sensor_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (temp.empty()) {
|
||||
LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
const SensorInfo &sensor_info = sensor_info_map_.at(sensor_name.data());
|
||||
TemperatureType_1_0 type =
|
||||
(static_cast<int>(sensor_info.type) > static_cast<int>(TemperatureType_1_0::SKIN))
|
||||
? TemperatureType_1_0::UNKNOWN
|
||||
: static_cast<TemperatureType_1_0>(sensor_info.type);
|
||||
out->type = type;
|
||||
out->name = sensor_name.data();
|
||||
out->currentValue = std::stof(temp) * sensor_info.multiplier;
|
||||
out->throttlingThreshold =
|
||||
sensor_info.hot_thresholds[static_cast<size_t>(ThrottlingSeverity::SEVERE)];
|
||||
out->shutdownThreshold =
|
||||
sensor_info.hot_thresholds[static_cast<size_t>(ThrottlingSeverity::SHUTDOWN)];
|
||||
out->vrThrottlingThreshold = sensor_info.vr_threshold;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThermalHelper::readTemperature(
|
||||
std::string_view sensor_name, Temperature_2_0 *out,
|
||||
std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status) const {
|
||||
// Read the file. If the file can't be read temp will be empty string.
|
||||
std::string temp;
|
||||
|
||||
if (!thermal_sensors_.readThermalFile(sensor_name, &temp)) {
|
||||
LOG(ERROR) << "readTemperature: sensor not found: " << sensor_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (temp.empty()) {
|
||||
LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &sensor_info = sensor_info_map_.at(sensor_name.data());
|
||||
out->type = sensor_info.type;
|
||||
out->name = sensor_name.data();
|
||||
out->value = std::stof(temp) * sensor_info.multiplier;
|
||||
|
||||
std::pair<ThrottlingSeverity, ThrottlingSeverity> status =
|
||||
std::make_pair(ThrottlingSeverity::NONE, ThrottlingSeverity::NONE);
|
||||
// Only update status if the thermal sensor is being monitored
|
||||
if (sensor_info.is_monitor) {
|
||||
ThrottlingSeverity prev_hot_severity, prev_cold_severity;
|
||||
{
|
||||
// reader lock, readTemperature will be called in Binder call and the watcher thread.
|
||||
std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
|
||||
prev_hot_severity = sensor_status_map_.at(sensor_name.data()).prev_hot_severity;
|
||||
prev_cold_severity = sensor_status_map_.at(sensor_name.data()).prev_cold_severity;
|
||||
}
|
||||
status = getSeverityFromThresholds(sensor_info.hot_thresholds, sensor_info.cold_thresholds,
|
||||
sensor_info.hot_hysteresis, sensor_info.cold_hysteresis,
|
||||
prev_hot_severity, prev_cold_severity, out->value);
|
||||
}
|
||||
if (throtting_status) {
|
||||
*throtting_status = status;
|
||||
}
|
||||
|
||||
out->throttlingStatus = static_cast<size_t>(status.first) > static_cast<size_t>(status.second)
|
||||
? status.first
|
||||
: status.second;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThermalHelper::readTemperatureThreshold(std::string_view sensor_name,
|
||||
TemperatureThreshold *out) const {
|
||||
// Read the file. If the file can't be read temp will be empty string.
|
||||
std::string temp;
|
||||
std::string path;
|
||||
|
||||
if (!sensor_info_map_.count(sensor_name.data())) {
|
||||
LOG(ERROR) << __func__ << ": sensor not found: " << sensor_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &sensor_info = sensor_info_map_.at(sensor_name.data());
|
||||
|
||||
out->type = sensor_info.type;
|
||||
out->name = sensor_name.data();
|
||||
out->hotThrottlingThresholds = sensor_info.hot_thresholds;
|
||||
out->coldThrottlingThresholds = sensor_info.cold_thresholds;
|
||||
out->vrThrottlingThreshold = sensor_info.vr_threshold;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<ThrottlingSeverity, ThrottlingSeverity> ThermalHelper::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 {
|
||||
ThrottlingSeverity ret_hot = ThrottlingSeverity::NONE;
|
||||
ThrottlingSeverity ret_hot_hysteresis = ThrottlingSeverity::NONE;
|
||||
ThrottlingSeverity ret_cold = ThrottlingSeverity::NONE;
|
||||
ThrottlingSeverity ret_cold_hysteresis = ThrottlingSeverity::NONE;
|
||||
|
||||
// Here we want to control the iteration from high to low, and hidl_enum_range doesn't support
|
||||
// a reverse iterator yet.
|
||||
for (size_t i = static_cast<size_t>(ThrottlingSeverity::SHUTDOWN);
|
||||
i > static_cast<size_t>(ThrottlingSeverity::NONE); --i) {
|
||||
if (!std::isnan(hot_thresholds[i]) && hot_thresholds[i] <= value &&
|
||||
ret_hot == ThrottlingSeverity::NONE) {
|
||||
ret_hot = static_cast<ThrottlingSeverity>(i);
|
||||
}
|
||||
if (!std::isnan(hot_thresholds[i]) && (hot_thresholds[i] - hot_hysteresis[i]) < value &&
|
||||
ret_hot_hysteresis == ThrottlingSeverity::NONE) {
|
||||
ret_hot_hysteresis = static_cast<ThrottlingSeverity>(i);
|
||||
}
|
||||
if (!std::isnan(cold_thresholds[i]) && cold_thresholds[i] >= value &&
|
||||
ret_cold == ThrottlingSeverity::NONE) {
|
||||
ret_cold = static_cast<ThrottlingSeverity>(i);
|
||||
}
|
||||
if (!std::isnan(cold_thresholds[i]) && (cold_thresholds[i] + cold_hysteresis[i]) > value &&
|
||||
ret_cold_hysteresis == ThrottlingSeverity::NONE) {
|
||||
ret_cold_hysteresis = static_cast<ThrottlingSeverity>(i);
|
||||
}
|
||||
}
|
||||
if (static_cast<size_t>(ret_hot) < static_cast<size_t>(prev_hot_severity)) {
|
||||
ret_hot = ret_hot_hysteresis;
|
||||
}
|
||||
if (static_cast<size_t>(ret_cold) < static_cast<size_t>(prev_cold_severity)) {
|
||||
ret_cold = ret_cold_hysteresis;
|
||||
}
|
||||
|
||||
return std::make_pair(ret_hot, ret_cold);
|
||||
}
|
||||
|
||||
bool ThermalHelper::initializeSensorMap(const std::map<std::string, std::string> &path_map) {
|
||||
for (const auto &sensor_info_pair : sensor_info_map_) {
|
||||
std::string_view sensor_name = sensor_info_pair.first;
|
||||
if (!path_map.count(sensor_name.data())) {
|
||||
LOG(ERROR) << "Could not find " << sensor_name << " in sysfs";
|
||||
continue;
|
||||
}
|
||||
std::string path = android::base::StringPrintf(
|
||||
"%s/%s", path_map.at(sensor_name.data()).c_str(), kSensorTempSuffix.data());
|
||||
if (!thermal_sensors_.addThermalFile(sensor_name, path)) {
|
||||
LOG(ERROR) << "Could not add " << sensor_name << "to sensors map";
|
||||
}
|
||||
}
|
||||
if (sensor_info_map_.size() == thermal_sensors_.getNumThermalFiles()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ThermalHelper::initializeCoolingDevices(const std::map<std::string, std::string> &path_map) {
|
||||
for (const auto &cooling_device_info_pair : cooling_device_info_map_) {
|
||||
std::string_view cooling_device_name = cooling_device_info_pair.first;
|
||||
if (!path_map.count(cooling_device_name.data())) {
|
||||
LOG(ERROR) << "Could not find " << cooling_device_name << " in sysfs";
|
||||
continue;
|
||||
}
|
||||
std::string path = android::base::StringPrintf(
|
||||
"%s/%s", path_map.at(cooling_device_name.data()).c_str(),
|
||||
kCoolingDeviceCurStateSuffix.data());
|
||||
if (!cooling_devices_.addThermalFile(cooling_device_name, path)) {
|
||||
LOG(ERROR) << "Could not add " << cooling_device_name << "to cooling device map";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (cooling_device_info_map_.size() == cooling_devices_.getNumThermalFiles()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ThermalHelper::initializeTrip(const std::map<std::string, std::string> &path_map) {
|
||||
for (const auto &sensor_info : sensor_info_map_) {
|
||||
if (sensor_info.second.is_monitor) {
|
||||
std::string_view sensor_name = sensor_info.first;
|
||||
std::string_view tz_path = path_map.at(sensor_name.data());
|
||||
std::string tz_policy;
|
||||
std::string path = android::base::StringPrintf("%s/%s", (tz_path.data()),
|
||||
kSensorPolicyFile.data());
|
||||
if (!android::base::ReadFileToString(path, &tz_policy)) {
|
||||
LOG(ERROR) << sensor_name << " could not open tz policy file:" << path;
|
||||
return false;
|
||||
}
|
||||
// Check if thermal zone support uevent notify
|
||||
tz_policy = android::base::Trim(tz_policy);
|
||||
if (tz_policy != kUserSpaceSuffix) {
|
||||
LOG(ERROR) << sensor_name << " does not support uevent notify";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update thermal zone trip point
|
||||
for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
|
||||
if (!std::isnan(sensor_info.second.hot_thresholds[i]) &&
|
||||
!std::isnan(sensor_info.second.hot_hysteresis[i])) {
|
||||
// Update trip_point_0_temp threshold
|
||||
std::string threshold = std::to_string(static_cast<int>(
|
||||
sensor_info.second.hot_thresholds[i] / sensor_info.second.multiplier));
|
||||
path = android::base::StringPrintf("%s/%s", (tz_path.data()),
|
||||
kSensorTripPointTempZeroFile.data());
|
||||
if (!android::base::WriteStringToFile(threshold, path)) {
|
||||
LOG(ERROR) << "fail to update " << sensor_name
|
||||
<< " trip point: " << threshold << path;
|
||||
return false;
|
||||
}
|
||||
// Update trip_point_0_hyst threshold
|
||||
threshold = std::to_string(static_cast<int>(
|
||||
sensor_info.second.hot_hysteresis[i] / sensor_info.second.multiplier));
|
||||
path = android::base::StringPrintf("%s/%s", (tz_path.data()),
|
||||
kSensorTripPointHystZeroFile.data());
|
||||
if (!android::base::WriteStringToFile(threshold, path)) {
|
||||
LOG(ERROR) << "fail to update " << sensor_name << "trip hyst" << threshold
|
||||
<< path;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
} else if (i == kThrottlingSeverityCount - 1) {
|
||||
LOG(ERROR) << sensor_name << ":all thresholds are NAN";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ThermalHelper::fillTemperatures(hidl_vec<Temperature_1_0> *temperatures) const {
|
||||
temperatures->resize(sensor_info_map_.size());
|
||||
int current_index = 0;
|
||||
for (const auto &name_info_pair : sensor_info_map_) {
|
||||
Temperature_1_0 temp;
|
||||
|
||||
if (readTemperature(name_info_pair.first, &temp)) {
|
||||
(*temperatures)[current_index] = temp;
|
||||
} else {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": error reading temperature for sensor: " << name_info_pair.first;
|
||||
return false;
|
||||
}
|
||||
++current_index;
|
||||
}
|
||||
return current_index > 0;
|
||||
}
|
||||
|
||||
bool ThermalHelper::fillCurrentTemperatures(bool filterType, TemperatureType_2_0 type,
|
||||
hidl_vec<Temperature_2_0> *temperatures) const {
|
||||
std::vector<Temperature_2_0> ret;
|
||||
for (const auto &name_info_pair : sensor_info_map_) {
|
||||
Temperature_2_0 temp;
|
||||
if (filterType && name_info_pair.second.type != type) {
|
||||
continue;
|
||||
}
|
||||
if (readTemperature(name_info_pair.first, &temp)) {
|
||||
ret.emplace_back(std::move(temp));
|
||||
} else {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": error reading temperature for sensor: " << name_info_pair.first;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*temperatures = ret;
|
||||
return ret.size() > 0;
|
||||
}
|
||||
|
||||
bool ThermalHelper::fillTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
|
||||
hidl_vec<TemperatureThreshold> *thresholds) const {
|
||||
std::vector<TemperatureThreshold> ret;
|
||||
for (const auto &name_info_pair : sensor_info_map_) {
|
||||
TemperatureThreshold temp;
|
||||
if (filterType && name_info_pair.second.type != type) {
|
||||
continue;
|
||||
}
|
||||
if (readTemperatureThreshold(name_info_pair.first, &temp)) {
|
||||
ret.emplace_back(std::move(temp));
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << ": error reading temperature threshold for sensor: "
|
||||
<< name_info_pair.first;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*thresholds = ret;
|
||||
return ret.size() > 0;
|
||||
}
|
||||
|
||||
bool ThermalHelper::fillCurrentCoolingDevices(bool filterType, CoolingType type,
|
||||
hidl_vec<CoolingDevice_2_0> *cooling_devices) const {
|
||||
std::vector<CoolingDevice_2_0> ret;
|
||||
for (const auto &name_info_pair : cooling_device_info_map_) {
|
||||
CoolingDevice_2_0 value;
|
||||
if (filterType && name_info_pair.second != type) {
|
||||
continue;
|
||||
}
|
||||
if (readCoolingDevice(name_info_pair.first, &value)) {
|
||||
ret.emplace_back(std::move(value));
|
||||
} else {
|
||||
LOG(ERROR) << __func__ << ": error reading cooling device: " << name_info_pair.first;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*cooling_devices = ret;
|
||||
return ret.size() > 0;
|
||||
}
|
||||
|
||||
bool ThermalHelper::fillCpuUsages(hidl_vec<CpuUsage> *cpu_usages) const {
|
||||
cpu_usages->resize(kMaxCpus);
|
||||
parseCpuUsagesFileAndAssignUsages(cpu_usages);
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is called in the different thread context and will update sensor_status
|
||||
// uevent_sensors is the set of sensors which trigger uevent from thermal core driver.
|
||||
bool ThermalHelper::thermalWatcherCallbackFunc(const std::set<std::string> &uevent_sensors) {
|
||||
std::vector<Temperature_2_0> temps;
|
||||
bool thermal_triggered = false;
|
||||
for (auto &name_status_pair : sensor_status_map_) {
|
||||
Temperature_2_0 temp;
|
||||
TemperatureThreshold threshold;
|
||||
SensorStatus &sensor_status = name_status_pair.second;
|
||||
const SensorInfo &sensor_info = sensor_info_map_.at(name_status_pair.first);
|
||||
// Only send notification on whitelisted sensors
|
||||
if (!sensor_info.is_monitor) {
|
||||
continue;
|
||||
}
|
||||
// If callback is triggered by uevent, only check the sensors within uevent_sensors
|
||||
if (uevent_sensors.size() != 0 &&
|
||||
uevent_sensors.find(name_status_pair.first) == uevent_sensors.end()) {
|
||||
if (sensor_status.severity != ThrottlingSeverity::NONE) {
|
||||
thermal_triggered = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
std::pair<ThrottlingSeverity, ThrottlingSeverity> throtting_status;
|
||||
if (!readTemperature(name_status_pair.first, &temp, &throtting_status)) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": error reading temperature for sensor: " << name_status_pair.first;
|
||||
continue;
|
||||
}
|
||||
if (!readTemperatureThreshold(name_status_pair.first, &threshold)) {
|
||||
LOG(ERROR) << __func__ << ": error reading temperature threshold for sensor: "
|
||||
<< name_status_pair.first;
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
// writer lock
|
||||
std::unique_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
|
||||
if (throtting_status.first != sensor_status.prev_hot_severity) {
|
||||
sensor_status.prev_hot_severity = throtting_status.first;
|
||||
}
|
||||
if (throtting_status.second != sensor_status.prev_cold_severity) {
|
||||
sensor_status.prev_cold_severity = throtting_status.second;
|
||||
}
|
||||
if (temp.throttlingStatus != sensor_status.severity) {
|
||||
temps.push_back(temp);
|
||||
sensor_status.severity = temp.throttlingStatus;
|
||||
}
|
||||
}
|
||||
if (sensor_status.severity != ThrottlingSeverity::NONE) {
|
||||
thermal_triggered = true;
|
||||
LOG(INFO) << temp.name << ": " << temp.value;
|
||||
}
|
||||
}
|
||||
if (!temps.empty() && cb_) {
|
||||
cb_(temps);
|
||||
}
|
||||
|
||||
return thermal_triggered;
|
||||
}
|
||||
|
||||
bool ThermalHelper::connectToPowerHal() {
|
||||
return power_hal_service_.connect();
|
||||
}
|
||||
|
||||
void ThermalHelper::updateSupportedPowerHints() {
|
||||
for (auto const &name_status_pair : sensor_info_map_) {
|
||||
if (!name_status_pair.second.send_powerhint) {
|
||||
continue;
|
||||
}
|
||||
ThrottlingSeverity current_severity = ThrottlingSeverity::NONE;
|
||||
for (const auto &severity : hidl_enum_range<ThrottlingSeverity>()) {
|
||||
LOG(ERROR) << "sensor: " << name_status_pair.first
|
||||
<< " current_severity :" << toString(current_severity) << " severity "
|
||||
<< toString(severity);
|
||||
if (severity == ThrottlingSeverity::NONE) {
|
||||
supported_powerhint_map_[name_status_pair.first][ThrottlingSeverity::NONE] =
|
||||
ThrottlingSeverity::NONE;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isSupported = false;
|
||||
ndk::ScopedAStatus isSupportedResult;
|
||||
|
||||
if (power_hal_service_.isPowerHalExtConnected()) {
|
||||
isSupported = power_hal_service_.isModeSupported(name_status_pair.first, severity);
|
||||
}
|
||||
if (isSupported)
|
||||
current_severity = severity;
|
||||
supported_powerhint_map_[name_status_pair.first][severity] = current_severity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ThermalHelper::sendPowerExtHint(const Temperature_2_0 &t) {
|
||||
std::lock_guard<std::shared_mutex> lock(sensor_status_map_mutex_);
|
||||
if (!isAidlPowerHalExist())
|
||||
return;
|
||||
|
||||
if (!sensor_info_map_.at(t.name).send_powerhint)
|
||||
return;
|
||||
|
||||
ThrottlingSeverity prev_hint_severity;
|
||||
prev_hint_severity = sensor_status_map_.at(t.name).prev_hint_severity;
|
||||
ThrottlingSeverity current_hint_severity = supported_powerhint_map_[t.name][t.throttlingStatus];
|
||||
|
||||
if (prev_hint_severity == current_hint_severity)
|
||||
return;
|
||||
|
||||
if (prev_hint_severity != ThrottlingSeverity::NONE) {
|
||||
power_hal_service_.setMode(t.name, prev_hint_severity, false);
|
||||
}
|
||||
|
||||
if (current_hint_severity != ThrottlingSeverity::NONE) {
|
||||
power_hal_service_.setMode(t.name, current_hint_severity, true);
|
||||
}
|
||||
|
||||
sensor_status_map_[t.name].prev_hint_severity = current_hint_severity;
|
||||
}
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
175
thermal/thermal-helper.h
Normal file
175
thermal/thermal-helper.h
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
* * * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of The Linux Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef THERMAL_THERMAL_HELPER_H__
|
||||
#define THERMAL_THERMAL_HELPER_H__
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <aidl/android/hardware/power/IPower.h>
|
||||
#include <aidl/google/hardware/power/extension/pixel/IPowerExt.h>
|
||||
#include <android/hardware/thermal/2.0/IThermal.h>
|
||||
|
||||
#include "utils/config_parser.h"
|
||||
#include "utils/thermal_files.h"
|
||||
#include "utils/thermal_watcher.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace thermal {
|
||||
namespace V2_0 {
|
||||
namespace implementation {
|
||||
|
||||
using ::aidl::android::hardware::power::IPower;
|
||||
using ::aidl::google::hardware::power::extension::pixel::IPowerExt;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::thermal::V1_0::CpuUsage;
|
||||
using ::android::hardware::thermal::V2_0::CoolingType;
|
||||
using ::android::hardware::thermal::V2_0::IThermal;
|
||||
using CoolingDevice_1_0 = ::android::hardware::thermal::V1_0::CoolingDevice;
|
||||
using CoolingDevice_2_0 = ::android::hardware::thermal::V2_0::CoolingDevice;
|
||||
using Temperature_1_0 = ::android::hardware::thermal::V1_0::Temperature;
|
||||
using Temperature_2_0 = ::android::hardware::thermal::V2_0::Temperature;
|
||||
using TemperatureType_1_0 = ::android::hardware::thermal::V1_0::TemperatureType;
|
||||
using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
|
||||
using ::android::hardware::thermal::V2_0::TemperatureThreshold;
|
||||
using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
|
||||
|
||||
using NotificationCallback = std::function<void(const std::vector<Temperature_2_0> &temps)>;
|
||||
using NotificationTime = std::chrono::time_point<std::chrono::steady_clock>;
|
||||
|
||||
struct SensorStatus {
|
||||
ThrottlingSeverity severity;
|
||||
ThrottlingSeverity prev_hot_severity;
|
||||
ThrottlingSeverity prev_cold_severity;
|
||||
ThrottlingSeverity prev_hint_severity;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
bool power_hal_aidl_exist_;
|
||||
std::shared_ptr<IPower> power_hal_aidl_;
|
||||
std::shared_ptr<IPowerExt> power_hal_ext_aidl_;
|
||||
std::mutex lock_;
|
||||
};
|
||||
|
||||
class ThermalHelper {
|
||||
public:
|
||||
ThermalHelper(const NotificationCallback &cb);
|
||||
~ThermalHelper() = default;
|
||||
|
||||
bool fillTemperatures(hidl_vec<Temperature_1_0> *temperatures) const;
|
||||
bool fillCurrentTemperatures(bool filterType, TemperatureType_2_0 type,
|
||||
hidl_vec<Temperature_2_0> *temperatures) const;
|
||||
bool fillTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
|
||||
hidl_vec<TemperatureThreshold> *thresholds) const;
|
||||
bool fillCurrentCoolingDevices(bool filterType, CoolingType type,
|
||||
hidl_vec<CoolingDevice_2_0> *coolingdevices) const;
|
||||
bool fillCpuUsages(hidl_vec<CpuUsage> *cpu_usages) const;
|
||||
|
||||
// Dissallow 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_1_0 *out) const;
|
||||
bool readTemperature(
|
||||
std::string_view sensor_name, Temperature_2_0 *out,
|
||||
std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status = nullptr) const;
|
||||
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_2_0 *out) const;
|
||||
// Get SensorInfo Map
|
||||
const std::map<std::string, SensorInfo> &GetSensorInfoMap() const { return sensor_info_map_; }
|
||||
|
||||
void sendPowerExtHint(const Temperature_2_0 &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::map<std::string, std::string> &path_map);
|
||||
bool initializeCoolingDevices(const std::map<std::string, std::string> &path_map);
|
||||
bool initializeTrip(const std::map<std::string, std::string> &path_map);
|
||||
|
||||
// For thermal_watcher_'s polling thread
|
||||
bool 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;
|
||||
|
||||
bool connectToPowerHal();
|
||||
void updateSupportedPowerHints();
|
||||
|
||||
sp<ThermalWatcher> thermal_watcher_;
|
||||
ThermalFiles thermal_sensors_;
|
||||
ThermalFiles cooling_devices_;
|
||||
bool is_initialized_;
|
||||
const NotificationCallback cb_;
|
||||
const std::map<std::string, CoolingType> cooling_device_info_map_;
|
||||
const std::map<std::string, SensorInfo> sensor_info_map_;
|
||||
std::map<std::string, std::map<ThrottlingSeverity, ThrottlingSeverity>>
|
||||
supported_powerhint_map_;
|
||||
PowerHalService power_hal_service_;
|
||||
|
||||
mutable std::shared_mutex sensor_status_map_mutex_;
|
||||
std::map<std::string, SensorStatus> sensor_status_map_;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // THERMAL_THERMAL_HELPER_H__
|
308
thermal/utils/config_parser.cpp
Normal file
308
thermal/utils/config_parser.cpp
Normal file
@ -0,0 +1,308 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <cmath>
|
||||
#include <set>
|
||||
|
||||
#include <json/reader.h>
|
||||
#include <json/value.h>
|
||||
|
||||
#include "config_parser.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace thermal {
|
||||
namespace V2_0 {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::hidl_enum_range;
|
||||
using ::android::hardware::thermal::V2_0::toString;
|
||||
using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
// Return false when failed parsing
|
||||
bool getTypeFromString(std::string_view str, T *out) {
|
||||
auto types = hidl_enum_range<T>();
|
||||
for (const auto &type : types) {
|
||||
if (toString(type) == str) {
|
||||
*out = type;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float getFloatFromValue(const Json::Value &value) {
|
||||
if (value.isString()) {
|
||||
return std::stof(value.asString());
|
||||
} else {
|
||||
return value.asFloat();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path) {
|
||||
std::string json_doc;
|
||||
std::map<std::string, SensorInfo> sensors_parsed;
|
||||
if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
|
||||
LOG(ERROR) << "Failed to read JSON config from " << config_path;
|
||||
return sensors_parsed;
|
||||
}
|
||||
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
|
||||
if (!reader.parse(json_doc, root)) {
|
||||
LOG(ERROR) << "Failed to parse JSON config";
|
||||
return sensors_parsed;
|
||||
}
|
||||
|
||||
Json::Value sensors = root["Sensors"];
|
||||
std::size_t total_parsed = 0;
|
||||
std::set<std::string> sensors_name_parsed;
|
||||
|
||||
for (Json::Value::ArrayIndex i = 0; i < sensors.size(); ++i) {
|
||||
const std::string &name = sensors[i]["Name"].asString();
|
||||
LOG(INFO) << "Sensor[" << i << "]'s Name: " << name;
|
||||
if (name.empty()) {
|
||||
LOG(ERROR) << "Failed to read "
|
||||
<< "Sensor[" << i << "]'s Name";
|
||||
sensors_parsed.clear();
|
||||
return sensors_parsed;
|
||||
}
|
||||
|
||||
auto result = sensors_name_parsed.insert(name);
|
||||
if (!result.second) {
|
||||
LOG(ERROR) << "Duplicate Sensor[" << i << "]'s Name";
|
||||
sensors_parsed.clear();
|
||||
return sensors_parsed;
|
||||
}
|
||||
|
||||
std::string sensor_type_str = sensors[i]["Type"].asString();
|
||||
LOG(INFO) << "Sensor[" << name << "]'s Type: " << sensor_type_str;
|
||||
TemperatureType_2_0 sensor_type;
|
||||
|
||||
if (!getTypeFromString(sensor_type_str, &sensor_type)) {
|
||||
LOG(ERROR) << "Invalid "
|
||||
<< "Sensor[" << name << "]'s Type: " << sensor_type_str;
|
||||
sensors_parsed.clear();
|
||||
return sensors_parsed;
|
||||
}
|
||||
|
||||
std::array<float, kThrottlingSeverityCount> hot_thresholds;
|
||||
hot_thresholds.fill(NAN);
|
||||
std::array<float, kThrottlingSeverityCount> cold_thresholds;
|
||||
cold_thresholds.fill(NAN);
|
||||
std::array<float, kThrottlingSeverityCount> hot_hysteresis;
|
||||
hot_hysteresis.fill(0.0);
|
||||
std::array<float, kThrottlingSeverityCount> cold_hysteresis;
|
||||
cold_hysteresis.fill(0.0);
|
||||
|
||||
Json::Value values = sensors[i]["HotThreshold"];
|
||||
if (values.size() != kThrottlingSeverityCount) {
|
||||
LOG(ERROR) << "Invalid "
|
||||
<< "Sensor[" << name << "]'s HotThreshold count" << values.size();
|
||||
sensors_parsed.clear();
|
||||
return sensors_parsed;
|
||||
} else {
|
||||
float min = std::numeric_limits<float>::min();
|
||||
for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
|
||||
hot_thresholds[j] = getFloatFromValue(values[j]);
|
||||
if (!std::isnan(hot_thresholds[j])) {
|
||||
if (hot_thresholds[j] < min) {
|
||||
LOG(ERROR) << "Invalid "
|
||||
<< "Sensor[" << name << "]'s HotThreshold[j" << j
|
||||
<< "]: " << hot_thresholds[j] << " < " << min;
|
||||
sensors_parsed.clear();
|
||||
return sensors_parsed;
|
||||
}
|
||||
min = hot_thresholds[j];
|
||||
}
|
||||
LOG(INFO) << "Sensor[" << name << "]'s HotThreshold[" << j
|
||||
<< "]: " << hot_thresholds[j];
|
||||
}
|
||||
}
|
||||
|
||||
values = sensors[i]["HotHysteresis"];
|
||||
if (values.size() != kThrottlingSeverityCount) {
|
||||
LOG(INFO) << "Cannot find valid "
|
||||
<< "Sensor[" << name << "]'s HotHysteresis, default all to 0.0";
|
||||
} else {
|
||||
for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
|
||||
hot_hysteresis[j] = getFloatFromValue(values[j]);
|
||||
if (std::isnan(hot_hysteresis[j])) {
|
||||
LOG(ERROR) << "Invalid "
|
||||
<< "Sensor[" << name << "]'s HotHysteresis: " << hot_hysteresis[j];
|
||||
sensors_parsed.clear();
|
||||
return sensors_parsed;
|
||||
}
|
||||
LOG(INFO) << "Sensor[" << name << "]'s HotHysteresis[" << j
|
||||
<< "]: " << hot_hysteresis[j];
|
||||
}
|
||||
}
|
||||
|
||||
values = sensors[i]["ColdThreshold"];
|
||||
if (values.size() != kThrottlingSeverityCount) {
|
||||
LOG(INFO) << "Cannot find valid "
|
||||
<< "Sensor[" << name << "]'s ColdThreshold, default all to NAN";
|
||||
} else {
|
||||
float max = std::numeric_limits<float>::max();
|
||||
for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
|
||||
cold_thresholds[j] = getFloatFromValue(values[j]);
|
||||
if (!std::isnan(cold_thresholds[j])) {
|
||||
if (cold_thresholds[j] > max) {
|
||||
LOG(ERROR) << "Invalid "
|
||||
<< "Sensor[" << name << "]'s ColdThreshold[j" << j
|
||||
<< "]: " << cold_thresholds[j] << " > " << max;
|
||||
sensors_parsed.clear();
|
||||
return sensors_parsed;
|
||||
}
|
||||
max = cold_thresholds[j];
|
||||
}
|
||||
LOG(INFO) << "Sensor[" << name << "]'s ColdThreshold[" << j
|
||||
<< "]: " << cold_thresholds[j];
|
||||
}
|
||||
}
|
||||
|
||||
values = sensors[i]["ColdHysteresis"];
|
||||
if (values.size() != kThrottlingSeverityCount) {
|
||||
LOG(INFO) << "Cannot find valid "
|
||||
<< "Sensor[" << name << "]'s ColdHysteresis, default all to 0.0";
|
||||
} else {
|
||||
for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
|
||||
cold_hysteresis[j] = getFloatFromValue(values[j]);
|
||||
if (std::isnan(cold_hysteresis[j])) {
|
||||
LOG(ERROR) << "Invalid "
|
||||
<< "Sensor[" << name
|
||||
<< "]'s ColdHysteresis: " << cold_hysteresis[j];
|
||||
sensors_parsed.clear();
|
||||
return sensors_parsed;
|
||||
}
|
||||
LOG(INFO) << "Sensor[" << name << "]'s ColdHysteresis[" << j
|
||||
<< "]: " << cold_hysteresis[j];
|
||||
}
|
||||
}
|
||||
|
||||
float vr_threshold = NAN;
|
||||
vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]);
|
||||
LOG(INFO) << "Sensor[" << name << "]'s VrThreshold: " << vr_threshold;
|
||||
|
||||
float multiplier = sensors[i]["Multiplier"].asFloat();
|
||||
LOG(INFO) << "Sensor[" << name << "]'s Multiplier: " << multiplier;
|
||||
|
||||
bool is_monitor = false;
|
||||
if (sensors[i]["Monitor"].empty() || !sensors[i]["Monitor"].isBool()) {
|
||||
LOG(INFO) << "Failed to read Sensor[" << name << "]'s Monitor, set to 'false'";
|
||||
} else {
|
||||
is_monitor = sensors[i]["Monitor"].asBool();
|
||||
}
|
||||
LOG(INFO) << "Sensor[" << name << "]'s Monitor: " << std::boolalpha << is_monitor
|
||||
<< std::noboolalpha;
|
||||
|
||||
bool send_powerhint = false;
|
||||
if (sensors[i]["SendPowerHint"].empty() || !sensors[i]["SendPowerHint"].isBool()) {
|
||||
LOG(INFO) << "Failed to read Sensor[" << name << "]'s SendPowerHint, set to 'false'";
|
||||
} else {
|
||||
send_powerhint = sensors[i]["SendPowerHint"].asBool();
|
||||
}
|
||||
LOG(INFO) << "Sensor[" << name << "]'s SendPowerHint: " << std::boolalpha << send_powerhint
|
||||
<< std::noboolalpha;
|
||||
|
||||
sensors_parsed[name] = {
|
||||
.type = sensor_type,
|
||||
.hot_thresholds = hot_thresholds,
|
||||
.cold_thresholds = cold_thresholds,
|
||||
.hot_hysteresis = hot_hysteresis,
|
||||
.cold_hysteresis = cold_hysteresis,
|
||||
.vr_threshold = vr_threshold,
|
||||
.multiplier = multiplier,
|
||||
.is_monitor = is_monitor,
|
||||
.send_powerhint = send_powerhint,
|
||||
};
|
||||
++total_parsed;
|
||||
}
|
||||
|
||||
LOG(INFO) << total_parsed << " Sensors parsed successfully";
|
||||
return sensors_parsed;
|
||||
}
|
||||
|
||||
std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_path) {
|
||||
std::string json_doc;
|
||||
std::map<std::string, CoolingType> cooling_devices_parsed;
|
||||
if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
|
||||
LOG(ERROR) << "Failed to read JSON config from " << config_path;
|
||||
return cooling_devices_parsed;
|
||||
}
|
||||
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
|
||||
if (!reader.parse(json_doc, root)) {
|
||||
LOG(ERROR) << "Failed to parse JSON config";
|
||||
return cooling_devices_parsed;
|
||||
}
|
||||
|
||||
Json::Value cooling_devices = root["CoolingDevices"];
|
||||
std::size_t total_parsed = 0;
|
||||
std::set<std::string> cooling_devices_name_parsed;
|
||||
|
||||
for (Json::Value::ArrayIndex i = 0; i < cooling_devices.size(); ++i) {
|
||||
const std::string &name = cooling_devices[i]["Name"].asString();
|
||||
LOG(INFO) << "CoolingDevice[" << i << "]'s Name: " << name;
|
||||
if (name.empty()) {
|
||||
LOG(ERROR) << "Failed to read "
|
||||
<< "CoolingDevice[" << i << "]'s Name";
|
||||
cooling_devices_parsed.clear();
|
||||
return cooling_devices_parsed;
|
||||
}
|
||||
|
||||
auto result = cooling_devices_name_parsed.insert(name.data());
|
||||
if (!result.second) {
|
||||
LOG(ERROR) << "Duplicate CoolingDevice[" << i << "]'s Name";
|
||||
cooling_devices_parsed.clear();
|
||||
return cooling_devices_parsed;
|
||||
}
|
||||
|
||||
std::string cooling_device_type_str = cooling_devices[i]["Type"].asString();
|
||||
LOG(INFO) << "CoolingDevice[" << name << "]'s Type: " << cooling_device_type_str;
|
||||
CoolingType cooling_device_type;
|
||||
|
||||
if (!getTypeFromString(cooling_device_type_str, &cooling_device_type)) {
|
||||
LOG(ERROR) << "Invalid "
|
||||
<< "CoolingDevice[" << name << "]'s Type: " << cooling_device_type_str;
|
||||
cooling_devices_parsed.clear();
|
||||
return cooling_devices_parsed;
|
||||
}
|
||||
|
||||
cooling_devices_parsed[name] = cooling_device_type;
|
||||
|
||||
++total_parsed;
|
||||
}
|
||||
|
||||
LOG(INFO) << total_parsed << " CoolingDevices parsed successfully";
|
||||
return cooling_devices_parsed;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
60
thermal/utils/config_parser.h
Normal file
60
thermal/utils/config_parser.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#ifndef THERMAL_UTILS_CONFIG_PARSER_H__
|
||||
#define THERMAL_UTILS_CONFIG_PARSER_H__
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <android/hardware/thermal/2.0/IThermal.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace thermal {
|
||||
namespace V2_0 {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::hidl_enum_range;
|
||||
using ::android::hardware::thermal::V2_0::CoolingType;
|
||||
using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
|
||||
using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
|
||||
constexpr size_t kThrottlingSeverityCount = std::distance(
|
||||
hidl_enum_range<ThrottlingSeverity>().begin(), hidl_enum_range<ThrottlingSeverity>().end());
|
||||
using ThrottlingArray = std::array<float, static_cast<size_t>(kThrottlingSeverityCount)>;
|
||||
|
||||
struct SensorInfo {
|
||||
TemperatureType_2_0 type;
|
||||
ThrottlingArray hot_thresholds;
|
||||
ThrottlingArray cold_thresholds;
|
||||
ThrottlingArray hot_hysteresis;
|
||||
ThrottlingArray cold_hysteresis;
|
||||
float vr_threshold;
|
||||
float multiplier;
|
||||
bool is_monitor;
|
||||
bool send_powerhint;
|
||||
};
|
||||
|
||||
std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path);
|
||||
std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_path);
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // THERMAL_UTILS_CONFIG_PARSER_H__
|
219
thermal/utils/config_schema.json
Normal file
219
thermal/utils/config_schema.json
Normal file
@ -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":"^(.+)$"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
65
thermal/utils/thermal_files.cpp
Normal file
65
thermal/utils/thermal_files.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
#include "thermal_files.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace thermal {
|
||||
namespace V2_0 {
|
||||
namespace implementation {
|
||||
|
||||
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 = "";
|
||||
if (file_path.empty()) {
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
55
thermal/utils/thermal_files.h
Normal file
55
thermal/utils/thermal_files.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#ifndef THERMAL_UTILS_THERMAL_FILES_H_
|
||||
#define THERMAL_UTILS_THERMAL_FILES_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace thermal {
|
||||
namespace V2_0 {
|
||||
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;
|
||||
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 V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // THERMAL_UTILS_THERMAL_FILES_H_
|
161
thermal/utils/thermal_watcher.cpp
Normal file
161
thermal/utils/thermal_watcher.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 <cutils/uevent.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/types.h>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "thermal_watcher.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace thermal {
|
||||
namespace V2_0 {
|
||||
namespace implementation {
|
||||
|
||||
using std::chrono_literals::operator""ms;
|
||||
|
||||
void ThermalWatcher::registerFilesToWatch(const std::set<std::string> &sensors_to_watch,
|
||||
bool uevent_monitor) {
|
||||
monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end());
|
||||
if (!uevent_monitor) {
|
||||
is_polling_ = true;
|
||||
return;
|
||||
}
|
||||
uevent_fd_.reset((TEMP_FAILURE_RETRY(uevent_open_socket(64 * 1024, true))));
|
||||
if (uevent_fd_.get() < 0) {
|
||||
LOG(ERROR) << "failed to open uevent socket";
|
||||
is_polling_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
|
||||
|
||||
looper_->addFd(uevent_fd_.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr);
|
||||
is_polling_ = false;
|
||||
thermal_triggered_ = true;
|
||||
last_update_time_ = boot_clock::now();
|
||||
}
|
||||
|
||||
bool ThermalWatcher::startWatchingDeviceFiles() {
|
||||
if (cb_) {
|
||||
auto ret = this->run("FileWatcherThread", PRIORITY_HIGHEST);
|
||||
if (ret != 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;
|
||||
if (!thermal_event) {
|
||||
if (uevent.find("SUBSYSTEM=") == 0) {
|
||||
if (uevent.find("SUBSYSTEM=thermal") != 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 (std::find(monitored_sensors_.begin(), monitored_sensors_.end(), name) !=
|
||||
monitored_sensors_.end()) {
|
||||
sensors_set->insert(name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (*cp++) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ThermalWatcher::wake() {
|
||||
looper_->wake();
|
||||
}
|
||||
|
||||
bool ThermalWatcher::threadLoop() {
|
||||
LOG(VERBOSE) << "ThermalWatcher polling...";
|
||||
// Polling interval 2s
|
||||
static constexpr int kMinPollIntervalMs = 2000;
|
||||
// Max uevent timeout 5mins
|
||||
static constexpr int kUeventPollTimeoutMs = 300000;
|
||||
int fd;
|
||||
std::set<std::string> sensors;
|
||||
|
||||
auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() -
|
||||
last_update_time_)
|
||||
.count();
|
||||
int timeout = (thermal_triggered_ || is_polling_) ? kMinPollIntervalMs : kUeventPollTimeoutMs;
|
||||
if (time_elapsed_ms < timeout && looper_->pollOnce(timeout, &fd, nullptr, nullptr) >= 0) {
|
||||
if (fd != uevent_fd_.get()) {
|
||||
return true;
|
||||
}
|
||||
parseUevent(&sensors);
|
||||
// Ignore cb_ if uevent is not from monitored sensors
|
||||
if (sensors.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
thermal_triggered_ = cb_(sensors);
|
||||
last_update_time_ = boot_clock::now();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
105
thermal/utils/thermal_watcher.h
Normal file
105
thermal/utils/thermal_watcher.h
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
#ifndef THERMAL_UTILS_THERMAL_WATCHER_H_
|
||||
#define THERMAL_UTILS_THERMAL_WATCHER_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <future>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/chrono_utils.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <utils/Looper.h>
|
||||
#include <utils/Thread.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace thermal {
|
||||
namespace V2_0 {
|
||||
namespace implementation {
|
||||
|
||||
using android::base::boot_clock;
|
||||
using android::base::unique_fd;
|
||||
using WatcherCallback = std::function<bool(const std::set<std::string> &name)>;
|
||||
|
||||
// A helper class for monitoring thermal files changes.
|
||||
class ThermalWatcher : public ::android::Thread {
|
||||
public:
|
||||
ThermalWatcher(const WatcherCallback &cb)
|
||||
: Thread(false), cb_(cb), looper_(new 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.
|
||||
void registerFilesToWatch(const std::set<std::string> &sensors_to_watch, bool uevent_monitor);
|
||||
// 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);
|
||||
|
||||
// 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_;
|
||||
|
||||
sp<Looper> looper_;
|
||||
|
||||
// For uevent socket registration.
|
||||
android::base::unique_fd uevent_fd_;
|
||||
// Sensor list which monitor flag is enabled.
|
||||
std::set<std::string> monitored_sensors_;
|
||||
// Flag to point out if any sensor across the first threshold.
|
||||
bool thermal_triggered_;
|
||||
// Flag to point out if device can support uevent notify.
|
||||
bool is_polling_;
|
||||
// Timestamp for last thermal update
|
||||
boot_clock::time_point last_update_time_;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // THERMAL_UTILS_THERMAL_WATCHER_H_
|
Loading…
x
Reference in New Issue
Block a user