summaryrefslogtreecommitdiff
path: root/thermal
diff options
context:
space:
mode:
authorVincent Palomares <paillon@google.com>2018-10-04 13:48:14 -0700
committerVincent Palomares <paillon@google.com>2018-10-05 15:36:27 -0700
commit28c812c5095885d9b70e88c8da07650aa7f74105 (patch)
treea02afd093c4fb46df77116dec85329a59f19ce6a /thermal
parent38a12db3544f3fbd3c85dff1129ad1c00d997d30 (diff)
downloadpixel-28c812c5095885d9b70e88c8da07650aa7f74105.tar.gz
thermal: Move common thermal HAL code to shared Pixel library.
Created new 'thermal_structs.h' file to avoid library -> HAL include dependency. Removed battery_thresholds.cpp/h. Bug: 117104007 Test: adb shell dumpsys hardware_properties adb shell su 0 lshal debug android.hardware.thermal@1.1::IThermal/default pts -m PtsThermalHalTestCases vts -m VtsHalThermalV1_0Target vts -m VtsHalThermalV1_1Target Change-Id: Id517eea8e3e9bee1fbc579f176ff8850180ed105 Signed-off-by: Vincent Palomares <paillon@google.com>
Diffstat (limited to 'thermal')
-rw-r--r--thermal/Android.bp25
-rw-r--r--thermal/ThermalConfigParser.cpp211
-rw-r--r--thermal/cooling_devices.cpp79
-rw-r--r--thermal/device_file_watcher.cpp146
-rw-r--r--thermal/include/pixelthermal/ThermalConfigParser.h44
-rw-r--r--thermal/include/pixelthermal/cooling_devices.h75
-rw-r--r--thermal/include/pixelthermal/device_file_watcher.h122
-rw-r--r--thermal/include/pixelthermal/sensors.h59
-rw-r--r--thermal/include/pixelthermal/thermal_structs.h54
-rw-r--r--thermal/sensors.cpp65
10 files changed, 880 insertions, 0 deletions
diff --git a/thermal/Android.bp b/thermal/Android.bp
new file mode 100644
index 00000000..0f2a5823
--- /dev/null
+++ b/thermal/Android.bp
@@ -0,0 +1,25 @@
+cc_library {
+ name: "libpixelthermal",
+ vendor_available: true,
+ export_include_dirs: ["include"],
+
+ srcs: [
+ "cooling_devices.cpp",
+ "device_file_watcher.cpp",
+ "sensors.cpp",
+ "ThermalConfigParser.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libhwbinder",
+ "libutils",
+ "android.hardware.thermal@1.0"
+ ],
+}
diff --git a/thermal/ThermalConfigParser.cpp b/thermal/ThermalConfigParser.cpp
new file mode 100644
index 00000000..04ed25be
--- /dev/null
+++ b/thermal/ThermalConfigParser.cpp
@@ -0,0 +1,211 @@
+/*
+ * 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 <regex>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+
+#include "include/pixelthermal/ThermalConfigParser.h"
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+namespace thermal {
+
+constexpr float kConfigMultiplier = .001;
+
+// For now this just defines the sensor name and thresholds.
+struct SensorConfig {
+ SensorConfig() : sensor_name(""), threshold(0.0), action(""),
+ rule_name("") {}
+ SensorConfig(
+ const std::string& sensor_name, float threshold,
+ const std::string& action, const std::string& rule_name)
+ : sensor_name(sensor_name),
+ threshold(threshold),
+ action(action),
+ rule_name(rule_name) {}
+ std::string sensor_name;
+ float threshold;
+ std::string action;
+ std::string rule_name;
+};
+
+// Assign out if out == NAN or config_threshold < out.
+static void checkAndAssignThreshold(float config_threshold, float* out) {
+ if (std::isnan(*out) || config_threshold < *out) {
+ *out = config_threshold;
+ }
+}
+
+static void assignThresholdsFromConfig(
+ const std::vector<SensorConfig>& configs,
+ const std::map<std::string, SensorInfo>& sensor_name_type_map,
+ ThrottlingThresholds* threshold) {
+ for (const SensorConfig& config : configs) {
+ switch (sensor_name_type_map.at(config.sensor_name).type) {
+ case TemperatureType::CPU:
+ checkAndAssignThreshold(config.threshold, &threshold->cpu);
+ break;
+ case TemperatureType::GPU:
+ checkAndAssignThreshold(config.threshold, &threshold->gpu);
+ break;
+ case TemperatureType::BATTERY:
+ checkAndAssignThreshold(config.threshold, &threshold->battery);
+ break;
+ case TemperatureType::SKIN:
+ // For the skin throttling threshold take the min value from
+ // the skin-monitor rule.
+ if (config.rule_name == "SKIN-MONITOR") {
+ checkAndAssignThreshold(config.threshold, &threshold->ss);
+ }
+ break;
+ default:
+ LOG(ERROR) << "Unknown sensor: " << config.sensor_name;
+ break;
+ }
+ }
+}
+
+static void parseThermalEngineConfig(
+ const std::string& config_path, std::vector<SensorConfig>* configs,
+ std::vector<SensorConfig>* shutdown_configs) {
+ std::string data;
+ if (!android::base::ReadFileToString(config_path, &data)) {
+ LOG(ERROR) << "Error reading config path: " << config_path;
+ return;
+ }
+
+ // Parse out sensor name and thresholds for ss configs.
+ static const std::regex ss_block_regex(
+ R"([.\n]*\[(.+)\]\nalgo_type\s+ss\n(?:.*\n)?sensor\s+)"
+ R"(([\w\d-]+)\n(?:.*\n)?set_point\s+(\d+))");
+ auto block_begin = std::sregex_iterator(
+ data.begin(), data.end(), ss_block_regex);
+ auto block_end = std::sregex_iterator();
+ for (std::sregex_iterator itr = block_begin; itr != block_end; ++itr) {
+ configs->emplace_back(
+ SensorConfig(
+ itr->str(2), std::stoi(itr->str(3)) * kConfigMultiplier, "",
+ itr->str(1)));
+ }
+
+ // Parse out sensor name, thresholds, and action for monitor configs.
+ static const std::regex monitor_block_regex(
+ R"([.\n]*\[(.+)\]\nalgo_type\s+monitor\n(?:.*\n)?sensor\s+)"
+ R"(([\w\d-]+)\n(?:.*\n)?thresholds\s+([^\n]+)\n(?:.*\n)?actions\s+([^\n]+))");
+ block_begin = std::sregex_iterator(
+ data.begin(), data.end(), monitor_block_regex);
+ for (std::sregex_iterator itr = block_begin; itr != block_end; ++itr) {
+ SensorConfig sensor_config;
+
+ sensor_config.rule_name = itr->str(1);
+ sensor_config.sensor_name = itr->str(2);
+ std::string thresholds_str = itr->str(3);
+ std::string actions_str = itr->str(4);
+
+ static const std::regex delim_regex{R"(\s+)"};
+ std::vector<std::string> thresholds{
+ std::sregex_token_iterator(thresholds_str.begin(), thresholds_str.end(), delim_regex, -1),
+ std::sregex_token_iterator()
+ };
+ std::vector<std::string> actions{
+ std::sregex_token_iterator(actions_str.begin(), actions_str.end(), delim_regex, -1),
+ std::sregex_token_iterator()
+ };
+
+ if (thresholds.size() != actions.size()) {
+ LOG(ERROR) << "Unbalanced thresholds ("
+ << thresholds.size()
+ << ") and actions ("
+ << actions.size() << ") in monitor algo for sensor ("
+ << sensor_config.sensor_name
+ << ") of rule (" << sensor_config.rule_name << ")";
+ return;
+ }
+
+ for (std::size_t i = 0; i < thresholds.size(); ++i) {
+ sensor_config.threshold = std::stoi(thresholds[i]) * kConfigMultiplier;
+ sensor_config.action = actions[i];
+
+ // Filter out the shutdown thresholds.
+ if (sensor_config.action.find("shutdown") != std::string::npos) {
+ shutdown_configs->push_back(sensor_config);
+ } else {
+ configs->push_back(sensor_config);
+ }
+ }
+ }
+}
+
+static void dumpSensorConfigs(
+ const std::vector<SensorConfig>& sensor_config_vec,
+ const std::map<std::string, SensorInfo>& typeMap) {
+ for (const auto& sensor_config : sensor_config_vec) {
+ LOG(INFO) << "Sensor name: " << sensor_config.sensor_name
+ << " type: " << android::hardware::thermal::V1_0::toString(
+ typeMap.at(sensor_config.sensor_name).type)
+ << " with threshold: " << sensor_config.threshold
+ << " from rule: " << sensor_config.rule_name
+ << sensor_config.action.empty() ? ""
+ : " sensor config action " + sensor_config.action;
+ }
+}
+
+void InitializeThresholdsFromThermalConfig(
+ const std::string& thermal_config,
+ const std::string& vr_thermal_config,
+ const std::map<std::string, SensorInfo>& typeMap,
+ ThrottlingThresholds *thresholds,
+ ThrottlingThresholds *shutdown_thresholds,
+ ThrottlingThresholds *vr_thresholds) {
+ std::vector<SensorConfig> sensor_configs;
+ std::vector<SensorConfig> shutdown_configs;
+ parseThermalEngineConfig(
+ thermal_config, &sensor_configs, &shutdown_configs);
+
+ assignThresholdsFromConfig(sensor_configs, typeMap, thresholds);
+ assignThresholdsFromConfig(shutdown_configs, typeMap, shutdown_thresholds);
+
+ LOG(INFO) << "Sensor configs";
+ dumpSensorConfigs(sensor_configs, typeMap);
+ LOG(INFO) << "Shutdown configs";
+ dumpSensorConfigs(shutdown_configs, typeMap);
+
+ sensor_configs.clear();
+ shutdown_configs.clear();
+ parseThermalEngineConfig(
+ vr_thermal_config, &sensor_configs, &shutdown_configs);
+
+ LOG(INFO) << "VR Sensor configs";
+ dumpSensorConfigs(sensor_configs, typeMap);
+ LOG(INFO) << "VR Shutdown configs";
+ dumpSensorConfigs(shutdown_configs, typeMap);
+
+ assignThresholdsFromConfig(
+ sensor_configs, typeMap, vr_thresholds);
+}
+
+} // namespace thermal
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
diff --git a/thermal/cooling_devices.cpp b/thermal/cooling_devices.cpp
new file mode 100644
index 00000000..a8babf94
--- /dev/null
+++ b/thermal/cooling_devices.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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 "include/pixelthermal/cooling_devices.h"
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+namespace thermal {
+
+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 thermal
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
diff --git a/thermal/device_file_watcher.cpp b/thermal/device_file_watcher.cpp
new file mode 100644
index 00000000..109ac9a3
--- /dev/null
+++ b/thermal/device_file_watcher.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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 <chrono>
+#include <dirent.h>
+#include <fstream>
+#include <sys/inotify.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "include/pixelthermal/device_file_watcher.h"
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+namespace thermal {
+
+using std::chrono_literals::operator""ms;
+
+// iNotify constants.
+constexpr int kiNotifyEventSize = sizeof(struct inotify_event);
+
+void DeviceFileWatcher::registerFilesToWatch(
+ const std::vector<std::string> files_to_watch) {
+ inotify_fd_ = unique_fd(inotify_init());
+ if (inotify_fd_ < 0) {
+ LOG(ERROR) << "iNotify init error.";
+ return;
+ }
+
+ for (const auto& path : files_to_watch) {
+ std::lock_guard<std::mutex> _lock(watcher_mutex_);
+ watch_to_file_path_map_.emplace(
+ inotify_add_watch(inotify_fd_, path.c_str(), IN_MODIFY),
+ path);
+ }
+}
+
+bool DeviceFileWatcher::startWatchingDeviceFiles() {
+ if (initializedOk()) {
+ // Start the watcher thread.
+ std::packaged_task<void(void)> watcher_task(
+ std::bind(&DeviceFileWatcher::watchFilesForModificationsAndCallback,
+ this));
+ watcher_future_ = watcher_task.get_future();
+ watcher_thread_ = std::thread(std::move(watcher_task));
+ watcher_thread_.detach();
+ return true;
+ }
+ return false;
+}
+
+void DeviceFileWatcher::registerCallback(
+ std::function<void(const std::pair<std::string, std::string>&)> cb) {
+ std::lock_guard<std::mutex> _lock(watcher_mutex_);
+ cb_ = cb;
+}
+
+void DeviceFileWatcher::registerQueueOverflowCallback(
+ std::function<void(void)> cb) {
+ std::lock_guard<std::mutex> _lock(watcher_mutex_);
+ queue_overflow_cb_ = cb;
+}
+
+void DeviceFileWatcher::watchFilesForModificationsAndCallback() {
+ // Increase the priority of the watcher thread so we minimize losing
+ // data.
+ if (int errval = setpriority(PRIO_PROCESS, 0, -5)) {
+ LOG(ERROR) << "Failed to setpriority the watcher thread: " << errval;
+ }
+
+ // We use this while(1) because inotify read() blocks, thus stopping the
+ // watcher thread from just spinning.
+ while (initializedOk()) {
+ char buffer[kiNotifyEventSize];
+ TEMP_FAILURE_RETRY(read(inotify_fd_, buffer, sizeof(buffer)));
+ struct inotify_event* event;
+ event = reinterpret_cast<struct inotify_event*>(buffer);
+
+ // Catch the case where we overflow the queue. In this case we probably
+ // dropped some modifications so we re-read everything and re-notify.
+ if (event->mask & IN_Q_OVERFLOW) {
+ LOG(ERROR) << "iNotify queue overflowed! Modification events "
+ << "dropped.";
+ {
+ std::lock_guard<std::mutex> _lock(watcher_mutex_);
+ if (queue_overflow_cb_) {
+ queue_overflow_cb_();
+ }
+ }
+ }
+
+ if (event->mask & IN_MODIFY) {
+ std::lock_guard<std::mutex> _lock(watcher_mutex_);
+ std::string path = watch_to_file_path_map_.at(event->wd);
+
+ // Bound the read to 2 characters because we should only be reading
+ // integer CPU throttling values.
+ char file_contents[2];
+ std::ifstream infile(path, std::ifstream::in);
+ infile.read(file_contents, 2);
+ std::string data(file_contents);
+ data = android::base::Trim(data);
+
+ if (!data.empty()) {
+ if (cb_) {
+ cb_(std::make_pair(path, data));
+ } else {
+ LOG(ERROR) << "Consumer callback is null!";
+ }
+ }
+ }
+
+ // Rate limit our thread to be able to service changes at around a
+ // second. We don't want to make the system do more work if things fall
+ // off a cliff. We are watching eight files, one per CPU, so 125
+ // milliseconds should be such that if all of them had a modification at
+ // the same time it would take the thread a second to read all of them.
+ std::this_thread::sleep_for(125ms);
+ }
+
+ // We should really not get here until DeviceFileWatcher is destroyed.
+ LOG(ERROR) << "Watcher thread has stopped.";
+}
+
+} // namespace thermal
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
diff --git a/thermal/include/pixelthermal/ThermalConfigParser.h b/thermal/include/pixelthermal/ThermalConfigParser.h
new file mode 100644
index 00000000..89b53497
--- /dev/null
+++ b/thermal/include/pixelthermal/ThermalConfigParser.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __THERMAL_CONFIG_PARSER_H__
+#define __THERMAL_CONFIG_PARSER_H__
+
+#include "thermal_structs.h"
+
+#include <map>
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+namespace thermal {
+
+void InitializeThresholdsFromThermalConfig(
+ const std::string& thermal_config,
+ const std::string& vr_thermal_config,
+ const std::map<std::string, SensorInfo>& typeMap,
+ ThrottlingThresholds *thresholds,
+ ThrottlingThresholds *shutdown_thresholds,
+ ThrottlingThresholds *vr_thresholds);
+
+} // namespace thermal
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
+
+#endif
diff --git a/thermal/include/pixelthermal/cooling_devices.h b/thermal/include/pixelthermal/cooling_devices.h
new file mode 100644
index 00000000..dde39be8
--- /dev/null
+++ b/thermal/include/pixelthermal/cooling_devices.h
@@ -0,0 +1,75 @@
+/*
+ * 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 <unordered_map>
+#include <string>
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+namespace thermal {
+
+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 thermal
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
+
+#endif // __COOLING_DEVICES_H__
+
diff --git a/thermal/include/pixelthermal/device_file_watcher.h b/thermal/include/pixelthermal/device_file_watcher.h
new file mode 100644
index 00000000..3d208545
--- /dev/null
+++ b/thermal/include/pixelthermal/device_file_watcher.h
@@ -0,0 +1,122 @@
+/*
+ * 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 <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "cooling_devices.h"
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+namespace thermal {
+
+using android::base::unique_fd;
+using std::chrono_literals::operator""ms;
+using WatcherCallback =
+ std::function<void(const std::pair<std::string, std::string>&)>;
+
+/* A helper class to watch modifications to files. */
+class DeviceFileWatcher {
+ public:
+ DeviceFileWatcher() = default;
+ ~DeviceFileWatcher() = default;
+
+ // Disallow copy and assign.
+ DeviceFileWatcher(const DeviceFileWatcher&) = delete;
+ void operator=(const DeviceFileWatcher&) = delete;
+
+ // Start the thread and return true if it succeeds. This function will
+ // check that there are files registered to watch and that there is a
+ // callback registered. If either conditions are not met this function will
+ // return false.
+ 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.
+ void registerFilesToWatch(const std::vector<std::string> files_to_watch);
+ // Give the file watcher a callback to be called when processing the data
+ // read from the watched changes list.
+ void registerCallback(std::function<void(
+ const std::pair<std::string, std::string>&)> cb);
+ // Register a function called back if we see a queue overflow event from
+ // inotify.
+ void registerQueueOverflowCallback(std::function<void(void)> cb);
+ // This class is properly initialized if there is a callback that's
+ // registered and if we have files to watch.
+ bool initializedOk() const {
+ return watch_to_file_path_map_.size() && cb_;
+ }
+
+ // Get the status of the threads. watcher_future_ can be invalid here, if
+ // we call getWatcherThreadStatus() without first calling
+ // startWatchingDeviceFiles(). In the case that we haven't spawned any
+ // threads, we'll just return std::future_status::ready which indicates
+ // that the thread is no longer running.
+ std::future_status getWatcherThreadStatus() const {
+ return watcher_future_.valid() ?
+ watcher_future_.wait_for(0ms) : std::future_status::ready;
+ }
+
+ 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.
+ void watchFilesForModificationsAndCallback();
+
+ unique_fd inotify_fd_;
+ // Maps iNotify watch descriptor to cooling device state file path.
+ std::unordered_map<int, std::string> watch_to_file_path_map_;
+
+ // This thread watches the files for modifications and fills out the values
+ // buffer.
+ std::thread watcher_thread_;
+ std::future<void> watcher_future_;
+
+ // 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.
+ WatcherCallback cb_;
+
+ // A callback function to use if we ever see the queue overflow. This is
+ // not required to start watching files. However, if no callback is
+ // registered and we see the queue overflow we will just log that
+ // modification events were dropped and do nothing.
+ std::function<void(void)> queue_overflow_cb_;
+
+ // Variables shared between the watcher and consumer threads.
+ std::mutex watcher_mutex_;
+};
+
+} // namespace thermal
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
+
+#endif // __DEVICE_FILE_WATCHER_H__
diff --git a/thermal/include/pixelthermal/sensors.h b/thermal/include/pixelthermal/sensors.h
new file mode 100644
index 00000000..82582104
--- /dev/null
+++ b/thermal/include/pixelthermal/sensors.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 __SENSORS_H__
+#define __SENSORS_H__
+
+#include <string>
+#include <unordered_map>
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+namespace thermal {
+
+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 thermal
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
+
+#endif // __SENSORS_H__
+
diff --git a/thermal/include/pixelthermal/thermal_structs.h b/thermal/include/pixelthermal/thermal_structs.h
new file mode 100644
index 00000000..5644a698
--- /dev/null
+++ b/thermal/include/pixelthermal/thermal_structs.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __THERMAL_STRUCTS_PARSER_H__
+#define __THERMAL_STRUCTS_PARSER_H__
+
+#include <android/hardware/thermal/1.0/IThermal.h>
+
+#include <cmath>
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+namespace thermal {
+
+using ::android::hardware::thermal::V1_0::TemperatureType;
+
+struct SensorInfo {
+ TemperatureType type;
+ bool is_override;
+ float throttling;
+ float shutdown;
+ float multiplier;
+};
+
+struct ThrottlingThresholds {
+ ThrottlingThresholds() : cpu(NAN), gpu(NAN), ss(NAN), battery(NAN) {}
+ float cpu;
+ float gpu;
+ float ss;
+ float battery;
+};
+
+} // namespace thermal
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android
+
+#endif
diff --git a/thermal/sensors.cpp b/thermal/sensors.cpp
new file mode 100644
index 00000000..c9b39e83
--- /dev/null
+++ b/thermal/sensors.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include "include/pixelthermal/sensors.h"
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+namespace thermal {
+
+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 thermal
+} // namespace pixel
+} // namespace google
+} // namespace hardware
+} // namespace android