* Will be replaced by AIDL implementation Change-Id: I98d97e2d4c79e10bebb2963511cd202f768870cburubino
parent
cddaf77314
commit
7e2c8c3a2b
@ -1,52 +0,0 @@ |
||||
cc_binary { |
||||
name: "android.hardware.thermal@2.0-service.samsung", |
||||
defaults: [ |
||||
"hidl_defaults", |
||||
], |
||||
vendor: true, |
||||
relative_install_path: "hw", |
||||
vintf_fragments: ["android.hardware.thermal@2.0-service.samsung.xml"], |
||||
init_rc: [ |
||||
"android.hardware.thermal@2.0-service.samsung.rc", |
||||
], |
||||
srcs: [ |
||||
"service.cpp", |
||||
"Thermal.cpp", |
||||
"thermal-helper.cpp", |
||||
"utils/config_parser.cpp", |
||||
"utils/thermal_files.cpp", |
||||
"utils/thermal_watcher.cpp", |
||||
], |
||||
static_libs: [ |
||||
"libjsoncpp", |
||||
], |
||||
shared_libs: [ |
||||
"libbase", |
||||
"libcutils", |
||||
"libhidlbase", |
||||
"libutils", |
||||
"android.hardware.thermal@1.0", |
||||
"android.hardware.thermal@2.0", |
||||
], |
||||
cflags: [ |
||||
"-Wall", |
||||
"-Werror", |
||||
"-Wextra", |
||||
"-Wunused", |
||||
], |
||||
tidy: true, |
||||
tidy_checks_as_errors: [ |
||||
"android-*", |
||||
"cert-*", |
||||
"clang-analyzer-security*", |
||||
], |
||||
} |
||||
|
||||
sh_binary { |
||||
name: "thermal_logd.samsung", |
||||
src: "init.thermal.logging.sh", |
||||
vendor: true, |
||||
init_rc: [ |
||||
"samsung-thermal-logd.rc", |
||||
], |
||||
} |
@ -1,40 +0,0 @@ |
||||
# Samsung HIDL thermal HAL |
||||
|
||||
The HAL uses the standard linux thermal interface and can be configured by |
||||
adding thermal zones and cooling devices present on the device in a |
||||
`thermal_info_config.json` file. |
||||
|
||||
To probe them, just connect the phone via ADB and check the nodes available |
||||
under `/sys/class/thermal/`. The name of each thermal zone and cooling device |
||||
can be found in the type node, e.g. |
||||
|
||||
/sys/class/thermal/thermal_zone0/type |
||||
/sys/class/thermal/cooling_device0/type |
||||
|
||||
For each thermal device it is possible to configure a "Sensor" node in |
||||
`thermal_info_config.json`, and setting up to 7 throttling levels, from NONE to |
||||
SHUTDOWN. At each severity level, the hal send signals to throttle the device to the |
||||
framework, according to : https://source.android.com/devices/architecture/hidl/thermal-mitigation |
||||
In order to set temperature curve for the desired component you can |
||||
took as a refererence the kernel trip points temperatures, for the specific devices, |
||||
available in the thermal_zoneX sysfs. |
||||
Each sensor can be classified as *CPU*, *GPU*, *USB_PORT* or *BATTERY* type. |
||||
If you have a thermal monitor which does not belong to any of this categories you can |
||||
classify it as *UNKNOWN*. |
||||
You can specify hysteresis as well as if the interface should be monitored. |
||||
If you monitor the interface, the HAL takes action when the specifc treshold is passed. |
||||
You should not enable monitor if your kernel already implement thermal mitigatoin for |
||||
the specified component. |
||||
Since each device reports temperatures multiplied by different factor of 10, |
||||
you should set the Multipler field such as |
||||
`/sys/class/thermal/thermal_zoneX/temp` is reported in Celusis degrees (e.g. |
||||
25100 reported by sysfs, multiplied by 0.001 is 25.1). |
||||
|
||||
The same can be said for cooling devices. For each cooling devices can be |
||||
created a CoolingDevices node in `thermal_info_config.json`. |
||||
Each cooling interface can be classified as *CPU*, *GPU* or *BATTERY* type. |
||||
If you have a cooling device which does not belong to any of this categories you can |
||||
classify it as *COMPONENT*. |
||||
The `thermal_info_config.json` should be copied under /vendor/etc. |
||||
|
||||
For more details, refer on the sample config schema. |
@ -1,394 +0,0 @@ |
||||
/*
|
||||
* 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); |
||||
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; |
||||
} |
||||
} |
||||
} |
||||
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
|
@ -1,94 +0,0 @@ |
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project |
||||
* 2022 The LineageOS Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <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
|
@ -1,6 +0,0 @@ |
||||
service vendor.thermal-hal-2-0 /vendor/bin/hw/android.hardware.thermal@2.0-service.samsung |
||||
interface android.hardware.thermal@1.0::IThermal default |
||||
interface android.hardware.thermal@2.0::IThermal default |
||||
class hal |
||||
user system |
||||
group system |
@ -1,12 +0,0 @@ |
||||
<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> |
@ -1,25 +0,0 @@ |
||||
#!/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 |
@ -1,14 +0,0 @@ |
||||
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.samsung ${persist.vendor.log.thermal.interval:-5} |
||||
class main |
||||
user root |
||||
group root system |
||||
disabled |
@ -1,60 +0,0 @@ |
||||
/*
|
||||
* 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) << "Samsung Thermal HAL Service is shutting down."; |
||||
return 1; |
||||
} |
||||
|
||||
int main(int /* argc */, char ** /* argv */) { |
||||
status_t status; |
||||
android::sp<IThermal> service = nullptr; |
||||
|
||||
LOG(INFO) << "Samsung 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) << "Samsung Thermal HAL Service 2.0 started successfully."; |
||||
joinRpcThreadpool(); |
||||
// We should not get past the joinRpcThreadpool().
|
||||
return shutdown(); |
||||
} |
@ -1,622 +0,0 @@ |
||||
/*
|
||||
* 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 <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; |
||||
|
||||
/*
|
||||
* The phone 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
|
||||
|
||||
/*
|
||||
* 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, |
||||
}; |
||||
} |
||||
|
||||
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."; |
||||
} |
||||
} |
||||
|
||||
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; |
||||
} |
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
@ -1,137 +0,0 @@ |
||||
/*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <array> |
||||
#include <chrono> |
||||
#include <mutex> |
||||
#include <shared_mutex> |
||||
#include <string> |
||||
#include <string_view> |
||||
#include <thread> |
||||
#include <unordered_map> |
||||
#include <vector> |
||||
|
||||
#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 ::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; |
||||
}; |
||||
|
||||
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_; } |
||||
|
||||
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; |
||||
|
||||
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_; |
||||
|
||||
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
|
@ -1,160 +0,0 @@ |
||||
{ |
||||
"Sensors":[ |
||||
{ |
||||
"Name":"therm_zone0", |
||||
"Type":"CPU", |
||||
"HotThreshold":[ |
||||
"NAN", |
||||
76.0, |
||||
81.0, |
||||
86.0, |
||||
96.0, |
||||
101.0, |
||||
115.0 |
||||
], |
||||
"VrThreshold":"NAN", |
||||
"Multiplier":0.001 |
||||
}, |
||||
{ |
||||
"Name":"therm_zone1", |
||||
"Type":"CPU", |
||||
"HotThreshold":[ |
||||
"NAN", |
||||
76.0, |
||||
81.0, |
||||
86.0, |
||||
96.0, |
||||
101.0, |
||||
115.0 |
||||
], |
||||
"VrThreshold":"NAN", |
||||
"Multiplier":0.001 |
||||
}, |
||||
{ |
||||
"Name":"therm_zone2", |
||||
"Type":"CPU", |
||||
"HotThreshold":[ |
||||
"NAN", |
||||
76.0, |
||||
81.0, |
||||
86.0, |
||||
96.0, |
||||
101.0, |
||||
115.0 |
||||
], |
||||
"VrThreshold":"NAN", |
||||
"Multiplier":0.001 |
||||
}, |
||||
{ |
||||
"Name":"therm_zone3", |
||||
"Type":"CPU", |
||||
"HotThreshold":[ |
||||
"NAN", |
||||
76.0, |
||||
81.0, |
||||
86.0, |
||||
96.0, |
||||
101.0, |
||||
115.0 |
||||
], |
||||
"VrThreshold":"NAN", |
||||
"Multiplier":0.001 |
||||
}, |
||||
{ |
||||
"Name":"therm_zone4", |
||||
"Type":"CPU", |
||||
"HotThreshold":[ |
||||
"NAN", |
||||
76.0, |
||||
81.0, |
||||
86.0, |
||||
96.0, |
||||
101.0, |
||||
115.0 |
||||
], |
||||
"VrThreshold":"NAN", |
||||
"Multiplier":0.001 |
||||
}, |
||||
{ |
||||
"Name":"ac", |
||||
"Type":"USB_PORT", |
||||
"HotThreshold":[ |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"60.0", |
||||
"NAN" |
||||
], |
||||
"HotHysteresis":[ |
||||
0.0, |
||||
0.0, |
||||
0.0, |
||||
0.0, |
||||
0.0, |
||||
5.0, |
||||
0.0 |
||||
], |
||||
"VrThreshold":"NAN", |
||||
"Multiplier":0.001, |
||||
"Monitor": true |
||||
}, |
||||
{ |
||||
"Name":"max77854-fuelgauge", |
||||
"Type":"UNKNOWN", |
||||
"HotThreshold":[ |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"NAN" |
||||
], |
||||
"VrThreshold":"NAN", |
||||
"Multiplier":0.001 |
||||
}, |
||||
{ |
||||
"Name":"battery", |
||||
"Type":"BATTERY", |
||||
"HotThreshold":[ |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
"NAN", |
||||
60.0 |
||||
], |
||||
"VrThreshold":"NAN", |
||||
"Multiplier":0.001 |
||||
} |
||||
], |
||||
"CoolingDevices":[ |
||||
{ |
||||
"Name":"thermal-cpufreq-0", |
||||
"Type":"CPU" |
||||
}, |
||||
{ |
||||
"Name":"thermal-cpufreq-1", |
||||
"Type":"CPU" |
||||
}, |
||||
{ |
||||
"Name":"thermal-cpufreq-2", |
||||
"Type":"CPU" |
||||
}, |
||||
{ |
||||
"Name":"thermal-gpufreq-0", |
||||
"Type":"GPU" |
||||
}, |
||||
{ |
||||
"Name":"thermal-isp-0", |
||||
"Type":"COMPONENT" |
||||
}, |
||||
{ |
||||
"Name":"battery", |
||||
"Type":"BATTERY" |
||||
} |
||||
] |
||||
} |
@ -1,302 +0,0 @@ |
||||
/*
|
||||
* 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::CharReaderBuilder builder; |
||||
std::unique_ptr<Json::CharReader> reader(builder.newCharReader()); |
||||
std::string errorMessage; |
||||
|
||||
if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) { |
||||
LOG(ERROR) << "Failed to parse JSON config"; |
||||
return 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; |
||||
|
||||
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, |
||||
}; |
||||
++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::CharReaderBuilder builder; |
||||
std::unique_ptr<Json::CharReader> reader(builder.newCharReader()); |
||||
std::string errorMessage; |
||||
|
||||
if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) { |
||||
LOG(ERROR) << "Failed to parse JSON config"; |
||||
return 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
|
@ -1,56 +0,0 @@ |
||||
/*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#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; |
||||
}; |
||||
|
||||
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
|
@ -1,219 +0,0 @@ |
||||
{ |
||||
"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":"^(.+)$" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,65 +0,0 @@ |
||||
/*
|
||||
* 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
|
@ -1,52 +0,0 @@ |
||||
/*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#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
|
@ -1,161 +0,0 @@ |
||||
/*
|
||||
* 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
|
@ -1,103 +0,0 @@ |
||||
/*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#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
|
Loading…
Reference in new issue