summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Lezcano <daniel.lezcano@linaro.org>2021-09-10 14:03:44 +0200
committerDaniel Lezcano <daniel.lezcano@linaro.org>2021-09-10 14:03:44 +0200
commita6ffd076d05fe504f15bebab848a4e0eca354109 (patch)
tree8bc500289584939ac3a145c3513c6c03f1bdc386
Initial commitHEADmaster
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
-rw-r--r--Android.bp49
-rw-r--r--Config.cpp330
-rw-r--r--Config.h102
-rw-r--r--CpuInfo.cpp113
-rw-r--r--CpuInfo.h45
-rw-r--r--LibThermal.c32
-rw-r--r--LibThermal.cpp111
-rw-r--r--LibThermal.h84
-rw-r--r--Thermal.cpp566
-rw-r--r--Thermal.h124
-rw-r--r--android.hardware.thermal@2.0-service-generic.rc6
-rw-r--r--android.hardware.thermal@2.0-service-generic.xml12
-rw-r--r--service.cpp63
13 files changed, 1637 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..c6230c0
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,49 @@
+//
+// 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.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+ name: "android.hardware.thermal@2.0-service.generic",
+ defaults: ["hidl_defaults"],
+ relative_install_path: "hw",
+ vendor: true,
+ init_rc: ["android.hardware.thermal@2.0-service-generic.rc"],
+ vintf_fragments: ["android.hardware.thermal@2.0-service-generic.xml"],
+ srcs: [
+ "Thermal.cpp",
+ "Config.cpp",
+ "CpuInfo.cpp",
+ "LibThermal.cpp",
+ "service.cpp"
+ ],
+ include_dirs: ["external/libthermal/include"],
+ shared_libs: [
+ "libthermal",
+ "libbase",
+ "libhidlbase",
+ "libutils",
+ "libjsoncpp",
+ "android.hardware.thermal@2.0",
+ "android.hardware.thermal@1.0",
+ ],
+}
diff --git a/Config.cpp b/Config.cpp
new file mode 100644
index 0000000..31f87b7
--- /dev/null
+++ b/Config.cpp
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2021 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 "Config.h"
+
+#include <cmath>
+#include <iostream>
+#include <fstream>
+
+#include <android-base/properties.h>
+#include <android-base/logging.h>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+template<class T> bool Config::typeToEnum(std::string &type, T &t)
+{
+ if (type.empty())
+ return false;;
+
+ for (const auto cdt : hidl_enum_range<T>()) {
+
+ const std::string upperType = toString(cdt);
+
+ /*
+ * Let's compare on uppercase only so we can be
+ * comfortable with the syntax used in the
+ * configuration file.
+ */
+ if (toUpper(type) != toUpper(upperType))
+ continue;
+
+ t = cdt;
+
+ return true;
+ }
+
+ return false;
+}
+
+std::string Config::toUpper(const std::string &str)
+{
+ std::string upper_str(str);
+
+ for (std::string::size_type i = 0; i < upper_str.length(); i++)
+ upper_str[i] = std::toupper(str[i], std::locale());
+
+ return upper_str;
+}
+
+bool Config::readHotColdThrottling(Json::Value &throttling,
+ float *tempThreshold)
+{
+ if (throttling.empty())
+ return false;
+
+ for (const std::string mn : throttling.getMemberNames()) {
+
+ for (const auto ts : hidl_enum_range<V2_0::ThrottlingSeverity>()) {
+
+ const std::string severity = toString(ts);
+
+ if (toUpper(severity) != toUpper(mn))
+ continue;
+
+ tempThreshold[(int)ts] = throttling[mn].asFloat();
+
+ LOG(INFO) << "Throttling[\"" << severity << "\"] = " <<
+ tempThreshold[(int)ts] << "°C";
+
+ }
+ }
+
+ return true;
+}
+
+bool Config::readColdThrottling(Json::Value &throttling,
+ TemperatureThreshold &tempThreshold)
+{
+ return readHotColdThrottling(throttling,
+ tempThreshold.coldThrottlingThresholds.data());
+}
+
+bool Config::readHotThrottling(Json::Value &throttling,
+ TemperatureThreshold &tempThreshold)
+{
+ return readHotColdThrottling(throttling,
+ tempThreshold.hotThrottlingThresholds.data());
+}
+
+bool Config::readVrThrottling(Json::Value &throttling,
+ TemperatureThreshold &tempThreshold)
+{
+ LOG(INFO) << "Reading Virtual Reality configuration";
+
+ if (throttling["None"].empty()) {
+ LOG(ERROR) << "Invalid temperature threshold";
+ return false;
+ }
+
+ tempThreshold.vrThrottlingThreshold = throttling["None"].asFloat();
+
+ LOG(INFO) << "Virtual Reality Throtlling: " <<
+ tempThreshold.vrThrottlingThreshold << "°C";
+
+ return true;
+}
+
+void Config::initThreshold(TemperatureThreshold &tempThreshold)
+{
+ for (int i = 0; i < tempThreshold.hotThrottlingThresholds.size(); i++)
+ tempThreshold.hotThrottlingThresholds[i] = NAN;
+
+ for (int i = 0; i < tempThreshold.coldThrottlingThresholds.size(); i++)
+ tempThreshold.coldThrottlingThresholds[i] = NAN;
+
+ tempThreshold.vrThrottlingThreshold = NAN;
+}
+
+bool Config::readThrottling(const std::string &name, Json::Value &throttling)
+{
+ TemperatureThreshold tempThreshold = {
+ .name = name,
+ };
+
+ initThreshold(tempThreshold);
+
+ if (throttling.empty()) {
+ LOG(INFO) << "No threshold specified for '" << name << "'";
+ return true;
+ }
+
+ for (Json::Value::ArrayIndex i = 0; i < throttling.size(); ++i) {
+
+ std::string type = throttling[i]["Type"].asString();
+
+ LOG(INFO) << "Getting '" << type << "' throttling configuration";
+
+ if (type == "Hot") {
+ if (!readHotThrottling(throttling[i], tempThreshold)) {
+ LOG(ERROR) << "Failed to read hot throttling entry";
+ return false;
+ }
+ } else if (type == "Cold") {
+ if (!readColdThrottling(throttling[i], tempThreshold)) {
+ LOG(ERROR) << "Failed to read cold throttling entry";
+ return false;
+ }
+ } else if (type == "Vr") {
+ if (!readVrThrottling(throttling[i], tempThreshold)) {
+ LOG(ERROR) << "Failed to read Virtual Reality throttling entry";
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "Invalid Throttling type: " << type;
+ return false;
+ }
+
+ tempThreshold.name = name;
+ }
+
+ this->m_threshold.insert(std::pair<std::string,
+ TemperatureThreshold>(name, tempThreshold));
+
+ return true;
+}
+
+bool Config::readSensor(Json::Value &sensor)
+{
+ std::string name = sensor["Name"].asString();
+ std::string type = sensor["Type"].asString();
+
+ Temperature_1_0 temperature_1_0;
+ Temperature_2_0 temperature_2_0;
+
+ if (name.empty()) {
+ LOG(ERROR) << "Missing sensor name section";
+ return false;
+ }
+
+ temperature_1_0.name = name;
+ temperature_2_0.name = name;
+
+ if (typeToEnum(type, temperature_1_0.type))
+ m_temperature_1_0.push_back(temperature_1_0);
+
+ if (typeToEnum(type, temperature_2_0.type))
+ m_temperature_2_0.push_back(temperature_2_0);
+
+ /*
+ * The skin temperature sensors are special ones and are
+ * stored in a second list for quick access for monitoring
+ */
+ if (temperature_1_0.type == V1_0::TemperatureType::SKIN)
+ m_skin_sensors.push_back(name);
+
+ LOG(INFO) << "Sensor: '" << name << "' / type: " << type;
+
+ if (!readThrottling(name, sensor["Throttling"]))
+ return false;
+
+ return true;
+}
+
+bool Config::readCoolingDevice(Json::Value &coolingDevice)
+{
+ std::string name = coolingDevice["Name"].asString();
+ std::string type = coolingDevice["Type"].asString();
+
+ CoolingDevice_1_0 coolingDevice_1_0;
+ CoolingDevice_2_0 coolingDevice_2_0;
+
+ if (name.empty() || type.empty()) {
+ LOG(ERROR) << "Missing Cooling device name/type";
+ return false;
+ }
+
+ coolingDevice_1_0.name = name;
+ coolingDevice_2_0.name = name;
+
+ if (typeToEnum(type, coolingDevice_1_0.type))
+ m_cooling_device_1_0.push_back(coolingDevice_1_0);
+
+ if (typeToEnum(type, coolingDevice_2_0.type))
+ m_cooling_device_2_0.push_back(coolingDevice_2_0);
+
+ return true;
+}
+
+bool Config::readFile(const std::string path, std::stringstream &config)
+{
+ std::ifstream file;
+
+ LOG(INFO) << "Reading configuration file: " << path;
+
+ file.open(path);
+ if (!file) {
+ LOG(ERROR) << "Failed to open: " << path;
+ return false;
+ }
+
+ config << file.rdbuf();
+
+ LOG(INFO) << config.str();
+
+ file.close();
+
+ return true;
+}
+
+bool Config::parseFile(std::string path, Json::Value &root)
+{
+ std::stringstream config;
+ std::string strerr;
+
+ Json::CharReaderBuilder builder;
+ std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
+
+ if (!readFile(path, config)) {
+ LOG(ERROR) << "Failed to read configuration file";
+ return false;
+ }
+
+ if (!reader->parse(&*config.str().begin(), &*config.str().end(), &root, &strerr)) {
+ LOG(ERROR) << "Failed to parse JSON config: " << strerr;
+ return false;
+ }
+
+ LOG(INFO) << "Configuration file parsed successfully";
+
+ return true;
+}
+
+bool Config::read(std::string path)
+{
+ Json::Value root;
+ Json::Value sensors;
+ Json::Value coolingDevices;
+
+ if (!parseFile(path, root))
+ return false;
+
+ sensors = root["Sensors"];
+
+ for (Json::Value::ArrayIndex i = 0; i < sensors.size(); ++i) {
+
+ if (!readSensor(sensors[i]))
+ return false;
+ }
+
+ coolingDevices = root["CoolingDevices"];
+
+ for (Json::Value::ArrayIndex i = 0; i < coolingDevices.size(); ++i) {
+
+ if (!readCoolingDevice(coolingDevices[i]))
+ return false;
+ }
+
+ return true;
+}
+
+bool Config::init(void)
+{
+ std::string property("vendor.thermal.config");
+ std::string default_conf("thermal.json");
+ std::string path = "/vendor/etc/" + android::base::GetProperty(property, default_conf);
+
+ return read(path);
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/Config.h b/Config.h
new file mode 100644
index 0000000..f4a2fa5
--- /dev/null
+++ b/Config.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2021 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_CONFIG_PARSER_H__
+#define THERMAL_CONFIG_PARSER_H__
+
+#include <android/hardware/thermal/2.0/IThermal.h>
+
+#include <map>
+#include <string>
+
+#include <json/reader.h>
+#include <json/value.h>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using Temperature_1_0 = ::android::hardware::thermal::V1_0::Temperature;
+using Temperature_2_0 = ::android::hardware::thermal::V2_0::Temperature;
+using CoolingDevice_1_0 = ::android::hardware::thermal::V1_0::CoolingDevice;
+using CoolingDevice_2_0 = ::android::hardware::thermal::V2_0::CoolingDevice;
+
+using ::android::hardware::thermal::V2_0::TemperatureThreshold;
+using ::android::hardware::thermal::V2_0::TemperatureType;
+
+class Config {
+private:
+ template<class T> bool typeToEnum(std::string &type, T &t);
+
+ std::string toUpper(const std::string &str);
+
+ bool read(std::string file);
+
+ void initThreshold(TemperatureThreshold &tempThreshold);
+
+ bool readHotColdThrottling(Json::Value &throttling, float *tempThreshold);
+
+ bool readHotThrottling(Json::Value &throttling,
+ TemperatureThreshold &tempThreshold);
+
+ bool readColdThrottling(Json::Value &throttling,
+ TemperatureThreshold &tempThreshold);
+
+ bool readVrThrottling(Json::Value &throttling,
+ TemperatureThreshold &tempThreshold);
+
+ bool readThrottling(const std::string &name,
+ Json::Value &throttling);
+
+ bool readCoolingDevice(Json::Value &coolingDevice);
+
+ bool readSensor(Json::Value &sensor);
+
+ bool readFile(const std::string path,
+ std::stringstream &config);
+
+ bool parseFile(std::string path, Json::Value &root);
+
+public:
+ std::vector<Temperature_1_0> m_temperature_1_0;
+ std::vector<Temperature_2_0> m_temperature_2_0;
+
+ std::vector<CoolingDevice_1_0> m_cooling_device_1_0;
+ std::vector<CoolingDevice_2_0> m_cooling_device_2_0;
+
+ /*
+ * Each sensor can have associated a configuration for the
+ * threshold, the key is the name of the sensor.
+ */
+ std::map<const std::string, TemperatureThreshold> m_threshold;
+
+ /*
+ * Contains the list of the skin temperature sensors.
+ */
+ std::vector<const std::string> m_skin_sensors;
+
+ bool init(void);
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
+
+#endif
diff --git a/CpuInfo.cpp b/CpuInfo.cpp
new file mode 100644
index 0000000..88eeecb
--- /dev/null
+++ b/CpuInfo.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 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 <unistd.h>
+
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <regex>
+
+#include <hidl/HidlTransportSupport.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "CpuInfo.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+#define CPU_USAGE "/proc/stat"
+
+bool CpuInfo::parseCpuUsagesFileAndAssignUsages(hidl_vec<CpuUsage> &cpuUsages)
+{
+ std::string line;
+ std::ifstream file;
+ std::regex regEx("^cpu[0-9](.+)");
+
+ file.open(CPU_USAGE);
+ if (!file) {
+ LOG(ERROR) << "Failed to open: " << CPU_USAGE;
+ return false;
+ }
+
+ while (std::getline(file, line)) {
+
+ uint64_t cpuNum, cpuUser, cpuNice, cpuSys, cpuIdle;
+ std::vector<std::string> fields;
+
+ if (!regex_match(line, regEx))
+ continue;
+
+ fields = android::base::Split(line, " ");
+
+ /*
+ * The /proc/stat file is an ABI and not supposed to
+ * change. We can trust its content and the
+ * format. There is no need to check if the CPU is
+ * online as it will be removed from the file if it is
+ * offline. Consequently, if we can read the usage for
+ * a particular CPU, that means it is in the list,
+ * thus online.
+ */
+ cpuNum = std::stoi(fields[0].substr(3));
+ cpuUser = std::stoi(fields[1]);
+ cpuNice = std::stoi(fields[2]);
+ cpuSys = std::stoi(fields[3]);
+ cpuIdle = std::stoi(fields[4]);
+
+ cpuUsages[cpuNum].name = fields[0];
+ cpuUsages[cpuNum].active = cpuUser + cpuNice + cpuSys;
+ cpuUsages[cpuNum].total = cpuUser + cpuNice + cpuSys + cpuIdle;
+ cpuUsages[cpuNum].isOnline = true;
+ }
+
+ file.close();
+
+ return true;
+}
+
+bool CpuInfo::CpuUsages(hidl_vec<CpuUsage> &cpuUsages)
+{
+ if (!m_NrCpus)
+ return false;
+
+ /*
+ * Set the vector size to the number of possible CPUs
+ */
+ cpuUsages.resize(m_NrCpus);
+
+ /*
+ * Parse the stat file
+ */
+ parseCpuUsagesFileAndAssignUsages(cpuUsages);
+
+ return true;
+}
+
+CpuInfo::CpuInfo()
+{
+ m_NrCpus = sysconf(_SC_NPROCESSORS_CONF);
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/CpuInfo.h b/CpuInfo.h
new file mode 100644
index 0000000..720f948
--- /dev/null
+++ b/CpuInfo.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 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_CPUINFO_H
+#define ANDROID_HARDWARE_THERMAL_V2_0_CPUINFO_H
+
+#include <android/hardware/thermal/2.0/IThermal.h>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::thermal::V1_0::CpuUsage;
+
+class CpuInfo {
+private:
+ std::size_t m_NrCpus;
+ bool parseCpuUsagesFileAndAssignUsages(hidl_vec<CpuUsage> &cpuUsages);
+public:
+ bool CpuUsages(hidl_vec<CpuUsage> &cpuUsages);
+ CpuInfo();
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
+#endif
diff --git a/LibThermal.c b/LibThermal.c
new file mode 100644
index 0000000..bfe6c7d
--- /dev/null
+++ b/LibThermal.c
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+int Thermal::show_tz(struct thermal_zone *tz, void *arg)
+{
+ LOG(INFO) << "thermal zone '" << tz->name << "' "
+ << "id=" << tz->id;
+
+ return arg == NULL;
+}
+
+LibThermal::LibThermal()
+{
+ m_th = thermal_init(&m_ops);
+
+ m_tz = thermal_zone_discover(m_th);
+
+ for_each_thermal_zone(tz, show_tz, m_th);
+}
diff --git a/LibThermal.cpp b/LibThermal.cpp
new file mode 100644
index 0000000..5384307
--- /dev/null
+++ b/LibThermal.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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 "LibThermal.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+
+struct thermal_zone *LibThermal::getThermalZone(int id)
+{
+ return thermal_zone_find_by_id(this->m_tz, id);
+}
+
+struct thermal_zone *LibThermal::getThermalZone(std::string name)
+{
+ return thermal_zone_find_by_name(this->m_tz, name.c_str());
+}
+
+int LibThermal::getThermalZonetemp(struct thermal_zone *tz)
+{
+ if (thermal_cmd_get_temp(this->m_th, tz)) {
+ LOG(ERROR) << "Failed to read '" << tz->name << "' temperature";
+ return INT_MAX;
+ }
+
+ return tz->temp;
+}
+
+int LibThermal::getThermalZonetemp(int id)
+{
+ struct thermal_zone *tz;
+
+ tz = getThermalZone(id);
+ if (!tz) {
+ LOG(ERROR) << "Thermal zone <id=" << id << "> not found";
+ return INT_MAX;
+ }
+
+ return getThermalZonetemp(tz);
+}
+
+int LibThermal::getThermalZonetemp(const std::string name)
+{
+ struct thermal_zone *tz;
+
+ tz = getThermalZone(name);
+ if (!tz) {
+ LOG(ERROR) << "Thermal zone <" << name << "> not found";
+ return INT_MAX;
+ }
+
+ return getThermalZonetemp(tz);
+}
+
+bool LibThermal::init(LibThermalCallbacks &cb)
+{
+ m_ops.events.tz_create = cb.thermalZoneCreate;
+ m_ops.events.tz_delete = cb.thermalZoneDelete;
+ m_ops.events.tz_disable = cb.thermalZoneDisable;
+ m_ops.events.tz_enable = cb.thermalZoneEnable;
+ m_ops.events.trip_high = cb.tripHigh;
+ m_ops.events.trip_low = cb.tripLow;
+ m_ops.events.trip_add = cb.tripAdd;
+ m_ops.events.trip_delete = cb.tripDelete;
+ m_ops.events.trip_change = cb.tripChange;
+ m_ops.events.cdev_add = cb.cdevAdd;
+ m_ops.events.cdev_delete = cb.cdevDelete;
+ m_ops.events.cdev_update = cb.cdevUpdate;
+ m_ops.events.gov_change = cb.govChange;
+
+ m_ops.sampling.tz_temp = cb.thermalZoneTemp;
+
+ m_th = thermal_init(&m_ops);
+ if (!m_th) {
+ LOG(ERROR) << "Failed to initialize the thermal library";
+ return false;
+ }
+
+ m_tz = thermal_zone_discover(m_th);
+ if (!m_tz) {
+ LOG(ERROR) << "Failed to discover the thermal zones";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/LibThermal.h b/LibThermal.h
new file mode 100644
index 0000000..809c930
--- /dev/null
+++ b/LibThermal.h
@@ -0,0 +1,84 @@
+/*
+ * 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 <string>
+
+#include "libthermal.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+class LibThermalCallbacks {
+public:
+ /*
+ * Callbacks called when a thermal zone event occurs
+ */
+ int (*thermalZoneCreate)(const char *, int, void *);
+ int (*thermalZoneDelete) (int, void *);
+ int (*thermalZoneEnable)(int, void *);
+ int (*thermalZoneDisable)(int, void *);
+
+ /*
+ * Callbacks called when a trip event occurs
+ */
+ int (*tripHigh)(int, int, int, void *);
+ int (*tripLow)(int, int, int, void *);
+ int (*tripAdd)(int, int, int, int, int, void *);
+ int (*tripChange)(int, int, int, int, int, void *);
+ int (*tripDelete)(int, int, void *);
+
+ /*
+ * Callbacks called when a cooling device event occurs
+ */
+ int (*cdevAdd)(const char *, int, int, void *);
+ int (*cdevDelete)(int, void *);
+ int (*cdevUpdate)(int, int, void *);
+
+ /*
+ * Callback called when a governor event occurs
+ */
+ int (*govChange)(int, const char *, void *);
+
+ /*
+ * Callback called when there is a temperature sampling
+ */
+ int (*thermalZoneTemp)(int, int, void *);
+};
+
+class LibThermal {
+
+private:
+ struct thermal_zone *getThermalZone(int id);
+ struct thermal_zone *getThermalZone(std::string name);
+
+public:
+ struct thermal_handler *m_th;
+ struct thermal_zone *m_tz;
+ struct thermal_ops m_ops;
+
+ bool init(LibThermalCallbacks &);
+ int getThermalZonetemp(const std::string name);
+ int getThermalZonetemp(struct thermal_zone *tz);
+ int getThermalZonetemp(int id);
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/Thermal.cpp b/Thermal.cpp
new file mode 100644
index 0000000..66885cc
--- /dev/null
+++ b/Thermal.cpp
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#define LOG_TAG "android.hardware.thermal@2.0-service-generic"
+
+#include <sys/epoll.h>
+#include <sys/stat.h>
+#include <sys/timerfd.h>
+#include <sys/types.h>
+
+#include <cmath>
+#include <set>
+
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "Thermal.h"
+
+#define MAX_EVENTS 10
+#define SKIN_POLLING_SEC 5 // Unit is in second
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::interfacesEqual;
+using ::android::hardware::thermal::V1_0::ThermalStatus;
+using ::android::hardware::thermal::V1_0::ThermalStatusCode;
+
+std::set<sp<IThermalChangedCallback>> gCallbacks;
+
+Return<void> Thermal::getCpuUsages(getCpuUsages_cb _hidl_cb)
+{
+ ThermalStatus status = { .code = ThermalStatusCode::SUCCESS };
+ hidl_vec<CpuUsage> cpuUsages;
+
+ if (m_cpuInfo.CpuUsages(cpuUsages))
+ _hidl_cb(status, cpuUsages);
+
+ return Void();
+}
+
+Return<void> Thermal::getCoolingDevices(getCoolingDevices_cb _hidl_cb)
+{
+ ThermalStatus status = { .code = ThermalStatusCode::SUCCESS };
+
+ if (this->m_config.m_cooling_device_1_0.empty()) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "No cooling devices";
+ }
+
+ _hidl_cb(status, this->m_config.m_cooling_device_1_0);
+
+ return Void();
+}
+
+Return<void> Thermal::getCurrentCoolingDevices(bool filterType, CoolingType type,
+ getCurrentCoolingDevices_cb _hidl_cb)
+{
+ ThermalStatus status = { .code = ThermalStatusCode::SUCCESS };
+
+ std::vector<CoolingDevice_2_0> cooling_devices;
+
+ for (auto const& p : this->m_config.m_cooling_device_2_0) {
+
+ if (filterType && (type != p.type))
+ continue;
+
+ cooling_devices.push_back(p);
+ }
+
+ if (cooling_devices.empty()) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "No cooling device matching the type";
+ }
+
+ _hidl_cb(status, cooling_devices);
+
+ return Void();
+}
+
+Return<void> Thermal::registerThermalChangedCallback(const sp<IThermalChangedCallback>& callback,
+ bool filterType, TemperatureType type,
+ registerThermalChangedCallback_cb _hidl_cb)
+{
+ ThermalStatus status = { .code = ThermalStatusCode::SUCCESS };
+ std::lock_guard<std::mutex> cbLock(m_callback_mutex);
+
+ if (callback == nullptr) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "Invalid nullptr callback";
+ LOG(ERROR) << status.debugMessage;
+ goto out;
+ }
+
+ if (std::any_of(m_callbacks.begin(),
+ m_callbacks.end(),
+ [&](const CallbackSetting& c) {
+ return interfacesEqual(c.callback, callback);
+ })) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "Same callback interface registered already";
+ LOG(ERROR) << status.debugMessage;
+ goto out;
+ }
+
+ m_callbacks.emplace_back(callback, filterType, type);
+ LOG(INFO) << "A callback has been registered to ThermalHAL, isFilter: "
+ << filterType << " Type: " << toString(type);
+out:
+ _hidl_cb(status);
+ return Void();
+}
+
+Return<void> Thermal::unregisterThermalChangedCallback(const sp<IThermalChangedCallback>& callback,
+ unregisterThermalChangedCallback_cb _hidl_cb)
+{
+ ThermalStatus status = { .code = ThermalStatusCode::SUCCESS };
+ std::lock_guard<std::mutex> cbLock(m_callback_mutex);
+ std::vector<CallbackSetting>::iterator it;
+
+ if (callback == nullptr) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "Invalid nullptr callback";
+ LOG(ERROR) << status.debugMessage;
+ goto out;
+ }
+
+ it = std::remove_if(m_callbacks.begin(), m_callbacks.end(),
+ [&](const CallbackSetting& c) {
+ return interfacesEqual(c.callback, callback);
+ });
+
+ if (it == m_callbacks.end()) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "The callback was not registered before";
+ LOG(ERROR) << status.debugMessage;
+ goto out;
+ }
+
+ LOG(INFO) << "A callback has been unregistered from ThermalHAL, isFilter: "
+ << (*it).is_filter_type << " Type: " << toString((*it).type);
+
+ m_callbacks.erase(it);
+out:
+ _hidl_cb(status);
+
+ return Void();
+}
+
+void Thermal::thermalChangedCallback(const std::vector<Temperature_2_0> &temperature)
+{
+ std::lock_guard<std::mutex> cbLock(m_callback_mutex);
+
+ for (auto &t : temperature) {
+
+ 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);
+
+ for (auto &c : m_callbacks) {
+
+ if (!c.is_filter_type || c.type == t.type) {
+ c.callback->notifyThrottling(t);
+ }
+ }
+ }
+}
+
+int Thermal::thermalZoneCreate(const char *name, int tz_id, void *arg)
+{
+ class Thermal *thermal = (class Thermal *)arg;
+
+ LOG(DEBUG) << "Thermal zone " << name << "/" << tz_id << " created";
+
+ thermal = NULL;
+
+ return 0;
+}
+
+int Thermal::thermalZoneDelete(int tz_id, void *arg)
+{
+ class Thermal *thermal = (class Thermal *)arg;
+
+ LOG(DEBUG) << "Thermal zone " << tz_id << " delete";
+
+ thermal = NULL;
+
+ return 0;
+}
+
+int Thermal::thermalZoneEnable(int tz_id, void *arg)
+{
+ class Thermal *thermal = (class Thermal *)arg;
+
+ LOG(DEBUG) << "Thermal zone " << tz_id << " enabled";
+
+ thermal = NULL;
+
+ return 0;
+}
+
+int Thermal::thermalZoneDisable(int tz_id, void *arg)
+{
+ class Thermal *thermal = (class Thermal *)arg;
+
+ LOG(DEBUG) << "Thermal zone " << tz_id << " disabled";
+
+ thermal = NULL;
+
+ return 0;
+}
+
+int Thermal::tripHigh(int tz_id, int trip_id, int temp, void *arg)
+{
+ class Thermal *thermal = (class Thermal *)arg;
+
+ LOG(DEBUG) << "Thermal zone " << tz_id << " trip crossed the way up "
+ << "with trip_id=" << trip_id << ",temp=" << temp;
+
+ temp = thermal->m_libThermal.getThermalZonetemp(tz_id);
+
+ return 0;
+}
+
+int Thermal::tripLow(int tz_id, int trip_id, int temp, void *arg)
+{
+ class Thermal *thermal = (class Thermal *)arg;
+
+ LOG(DEBUG) << "Thermal zone " << tz_id << " trip crossed the way down "
+ << "with trip_id=" << trip_id << ",temp=" << temp;
+
+ temp = thermal->m_libThermal.getThermalZonetemp(tz_id);
+
+ return 0;
+}
+
+int Thermal::tripAdd(int tz_id, int trip_id, int type,
+ int temp, int hyst, void *arg)
+{
+ class Thermal *thermal = (class Thermal *)arg;
+
+ LOG(DEBUG) << "Trip " << trip_id << " from thermal zone "
+ << tz_id << " added: type="
+ << type << ", temp=" << temp << " ,hyst=" << hyst;
+
+ thermal = NULL;
+
+ return 0;
+}
+
+int Thermal::tripChange(int tz_id, int trip_id, int type,
+ int temp, int hyst, void *arg)
+{
+ class Thermal *thermal = (class Thermal *)arg;
+
+ LOG(DEBUG) << "Trip " << trip_id << " from thermal zone "
+ << tz_id << " changed: type="
+ << type << ", temp=" << temp << " ,hyst=" << hyst;
+
+ thermal = NULL;
+
+ return 0;
+}
+
+int Thermal::tripDelete(int tz_id, int trip_id, void *arg)
+{
+ class Thermal *thermal = (class Thermal *)arg;
+
+ LOG(DEBUG) << "Trip " << trip_id << " from thermal zone "
+ << tz_id << " deleted";
+
+ thermal = NULL;
+
+ return 0;
+}
+
+int Thermal::cdevAdd(const char *name, int cdev_id, int max_state, void *arg)
+{
+ class Thermal *thermal = (class Thermal *)arg;
+
+ LOG(DEBUG) << "Cooling device '" << name << "'/" << cdev_id
+ << " max state" << max_state << " created";
+
+ thermal = NULL;
+
+ return 0;
+}
+
+int Thermal::cdevDelete(int cdev_id, void *arg)
+{
+ class Thermal *thermal = (class Thermal *)arg;
+
+ LOG(DEBUG) << "Cooling device " << cdev_id << " deleted";
+
+ thermal = NULL;
+
+ return 0;
+}
+
+int Thermal::cdevUpdate(int cdev_id, int max_state, void *arg)
+{
+ class Thermal *thermal = (class Thermal *)arg;
+
+ LOG(DEBUG) << "Coolling device " << cdev_id << " max state"
+ << max_state;
+
+ thermal = NULL;
+
+ return 0;
+}
+
+int Thermal::govChange(int tz_id, const char *name, void *arg)
+{
+ class Thermal *thermal = (class Thermal *)arg;
+
+ LOG(DEBUG) << "Governor change '" << name << "' "
+ << "for thermal zone " << tz_id;
+
+ thermal = NULL;
+
+ return 0;
+}
+
+bool Thermal::skinTemperatureMonitor(class Thermal *thermal)
+{
+ uint64_t expirations;
+
+ if (read(thermal->m_timerfd, &expirations, sizeof(expirations)) < 0) {
+ LOG(ERROR) << "Failed to read signal data";
+ return false;
+ }
+
+ for (auto const& p : thermal->m_config.m_skin_sensors) {
+ LOG(DEBUG) << "Skin temperature for '" << p << "' "
+ << thermal->m_libThermal.getThermalZonetemp(p) << "°C";
+ }
+
+ return true;
+}
+
+Return<void> Thermal::getTemperatureThresholds(bool filterType, TemperatureType type,
+ getTemperatureThresholds_cb _hidl_cb)
+{
+ ThermalStatus status = { .code = ThermalStatusCode::SUCCESS };
+ std::vector<TemperatureThreshold> thresholds;
+
+ for (auto const& p : this->m_config.m_threshold) {
+
+ if (filterType && (type != p.second.type))
+ continue;
+
+ thresholds.push_back(p.second);
+ }
+
+ if (thresholds.empty()) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "No threshold temperature matching the type";
+ }
+
+ _hidl_cb(status, thresholds);
+
+ return Void();
+}
+
+// Methods from ::android::hardware::thermal::V2_0::IThermal follow.
+Return<void> Thermal::getCurrentTemperatures(bool filterType, TemperatureType type,
+ getCurrentTemperatures_cb _hidl_cb)
+{
+ ThermalStatus status = { .code = ThermalStatusCode::SUCCESS };
+ std::vector<Temperature_2_0> temperatures;
+
+ for (auto &p : this->m_config.m_temperature_2_0) {
+
+ std::string name = p.name;
+ int temperature;
+
+ if (filterType && (type != p.type))
+ continue;
+
+ temperature = m_libThermal.getThermalZonetemp(name);
+ if (temperature == INT_MAX) {
+ LOG(ERROR) << "Failed to read thermal zone temperature"
+ << "for <" << name << ">";
+ continue;
+ }
+
+ p.value = temperature / 1000;
+ temperatures.push_back(p);
+ }
+
+ if (temperatures.empty()) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "No temperature matching the type";
+ }
+
+ _hidl_cb(status, temperatures);
+
+ return Void();
+}
+
+// Methods from ::android::hardware::thermal::V1_0::IThermal follow.
+Return<void> Thermal::getTemperatures(getTemperatures_cb _hidl_cb)
+{
+ ThermalStatus status = { . code = ThermalStatusCode::SUCCESS };
+
+ for (auto &p : this->m_config.m_temperature_1_0) {
+
+ std::string name = p.name;
+ int temperature;
+
+ temperature = m_libThermal.getThermalZonetemp(name);
+ if (temperature == INT_MAX) {
+ LOG(ERROR) << "Failed to read thermal zone temperature"
+ << "for <" << name << ">";
+ continue;
+ }
+
+ p.currentValue = temperature / 1000;
+ }
+
+ _hidl_cb(status, m_config.m_temperature_1_0);
+
+ return Void();
+}
+
+bool Thermal::threadLoop() {
+
+ struct epoll_event ev;
+ struct epoll_event events[MAX_EVENTS];
+ int epollfd;
+ int nfds;
+ int i;
+
+ LibThermalCallbacks cb;
+
+ cb.thermalZoneCreate = this->thermalZoneCreate;
+ cb.thermalZoneDelete = this->thermalZoneDelete;
+ cb.thermalZoneEnable = this->thermalZoneEnable;
+ cb.thermalZoneDisable = this->thermalZoneDisable;
+ cb.tripHigh = this->tripHigh;
+ cb.tripLow = this->tripLow;
+ cb.tripAdd = this->tripAdd;
+ cb.tripChange = this->tripChange;
+ cb.tripDelete = this->tripDelete;
+ cb.cdevAdd = this->cdevAdd;
+ cb.cdevDelete = this->cdevDelete;
+ cb.cdevUpdate = this->cdevUpdate;
+ cb.govChange = this->govChange;
+
+ if (!m_config.init())
+ LOG(FATAL) << "ThermalHAL failed to initialize the configuration";
+
+ if (!m_libThermal.init(cb))
+ LOG(FATAL) << "ThermalHAL failed to initialize the thermal library";
+
+ epollfd = epoll_create1(0);
+ if (epollfd < 0)
+ return false;
+
+ ev.events = EPOLLIN;
+ ev.data.ptr = (void *)thermal_events_handle;
+
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD,
+ thermal_events_fd(m_libThermal.m_th), &ev) == -1)
+ return false;
+
+ /*
+ * We want to read the temperature every 2000ms, set a timer
+ * file descriptor to be woken up at regular intervals.
+ */
+ struct itimerspec iti = {
+ .it_interval = { SKIN_POLLING_SEC, 0 },
+ .it_value = { SKIN_POLLING_SEC, 0 },
+ };
+
+ m_timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
+ if (m_timerfd < 0) {
+ LOG(ERROR) << "Failed to create timer";
+ return false;
+ }
+
+ if (timerfd_settime(m_timerfd, 0, &iti, NULL) < 0) {
+ LOG(ERROR) << "Failed to set timer time";
+ return false;
+ }
+
+ /*
+ * There are skin temperature sensors, set a timer to poll the
+ * temperature.
+ */
+ if (!m_config.m_skin_sensors.empty()) {
+
+ LOG(INFO) << "Skin sensors configured, setting up the monitoring.";
+
+ ev.events = EPOLLIN;
+ ev.data.ptr = (void *)skinTemperatureMonitor;
+
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, m_timerfd, &ev) == -1)
+ return false;
+ }
+
+ /*
+ * No ops was specified for the temperature sampling, so no
+ * event of this type should occur, but add the corresponding
+ * code to handle it for unlikely future changes if the code
+ * needs to have the temperature sampling.
+ * ev.events = EPOLLIN;
+ * ev.data.ptr = (void *)thermal_sampling_handle;
+ *
+ * if (epoll_ctl(epollfd, EPOLL_CTL_ADD,
+ * thermal_sampling_fd(m_libThermal.m_th), &ev) == -1)
+ * return false;
+ */
+
+ while (1) {
+
+ nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
+
+ if (nfds < 0) {
+ if (errno == EINTR)
+ continue;
+ LOG(FATAL) << "Mainloop failed with error: " << strerror(errno);
+ }
+
+ for (i = 0; i < nfds; i++) {
+ if (events[i].data.ptr == thermal_events_handle) {
+ thermal_events_handle(m_libThermal.m_th, NULL);
+ } else if (events[i].data.ptr == thermal_sampling_handle) {
+ thermal_sampling_handle(m_libThermal.m_th, NULL);
+ } else if (events[i].data.ptr == skinTemperatureMonitor) {
+ skinTemperatureMonitor(this);
+ }
+ }
+ }
+
+ return true;
+}
+
+Thermal::Thermal()
+{
+ this->run("ThermalMonitorThread", PRIORITY_HIGHEST);
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/Thermal.h b/Thermal.h
new file mode 100644
index 0000000..0bfe6d2
--- /dev/null
+++ b/Thermal.h
@@ -0,0 +1,124 @@
+/*
+ * 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_THERMAL_H
+#define ANDROID_HARDWARE_THERMAL_V2_0_THERMAL_H
+
+#include <android/hardware/thermal/2.0/IThermal.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <utils/Thread.h>
+
+#include "Config.h"
+#include "CpuInfo.h"
+#include "LibThermal.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+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 ::android::hardware::thermal::V2_0::IThermalChangedCallback;
+using ::android::hardware::thermal::V2_0::TemperatureThreshold;
+using ::android::hardware::thermal::V2_0::TemperatureType;
+
+struct CallbackSetting {
+ CallbackSetting(sp<IThermalChangedCallback> callback, bool is_filter_type, TemperatureType type)
+ : callback(callback), is_filter_type(is_filter_type), type(type) {}
+ sp<IThermalChangedCallback> callback;
+ bool is_filter_type;
+ TemperatureType type;
+};
+
+class Thermal : public IThermal, ::android::Thread {
+ public:
+ // Methods from ::android::hardware::thermal::V1_0::IThermal follow.
+ 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 type,
+ getCurrentTemperatures_cb _hidl_cb) override;
+ Return<void> getTemperatureThresholds(bool filterType, TemperatureType type,
+ getTemperatureThresholds_cb _hidl_cb) override;
+ Return<void> registerThermalChangedCallback(
+ const sp<IThermalChangedCallback>& callback, bool filterType, TemperatureType 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;
+
+ Thermal();
+
+private:
+ static int thermalZoneDelete(int, void *);
+ static int thermalZoneCreate(const char *, int, void *);
+ static int thermalZoneEnable(int tz_id, void *arg);
+ static int thermalZoneDisable(int tz_id, void *arg);
+ static int tripHigh(int tz_id, int trip_id, int temp, void *arg);
+ static int tripLow(int tz_id, int trip_id, int temp, void *arg);
+ static int tripAdd(int tz_id, int trip_id, int type,
+ int temp, int hyst, void *arg);
+ static int tripChange(int tz_id, int trip_id, int type,
+ int temp, int hyst, void *arg);
+ static int tripDelete(int tz_id, int trip_id, void *arg);
+ static int cdevAdd(const char *name, int cdev_id, int max_state, void *arg);
+ static int cdevDelete(int cdev_id, void *arg);
+ static int cdevUpdate(int cdev_id, int max_state, void *arg);
+ static int govChange(int tz_id, const char *name, void *arg);
+
+ static bool skinTemperatureMonitor(class Thermal *thermal);
+
+ void thermalChangedCallback(const std::vector<Temperature_2_0> &temperature);
+
+ bool threadLoop() override;
+
+ Config m_config;
+ CpuInfo m_cpuInfo;
+ LibThermal m_libThermal;
+
+ int m_timerfd;
+
+ std::mutex m_callback_mutex;
+ std::vector<CallbackSetting> m_callbacks;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_THERMAL_V2_0_THERMAL_H
diff --git a/android.hardware.thermal@2.0-service-generic.rc b/android.hardware.thermal@2.0-service-generic.rc
new file mode 100644
index 0000000..b9ba400
--- /dev/null
+++ b/android.hardware.thermal@2.0-service-generic.rc
@@ -0,0 +1,6 @@
+service vendor.thermal-hal-2-0-generic /vendor/bin/hw/android.hardware.thermal@2.0-service.generic
+ interface android.hardware.thermal@1.0::IThermal generic
+ interface android.hardware.thermal@2.0::IThermal generic
+ class hal
+ user system
+ group system
diff --git a/android.hardware.thermal@2.0-service-generic.xml b/android.hardware.thermal@2.0-service-generic.xml
new file mode 100644
index 0000000..9dad190
--- /dev/null
+++ b/android.hardware.thermal@2.0-service-generic.xml
@@ -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>generic</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/service.cpp b/service.cpp
new file mode 100644
index 0000000..62666a6
--- /dev/null
+++ b/service.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.thermal@2.0-service-generic"
+
+#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) << "Thermal Service is shutting down.";
+ return 1;
+}
+
+int main(int /* argc */, char** /* argv */) {
+ status_t status;
+ android::sp<IThermal> service = nullptr;
+
+ LOG(INFO) << "Thermal HAL Service generic 2.0 starting...";
+
+ service = new Thermal();
+ if (service == nullptr) {
+ LOG(ERROR) << "Error creating an instance of ThermalHAL. Exiting...";
+ return shutdown();
+ }
+
+ configureRpcThreadpool(1, true /* callerWillJoin */);
+
+ status = service->registerAsService("generic");
+ if (status != OK) {
+ LOG(ERROR) << "Could not register service for ThermalHAL (" << status << ")";
+ return shutdown();
+ }
+
+ LOG(INFO) << "Thermal Service started successfully.";
+ joinRpcThreadpool();
+ // We should not get past the joinRpcThreadpool().
+ return shutdown();
+}