summaryrefslogtreecommitdiff
path: root/thermal
diff options
context:
space:
mode:
authorWei Wang <wvw@google.com>2019-01-31 14:19:18 -0800
committerWei Wang <wvw@google.com>2019-01-31 14:20:43 -0800
commit011ff4a73be5bedea8e46a0c3e1c5d20a9f4f8ff (patch)
tree2b210ed9c385d7deef05bd4034c669f7f8a65cc7 /thermal
parentd26f9243c54abf22ea07c8827f46790087b9e7e6 (diff)
downloadpixel-011ff4a73be5bedea8e46a0c3e1c5d20a9f4f8ff.tar.gz
ThermalHAL: change log level for callback
Test: Build Change-Id: Icfcfb318c2bfcfa27857ea436f62eed5252c503e
Diffstat (limited to 'thermal')
-rw-r--r--thermal/Android.bp46
-rw-r--r--thermal/Thermal.cpp378
-rw-r--r--thermal/Thermal.h95
-rw-r--r--thermal/android.hardware.thermal@2.0-service.pixel.rc5
-rw-r--r--thermal/android.hardware.thermal@2.0-service.pixel.xml12
-rw-r--r--thermal/service.cpp60
-rw-r--r--thermal/thermal-helper.cpp498
-rw-r--r--thermal/thermal-helper.h142
-rw-r--r--thermal/utils/config_parser.cpp298
-rw-r--r--thermal/utils/config_parser.h59
-rw-r--r--thermal/utils/config_schema.json219
-rw-r--r--thermal/utils/cooling_devices.cpp73
-rw-r--r--thermal/utils/cooling_devices.h68
-rw-r--r--thermal/utils/sensors.cpp60
-rw-r--r--thermal/utils/sensors.h57
-rw-r--r--thermal/utils/thermal_watcher.cpp88
-rw-r--r--thermal/utils/thermal_watcher.h90
17 files changed, 2248 insertions, 0 deletions
diff --git a/thermal/Android.bp b/thermal/Android.bp
new file mode 100644
index 00000000..d9f2b739
--- /dev/null
+++ b/thermal/Android.bp
@@ -0,0 +1,46 @@
+cc_binary {
+ name: "android.hardware.thermal@2.0-service.pixel",
+ cpp_std: "c++17",
+ defaults: [
+ "hidl_defaults",
+ ],
+ vendor: true,
+ relative_install_path: "hw",
+ vintf_fragments: ["android.hardware.thermal@2.0-service.pixel.xml"],
+ init_rc: [
+ "android.hardware.thermal@2.0-service.pixel.rc",
+ ],
+ srcs: [
+ "service.cpp",
+ "Thermal.cpp",
+ "thermal-helper.cpp",
+ "utils/config_parser.cpp",
+ "utils/cooling_devices.cpp",
+ "utils/thermal_watcher.cpp",
+ "utils/sensors.cpp",
+ ],
+ static_libs: [
+ "libjsoncpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libhidlbase",
+ "libhidltransport",
+ "libutils",
+ "android.hardware.thermal@1.0",
+ "android.hardware.thermal@2.0",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ tidy: true,
+ tidy_checks: [
+ "android-*",
+ "cert-*",
+ "clang-analyzer-security*",
+ ],
+ tidy_flags: [
+ "-warnings-as-errors=android-*,clang-analyzer-security*,cert-*"
+ ],
+}
diff --git a/thermal/Thermal.cpp b/thermal/Thermal.cpp
new file mode 100644
index 00000000..60fc203f
--- /dev/null
+++ b/thermal/Thermal.cpp
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cerrno>
+#include <mutex>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "Thermal.h"
+#include "thermal-helper.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+namespace {
+
+using ::android::hardware::interfacesEqual;
+using ::android::hardware::thermal::V1_0::ThermalStatus;
+using ::android::hardware::thermal::V1_0::ThermalStatusCode;
+using ::android::hidl::base::V1_0::IBase;
+
+template <typename T, typename U>
+Return<void> setFailureAndCallback(T _hidl_cb, hidl_vec<U> data, const std::string &debug_msg) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = debug_msg;
+ _hidl_cb(status, data);
+ return Void();
+}
+
+template <typename T, typename U>
+Return<void> setInitFailureAndCallback(T _hidl_cb, hidl_vec<U> data) {
+ return setFailureAndCallback(_hidl_cb, data, "Failure initializing thermal HAL");
+}
+
+} // namespace
+
+// On init we will spawn a thread which will continually watch for
+// throttling. When throttling is seen, if we have a callback registered
+// the thread will call notifyThrottling() else it will log the dropped
+// throttling event and do nothing. The thread is only killed when
+// Thermal() is killed.
+Thermal::Thermal()
+ : thermal_helper_(
+ std::bind(&Thermal::sendThermalChangedCallback, this, std::placeholders::_1)) {}
+
+// Methods from ::android::hardware::thermal::V1_0::IThermal.
+Return<void> Thermal::getTemperatures(getTemperatures_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ hidl_vec<Temperature_1_0> temperatures;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ LOG(ERROR) << "ThermalHAL not initialized properly.";
+ return setInitFailureAndCallback(_hidl_cb, temperatures);
+ }
+
+ if (!thermal_helper_.fillTemperatures(&temperatures)) {
+ return setFailureAndCallback(_hidl_cb, temperatures, "Failed to read thermal sensors.");
+ }
+
+ _hidl_cb(status, temperatures);
+ return Void();
+}
+
+Return<void> Thermal::getCpuUsages(getCpuUsages_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ hidl_vec<CpuUsage> cpu_usages;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ return setInitFailureAndCallback(_hidl_cb, cpu_usages);
+ }
+
+ if (!thermal_helper_.fillCpuUsages(&cpu_usages)) {
+ return setFailureAndCallback(_hidl_cb, cpu_usages, "Failed to get CPU usages.");
+ }
+
+ _hidl_cb(status, cpu_usages);
+ return Void();
+}
+
+Return<void> Thermal::getCoolingDevices(getCoolingDevices_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ hidl_vec<CoolingDevice_1_0> cooling_devices;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ return setInitFailureAndCallback(_hidl_cb, cooling_devices);
+ }
+ _hidl_cb(status, cooling_devices);
+ return Void();
+}
+
+Return<void> Thermal::getCurrentTemperatures(bool filterType, TemperatureType_2_0 type,
+ getCurrentTemperatures_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ hidl_vec<Temperature_2_0> temperatures;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ LOG(ERROR) << "ThermalHAL not initialized properly.";
+ return setInitFailureAndCallback(_hidl_cb, temperatures);
+ }
+
+ if (!thermal_helper_.fillCurrentTemperatures(filterType, type, &temperatures)) {
+ return setFailureAndCallback(_hidl_cb, temperatures, "Failed to read thermal sensors.");
+ }
+
+ _hidl_cb(status, temperatures);
+ return Void();
+}
+
+Return<void> Thermal::getTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
+ getTemperatureThresholds_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ hidl_vec<TemperatureThreshold> temperatures;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ LOG(ERROR) << "ThermalHAL not initialized properly.";
+ return setInitFailureAndCallback(_hidl_cb, temperatures);
+ }
+
+ if (!thermal_helper_.fillTemperatureThresholds(filterType, type, &temperatures)) {
+ return setFailureAndCallback(_hidl_cb, temperatures, "Failed to read thermal sensors.");
+ }
+
+ _hidl_cb(status, temperatures);
+ return Void();
+}
+
+Return<void> Thermal::getCurrentCoolingDevices(bool filterType, CoolingType type,
+ getCurrentCoolingDevices_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ hidl_vec<CoolingDevice_2_0> cooling_devices;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ LOG(ERROR) << "ThermalHAL not initialized properly.";
+ return setInitFailureAndCallback(_hidl_cb, cooling_devices);
+ }
+
+ if (!thermal_helper_.fillCurrentCoolingDevices(filterType, type, &cooling_devices)) {
+ return setFailureAndCallback(_hidl_cb, cooling_devices, "Failed to read thermal sensors.");
+ }
+
+ _hidl_cb(status, cooling_devices);
+ return Void();
+}
+
+Return<void> Thermal::registerThermalChangedCallback(const sp<IThermalChangedCallback> &callback,
+ bool filterType, TemperatureType_2_0 type,
+ registerThermalChangedCallback_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
+ if (std::any_of(callbacks_.begin(), callbacks_.end(), [&](const CallbackSetting &c) {
+ return interfacesEqual(c.callback, callback);
+ })) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "Same callback registered already";
+ LOG(ERROR) << status.debugMessage;
+ } else {
+ callbacks_.emplace_back(callback, filterType, type);
+ LOG(INFO) << "a callback has been registered to ThermalHAL, isFilter: " << filterType
+ << " Type: " << android::hardware::thermal::V2_0::toString(type);
+ }
+ _hidl_cb(status);
+ return Void();
+}
+
+Return<void> Thermal::unregisterThermalChangedCallback(
+ const sp<IThermalChangedCallback> &callback, unregisterThermalChangedCallback_cb _hidl_cb) {
+ ThermalStatus status;
+ status.code = ThermalStatusCode::SUCCESS;
+ bool removed = false;
+ std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
+ callbacks_.erase(
+ std::remove_if(callbacks_.begin(), callbacks_.end(),
+ [&](const CallbackSetting &c) {
+ if (interfacesEqual(c.callback, callback)) {
+ LOG(INFO)
+ << "a callback has been unregistered to ThermalHAL, isFilter: "
+ << c.is_filter_type << " Type: "
+ << android::hardware::thermal::V2_0::toString(c.type);
+ removed = true;
+ return true;
+ }
+ return false;
+ }),
+ callbacks_.end());
+ if (!removed) {
+ status.code = ThermalStatusCode::FAILURE;
+ status.debugMessage = "The callback was not registered before";
+ LOG(ERROR) << status.debugMessage;
+ }
+ _hidl_cb(status);
+ return Void();
+}
+
+void Thermal::sendThermalChangedCallback(const std::vector<Temperature_2_0> &temps) {
+ std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
+ for (auto &t : temps) {
+ LOG(INFO) << "Sending notification: "
+ << " Type: " << android::hardware::thermal::V2_0::toString(t.type)
+ << " Name: " << t.name << " CurrentValue: " << t.value << " ThrottlingStatus: "
+ << android::hardware::thermal::V2_0::toString(t.throttlingStatus);
+ callbacks_.erase(
+ std::remove_if(callbacks_.begin(), callbacks_.end(),
+ [&](const CallbackSetting &c) {
+ if (!c.is_filter_type || t.type == c.type) {
+ Return<void> ret = c.callback->notifyThrottling(t);
+ return !ret.isOk();
+ }
+ LOG(ERROR)
+ << "a Thermal callback is dead, removed from callback list.";
+ return false;
+ }),
+ callbacks_.end());
+ }
+}
+
+Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_string> &) {
+ if (handle != nullptr && handle->numFds >= 1) {
+ int fd = handle->data[0];
+ std::ostringstream dump_buf;
+
+ if (!thermal_helper_.isInitializedOk()) {
+ dump_buf << "ThermalHAL not initialized properly." << std::endl;
+ } else {
+ {
+ hidl_vec<Temperature_1_0> temperatures;
+ dump_buf << "getTemperatures:" << std::endl;
+ if (!thermal_helper_.fillTemperatures(&temperatures)) {
+ dump_buf << "Failed to read thermal sensors." << std::endl;
+ }
+
+ for (const auto &t : temperatures) {
+ dump_buf << " Type: " << android::hardware::thermal::V1_0::toString(t.type)
+ << " Name: " << t.name << " CurrentValue: " << t.currentValue
+ << " ThrottlingThreshold: " << t.throttlingThreshold
+ << " ShutdownThreshold: " << t.shutdownThreshold
+ << " VrThrottlingThreshold: " << t.vrThrottlingThreshold << std::endl;
+ }
+ }
+ {
+ hidl_vec<CpuUsage> cpu_usages;
+ dump_buf << "getCpuUsages:" << std::endl;
+ if (!thermal_helper_.fillCpuUsages(&cpu_usages)) {
+ dump_buf << "Failed to get CPU usages." << std::endl;
+ }
+
+ for (const auto &usage : cpu_usages) {
+ dump_buf << " Name: " << usage.name << " Active: " << usage.active
+ << " Total: " << usage.total << " IsOnline: " << usage.isOnline
+ << std::endl;
+ }
+ }
+ {
+ dump_buf << "getCurrentTemperatures:" << std::endl;
+ hidl_vec<Temperature_2_0> temperatures;
+ if (!thermal_helper_.fillCurrentTemperatures(false, TemperatureType_2_0::SKIN,
+ &temperatures)) {
+ dump_buf << "Failed to getCurrentTemperatures." << std::endl;
+ }
+
+ for (const auto &t : temperatures) {
+ dump_buf << " Type: " << android::hardware::thermal::V2_0::toString(t.type)
+ << " Name: " << t.name << " CurrentValue: " << t.value
+ << " ThrottlingStatus: "
+ << android::hardware::thermal::V2_0::toString(t.throttlingStatus)
+ << std::endl;
+ }
+ }
+ {
+ dump_buf << "getTemperatureThresholds:" << std::endl;
+ hidl_vec<TemperatureThreshold> temperatures;
+ if (!thermal_helper_.fillTemperatureThresholds(false, TemperatureType_2_0::SKIN,
+ &temperatures)) {
+ dump_buf << "Failed to getTemperatureThresholds." << std::endl;
+ }
+
+ for (const auto &t : temperatures) {
+ dump_buf << " Type: " << android::hardware::thermal::V2_0::toString(t.type)
+ << " Name: " << t.name;
+ dump_buf << " hotThrottlingThreshold: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ dump_buf << t.hotThrottlingThresholds[i] << " ";
+ }
+ dump_buf << "] coldThrottlingThreshold: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ dump_buf << t.coldThrottlingThresholds[i] << " ";
+ }
+ dump_buf << "] vrThrottlingThreshold: " << t.vrThrottlingThreshold;
+ dump_buf << std::endl;
+ }
+ }
+ {
+ dump_buf << "getCurrentCoolingDevices:" << std::endl;
+ hidl_vec<CoolingDevice_2_0> cooling_devices;
+ if (!thermal_helper_.fillCurrentCoolingDevices(false, CoolingType::CPU,
+ &cooling_devices)) {
+ dump_buf << "Failed to getCurrentCoolingDevices." << std::endl;
+ }
+
+ for (const auto &c : cooling_devices) {
+ dump_buf << " Type: " << android::hardware::thermal::V2_0::toString(c.type)
+ << " Name: " << c.name << " CurrentValue: " << c.value << std::endl;
+ }
+ }
+ {
+ dump_buf << "Callbacks: Total " << callbacks_.size() << std::endl;
+ for (const auto &c : callbacks_) {
+ dump_buf << " IsFilter: " << c.is_filter_type
+ << " Type: " << android::hardware::thermal::V2_0::toString(c.type)
+ << std::endl;
+ }
+ }
+ {
+ dump_buf << "getHysteresis:" << std::endl;
+ const auto &map = thermal_helper_.GetSensorInfoMap();
+ for (const auto &name_info_pair : map) {
+ dump_buf << " Name: " << name_info_pair.first;
+ dump_buf << " hotHysteresis: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ dump_buf << name_info_pair.second.hot_hysteresis[i] << " ";
+ }
+ dump_buf << "] coldHysteresis: [";
+ for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+ dump_buf << name_info_pair.second.cold_hysteresis[i] << " ";
+ }
+ dump_buf << "]" << std::endl;
+ }
+ }
+ {
+ dump_buf << "Monitor:" << std::endl;
+ const auto &map = thermal_helper_.GetSensorInfoMap();
+ for (const auto &name_info_pair : map) {
+ dump_buf << " Name: " << name_info_pair.first;
+ dump_buf << " Monitor: " << std::boolalpha << name_info_pair.second.is_monitor
+ << std::noboolalpha << std::endl;
+ }
+ }
+ }
+ std::string buf = dump_buf.str();
+ if (!android::base::WriteStringToFd(buf, fd)) {
+ PLOG(ERROR) << "Failed to dump state to fd";
+ }
+ fsync(fd);
+ }
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/Thermal.h b/thermal/Thermal.h
new file mode 100644
index 00000000..317936f5
--- /dev/null
+++ b/thermal/Thermal.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
+#define ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
+
+#include <mutex>
+#include <thread>
+
+#include <android/hardware/thermal/2.0/IThermal.h>
+#include <android/hardware/thermal/2.0/IThermalChangedCallback.h>
+#include <hidl/Status.h>
+
+#include "thermal-helper.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::thermal::V2_0::IThermal;
+using ::android::hardware::thermal::V2_0::IThermalChangedCallback;
+
+struct CallbackSetting {
+ CallbackSetting(sp<IThermalChangedCallback> callback, bool is_filter_type,
+ TemperatureType_2_0 type)
+ : callback(callback), is_filter_type(is_filter_type), type(type) {}
+ sp<IThermalChangedCallback> callback;
+ bool is_filter_type;
+ TemperatureType_2_0 type;
+};
+
+class Thermal : public IThermal {
+ public:
+ Thermal();
+ ~Thermal() = default;
+
+ // Disallow copy and assign.
+ Thermal(const Thermal &) = delete;
+ void operator=(const Thermal &) = delete;
+
+ // Methods from ::android::hardware::thermal::V1_0::IThermal.
+ Return<void> getTemperatures(getTemperatures_cb _hidl_cb) override;
+ Return<void> getCpuUsages(getCpuUsages_cb _hidl_cb) override;
+ Return<void> getCoolingDevices(getCoolingDevices_cb _hidl_cb) override;
+
+ // Methods from ::android::hardware::thermal::V2_0::IThermal follow.
+ Return<void> getCurrentTemperatures(bool filterType, TemperatureType_2_0 type,
+ getCurrentTemperatures_cb _hidl_cb) override;
+ Return<void> getTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
+ getTemperatureThresholds_cb _hidl_cb) override;
+ Return<void> registerThermalChangedCallback(const sp<IThermalChangedCallback> &callback,
+ bool filterType, TemperatureType_2_0 type,
+ registerThermalChangedCallback_cb _hidl_cb) override;
+ Return<void> unregisterThermalChangedCallback(
+ const sp<IThermalChangedCallback> &callback,
+ unregisterThermalChangedCallback_cb _hidl_cb) override;
+ Return<void> getCurrentCoolingDevices(bool filterType, CoolingType type,
+ getCurrentCoolingDevices_cb _hidl_cb) override;
+
+ // Methods from ::android::hidl::base::V1_0::IBase follow.
+ Return<void> debug(const hidl_handle &fd, const hidl_vec<hidl_string> &args) override;
+
+ // Helper function for calling callbacks
+ void sendThermalChangedCallback(const std::vector<Temperature_2_0> &temps);
+
+ private:
+ ThermalHelper thermal_helper_;
+ std::mutex thermal_callback_mutex_;
+ std::vector<CallbackSetting> callbacks_;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
diff --git a/thermal/android.hardware.thermal@2.0-service.pixel.rc b/thermal/android.hardware.thermal@2.0-service.pixel.rc
new file mode 100644
index 00000000..27fc14a3
--- /dev/null
+++ b/thermal/android.hardware.thermal@2.0-service.pixel.rc
@@ -0,0 +1,5 @@
+service vendor.thermal-hal-2-0 /vendor/bin/hw/android.hardware.thermal@2.0-service.pixel
+ interface android.hardware.thermal@2.0::IThermal default
+ class hal
+ user system
+ group system
diff --git a/thermal/android.hardware.thermal@2.0-service.pixel.xml b/thermal/android.hardware.thermal@2.0-service.pixel.xml
new file mode 100644
index 00000000..bcd6344b
--- /dev/null
+++ b/thermal/android.hardware.thermal@2.0-service.pixel.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>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/thermal/service.cpp b/thermal/service.cpp
new file mode 100644
index 00000000..190269b7
--- /dev/null
+++ b/thermal/service.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+#include "Thermal.h"
+
+using ::android::OK;
+using ::android::status_t;
+
+// libhwbinder:
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::joinRpcThreadpool;
+
+// Generated HIDL files:
+using ::android::hardware::thermal::V2_0::IThermal;
+using ::android::hardware::thermal::V2_0::implementation::Thermal;
+
+static int shutdown() {
+ LOG(ERROR) << "Pixel Thermal HAL Service is shutting down.";
+ return 1;
+}
+
+int main(int /* argc */, char ** /* argv */) {
+ status_t status;
+ android::sp<IThermal> service = nullptr;
+
+ LOG(INFO) << "Pixel Thermal HAL Service 2.0 starting...";
+
+ service = new Thermal();
+ if (service == nullptr) {
+ LOG(ERROR) << "Error creating an instance of Thermal HAL. Exiting...";
+ return shutdown();
+ }
+
+ configureRpcThreadpool(1, true /* callerWillJoin */);
+
+ status = service->registerAsService();
+ if (status != OK) {
+ LOG(ERROR) << "Could not register service for ThermalHAL (" << status << ")";
+ return shutdown();
+ }
+
+ LOG(INFO) << "Pixel Thermal HAL Service 2.0 started successfully.";
+ joinRpcThreadpool();
+ // We should not get past the joinRpcThreadpool().
+ return shutdown();
+}
diff --git a/thermal/thermal-helper.cpp b/thermal/thermal-helper.cpp
new file mode 100644
index 00000000..d3d242fd
--- /dev/null
+++ b/thermal/thermal-helper.cpp
@@ -0,0 +1,498 @@
+/*
+ * 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 <set>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "thermal-helper.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+constexpr char kThermalSensorsRoot[] = "/sys/devices/virtual/thermal";
+constexpr char kCpuOnlineRoot[] = "/sys/devices/system/cpu";
+constexpr char kCpuUsageFile[] = "/proc/stat";
+constexpr char kCpuOnlineFileSuffix[] = "online";
+constexpr char kCpuPresentFile[] = "/sys/devices/system/cpu/present";
+
+namespace {
+using android::base::StringPrintf;
+
+// Pixel don't offline CPU, see std::thread::hardware_concurrency(); should work.
+// However /sys/devices/system/cpu/present is preferred.
+// The file is expected to contain single text line with two numbers %d-%d,
+// which is a range of available cpu numbers, e.g. 0-7 would mean there
+// are 8 cores number from 0 to 7.
+// For Android systems this approach is safer than using cpufeatures, see bug
+// b/36941727.
+std::size_t getNumberOfCores() {
+ std::string file;
+ if (!android::base::ReadFileToString(kCpuPresentFile, &file)) {
+ LOG(ERROR) << "Error reading Cpu present file: " << kCpuPresentFile;
+ return 0;
+ }
+ std::vector<std::string> pieces = android::base::Split(file, "-");
+ if (pieces.size() != 2) {
+ LOG(ERROR) << "Error parsing Cpu present file content: " << file;
+ return 0;
+ }
+ auto min_core = std::stoul(pieces[0]);
+ auto max_core = std::stoul(pieces[1]);
+ if (max_core < min_core) {
+ LOG(ERROR) << "Error parsing Cpu present min and max: " << min_core << " - " << max_core;
+ return 0;
+ }
+ return static_cast<std::size_t>(max_core - min_core + 1);
+}
+const std::size_t kMaxCpus = getNumberOfCores();
+
+void parseCpuUsagesFileAndAssignUsages(hidl_vec<CpuUsage> *cpu_usages) {
+ uint64_t cpu_num, user, nice, system, idle;
+ std::string cpu_name;
+ std::string data;
+ if (!android::base::ReadFileToString(kCpuUsageFile, &data)) {
+ LOG(ERROR) << "Error reading Cpu usage file: " << kCpuUsageFile;
+ return;
+ }
+
+ std::istringstream stat_data(data);
+ std::string line;
+ while (std::getline(stat_data, line)) {
+ if (line.find("cpu") == 0 && isdigit(line[3])) {
+ // Split the string using spaces.
+ std::vector<std::string> words = android::base::Split(line, " ");
+ cpu_name = words[0];
+ cpu_num = std::stoi(cpu_name.substr(3));
+
+ if (cpu_num < kMaxCpus) {
+ user = std::stoi(words[1]);
+ nice = std::stoi(words[2]);
+ system = std::stoi(words[3]);
+ idle = std::stoi(words[4]);
+
+ // Check if the CPU is online by reading the online file.
+ std::string cpu_online_path = StringPrintf("%s/%s/%s", kCpuOnlineRoot,
+ cpu_name.c_str(), kCpuOnlineFileSuffix);
+ std::string is_online;
+ if (!android::base::ReadFileToString(cpu_online_path, &is_online)) {
+ LOG(ERROR) << "Could not open Cpu online file: " << cpu_online_path;
+ return;
+ }
+ is_online = android::base::Trim(is_online);
+
+ (*cpu_usages)[cpu_num].name = cpu_name;
+ (*cpu_usages)[cpu_num].active = user + nice + system;
+ (*cpu_usages)[cpu_num].total = user + nice + system + idle;
+ (*cpu_usages)[cpu_num].isOnline = (is_online == "1") ? true : false;
+ } else {
+ LOG(ERROR) << "Unexpected cpu number: " << words[0];
+ return;
+ }
+ }
+ }
+}
+
+} // namespace
+
+/*
+ * Populate the sensor_name_to_file_map_ map by walking through the file tree,
+ * reading the type file and assigning the temp file path to the map. If we do
+ * not succeed, abort.
+ */
+ThermalHelper::ThermalHelper(const NotificationCallback &cb)
+ : thermal_watcher_(
+ new ThermalWatcher(std::bind(&ThermalHelper::thermalWatcherCallbackFunc, this,
+ std::placeholders::_1, std::placeholders::_2))),
+ cb_(cb),
+ cooling_device_info_map_(ParseCoolingDevice(
+ "/vendor/etc/" +
+ android::base::GetProperty("vendor.thermal.config", "thermal_info_config.json"))),
+ sensor_info_map_(ParseSensorInfo(
+ "/vendor/etc/" +
+ android::base::GetProperty("vendor.thermal.config", "thermal_info_config.json"))) {
+ for (auto const &name_status_pair : sensor_info_map_) {
+ sensor_status_map_[name_status_pair.first] = {
+ .severity = ThrottlingSeverity::NONE,
+ .prev_hot_severity = ThrottlingSeverity::NONE,
+ .prev_cold_severity = ThrottlingSeverity::NONE,
+ };
+ }
+
+ is_initialized_ = initializeSensorMap() && initializeCoolingDevices();
+ if (!is_initialized_) {
+ LOG(FATAL) << "ThermalHAL could not be initialized properly.";
+ }
+ std::vector<std::string> paths;
+ for (const auto &entry : cooling_device_info_map_) {
+ std::string path = cooling_devices_.getCoolingDevicePath(entry.first);
+ if (!path.empty()) {
+ paths.push_back(path + "/cur_state");
+ }
+ }
+ thermal_watcher_->registerFilesToWatch(paths);
+ // Need start watching after status map initialized
+ is_initialized_ = thermal_watcher_->startWatchingDeviceFiles();
+ if (!is_initialized_) {
+ LOG(FATAL) << "ThermalHAL could not start watching thread properly.";
+ }
+}
+
+std::vector<std::string> ThermalHelper::getCoolingDevicePaths() const {
+ std::vector<std::string> paths;
+ for (const auto &entry : cooling_device_info_map_) {
+ std::string path = cooling_devices_.getCoolingDevicePath(entry.first);
+ if (!path.empty()) {
+ paths.push_back(path + "/cur_state");
+ }
+ }
+ return paths;
+}
+
+bool ThermalHelper::readCoolingDevice(const std::string &cooling_device,
+ CoolingDevice_2_0 *out) const {
+ // Read the file. If the file can't be read temp will be empty string.
+ int data;
+ std::string path;
+
+ if (!cooling_devices_.getCoolingDeviceState(cooling_device, &data)) {
+ LOG(ERROR) << "readCoolingDevice: failed to read cooling_device: " << cooling_device;
+ return false;
+ }
+
+ const CoolingType &type = cooling_device_info_map_.at(cooling_device);
+
+ out->type = type;
+ out->name = cooling_device;
+ out->value = data;
+
+ return true;
+}
+
+bool ThermalHelper::readTemperature(const std::string &sensor_name, Temperature_1_0 *out) const {
+ // Read the file. If the file can't be read temp will be empty string.
+ std::string temp;
+ std::string path;
+
+ if (!thermal_sensors_.readSensorFile(sensor_name, &temp, &path)) {
+ LOG(ERROR) << "readTemperature: sensor not found: " << sensor_name;
+ return false;
+ }
+
+ if (temp.empty() && !path.empty()) {
+ LOG(ERROR) << "readTemperature: failed to open file: " << path;
+ return false;
+ }
+
+ const SensorInfo &sensor_info = sensor_info_map_.at(sensor_name);
+ TemperatureType_1_0 type =
+ (static_cast<int>(sensor_info.type) > static_cast<int>(TemperatureType_1_0::SKIN))
+ ? TemperatureType_1_0::UNKNOWN
+ : static_cast<TemperatureType_1_0>(sensor_info.type);
+ out->type = type;
+ out->name = sensor_name;
+ out->currentValue = std::stof(temp) * sensor_info.multiplier;
+ out->throttlingThreshold =
+ sensor_info.hot_thresholds[static_cast<size_t>(ThrottlingSeverity::SEVERE)];
+ out->shutdownThreshold =
+ sensor_info.hot_thresholds[static_cast<size_t>(ThrottlingSeverity::SHUTDOWN)];
+ out->vrThrottlingThreshold = sensor_info.vr_threshold;
+
+ return true;
+}
+
+bool ThermalHelper::readTemperature(
+ const std::string &sensor_name, Temperature_2_0 *out,
+ std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status) const {
+ // Read the file. If the file can't be read temp will be empty string.
+ std::string temp;
+ std::string path;
+
+ if (!thermal_sensors_.readSensorFile(sensor_name, &temp, &path)) {
+ LOG(ERROR) << "readTemperature: sensor not found: " << sensor_name;
+ return false;
+ }
+
+ if (temp.empty() && !path.empty()) {
+ LOG(ERROR) << "readTemperature: failed to open file: " << path;
+ return false;
+ }
+
+ const auto &sensor_info = sensor_info_map_.at(sensor_name);
+ out->type = sensor_info.type;
+ out->name = sensor_name;
+ out->value = std::stof(temp) * sensor_info.multiplier;
+
+ ThrottlingSeverity prev_hot_severity, prev_cold_severity;
+ {
+ // reader lock, readTemperature will be called in Binder call and the watcher thread.
+ std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
+ prev_hot_severity = sensor_status_map_.at(sensor_name).prev_hot_severity;
+ prev_cold_severity = sensor_status_map_.at(sensor_name).prev_cold_severity;
+ }
+
+ std::pair<ThrottlingSeverity, ThrottlingSeverity> status = getSeverityFromThresholds(
+ sensor_info.hot_thresholds, sensor_info.cold_thresholds, sensor_info.hot_hysteresis,
+ sensor_info.cold_hysteresis, prev_hot_severity, prev_cold_severity, out->value);
+
+ out->throttlingStatus = static_cast<size_t>(status.first) > static_cast<size_t>(status.second)
+ ? status.first
+ : status.second;
+ if (throtting_status) {
+ *throtting_status = status;
+ }
+ return true;
+}
+
+bool ThermalHelper::readTemperatureThreshold(const std::string &sensor_name,
+ TemperatureThreshold *out) const {
+ // Read the file. If the file can't be read temp will be empty string.
+ std::string temp;
+ std::string path;
+
+ if (!sensor_info_map_.count(sensor_name)) {
+ LOG(ERROR) << __func__ << ": sensor not found: " << sensor_name;
+ return false;
+ }
+
+ const auto &sensor_info = sensor_info_map_.at(sensor_name);
+
+ out->type = sensor_info.type;
+ out->name = sensor_name;
+ out->hotThrottlingThresholds = sensor_info.hot_thresholds;
+ out->coldThrottlingThresholds = sensor_info.cold_thresholds;
+ out->vrThrottlingThreshold = sensor_info.vr_threshold;
+ return true;
+}
+
+std::pair<ThrottlingSeverity, ThrottlingSeverity> ThermalHelper::getSeverityFromThresholds(
+ const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds,
+ const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis,
+ ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity,
+ float value) const {
+ ThrottlingSeverity ret_hot = ThrottlingSeverity::NONE;
+ ThrottlingSeverity ret_hot_hysteresis = ThrottlingSeverity::NONE;
+ ThrottlingSeverity ret_cold = ThrottlingSeverity::NONE;
+ ThrottlingSeverity ret_cold_hysteresis = ThrottlingSeverity::NONE;
+
+ // Here we want to control the iteration from high to low, and hidl_enum_range doesn't support
+ // a reverse iterator yet.
+ for (size_t i = static_cast<size_t>(ThrottlingSeverity::SHUTDOWN);
+ i > static_cast<size_t>(ThrottlingSeverity::NONE); --i) {
+ if (!std::isnan(hot_thresholds[i]) && hot_thresholds[i] <= value &&
+ ret_hot == ThrottlingSeverity::NONE) {
+ ret_hot = static_cast<ThrottlingSeverity>(i);
+ }
+ if (!std::isnan(hot_thresholds[i]) && (hot_thresholds[i] - hot_hysteresis[i]) < value &&
+ ret_hot_hysteresis == ThrottlingSeverity::NONE) {
+ ret_hot_hysteresis = static_cast<ThrottlingSeverity>(i);
+ }
+ if (!std::isnan(cold_thresholds[i]) && cold_thresholds[i] >= value &&
+ ret_cold == ThrottlingSeverity::NONE) {
+ ret_cold = static_cast<ThrottlingSeverity>(i);
+ }
+ if (!std::isnan(cold_thresholds[i]) && (cold_thresholds[i] + cold_hysteresis[i]) > value &&
+ ret_cold_hysteresis == ThrottlingSeverity::NONE) {
+ ret_cold_hysteresis = static_cast<ThrottlingSeverity>(i);
+ }
+ }
+ if (static_cast<size_t>(ret_hot) < static_cast<size_t>(prev_hot_severity)) {
+ ret_hot = ret_hot_hysteresis;
+ }
+ if (static_cast<size_t>(ret_cold) < static_cast<size_t>(prev_cold_severity)) {
+ ret_cold = ret_cold_hysteresis;
+ }
+
+ return std::make_pair(ret_hot, ret_cold);
+}
+
+bool ThermalHelper::initializeSensorMap() {
+ for (const auto &e : sensor_info_map_) {
+ std::string sensor_name = e.first;
+ std::string sensor_temp_path =
+ StringPrintf("%s/tz-by-name/%s/temp", kThermalSensorsRoot, sensor_name.c_str());
+ if (!thermal_sensors_.addSensor(sensor_name, sensor_temp_path)) {
+ LOG(ERROR) << "Could not add " << sensor_name << "to sensors map";
+ }
+ }
+ if (sensor_info_map_.size() == thermal_sensors_.getNumSensors()) {
+ return true;
+ }
+ return false;
+}
+
+bool ThermalHelper::initializeCoolingDevices() {
+ for (const auto &cooling_device_info : cooling_device_info_map_) {
+ std::string cooling_device_name = cooling_device_info.first;
+ std::string cooling_device_path =
+ StringPrintf("%s/cdev-by-name/%s", kThermalSensorsRoot, cooling_device_name.c_str());
+
+ if (!cooling_devices_.addCoolingDevice(cooling_device_name, cooling_device_path)) {
+ LOG(ERROR) << "Could not add " << cooling_device_name << "to cooling device map";
+ continue;
+ }
+ }
+
+ if (cooling_device_info_map_.size() == cooling_devices_.getNumCoolingDevices()) {
+ return true;
+ }
+ return false;
+}
+
+bool ThermalHelper::fillTemperatures(hidl_vec<Temperature_1_0> *temperatures) const {
+ temperatures->resize(sensor_info_map_.size());
+ int current_index = 0;
+ for (const auto &name_info_pair : sensor_info_map_) {
+ Temperature_1_0 temp;
+
+ if (readTemperature(name_info_pair.first, &temp)) {
+ (*temperatures)[current_index] = temp;
+ } else {
+ LOG(ERROR) << __func__
+ << ": error reading temperature for sensor: " << name_info_pair.first;
+ return false;
+ }
+ ++current_index;
+ }
+ return current_index > 0;
+}
+
+bool ThermalHelper::fillCurrentTemperatures(bool filterType, TemperatureType_2_0 type,
+ hidl_vec<Temperature_2_0> *temperatures) const {
+ std::vector<Temperature_2_0> ret;
+ for (const auto &name_info_pair : sensor_info_map_) {
+ Temperature_2_0 temp;
+ if (filterType && name_info_pair.second.type != type) {
+ continue;
+ }
+ if (readTemperature(name_info_pair.first, &temp)) {
+ ret.emplace_back(std::move(temp));
+ } else {
+ LOG(ERROR) << __func__
+ << ": error reading temperature for sensor: " << name_info_pair.first;
+ return false;
+ }
+ }
+ *temperatures = ret;
+ return ret.size() > 0;
+}
+
+bool ThermalHelper::fillTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
+ hidl_vec<TemperatureThreshold> *thresholds) const {
+ std::vector<TemperatureThreshold> ret;
+ for (const auto &name_info_pair : sensor_info_map_) {
+ TemperatureThreshold temp;
+ if (filterType && name_info_pair.second.type != type) {
+ continue;
+ }
+ if (readTemperatureThreshold(name_info_pair.first, &temp)) {
+ ret.emplace_back(std::move(temp));
+ } else {
+ LOG(ERROR) << __func__ << ": error reading temperature threshold for sensor: "
+ << name_info_pair.first;
+ return false;
+ }
+ }
+ *thresholds = ret;
+ return ret.size() > 0;
+}
+
+bool ThermalHelper::fillCurrentCoolingDevices(bool filterType, CoolingType type,
+ hidl_vec<CoolingDevice_2_0> *cooling_devices) const {
+ std::vector<CoolingDevice_2_0> ret;
+ for (const auto &name_info_pair : cooling_device_info_map_) {
+ CoolingDevice_2_0 value;
+ if (filterType && name_info_pair.second != type) {
+ continue;
+ }
+ if (readCoolingDevice(name_info_pair.first, &value)) {
+ ret.emplace_back(std::move(value));
+ } else {
+ LOG(ERROR) << __func__ << ": error reading cooling device: " << name_info_pair.first;
+ return false;
+ }
+ }
+ *cooling_devices = ret;
+ return ret.size() > 0;
+}
+
+bool ThermalHelper::fillCpuUsages(hidl_vec<CpuUsage> *cpu_usages) const {
+ cpu_usages->resize(kMaxCpus);
+ parseCpuUsagesFileAndAssignUsages(cpu_usages);
+ return true;
+}
+
+// This is called in the different thread context and will update sensor_status
+void ThermalHelper::thermalWatcherCallbackFunc(const std::string &, const int) {
+ std::vector<Temperature_2_0> temps;
+ for (auto &name_status_pair : sensor_status_map_) {
+ Temperature_2_0 temp;
+ TemperatureThreshold threshold;
+ SensorStatus &sensor_status = name_status_pair.second;
+ const SensorInfo &sensor_info = sensor_info_map_.at(name_status_pair.first);
+ // Only send notification on whitelisted sensors
+ if (!sensor_info.is_monitor) {
+ continue;
+ }
+
+ std::pair<ThrottlingSeverity, ThrottlingSeverity> throtting_status;
+ if (!readTemperature(name_status_pair.first, &temp, &throtting_status)) {
+ LOG(ERROR) << __func__
+ << ": error reading temperature for sensor: " << name_status_pair.first;
+ continue;
+ }
+ if (!readTemperatureThreshold(name_status_pair.first, &threshold)) {
+ LOG(ERROR) << __func__ << ": error reading temperature threshold for sensor: "
+ << name_status_pair.first;
+ continue;
+ }
+
+ {
+ // writer lock
+ std::unique_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
+ if (throtting_status.first != sensor_status.prev_hot_severity) {
+ sensor_status.prev_hot_severity = throtting_status.first;
+ }
+ if (throtting_status.second != sensor_status.prev_cold_severity) {
+ sensor_status.prev_cold_severity = throtting_status.second;
+ }
+ if (temp.throttlingStatus != sensor_status.severity) {
+ temps.push_back(temp);
+ sensor_status.severity = temp.throttlingStatus;
+ }
+ }
+ }
+ if (!temps.empty() && cb_) {
+ cb_(temps);
+ }
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/thermal-helper.h b/thermal/thermal-helper.h
new file mode 100644
index 00000000..f1d4bd7b
--- /dev/null
+++ b/thermal/thermal-helper.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __THERMAL_HELPER_H__
+#define __THERMAL_HELPER_H__
+
+#include <array>
+#include <chrono>
+#include <mutex>
+#include <shared_mutex>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <android/hardware/thermal/2.0/IThermal.h>
+
+#include "utils/config_parser.h"
+#include "utils/cooling_devices.h"
+#include "utils/sensors.h"
+#include "utils/thermal_watcher.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::thermal::V1_0::CpuUsage;
+using ::android::hardware::thermal::V2_0::CoolingType;
+using ::android::hardware::thermal::V2_0::IThermal;
+using CoolingDevice_1_0 = ::android::hardware::thermal::V1_0::CoolingDevice;
+using CoolingDevice_2_0 = ::android::hardware::thermal::V2_0::CoolingDevice;
+using Temperature_1_0 = ::android::hardware::thermal::V1_0::Temperature;
+using Temperature_2_0 = ::android::hardware::thermal::V2_0::Temperature;
+using TemperatureType_1_0 = ::android::hardware::thermal::V1_0::TemperatureType;
+using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
+using ::android::hardware::thermal::V2_0::TemperatureThreshold;
+using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
+
+using NotificationCallback = std::function<void(const std::vector<Temperature_2_0> &temps)>;
+using NotificationTime = std::chrono::time_point<std::chrono::steady_clock>;
+
+struct SensorStatus {
+ ThrottlingSeverity severity;
+ ThrottlingSeverity prev_hot_severity;
+ ThrottlingSeverity prev_cold_severity;
+};
+
+class ThermalHelper {
+ public:
+ ThermalHelper(const NotificationCallback &cb);
+ ~ThermalHelper() = default;
+
+ bool fillTemperatures(hidl_vec<Temperature_1_0> *temperatures) const;
+ bool fillCurrentTemperatures(bool filterType, TemperatureType_2_0 type,
+ hidl_vec<Temperature_2_0> *temperatures) const;
+ bool fillTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
+ hidl_vec<TemperatureThreshold> *thresholds) const;
+ bool fillCurrentCoolingDevices(bool filterType, CoolingType type,
+ hidl_vec<CoolingDevice_2_0> *coolingdevices) const;
+ bool fillCpuUsages(hidl_vec<CpuUsage> *cpu_usages) const;
+
+ // Dissallow copy and assign.
+ ThermalHelper(const ThermalHelper &) = delete;
+ void operator=(const ThermalHelper &) = delete;
+
+ bool isInitializedOk() const { return is_initialized_; }
+
+ // Returns a vector of all cooling devices that has been found on the
+ // device.
+ std::vector<std::string> getCoolingDevicePaths() const;
+
+ // Read the temperature of a single sensor.
+ bool readTemperature(const std::string &sensor_name, Temperature_1_0 *out) const;
+ bool readTemperature(
+ const std::string &sensor_name, Temperature_2_0 *out,
+ std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status = nullptr) const;
+ bool readTemperatureThreshold(const std::string &sensor_name, TemperatureThreshold *out) const;
+ // Read the value of a single cooling device.
+ bool readCoolingDevice(const std::string &cooling_device, CoolingDevice_2_0 *out) const;
+ // Get SensorInfo Map
+ const std::map<std::string, SensorInfo> &GetSensorInfoMap() const { return sensor_info_map_; }
+
+ private:
+ bool initializeSensorMap();
+ bool initializeCoolingDevices();
+ // For thermal_watcher_'s polling thread
+ void thermalWatcherCallbackFunc(const std::string &, const int);
+ // Return hot and cold severity status as std::pair
+ std::pair<ThrottlingSeverity, ThrottlingSeverity> getSeverityFromThresholds(
+ const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds,
+ const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis,
+ ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity,
+ float value) const;
+
+ sp<ThermalWatcher> thermal_watcher_;
+ Sensors thermal_sensors_;
+ CoolingDevices cooling_devices_;
+ bool is_initialized_;
+ const NotificationCallback cb_;
+ const std::map<std::string, CoolingType> cooling_device_info_map_;
+ const std::map<std::string, SensorInfo> sensor_info_map_;
+
+ mutable std::shared_mutex sensor_status_map_mutex_;
+ std::map<std::string, SensorStatus> sensor_status_map_;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
+
+#endif // __THERMAL_HELPER_H__
diff --git a/thermal/utils/config_parser.cpp b/thermal/utils/config_parser.cpp
new file mode 100644
index 00000000..cefdb151
--- /dev/null
+++ b/thermal/utils/config_parser.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <cmath>
+#include <set>
+
+#include <json/reader.h>
+#include <json/value.h>
+
+#include "config_parser.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_enum_range;
+using ::android::hardware::thermal::V2_0::toString;
+using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
+
+namespace {
+
+template <typename T>
+// Return false when failed parsing
+bool getTypeFromString(const std::string &str, T *out) {
+ auto types = hidl_enum_range<T>();
+ for (const auto &type : types) {
+ if (toString(type) == str) {
+ *out = type;
+ return true;
+ }
+ }
+ return false;
+}
+
+float getFloatFromValue(const Json::Value &value) {
+ if (value.isString()) {
+ return std::stof(value.asString());
+ } else {
+ return value.asFloat();
+ }
+}
+
+} // namespace
+
+std::map<std::string, SensorInfo> ParseSensorInfo(const std::string &config_path) {
+ std::string json_doc;
+ std::map<std::string, SensorInfo> sensors_parsed;
+ if (!android::base::ReadFileToString(config_path, &json_doc)) {
+ LOG(ERROR) << "Failed to read JSON config from " << config_path;
+ return sensors_parsed;
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+
+ if (!reader.parse(json_doc, root)) {
+ LOG(ERROR) << "Failed to parse JSON config";
+ return sensors_parsed;
+ }
+
+ Json::Value sensors = root["Sensors"];
+ std::size_t total_parsed = 0;
+ std::set<std::string> sensors_name_parsed;
+
+ for (Json::Value::ArrayIndex i = 0; i < sensors.size(); ++i) {
+ const std::string &name = sensors[i]["Name"].asString();
+ LOG(INFO) << "Sensor[" << i << "]'s Name: " << name;
+ if (name.empty()) {
+ LOG(ERROR) << "Failed to read "
+ << "Sensor[" << i << "]'s Name";
+ sensors_parsed.clear();
+ return sensors_parsed;
+ }
+
+ auto result = sensors_name_parsed.insert(name);
+ if (!result.second) {
+ LOG(ERROR) << "Duplicate Sensor[" << i << "]'s Name";
+ sensors_parsed.clear();
+ return sensors_parsed;
+ }
+
+ std::string sensor_type_str = sensors[i]["Type"].asString();
+ LOG(INFO) << "Sensor[" << name << "]'s Type: " << sensor_type_str;
+ TemperatureType_2_0 sensor_type;
+
+ if (!getTypeFromString(sensor_type_str, &sensor_type)) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s Type: " << sensor_type_str;
+ sensors_parsed.clear();
+ return sensors_parsed;
+ }
+
+ std::array<float, kThrottlingSeverityCount> hot_thresholds;
+ hot_thresholds.fill(NAN);
+ std::array<float, kThrottlingSeverityCount> cold_thresholds;
+ cold_thresholds.fill(NAN);
+ std::array<float, kThrottlingSeverityCount> hot_hysteresis;
+ hot_hysteresis.fill(0.0);
+ std::array<float, kThrottlingSeverityCount> cold_hysteresis;
+ cold_hysteresis.fill(0.0);
+
+ Json::Value values = sensors[i]["HotThreshold"];
+ if (values.size() != kThrottlingSeverityCount) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s HotThreshold count" << values.size();
+ sensors_parsed.clear();
+ return sensors_parsed;
+ } else {
+ float min = std::numeric_limits<float>::min();
+ for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
+ hot_thresholds[j] = getFloatFromValue(values[j]);
+ if (!std::isnan(hot_thresholds[j])) {
+ if (hot_thresholds[j] < min) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s HotThreshold[j" << j
+ << "]: " << hot_thresholds[j] << " < " << min;
+ sensors_parsed.clear();
+ return sensors_parsed;
+ }
+ min = hot_thresholds[j];
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s HotThreshold[" << j
+ << "]: " << hot_thresholds[j];
+ }
+ }
+
+ values = sensors[i]["HotHysteresis"];
+ if (values.size() != kThrottlingSeverityCount) {
+ LOG(INFO) << "Cannot find valid "
+ << "Sensor[" << name << "]'s HotHysteresis, default all to 0.0";
+ } else {
+ for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
+ hot_hysteresis[j] = getFloatFromValue(values[j]);
+ if (std::isnan(hot_hysteresis[j])) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s HotHysteresis: " << hot_hysteresis[j];
+ sensors_parsed.clear();
+ return sensors_parsed;
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s HotHysteresis[" << j
+ << "]: " << hot_hysteresis[j];
+ }
+ }
+
+ values = sensors[i]["ColdThreshold"];
+ if (values.size() != kThrottlingSeverityCount) {
+ LOG(INFO) << "Cannot find valid "
+ << "Sensor[" << name << "]'s ColdThreshold, default all to NAN";
+ } else {
+ float max = std::numeric_limits<float>::max();
+ for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
+ cold_thresholds[j] = getFloatFromValue(values[j]);
+ if (!std::isnan(cold_thresholds[j])) {
+ if (cold_thresholds[j] > max) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name << "]'s ColdThreshold[j" << j
+ << "]: " << cold_thresholds[j] << " > " << max;
+ sensors_parsed.clear();
+ return sensors_parsed;
+ }
+ max = cold_thresholds[j];
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s ColdThreshold[" << j
+ << "]: " << cold_thresholds[j];
+ }
+ }
+
+ values = sensors[i]["ColdHysteresis"];
+ if (values.size() != kThrottlingSeverityCount) {
+ LOG(INFO) << "Cannot find valid "
+ << "Sensor[" << name << "]'s ColdHysteresis, default all to 0.0";
+ } else {
+ for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) {
+ cold_hysteresis[j] = getFloatFromValue(values[j]);
+ if (std::isnan(cold_hysteresis[j])) {
+ LOG(ERROR) << "Invalid "
+ << "Sensor[" << name
+ << "]'s ColdHysteresis: " << cold_hysteresis[j];
+ sensors_parsed.clear();
+ return sensors_parsed;
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s ColdHysteresis[" << j
+ << "]: " << cold_hysteresis[j];
+ }
+ }
+
+ float vr_threshold = NAN;
+ vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]);
+ LOG(INFO) << "Sensor[" << name << "]'s VrThreshold: " << vr_threshold;
+
+ float multiplier = sensors[i]["Multiplier"].asFloat();
+ LOG(INFO) << "Sensor[" << name << "]'s Multiplier: " << multiplier;
+
+ bool is_monitor = false;
+ if (sensors[i]["Monitor"].empty() || !sensors[i]["Monitor"].isBool()) {
+ LOG(INFO) << "Failed to read Sensor[" << name << "]'s Monitor, set to 'false'";
+ } else {
+ is_monitor = sensors[i]["Monitor"].asBool();
+ }
+ LOG(INFO) << "Sensor[" << name << "]'s Monitor: " << std::boolalpha << is_monitor
+ << std::noboolalpha;
+
+ sensors_parsed[name] = {
+ .type = sensor_type,
+ .hot_thresholds = hot_thresholds,
+ .cold_thresholds = cold_thresholds,
+ .hot_hysteresis = hot_hysteresis,
+ .cold_hysteresis = cold_hysteresis,
+ .vr_threshold = vr_threshold,
+ .multiplier = multiplier,
+ .is_monitor = is_monitor,
+ };
+ ++total_parsed;
+ }
+
+ LOG(INFO) << total_parsed << " Sensors parsed successfully";
+ return sensors_parsed;
+}
+
+std::map<std::string, CoolingType> ParseCoolingDevice(const std::string &config_path) {
+ std::string json_doc;
+ std::map<std::string, CoolingType> cooling_devices_parsed;
+ if (!android::base::ReadFileToString(config_path, &json_doc)) {
+ LOG(ERROR) << "Failed to read JSON config from " << config_path;
+ return cooling_devices_parsed;
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+
+ if (!reader.parse(json_doc, root)) {
+ LOG(ERROR) << "Failed to parse JSON config";
+ return cooling_devices_parsed;
+ }
+
+ Json::Value cooling_devices = root["CoolingDevices"];
+ std::size_t total_parsed = 0;
+ std::set<std::string> cooling_devices_name_parsed;
+
+ for (Json::Value::ArrayIndex i = 0; i < cooling_devices.size(); ++i) {
+ const std::string &name = cooling_devices[i]["Name"].asString();
+ LOG(INFO) << "CoolingDevice[" << i << "]'s Name: " << name;
+ if (name.empty()) {
+ LOG(ERROR) << "Failed to read "
+ << "CoolingDevice[" << i << "]'s Name";
+ cooling_devices_parsed.clear();
+ return cooling_devices_parsed;
+ }
+
+ auto result = cooling_devices_name_parsed.insert(name);
+ if (!result.second) {
+ LOG(ERROR) << "Duplicate CoolingDevice[" << i << "]'s Name";
+ cooling_devices_parsed.clear();
+ return cooling_devices_parsed;
+ }
+
+ std::string cooling_device_type_str = cooling_devices[i]["Type"].asString();
+ LOG(INFO) << "CoolingDevice[" << name << "]'s Type: " << cooling_device_type_str;
+ CoolingType cooling_device_type;
+
+ if (!getTypeFromString(cooling_device_type_str, &cooling_device_type)) {
+ LOG(ERROR) << "Invalid "
+ << "CoolingDevice[" << name << "]'s Type: " << cooling_device_type_str;
+ cooling_devices_parsed.clear();
+ return cooling_devices_parsed;
+ }
+
+ cooling_devices_parsed[name] = cooling_device_type;
+
+ ++total_parsed;
+ }
+
+ LOG(INFO) << total_parsed << " CoolingDevices parsed successfully";
+ return cooling_devices_parsed;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/utils/config_parser.h b/thermal/utils/config_parser.h
new file mode 100644
index 00000000..b12a9fa2
--- /dev/null
+++ b/thermal/utils/config_parser.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONFIG_PARSER_H__
+#define __CONFIG_PARSER_H__
+
+#include <map>
+#include <string>
+
+#include <android/hardware/thermal/2.0/IThermal.h>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_enum_range;
+using ::android::hardware::thermal::V2_0::CoolingType;
+using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType;
+using ::android::hardware::thermal::V2_0::ThrottlingSeverity;
+constexpr size_t kThrottlingSeverityCount = std::distance(
+ hidl_enum_range<ThrottlingSeverity>().begin(), hidl_enum_range<ThrottlingSeverity>().end());
+using ThrottlingArray = std::array<float, static_cast<size_t>(kThrottlingSeverityCount)>;
+
+struct SensorInfo {
+ TemperatureType_2_0 type;
+ ThrottlingArray hot_thresholds;
+ ThrottlingArray cold_thresholds;
+ ThrottlingArray hot_hysteresis;
+ ThrottlingArray cold_hysteresis;
+ float vr_threshold;
+ float multiplier;
+ bool is_monitor;
+};
+
+std::map<std::string, SensorInfo> ParseSensorInfo(const std::string &config_path);
+std::map<std::string, CoolingType> ParseCoolingDevice(const std::string &config_path);
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
+
+#endif // __CONFIG_PARSER_H__
diff --git a/thermal/utils/config_schema.json b/thermal/utils/config_schema.json
new file mode 100644
index 00000000..fd493e14
--- /dev/null
+++ b/thermal/utils/config_schema.json
@@ -0,0 +1,219 @@
+{
+ "definitions":{
+
+ },
+ "$schema":"http://json-schema.org/draft-07/schema#",
+ "$id":"http://example.com/root.json",
+ "type":"object",
+ "title":"The Root Schema",
+ "required":[
+ "Sensors"
+ ],
+ "properties":{
+ "Sensors":{
+ "$id":"#/properties/Sensors",
+ "type":"array",
+ "title":"The Sensors Schema",
+ "items":{
+ "$id":"#/properties/Sensors/items",
+ "type":"object",
+ "title":"The Items Schema",
+ "required":[
+ "Name",
+ "Type",
+ "HotThreshold",
+ "VrThreshold",
+ "Multiplier"
+ ],
+ "properties":{
+ "Name":{
+ "$id":"#/properties/Sensors/items/properties/Name",
+ "type":"string",
+ "title":"The Name Schema",
+ "default":"",
+ "examples":[
+ "cpu0-silver-usr"
+ ],
+ "pattern":"^(.+)$"
+ },
+ "Type":{
+ "$id":"#/properties/Sensors/items/properties/Type",
+ "type":"string",
+ "title":"The Type Schema",
+ "default":"",
+ "examples":[
+ "CPU"
+ ],
+ "pattern":"^(.+)$"
+ },
+ "HotThreshold":{
+ "$id":"#/properties/Sensors/items/properties/HotThreshold",
+ "type":"array",
+ "title":"The hot threshold Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN",
+ "default":"NAN",
+ "maxItems":7,
+ "minItems":7,
+ "items":{
+ "$id":"#/properties/Sensors/items/properties/HotThreshold/items",
+ "type":[
+ "string",
+ "number"
+ ],
+ "title":"The Items Schema",
+ "default":"",
+ "examples":[
+ "NAN",
+ "NAN",
+ "NAN",
+ 95,
+ "NAN",
+ "NAN",
+ 125
+ ],
+ "pattern":"^([-+]?[0-9]*\\.?[0-9]+|NAN)$"
+ }
+ },
+ "HotHysteresis":{
+ "$id":"#/properties/Sensors/items/properties/HotHysteresis",
+ "type":"array",
+ "title":"The hot hysteresis Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN. Throttling status will be cleared HotThreshold - HotHysteresis.",
+ "default":null,
+ "maxItems":7,
+ "minItems":7,
+ "items":{
+ "$id":"#/properties/Sensors/items/properties/HotHysteresis/items",
+ "type":[
+ "number"
+ ],
+ "title":"The Items Schema",
+ "default":0.0,
+ "examples":[
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 1.5,
+ 1.0,
+ 2.0
+ ]
+ }
+ },
+ "ColdThreshold":{
+ "$id":"#/properties/Sensors/items/properties/ColdThreshold",
+ "type":"array",
+ "title":"The cold threshold Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN, default to NAN",
+ "default":null,
+ "maxItems":7,
+ "minItems":7,
+ "items":{
+ "$id":"#/properties/Sensors/items/properties/ColdThreshold/items",
+ "type":"string",
+ "title":"The Items Schema",
+ "default":"NAN",
+ "examples":[
+ "NAN",
+ "NAN",
+ "NAN",
+ "NAN",
+ "NAN",
+ "NAN",
+ "NAN"
+ ],
+ "pattern":"^([-+]?[0-9]*\\.?[0-9]+|NAN)$"
+ }
+ },
+ "ColdHysteresis":{
+ "$id":"#/properties/Sensors/items/properties/ColdHysteresis",
+ "type":"array",
+ "title":"The cold hysteresis Schema, values are thresholds from ThrottlingSeverity::NONE to ThrottlingSeverity::SHUTDOWN. Throttling status will be cleared ColdThreshold + ColdHysteresis.",
+ "default":null,
+ "maxItems":7,
+ "minItems":7,
+ "items":{
+ "$id":"#/properties/Sensors/items/properties/ColdHysteresis/items",
+ "type":[
+ "number"
+ ],
+ "title":"The Items Schema",
+ "default":0.0,
+ "examples":[
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 1.5,
+ 1.0,
+ 2.0
+ ]
+ }
+ },
+ "VrThreshold":{
+ "$id":"#/properties/Sensors/items/properties/VrThreshold",
+ "type":"string",
+ "title":"The Vrthreshold Schema",
+ "default":"",
+ "examples":[
+ "NAN"
+ ],
+ "pattern":"^(.*)$"
+ },
+ "Multiplier":{
+ "$id":"#/properties/Sensors/items/properties/Multiplier",
+ "type":"number",
+ "title":"The Multiplier Schema",
+ "default":0.001,
+ "examples":[
+ 0.001
+ ],
+ "exclusiveMinimum":0.0
+ },
+ "Monitor":{
+ "$id":"#/properties/Sensors/items/properties/Monitor",
+ "type":"boolean",
+ "title":"The Monitor Schema, if the sensor will be monitored and used to trigger throttling event",
+ "default":false,
+ "examples":[
+ true
+ ]
+ }
+ }
+ }
+ },
+ "CoolingDevices":{
+ "$id":"#/properties/CoolingDevices",
+ "type":"array",
+ "title":"The Coolingdevices Schema",
+ "items":{
+ "$id":"#/properties/CoolingDevices/items",
+ "type":"object",
+ "title":"The Items Schema",
+ "required":[
+ "Name",
+ "Type"
+ ],
+ "properties":{
+ "Name":{
+ "$id":"#/properties/CoolingDevices/items/properties/Name",
+ "type":"string",
+ "title":"The Name Schema",
+ "default":"",
+ "examples":[
+ "thermal-cpufreq-0"
+ ],
+ "pattern":"^(.+)$"
+ },
+ "Type":{
+ "$id":"#/properties/CoolingDevices/items/properties/Type",
+ "type":"string",
+ "title":"The Type Schema",
+ "default":"",
+ "examples":[
+ "CPU"
+ ],
+ "pattern":"^(.+)$"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/thermal/utils/cooling_devices.cpp b/thermal/utils/cooling_devices.cpp
new file mode 100644
index 00000000..bb2b953e
--- /dev/null
+++ b/thermal/utils/cooling_devices.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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 <fstream>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "cooling_devices.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+constexpr char kCoolingDeviceCurStateSuffix[] = "cur_state";
+
+bool CoolingDevices::addCoolingDevice(const std::string &cooling_device_name,
+ const std::string &path) {
+ return cooling_device_name_to_path_map_.emplace(cooling_device_name, path).second;
+}
+
+bool CoolingDevices::getCoolingDeviceState(const std::string &cooling_device_name,
+ int *data) const {
+ auto cooling_device_itr = cooling_device_name_to_path_map_.find(cooling_device_name);
+ if (cooling_device_itr == cooling_device_name_to_path_map_.end()) {
+ return false;
+ }
+
+ std::string path = android::base::StringPrintf("%s/%s", cooling_device_itr->second.c_str(),
+ kCoolingDeviceCurStateSuffix);
+
+ std::string cooling_device_data;
+ android::base::ReadFileToString(path, &cooling_device_data);
+ cooling_device_data = android::base::Trim(cooling_device_data);
+
+ if (cooling_device_data.empty()) {
+ LOG(ERROR) << "Could not read " << getCoolingDevicePath(cooling_device_name);
+ return false;
+ }
+
+ *data = std::stoi(cooling_device_data);
+ return true;
+}
+
+std::string CoolingDevices::getCoolingDevicePath(const std::string &cooling_device_name) const {
+ auto cooling_device_itr = cooling_device_name_to_path_map_.find(cooling_device_name);
+ if (cooling_device_itr == cooling_device_name_to_path_map_.end()) {
+ return "";
+ }
+ return cooling_device_itr->second;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/utils/cooling_devices.h b/thermal/utils/cooling_devices.h
new file mode 100644
index 00000000..6d1a3ecd
--- /dev/null
+++ b/thermal/utils/cooling_devices.h
@@ -0,0 +1,68 @@
+/*
+ * 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 __COOLING_DEVICES_H__
+#define __COOLING_DEVICES_H__
+
+#include <string>
+#include <unordered_map>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+class CoolingDevices {
+ public:
+ CoolingDevices() = default;
+ ~CoolingDevices() = default;
+ CoolingDevices(const CoolingDevices &) = delete;
+ void operator=(const CoolingDevices &) = delete;
+
+ // Keep track of the cooling device given the path to its directory.
+ // The cooling device names are defined in: thermal-helper.cpp in
+ // kValidCoolingDeviceNameTypeMap. Path is the cooling device directory.
+ // This is something like: /sys/devices/virtual/thermal/cooling_device_X.
+ // This function assumes that the top level directory at least has a
+ // cur_state file in it, which represents the current state of the cooling
+ // device. Returns true if add succeeds. False otherwise.
+ bool addCoolingDevice(const std::string &cooling_device_name, const std::string &path);
+
+ // If the cooling device name is in the map this will read the cur_state
+ // file within the directory, fill in the data param with the contents of
+ // that file, and return true. Else it will return false and leave data
+ // untouched.
+ bool getCoolingDeviceState(const std::string &cooling_device_name, int *data) const;
+
+ // Get the file path given the cooling device name. If the cooling device
+ // name has not been added to CoolingDevices, this will return an empty
+ // string.
+ std::string getCoolingDevicePath(const std::string &cooling_device_name) const;
+
+ size_t getNumCoolingDevices() const { return cooling_device_name_to_path_map_.size(); }
+
+ private:
+ std::unordered_map<std::string, std::string> cooling_device_name_to_path_map_;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
+
+#endif // __COOLING_DEVICES_H__
diff --git a/thermal/utils/sensors.cpp b/thermal/utils/sensors.cpp
new file mode 100644
index 00000000..34718fa0
--- /dev/null
+++ b/thermal/utils/sensors.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include "sensors.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+std::string Sensors::getSensorPath(const std::string &sensor_name) {
+ if (sensor_names_to_path_map_.find(sensor_name) != sensor_names_to_path_map_.end()) {
+ return sensor_names_to_path_map_.at(sensor_name);
+ }
+ return "";
+}
+
+bool Sensors::addSensor(const std::string &sensor_name, const std::string &path) {
+ return sensor_names_to_path_map_.emplace(sensor_name, path).second;
+}
+
+bool Sensors::readSensorFile(const std::string &sensor_name, std::string *data,
+ std::string *file_path) const {
+ std::string sensor_reading;
+ if (sensor_names_to_path_map_.find(sensor_name) == sensor_names_to_path_map_.end()) {
+ *data = "";
+ *file_path = "";
+ return false;
+ }
+
+ android::base::ReadFileToString(sensor_names_to_path_map_.at(sensor_name), &sensor_reading);
+ // Strip the newline.
+ *data = ::android::base::Trim(sensor_reading);
+ *file_path = sensor_names_to_path_map_.at(sensor_name);
+ return true;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/utils/sensors.h b/thermal/utils/sensors.h
new file mode 100644
index 00000000..4a4307d2
--- /dev/null
+++ b/thermal/utils/sensors.h
@@ -0,0 +1,57 @@
+/*
+ * 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 __SENSORS_H__
+#define __SENSORS_H__
+
+#include <string>
+#include <unordered_map>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+class Sensors {
+ public:
+ Sensors() = default;
+ ~Sensors() = default;
+ Sensors(const Sensors &) = delete;
+ void operator=(const Sensors &) = delete;
+
+ std::string getSensorPath(const std::string &sensor_name);
+ // Returns true if add was successful, false otherwise.
+ bool addSensor(const std::string &sensor_name, const std::string &path);
+ // If sensor is not found in the sensor names to path map, this will set
+ // data and file path to empty and return false. If the sensor is found,
+ // this function will fill in data and file_path accordingly then return
+ // true.
+ bool readSensorFile(const std::string &sensor_name, std::string *data,
+ std::string *file_path) const;
+ size_t getNumSensors() const { return sensor_names_to_path_map_.size(); }
+
+ private:
+ std::unordered_map<std::string, std::string> sensor_names_to_path_map_;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
+
+#endif // __SENSORS_H__
diff --git a/thermal/utils/thermal_watcher.cpp b/thermal/utils/thermal_watcher.cpp
new file mode 100644
index 00000000..54730ba7
--- /dev/null
+++ b/thermal/utils/thermal_watcher.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 <dirent.h>
+#include <sys/inotify.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <chrono>
+#include <fstream>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "thermal_watcher.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using std::chrono_literals::operator""ms;
+
+void ThermalWatcher::registerFilesToWatch(const std::vector<std::string> &files_to_watch) {
+ int flags = O_RDONLY | O_CLOEXEC | O_BINARY;
+
+ for (const auto &path : files_to_watch) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
+ if (fd == -1) {
+ PLOG(ERROR) << "failed to watch: " << path;
+ continue;
+ }
+ watch_to_file_path_map_.emplace(fd.get(), path);
+ fds_.emplace_back(std::move(fd));
+ looper_->addFd(fd.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr);
+ }
+}
+
+bool ThermalWatcher::startWatchingDeviceFiles() {
+ if (cb_) {
+ auto ret = this->run("FileWatcherThread", PRIORITY_HIGHEST);
+ if (ret != NO_ERROR) {
+ LOG(ERROR) << "ThermalWatcherThread start fail";
+ return false;
+ } else {
+ LOG(INFO) << "ThermalWatcherThread started";
+ return true;
+ }
+ }
+ return false;
+}
+
+void ThermalWatcher::wake() {
+ looper_->wake();
+}
+
+bool ThermalWatcher::threadLoop() {
+ LOG(VERBOSE) << "ThermalWatcher polling...";
+ // Max poll timeout 2s
+ static constexpr int kMinPollIntervalMs = 2000;
+ int fd = looper_->pollOnce(kMinPollIntervalMs);
+ std::string path;
+ if (fd > 0) {
+ path = watch_to_file_path_map_.at(fd);
+ }
+ cb_(path, fd);
+ return true;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/utils/thermal_watcher.h b/thermal/utils/thermal_watcher.h
new file mode 100644
index 00000000..0f1decdb
--- /dev/null
+++ b/thermal/utils/thermal_watcher.h
@@ -0,0 +1,90 @@
+/*
+ * 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 __DEVICE_FILE_WATCHER_H__
+#define __DEVICE_FILE_WATCHER_H__
+
+#include <chrono>
+#include <condition_variable>
+#include <future>
+#include <list>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <utils/Looper.h>
+#include <utils/Thread.h>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+using android::base::unique_fd;
+using WatcherCallback = std::function<void(const std::string &path, const int fd)>;
+
+// A helper class for polling thermal files for changes.
+// TODO: change to use uevent (b/119189816)
+class ThermalWatcher : public ::android::Thread {
+ public:
+ ThermalWatcher(const WatcherCallback &cb)
+ : Thread(false), cb_(cb), looper_(new Looper(true)) {}
+ ~ThermalWatcher() = default;
+
+ // Disallow copy and assign.
+ ThermalWatcher(const ThermalWatcher &) = delete;
+ void operator=(const ThermalWatcher &) = delete;
+
+ // Start the thread and return true if it succeeds.
+ bool startWatchingDeviceFiles();
+ // Give the file watcher a list of files to start watching. This helper
+ // class will by default wait for modifications to the file with a looper.
+ // This should be called before starting watcher thread.
+ void registerFilesToWatch(const std::vector<std::string> &files_to_watch);
+ // Wake up the looper thus the worker thread, immediately. This can be called
+ // in any thread.
+ void wake();
+
+ private:
+ // The work done by the watcher thread. This will use inotify to check for
+ // modifications to the files to watch. If any modification is seen this
+ // will callback the registered function with the new data read from the
+ // modified file.
+ bool threadLoop() override;
+
+ // Maps watcher filer descriptor to watched file path.
+ std::unordered_map<int, std::string> watch_to_file_path_map_;
+ std::vector<android::base::unique_fd> fds_;
+
+ // The callback function. Called whenever a modification is seen. The
+ // function passed in should expect a pair of strings in the form
+ // (path, data). Where path is the path of the file that saw a modification
+ // and data was the modification. Callback will return a time for next polling.
+ const WatcherCallback cb_;
+
+ sp<Looper> looper_;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace thermal
+} // namespace hardware
+} // namespace android
+
+#endif // __DEVICE_FILE_WATCHER_H__