sm6250-common: Import Pixel thermal HAL

* From hardware/google/pixel at 728fb99bbb910be05711421310efa6827aaaa4fa.

Change-Id: I763b4dbef65084cfee337065b2c5ab465f69bca8
This commit is contained in:
Alexander Winkowski 2021-08-05 14:48:13 +02:00
parent ea179f21b0
commit c03d9330bc
No known key found for this signature in database
GPG Key ID: 72762A66704CDE44
26 changed files with 2633 additions and 0 deletions

3
Android.bp Normal file
View File

@ -0,0 +1,3 @@
soong_namespace {
imports: ["hardware/google/interfaces"],
}

View File

@ -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
View File

@ -0,0 +1 @@
type thermal_link_device, dev_type;

View File

@ -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
View 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
View File

@ -0,0 +1 @@
type vendor_thermal_prop, property_type;

2
sepolicy/vendor/property_contexts vendored Normal file
View File

@ -0,0 +1,2 @@
# Thermal
vendor.thermal. u:object_r:vendor_thermal_prop:s0

2
sepolicy/vendor/vendor_init.te vendored Normal file
View 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
View 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
View File

@ -0,0 +1,3 @@
wvw@google.com
paillon@google.com
jychen@google.com

414
thermal/Thermal.cpp Normal file
View 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
View 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

View 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

View 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
View 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

View 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
View 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
View 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
View 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__

View 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

View 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__

View 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":"^(.+)$"
}
}
}
}
}
}

View 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

View 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_

View 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

View 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_