hidl: Import pixel thermal HAL

* imported from https://android.googlesource.com/platform/hardware/google/pixel/+/refs/tags/android-10.0.0_r40
 * commit 2f5b7f6916cde0633e0276c4ff5fe3d1f84d1d21

Change-Id: I032cb6aa43245e974c14fbae5c030a9e11d7b840
tirimbino
Francescodario Cuzzocrea 4 years ago
parent 7b15efd076
commit bd62c30a6c
  1. 47
      hidl/thermal/Android.bp
  2. 394
      hidl/thermal/Thermal.cpp
  3. 95
      hidl/thermal/Thermal.h
  4. 5
      hidl/thermal/android.hardware.thermal@2.0-service.pixel.rc
  5. 12
      hidl/thermal/android.hardware.thermal@2.0-service.pixel.xml
  6. 60
      hidl/thermal/service.cpp
  7. 631
      hidl/thermal/thermal-helper.cpp
  8. 140
      hidl/thermal/thermal-helper.h
  9. 298
      hidl/thermal/utils/config_parser.cpp
  10. 59
      hidl/thermal/utils/config_parser.h
  11. 219
      hidl/thermal/utils/config_schema.json
  12. 65
      hidl/thermal/utils/thermal_files.cpp
  13. 55
      hidl/thermal/utils/thermal_files.h
  14. 169
      hidl/thermal/utils/thermal_watcher.cpp
  15. 103
      hidl/thermal/utils/thermal_watcher.h

@ -0,0 +1,47 @@
cc_binary {
name: "android.hardware.thermal@2.0-service.pixel",
defaults: [
"hidl_defaults",
],
vendor: true,
relative_install_path: "hw",
vintf_fragments: ["android.hardware.thermal@2.0-service.pixel.xml"],
init_rc: [
"android.hardware.thermal@2.0-service.pixel.rc",
],
srcs: [
"service.cpp",
"Thermal.cpp",
"thermal-helper.cpp",
"utils/config_parser.cpp",
"utils/thermal_files.cpp",
"utils/thermal_watcher.cpp",
],
static_libs: [
"libjsoncpp",
],
shared_libs: [
"libbase",
"libcutils",
"libhidlbase",
"libhidltransport",
"libutils",
"android.hardware.thermal@1.0",
"android.hardware.thermal@2.0",
],
cflags: [
"-Wall",
"-Werror",
"-Wextra",
"-Wunused",
],
tidy: true,
tidy_checks: [
"android-*",
"cert-*",
"clang-analyzer-security*",
],
tidy_flags: [
"-warnings-as-errors=android-*,clang-analyzer-security*,cert-*"
],
}

@ -0,0 +1,394 @@
/*
* 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

@ -0,0 +1,95 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
#define ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
#include <mutex>
#include <thread>
#include <android/hardware/thermal/2.0/IThermal.h>
#include <android/hardware/thermal/2.0/IThermalChangedCallback.h>
#include <hidl/Status.h>
#include "thermal-helper.h"
namespace android {
namespace hardware {
namespace thermal {
namespace V2_0 {
namespace implementation {
using ::android::sp;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::thermal::V2_0::IThermal;
using ::android::hardware::thermal::V2_0::IThermalChangedCallback;
struct CallbackSetting {
CallbackSetting(sp<IThermalChangedCallback> callback, bool is_filter_type,
TemperatureType_2_0 type)
: callback(callback), is_filter_type(is_filter_type), type(type) {}
sp<IThermalChangedCallback> callback;
bool is_filter_type;
TemperatureType_2_0 type;
};
class Thermal : public IThermal {
public:
Thermal();
~Thermal() = default;
// Disallow copy and assign.
Thermal(const Thermal &) = delete;
void operator=(const Thermal &) = delete;
// Methods from ::android::hardware::thermal::V1_0::IThermal.
Return<void> getTemperatures(getTemperatures_cb _hidl_cb) override;
Return<void> getCpuUsages(getCpuUsages_cb _hidl_cb) override;
Return<void> getCoolingDevices(getCoolingDevices_cb _hidl_cb) override;
// Methods from ::android::hardware::thermal::V2_0::IThermal follow.
Return<void> getCurrentTemperatures(bool filterType, TemperatureType_2_0 type,
getCurrentTemperatures_cb _hidl_cb) override;
Return<void> getTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
getTemperatureThresholds_cb _hidl_cb) override;
Return<void> registerThermalChangedCallback(const sp<IThermalChangedCallback> &callback,
bool filterType, TemperatureType_2_0 type,
registerThermalChangedCallback_cb _hidl_cb) override;
Return<void> unregisterThermalChangedCallback(
const sp<IThermalChangedCallback> &callback,
unregisterThermalChangedCallback_cb _hidl_cb) override;
Return<void> getCurrentCoolingDevices(bool filterType, CoolingType type,
getCurrentCoolingDevices_cb _hidl_cb) override;
// Methods from ::android::hidl::base::V1_0::IBase follow.
Return<void> debug(const hidl_handle &fd, const hidl_vec<hidl_string> &args) override;
// Helper function for calling callbacks
void sendThermalChangedCallback(const std::vector<Temperature_2_0> &temps);
private:
ThermalHelper thermal_helper_;
std::mutex thermal_callback_mutex_;
std::vector<CallbackSetting> callbacks_;
};
} // namespace implementation
} // namespace V2_0
} // namespace thermal
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H

@ -0,0 +1,5 @@
service vendor.thermal-hal-2-0 /vendor/bin/hw/android.hardware.thermal@2.0-service.pixel
interface android.hardware.thermal@2.0::IThermal default
class hal
user system
group system

@ -0,0 +1,12 @@
<manifest version="1.0" type="device">
<hal format="hidl">
<name>android.hardware.thermal</name>
<transport>hwbinder</transport>
<version>1.0</version>
<version>2.0</version>
<interface>
<name>IThermal</name>
<instance>default</instance>
</interface>
</hal>
</manifest>

@ -0,0 +1,60 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android-base/logging.h>
#include <hidl/HidlTransportSupport.h>
#include "Thermal.h"
using ::android::OK;
using ::android::status_t;
// libhwbinder:
using ::android::hardware::configureRpcThreadpool;
using ::android::hardware::joinRpcThreadpool;
// Generated HIDL files:
using ::android::hardware::thermal::V2_0::IThermal;
using ::android::hardware::thermal::V2_0::implementation::Thermal;
static int shutdown() {
LOG(ERROR) << "Pixel Thermal HAL Service is shutting down.";
return 1;
}
int main(int /* argc */, char ** /* argv */) {
status_t status;
android::sp<IThermal> service = nullptr;
LOG(INFO) << "Pixel Thermal HAL Service 2.0 starting...";
service = new Thermal();
if (service == nullptr) {
LOG(ERROR) << "Error creating an instance of Thermal HAL. Exiting...";
return shutdown();
}
configureRpcThreadpool(1, true /* callerWillJoin */);
status = service->registerAsService();
if (status != OK) {
LOG(ERROR) << "Could not register service for ThermalHAL (" << status << ")";
return shutdown();
}
LOG(INFO) << "Pixel Thermal HAL Service 2.0 started successfully.";
joinRpcThreadpool();
// We should not get past the joinRpcThreadpool().
return shutdown();
}

@ -0,0 +1,631 @@
/*
* 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;
/*
* Pixel don't offline CPU, so std::thread::hardware_concurrency(); should work.
* However /sys/devices/system/cpu/present is preferred.
* The file is expected to contain single text line with two numbers %d-%d,
* which is a range of available cpu numbers, e.g. 0-7 would mean there
* are 8 cores number from 0 to 7.
* For Android systems this approach is safer than using cpufeatures, see bug
* b/36941727.
*/
std::size_t getNumberOfCores() {
std::string file;
if (!android::base::ReadFileToString(kCpuPresentFile.data(), &file)) {
LOG(ERROR) << "Error reading Cpu present file: " << kCpuPresentFile;
return 0;
}
std::vector<std::string> pieces = android::base::Split(file, "-");
if (pieces.size() != 2) {
LOG(ERROR) << "Error parsing Cpu present file content: " << file;
return 0;
}
auto min_core = std::stoul(pieces[0]);
auto max_core = std::stoul(pieces[1]);
if (max_core < min_core) {
LOG(ERROR) << "Error parsing Cpu present min and max: " << min_core << " - " << max_core;
return 0;
}
return static_cast<std::size_t>(max_core - min_core + 1);
}
const std::size_t kMaxCpus = getNumberOfCores();
void parseCpuUsagesFileAndAssignUsages(hidl_vec<CpuUsage> *cpu_usages) {
uint64_t cpu_num, user, nice, system, idle;
std::string cpu_name;
std::string data;
if (!android::base::ReadFileToString(kCpuUsageFile.data(), &data)) {
LOG(ERROR) << "Error reading Cpu usage file: " << kCpuUsageFile;
return;
}
std::istringstream stat_data(data);
std::string line;
while (std::getline(stat_data, line)) {
if (line.find("cpu") == 0 && isdigit(line[3])) {
// Split the string using spaces.
std::vector<std::string> words = android::base::Split(line, " ");
cpu_name = words[0];
cpu_num = std::stoi(cpu_name.substr(3));
if (cpu_num < kMaxCpus) {
user = std::stoi(words[1]);
nice = std::stoi(words[2]);
system = std::stoi(words[3]);
idle = std::stoi(words[4]);
// Check if the CPU is online by reading the online file.
std::string cpu_online_path =
StringPrintf("%s/%s/%s", kCpuOnlineRoot.data(), cpu_name.c_str(),
kCpuOnlineFileSuffix.data());
std::string is_online;
if (!android::base::ReadFileToString(cpu_online_path, &is_online)) {
LOG(ERROR) << "Could not open Cpu online file: " << cpu_online_path;
return;
}
is_online = android::base::Trim(is_online);
(*cpu_usages)[cpu_num].name = cpu_name;
(*cpu_usages)[cpu_num].active = user + nice + system;
(*cpu_usages)[cpu_num].total = user + nice + system + idle;
(*cpu_usages)[cpu_num].isOnline = (is_online == "1") ? true : false;
} else {
LOG(ERROR) << "Unexpected cpu number: " << words[0];
return;
}
}
}
}
std::map<std::string, std::string> parseThermalPathMap(std::string_view prefix) {
std::map<std::string, std::string> path_map;
std::unique_ptr<DIR, int (*)(DIR *)> dir(opendir(kThermalSensorsRoot.data()), closedir);
if (!dir) {
return path_map;
}
// std::filesystem is not available for vendor yet
// see discussion: aosp/894015
while (struct dirent *dp = readdir(dir.get())) {
if (dp->d_type != DT_DIR) {
continue;
}
if (!android::base::StartsWith(dp->d_name, prefix.data())) {
continue;
}
std::string path = android::base::StringPrintf("%s/%s/%s", kThermalSensorsRoot.data(),
dp->d_name, kThermalNameFile.data());
std::string name;
if (!android::base::ReadFileToString(path, &name)) {
PLOG(ERROR) << "Failed to read from " << path;
continue;
}
path_map.emplace(
android::base::Trim(name),
android::base::StringPrintf("%s/%s", kThermalSensorsRoot.data(), dp->d_name));
}
return path_map;
}
} // namespace
/*
* 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> cdev_paths;
std::transform(cooling_device_info_map_.cbegin(), cooling_device_info_map_.cend(),
std::inserter(cdev_paths, cdev_paths.begin()),
[this](std::pair<std::string, const CoolingType> const &cdev) {
std::string path =
cooling_devices_.getThermalFilePath(std::string_view(cdev.first));
if (!path.empty())
return path;
else
return std::string();
});
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, cdev_paths, 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;
}
}
if (!temps.empty() && cb_) {
cb_(temps);
}
return thermal_triggered;
}
} // namespace implementation
} // namespace V2_0
} // namespace thermal
} // namespace hardware
} // namespace android

@ -0,0 +1,140 @@
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef THERMAL_THERMAL_HELPER_H__
#define THERMAL_THERMAL_HELPER_H__
#include <array>
#include <chrono>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <string_view>
#include <thread>
#include <unordered_map>
#include <vector>
#include <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
#endif // THERMAL_THERMAL_HELPER_H__

@ -0,0 +1,298 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <cmath>
#include <set>
#include <json/reader.h>
#include <json/value.h>
#include "config_parser.h"
namespace android {
namespace hardware {
namespace thermal {
namespace V2_0 {
namespace implementation {
using ::android::hardware::hidl_enum_range;
using ::android::hardware::thermal::V2_0::toString;
using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
namespace {
template <typename T>
// Return false when failed parsing
bool getTypeFromString(std::string_view str, T *out) {
auto types = hidl_enum_range<T>();
for (const auto &type : types) {
if (toString(type) == str) {
*out = type;
return true;
}
}
return false;
}
float getFloatFromValue(const Json::Value &value) {
if (value.isString()) {
return std::stof(value.asString());
} else {
return value.asFloat();
}
}
} // namespace
std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path) {
std::string json_doc;
std::map<std::string, SensorInfo> sensors_parsed;
if (!android::base::ReadFileToString(config_path.data(), &json_doc)) {
LOG(ERROR) << "Failed to read JSON config from " << config_path;
return sensors_parsed;
}
Json::Value root;
Json::Reader reader;
if (!reader.parse(json_doc, root)) {
LOG(ERROR) << "Failed to parse JSON config";
return sensors_parsed;
}
Json::Value sensors = root["Sensors"];
std::size_t total_parsed = 0;
std::set<std::string> sensors_name_parsed;
for (Json::Value::ArrayIndex i = 0; i < sensors.size(); ++i) {
const std::string &name = sensors[i]["Name"].asString();
LOG(INFO) << "Sensor[" << i << "]'s Name: " << name;
if (name.empty()) {
LOG(ERROR) << "Failed to read "
<< "Sensor[" << i << "]'s Name";
sensors_parsed.clear();
return sensors_parsed;
}
auto result = sensors_name_parsed.insert(name);
if (!result.second) {
LOG(ERROR) << "Duplicate Sensor[" << i << "]'s Name";
sensors_parsed.clear();
return sensors_parsed;
}
std::string sensor_type_str = sensors[i]["Type"].asString();
LOG(INFO) << "Sensor[" << name << "]'s Type: " << sensor_type_str;
TemperatureType_2_0 sensor_type;
if (!getTypeFromString(sensor_type_str, &sensor_type)) {
LOG(ERROR) << "Invalid "
<< "Sensor[" << name << "]'s Type: " << sensor_type_str;
sensors_parsed.clear();
return sensors_parsed;
}
std::array<float, kThrottlingSeverityCount> hot_thresholds;
hot_thresholds.fill(NAN);
std::array<float, kThrottlingSeverityCount> cold_thresholds;
cold_thresholds.fill(NAN);
std::array<float, kThrottlingSeverityCount> hot_hysteresis;
hot_hysteresis.fill(0.0);
std::array<float, kThrottlingSeverityCount> cold_hysteresis;
cold_hysteresis.fill(0.0);
Json::Value values = sensors[i]["HotThreshold"];
if (values.size() != kThrottlingSeverityCount) {
LOG(ERROR) << "Invalid "
<< "Sensor[" << name << "]'s HotThreshold count" << values.size();
sensors_parsed.clear();
return sensors_parsed;
} else {
float min = std::numeric_limits<float>::min();
for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
hot_thresholds[j] = getFloatFromValue(values[j]);
if (!std::isnan(hot_thresholds[j])) {
if (hot_thresholds[j] < min) {
LOG(ERROR) << "Invalid "
<< "Sensor[" << name << "]'s HotThreshold[j" << j
<< "]: " << hot_thresholds[j] << " < " << min;
sensors_parsed.clear();
return sensors_parsed;
}
min = hot_thresholds[j];
}
LOG(INFO) << "Sensor[" << name << "]'s HotThreshold[" << j
<< "]: " << hot_thresholds[j];
}
}
values = sensors[i]["HotHysteresis"];
if (values.size() != kThrottlingSeverityCount) {
LOG(INFO) << "Cannot find valid "
<< "Sensor[" << name << "]'s HotHysteresis, default all to 0.0";
} else {
for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
hot_hysteresis[j] = getFloatFromValue(values[j]);
if (std::isnan(hot_hysteresis[j])) {
LOG(ERROR) << "Invalid "
<< "Sensor[" << name << "]'s HotHysteresis: " << hot_hysteresis[j];
sensors_parsed.clear();
return sensors_parsed;
}
LOG(INFO) << "Sensor[" << name << "]'s HotHysteresis[" << j
<< "]: " << hot_hysteresis[j];
}
}
values = sensors[i]["ColdThreshold"];
if (values.size() != kThrottlingSeverityCount) {
LOG(INFO) << "Cannot find valid "
<< "Sensor[" << name << "]'s ColdThreshold, default all to NAN";
} else {
float max = std::numeric_limits<float>::max();
for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
cold_thresholds[j] = getFloatFromValue(values[j]);
if (!std::isnan(cold_thresholds[j])) {
if (cold_thresholds[j] > max) {
LOG(ERROR) << "Invalid "
<< "Sensor[" << name << "]'s ColdThreshold[j" << j
<< "]: " << cold_thresholds[j] << " > " << max;
sensors_parsed.clear();
return sensors_parsed;
}
max = cold_thresholds[j];
}
LOG(INFO) << "Sensor[" << name << "]'s ColdThreshold[" << j
<< "]: " << cold_thresholds[j];
}
}
values = sensors[i]["ColdHysteresis"];
if (values.size() != kThrottlingSeverityCount) {
LOG(INFO) << "Cannot find valid "
<< "Sensor[" << name << "]'s ColdHysteresis, default all to 0.0";
} else {
for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
cold_hysteresis[j] = getFloatFromValue(values[j]);
if (std::isnan(cold_hysteresis[j])) {
LOG(ERROR) << "Invalid "
<< "Sensor[" << name
<< "]'s ColdHysteresis: " << cold_hysteresis[j];
sensors_parsed.clear();
return sensors_parsed;
}
LOG(INFO) << "Sensor[" << name << "]'s ColdHysteresis[" << j
<< "]: " << cold_hysteresis[j];
}
}
float vr_threshold = NAN;
vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]);
LOG(INFO) << "Sensor[" << name << "]'s VrThreshold: " << vr_threshold;
float multiplier = sensors[i]["Multiplier"].asFloat();
LOG(INFO) << "Sensor[" << name << "]'s Multiplier: " << multiplier;
bool is_monitor = false;
if (sensors[i]["Monitor"].empty() || !sensors[i]["Monitor"].isBool()) {
LOG(INFO) << "Failed to read Sensor[" << name << "]'s Monitor, set to 'false'";
} else {
is_monitor = sensors[i]["Monitor"].asBool();
}
LOG(INFO) << "Sensor[" << name << "]'s Monitor: " << std::boolalpha << is_monitor
<< std::noboolalpha;
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::Reader reader;
if (!reader.parse(json_doc, root)) {
LOG(ERROR) << "Failed to parse JSON config";
return cooling_devices_parsed;
}
Json::Value cooling_devices = root["CoolingDevices"];
std::size_t total_parsed = 0;
std::set<std::string> cooling_devices_name_parsed;
for (Json::Value::ArrayIndex i = 0; i < cooling_devices.size(); ++i) {
const std::string &name = cooling_devices[i]["Name"].asString();
LOG(INFO) << "CoolingDevice[" << i << "]'s Name: " << name;
if (name.empty()) {
LOG(ERROR) << "Failed to read "
<< "CoolingDevice[" << i << "]'s Name";
cooling_devices_parsed.clear();
return cooling_devices_parsed;
}
auto result = cooling_devices_name_parsed.insert(name.data());
if (!result.second) {
LOG(ERROR) << "Duplicate CoolingDevice[" << i << "]'s Name";
cooling_devices_parsed.clear();
return cooling_devices_parsed;
}
std::string cooling_device_type_str = cooling_devices[i]["Type"].asString();
LOG(INFO) << "CoolingDevice[" << name << "]'s Type: " << cooling_device_type_str;
CoolingType cooling_device_type;
if (!getTypeFromString(cooling_device_type_str, &cooling_device_type)) {
LOG(ERROR) << "Invalid "
<< "CoolingDevice[" << name << "]'s Type: " << cooling_device_type_str;
cooling_devices_parsed.clear();
return cooling_devices_parsed;
}
cooling_devices_parsed[name] = cooling_device_type;
++total_parsed;
}
LOG(INFO) << total_parsed << " CoolingDevices parsed successfully";
return cooling_devices_parsed;
}
} // namespace implementation
} // namespace V2_0
} // namespace thermal
} // namespace hardware
} // namespace android

@ -0,0 +1,59 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef THERMAL_UTILS_CONFIG_PARSER_H__
#define THERMAL_UTILS_CONFIG_PARSER_H__
#include <map>
#include <string>
#include <android/hardware/thermal/2.0/IThermal.h>
namespace android {
namespace hardware {
namespace thermal {
namespace V2_0 {
namespace implementation {
using ::android::hardware::hidl_enum_range;
using ::android::hardware::thermal::V2_0::CoolingType;
using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
constexpr size_t kThrottlingSeverityCount = std::distance(
hidl_enum_range<ThrottlingSeverity>().begin(), hidl_enum_range<ThrottlingSeverity>().end());
using ThrottlingArray = std::array<float, static_cast<size_t>(kThrottlingSeverityCount)>;
struct SensorInfo {
TemperatureType_2_0 type;
ThrottlingArray hot_thresholds;
ThrottlingArray cold_thresholds;
ThrottlingArray hot_hysteresis;
ThrottlingArray cold_hysteresis;
float vr_threshold;
float multiplier;
bool is_monitor;
};
std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path);
std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_path);
} // namespace implementation
} // namespace V2_0
} // namespace thermal
} // namespace hardware
} // namespace android
#endif // THERMAL_UTILS_CONFIG_PARSER_H__

@ -0,0 +1,219 @@
{
"definitions":{
},
"$schema":"http://json-schema.org/draft-07/schema#",
"$id":"http://example.com/root.json",
"type":"object",
"title":"The Root Schema",
"required":[
"Sensors"
],
"properties":{
"Sensors":{
"$id":"#/properties/Sensors",
"type":"array",
"title":"The Sensors Schema",
"items":{
"$id":"#/properties/Sensors/items",
"type":"object",
"title":"The Items Schema",
"required":[
"Name",
"Type",
"HotThreshold",
"VrThreshold",
"Multiplier"
],
"properties":{
"Name":{
"$id":"#/properties/Sensors/items/properties/Name",
"type":"string",
"title":"The Name Schema",
"default":"",
"examples":[
"cpu0-silver-usr"
],
"pattern":"^(.+)$"
},
"Type":{
"$id":"#/properties/Sensors/items/properties/Type",
"type":"string",
"title":"The Type Schema",
"default":"",
"examples":[
"CPU"
],
"pattern":"^(.+)$"
},
"HotThreshold":{
"$id":"#/properties/Sensors/items/properties/HotThreshold",
"type":"array",
"title":"The hot threshold Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN",
"default":"NAN",
"maxItems":7,
"minItems":7,
"items":{
"$id":"#/properties/Sensors/items/properties/HotThreshold/items",
"type":[
"string",
"number"
],
"title":"The Items Schema",
"default":"",
"examples":[
"NAN",
"NAN",
"NAN",
95,
"NAN",
"NAN",
125
],
"pattern":"^([-+]?[0-9]*\\.?[0-9]+|NAN)$"
}
},
"HotHysteresis":{
"$id":"#/properties/Sensors/items/properties/HotHysteresis",
"type":"array",
"title":"The hot hysteresis Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN. Throttling status will be cleared HotThreshold - HotHysteresis.",
"default":null,
"maxItems":7,
"minItems":7,
"items":{
"$id":"#/properties/Sensors/items/properties/HotHysteresis/items",
"type":[
"number"
],
"title":"The Items Schema",
"default":0.0,
"examples":[
0.0,
0.0,
0.0,
1.0,
1.5,
1.0,
2.0
]
}
},
"ColdThreshold":{
"$id":"#/properties/Sensors/items/properties/ColdThreshold",
"type":"array",
"title":"The cold threshold Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN, default to NAN",
"default":null,
"maxItems":7,
"minItems":7,
"items":{
"$id":"#/properties/Sensors/items/properties/ColdThreshold/items",
"type":"string",
"title":"The Items Schema",
"default":"NAN",
"examples":[
"NAN",
"NAN",
"NAN",
"NAN",
"NAN",
"NAN",
"NAN"
],
"pattern":"^([-+]?[0-9]*\\.?[0-9]+|NAN)$"
}
},
"ColdHysteresis":{
"$id":"#/properties/Sensors/items/properties/ColdHysteresis",
"type":"array",
"title":"The cold hysteresis Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN. Throttling status will be cleared ColdThreshold + ColdHysteresis.",
"default":null,
"maxItems":7,
"minItems":7,
"items":{
"$id":"#/properties/Sensors/items/properties/ColdHysteresis/items",
"type":[
"number"
],
"title":"The Items Schema",
"default":0.0,
"examples":[
0.0,
0.0,
0.0,
1.0,
1.5,
1.0,
2.0
]
}
},
"VrThreshold":{
"$id":"#/properties/Sensors/items/properties/VrThreshold",
"type":"string",
"title":"The Vrthreshold Schema",
"default":"",
"examples":[
"NAN"
],
"pattern":"^(.*)$"
},
"Multiplier":{
"$id":"#/properties/Sensors/items/properties/Multiplier",
"type":"number",
"title":"The Multiplier Schema",
"default":0.001,
"examples":[
0.001
],
"exclusiveMinimum":0.0
},
"Monitor":{
"$id":"#/properties/Sensors/items/properties/Monitor",
"type":"boolean",
"title":"The Monitor Schema, if the sensor will be monitored and used to trigger throttling event",
"default":false,
"examples":[
true
]
}
}
}
},
"CoolingDevices":{
"$id":"#/properties/CoolingDevices",
"type":"array",
"title":"The Coolingdevices Schema",
"items":{
"$id":"#/properties/CoolingDevices/items",
"type":"object",
"title":"The Items Schema",
"required":[
"Name",
"Type"
],
"properties":{
"Name":{
"$id":"#/properties/CoolingDevices/items/properties/Name",
"type":"string",
"title":"The Name Schema",
"default":"",
"examples":[
"thermal-cpufreq-0"
],
"pattern":"^(.+)$"
},
"Type":{
"$id":"#/properties/CoolingDevices/items/properties/Type",
"type":"string",
"title":"The Type Schema",
"default":"",
"examples":[
"CPU"
],
"pattern":"^(.+)$"
}
}
}
}
}
}

@ -0,0 +1,65 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <string_view>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include "thermal_files.h"
namespace android {
namespace hardware {
namespace thermal {
namespace V2_0 {
namespace implementation {
std::string ThermalFiles::getThermalFilePath(std::string_view thermal_name) const {
auto sensor_itr = thermal_name_to_path_map_.find(thermal_name.data());
if (sensor_itr == thermal_name_to_path_map_.end()) {
return "";
}
return sensor_itr->second;
}
bool ThermalFiles::addThermalFile(std::string_view thermal_name, std::string_view path) {
return thermal_name_to_path_map_.emplace(thermal_name, path).second;
}
bool ThermalFiles::readThermalFile(std::string_view thermal_name, std::string *data) const {
std::string sensor_reading;
std::string file_path = getThermalFilePath(std::string_view(thermal_name));
*data = "";
if (file_path.empty()) {
return false;
}
if (!::android::base::ReadFileToString(file_path, &sensor_reading)) {
PLOG(WARNING) << "Failed to read sensor: " << thermal_name;
return false;
}
// Strip the newline.
*data = ::android::base::Trim(sensor_reading);
return true;
}
} // namespace implementation
} // namespace V2_0
} // namespace thermal
} // namespace hardware
} // namespace android

@ -0,0 +1,55 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef THERMAL_UTILS_THERMAL_FILES_H_
#define THERMAL_UTILS_THERMAL_FILES_H_
#include <string>
#include <unordered_map>
namespace android {
namespace hardware {
namespace thermal {
namespace V2_0 {
namespace implementation {
class ThermalFiles {
public:
ThermalFiles() = default;
~ThermalFiles() = default;
ThermalFiles(const ThermalFiles &) = delete;
void operator=(const ThermalFiles &) = delete;
std::string getThermalFilePath(std::string_view thermal_name) const;
// Returns true if add was successful, false otherwise.
bool addThermalFile(std::string_view thermal_name, std::string_view path);
// If thermal_name is not found in the thermal names to path map, this will set
// data to empty and return false. If the thermal_name is found and its content
// is read, this function will fill in data accordingly then return true.
bool readThermalFile(std::string_view thermal_name, std::string *data) const;
size_t getNumThermalFiles() const { return thermal_name_to_path_map_.size(); }
private:
std::unordered_map<std::string, std::string> thermal_name_to_path_map_;
};
} // namespace implementation
} // namespace V2_0
} // namespace thermal
} // namespace hardware
} // namespace android
#endif // THERMAL_UTILS_THERMAL_FILES_H_

@ -0,0 +1,169 @@
/*
* 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,
const std::set<std::string> &cdev_to_watch,
bool uevent_monitor) {
int flags = O_RDONLY | O_CLOEXEC | O_BINARY;
for (const auto &path : cdev_to_watch) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
if (fd == -1) {
PLOG(ERROR) << "failed to watch: " << path;
continue;
}
watch_to_file_path_map_.emplace(fd.get(), path);
fds_.emplace_back(std::move(fd));
looper_->addFd(fd.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr);
}
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;
}
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;
int timeout = (thermal_triggered_ || is_polling_) ? kMinPollIntervalMs : kUeventPollTimeoutMs;
if (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);
return true;
}
} // namespace implementation
} // namespace V2_0
} // namespace thermal
} // namespace hardware
} // namespace android

@ -0,0 +1,103 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef THERMAL_UTILS_THERMAL_WATCHER_H_
#define THERMAL_UTILS_THERMAL_WATCHER_H_
#include <chrono>
#include <condition_variable>
#include <future>
#include <list>
#include <mutex>
#include <set>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
#include <android-base/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::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,
const std::set<std::string> &cdev_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_;
std::vector<android::base::unique_fd> fds_;
// 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_;
};
} // namespace implementation
} // namespace V2_0
} // namespace thermal
} // namespace hardware
} // namespace android
#endif // THERMAL_UTILS_THERMAL_WATCHER_H_
Loading…
Cancel
Save