diff options
author | Daniel Lezcano <daniel.lezcano@linaro.org> | 2021-09-10 14:03:44 +0200 |
---|---|---|
committer | Daniel Lezcano <daniel.lezcano@linaro.org> | 2021-09-10 14:03:44 +0200 |
commit | a6ffd076d05fe504f15bebab848a4e0eca354109 (patch) | |
tree | 8bc500289584939ac3a145c3513c6c03f1bdc386 |
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
-rw-r--r-- | Android.bp | 49 | ||||
-rw-r--r-- | Config.cpp | 330 | ||||
-rw-r--r-- | Config.h | 102 | ||||
-rw-r--r-- | CpuInfo.cpp | 113 | ||||
-rw-r--r-- | CpuInfo.h | 45 | ||||
-rw-r--r-- | LibThermal.c | 32 | ||||
-rw-r--r-- | LibThermal.cpp | 111 | ||||
-rw-r--r-- | LibThermal.h | 84 | ||||
-rw-r--r-- | Thermal.cpp | 566 | ||||
-rw-r--r-- | Thermal.h | 124 | ||||
-rw-r--r-- | android.hardware.thermal@2.0-service-generic.rc | 6 | ||||
-rw-r--r-- | android.hardware.thermal@2.0-service-generic.xml | 12 | ||||
-rw-r--r-- | service.cpp | 63 |
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(); +} |