diff options
author | Wei Wang <wvw@google.com> | 2019-01-31 14:19:18 -0800 |
---|---|---|
committer | Wei Wang <wvw@google.com> | 2019-01-31 14:20:43 -0800 |
commit | 011ff4a73be5bedea8e46a0c3e1c5d20a9f4f8ff (patch) | |
tree | 2b210ed9c385d7deef05bd4034c669f7f8a65cc7 /thermal | |
parent | d26f9243c54abf22ea07c8827f46790087b9e7e6 (diff) | |
download | pixel-011ff4a73be5bedea8e46a0c3e1c5d20a9f4f8ff.tar.gz |
ThermalHAL: change log level for callback
Test: Build
Change-Id: Icfcfb318c2bfcfa27857ea436f62eed5252c503e
Diffstat (limited to 'thermal')
-rw-r--r-- | thermal/Android.bp | 46 | ||||
-rw-r--r-- | thermal/Thermal.cpp | 378 | ||||
-rw-r--r-- | thermal/Thermal.h | 95 | ||||
-rw-r--r-- | thermal/android.hardware.thermal@2.0-service.pixel.rc | 5 | ||||
-rw-r--r-- | thermal/android.hardware.thermal@2.0-service.pixel.xml | 12 | ||||
-rw-r--r-- | thermal/service.cpp | 60 | ||||
-rw-r--r-- | thermal/thermal-helper.cpp | 498 | ||||
-rw-r--r-- | thermal/thermal-helper.h | 142 | ||||
-rw-r--r-- | thermal/utils/config_parser.cpp | 298 | ||||
-rw-r--r-- | thermal/utils/config_parser.h | 59 | ||||
-rw-r--r-- | thermal/utils/config_schema.json | 219 | ||||
-rw-r--r-- | thermal/utils/cooling_devices.cpp | 73 | ||||
-rw-r--r-- | thermal/utils/cooling_devices.h | 68 | ||||
-rw-r--r-- | thermal/utils/sensors.cpp | 60 | ||||
-rw-r--r-- | thermal/utils/sensors.h | 57 | ||||
-rw-r--r-- | thermal/utils/thermal_watcher.cpp | 88 | ||||
-rw-r--r-- | thermal/utils/thermal_watcher.h | 90 |
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__ |