hidl: Remove thermal HAL

* Will be replaced by AIDL implementation

Change-Id: I98d97e2d4c79e10bebb2963511cd202f768870cb
urubino
Tim Zimmermann 7 months ago
parent cddaf77314
commit 7e2c8c3a2b
No known key found for this signature in database
GPG Key ID: 6DC21A63F819C5EF
  1. 52
      hidl/thermal/Android.bp
  2. 40
      hidl/thermal/README.md
  3. 394
      hidl/thermal/Thermal.cpp
  4. 94
      hidl/thermal/Thermal.h
  5. 6
      hidl/thermal/android.hardware.thermal@2.0-service.samsung.rc
  6. 12
      hidl/thermal/android.hardware.thermal@2.0-service.samsung.xml
  7. 25
      hidl/thermal/init.thermal.logging.sh
  8. 14
      hidl/thermal/samsung-thermal-logd.rc
  9. 60
      hidl/thermal/service.cpp
  10. 622
      hidl/thermal/thermal-helper.cpp
  11. 137
      hidl/thermal/thermal-helper.h
  12. 160
      hidl/thermal/thermal_info_config_template.json
  13. 302
      hidl/thermal/utils/config_parser.cpp
  14. 56
      hidl/thermal/utils/config_parser.h
  15. 219
      hidl/thermal/utils/config_schema.json
  16. 65
      hidl/thermal/utils/thermal_files.cpp
  17. 52
      hidl/thermal/utils/thermal_files.h
  18. 161
      hidl/thermal/utils/thermal_watcher.cpp
  19. 103
      hidl/thermal/utils/thermal_watcher.h

@ -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…
Cancel
Save