hidl: Import pixel thermal HAL
authorFrancescodario Cuzzocrea <bosconovic@gmail.com>
Wed, 15 Jul 2020 17:56:58 +0000 (19:56 +0200)
committerFrancescodario Cuzzocrea <bosconovic@gmail.com>
Thu, 23 Jul 2020 16:57:58 +0000 (18:57 +0200)
 * imported from https://android.googlesource.com/platform/hardware/google/pixel/+/refs/tags/android-10.0.0_r40
 * commit 2f5b7f6916cde0633e0276c4ff5fe3d1f84d1d21

Change-Id: I032cb6aa43245e974c14fbae5c030a9e11d7b840

15 files changed:
hidl/thermal/Android.bp [new file with mode: 0644]
hidl/thermal/Thermal.cpp [new file with mode: 0644]
hidl/thermal/Thermal.h [new file with mode: 0644]
hidl/thermal/android.hardware.thermal@2.0-service.pixel.rc [new file with mode: 0644]
hidl/thermal/android.hardware.thermal@2.0-service.pixel.xml [new file with mode: 0644]
hidl/thermal/service.cpp [new file with mode: 0644]
hidl/thermal/thermal-helper.cpp [new file with mode: 0644]
hidl/thermal/thermal-helper.h [new file with mode: 0644]
hidl/thermal/utils/config_parser.cpp [new file with mode: 0644]
hidl/thermal/utils/config_parser.h [new file with mode: 0644]
hidl/thermal/utils/config_schema.json [new file with mode: 0644]
hidl/thermal/utils/thermal_files.cpp [new file with mode: 0644]
hidl/thermal/utils/thermal_files.h [new file with mode: 0644]
hidl/thermal/utils/thermal_watcher.cpp [new file with mode: 0644]
hidl/thermal/utils/thermal_watcher.h [new file with mode: 0644]

diff --git a/hidl/thermal/Android.bp b/hidl/thermal/Android.bp
new file mode 100644 (file)
index 0000000..60d1c70
--- /dev/null
@@ -0,0 +1,47 @@
+cc_binary {
+  name: "android.hardware.thermal@2.0-service.pixel",
+  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/thermal_files.cpp",
+    "utils/thermal_watcher.cpp",
+  ],
+  static_libs: [
+    "libjsoncpp",
+  ],
+  shared_libs: [
+    "libbase",
+    "libcutils",
+    "libhidlbase",
+    "libhidltransport",
+    "libutils",
+    "android.hardware.thermal@1.0",
+    "android.hardware.thermal@2.0",
+  ],
+  cflags: [
+    "-Wall",
+    "-Werror",
+    "-Wextra",
+    "-Wunused",
+  ],
+  tidy: true,
+  tidy_checks: [
+    "android-*",
+    "cert-*",
+    "clang-analyzer-security*",
+  ],
+  tidy_flags: [
+      "-warnings-as-errors=android-*,clang-analyzer-security*,cert-*"
+  ],
+}
diff --git a/hidl/thermal/Thermal.cpp b/hidl/thermal/Thermal.cpp
new file mode 100644 (file)
index 0000000..bd47f6e
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * 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, std::string_view debug_msg) {
+    ThermalStatus status;
+    status.code = ThermalStatusCode::FAILURE;
+    status.debugMessage = debug_msg.data();
+    _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;
+    if (callback == nullptr) {
+        status.code = ThermalStatusCode::FAILURE;
+        status.debugMessage = "Invalid nullptr callback";
+        LOG(ERROR) << status.debugMessage;
+        _hidl_cb(status);
+        return Void();
+    } else {
+        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;
+    if (callback == nullptr) {
+        status.code = ThermalStatusCode::FAILURE;
+        status.debugMessage = "Invalid nullptr callback";
+        LOG(ERROR) << status.debugMessage;
+        _hidl_cb(status);
+        return Void();
+    } else {
+        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/hidl/thermal/Thermal.h b/hidl/thermal/Thermal.h
new file mode 100644 (file)
index 0000000..317936f
--- /dev/null
@@ -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/hidl/thermal/android.hardware.thermal@2.0-service.pixel.rc b/hidl/thermal/android.hardware.thermal@2.0-service.pixel.rc
new file mode 100644 (file)
index 0000000..27fc14a
--- /dev/null
@@ -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/hidl/thermal/android.hardware.thermal@2.0-service.pixel.xml b/hidl/thermal/android.hardware.thermal@2.0-service.pixel.xml
new file mode 100644 (file)
index 0000000..bcd6344
--- /dev/null
@@ -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/hidl/thermal/service.cpp b/hidl/thermal/service.cpp
new file mode 100644 (file)
index 0000000..190269b
--- /dev/null
@@ -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/hidl/thermal/thermal-helper.cpp b/hidl/thermal/thermal-helper.cpp
new file mode 100644 (file)
index 0000000..732d181
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * 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 <iterator>
+#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 std::string_view kCpuOnlineRoot("/sys/devices/system/cpu");
+constexpr std::string_view kThermalSensorsRoot("/sys/devices/virtual/thermal");
+constexpr std::string_view kCpuUsageFile("/proc/stat");
+constexpr std::string_view kCpuOnlineFileSuffix("online");
+constexpr std::string_view kCpuPresentFile("/sys/devices/system/cpu/present");
+constexpr std::string_view kSensorPrefix("thermal_zone");
+constexpr std::string_view kCoolingDevicePrefix("cooling_device");
+constexpr std::string_view kThermalNameFile("type");
+constexpr std::string_view kSensorPolicyFile("policy");
+constexpr std::string_view kSensorTempSuffix("temp");
+constexpr std::string_view kSensorTripPointTempZeroFile("trip_point_0_temp");
+constexpr std::string_view kSensorTripPointHystZeroFile("trip_point_0_hyst");
+constexpr std::string_view kUserSpaceSuffix("user_space");
+constexpr std::string_view kCoolingDeviceCurStateSuffix("cur_state");
+constexpr std::string_view kConfigProperty("vendor.thermal.config");
+constexpr std::string_view kConfigDefaultFileName("thermal_info_config.json");
+
+namespace {
+using android::base::StringPrintf;
+
+/*
+ * Pixel don't offline CPU, so 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.data(), &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(), &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.data(), cpu_name.c_str(),
+                                     kCpuOnlineFileSuffix.data());
+                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;
+            }
+        }
+    }
+}
+
+std::map<std::string, std::string> parseThermalPathMap(std::string_view prefix) {
+    std::map<std::string, std::string> path_map;
+    std::unique_ptr<DIR, int (*)(DIR *)> dir(opendir(kThermalSensorsRoot.data()), closedir);
+    if (!dir) {
+        return path_map;
+    }
+
+    // std::filesystem is not available for vendor yet
+    // see discussion: aosp/894015
+    while (struct dirent *dp = readdir(dir.get())) {
+        if (dp->d_type != DT_DIR) {
+            continue;
+        }
+
+        if (!android::base::StartsWith(dp->d_name, prefix.data())) {
+            continue;
+        }
+
+        std::string path = android::base::StringPrintf("%s/%s/%s", kThermalSensorsRoot.data(),
+                                                       dp->d_name, kThermalNameFile.data());
+        std::string name;
+        if (!android::base::ReadFileToString(path, &name)) {
+            PLOG(ERROR) << "Failed to read from " << path;
+            continue;
+        }
+
+        path_map.emplace(
+                android::base::Trim(name),
+                android::base::StringPrintf("%s/%s", kThermalSensorsRoot.data(), dp->d_name));
+    }
+
+    return path_map;
+}
+
+}  // 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))),
+      cb_(cb),
+      cooling_device_info_map_(ParseCoolingDevice(
+              "/vendor/etc/" +
+              android::base::GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data()))),
+      sensor_info_map_(ParseSensorInfo(
+              "/vendor/etc/" +
+              android::base::GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data()))) {
+    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,
+        };
+    }
+
+    auto tz_map = parseThermalPathMap(kSensorPrefix.data());
+    auto cdev_map = parseThermalPathMap(kCoolingDevicePrefix.data());
+
+    is_initialized_ = initializeSensorMap(tz_map) && initializeCoolingDevices(cdev_map);
+    if (!is_initialized_) {
+        LOG(FATAL) << "ThermalHAL could not be initialized properly.";
+    }
+    std::set<std::string> cdev_paths;
+    std::transform(cooling_device_info_map_.cbegin(), cooling_device_info_map_.cend(),
+                   std::inserter(cdev_paths, cdev_paths.begin()),
+                   [this](std::pair<std::string, const CoolingType> const &cdev) {
+                       std::string path =
+                               cooling_devices_.getThermalFilePath(std::string_view(cdev.first));
+                       if (!path.empty())
+                           return path;
+                       else
+                           return std::string();
+                   });
+    std::set<std::string> monitored_sensors;
+    std::transform(sensor_info_map_.cbegin(), sensor_info_map_.cend(),
+                   std::inserter(monitored_sensors, monitored_sensors.begin()),
+                   [](std::pair<std::string, SensorInfo> const &sensor) {
+                       if (sensor.second.is_monitor)
+                           return sensor.first;
+                       else
+                           return std::string();
+                   });
+
+    thermal_watcher_->registerFilesToWatch(monitored_sensors, cdev_paths, initializeTrip(tz_map));
+
+    // Need start watching after status map initialized
+    is_initialized_ = thermal_watcher_->startWatchingDeviceFiles();
+    if (!is_initialized_) {
+        LOG(FATAL) << "ThermalHAL could not start watching thread properly.";
+    }
+}
+
+bool ThermalHelper::readCoolingDevice(std::string_view cooling_device,
+                                      CoolingDevice_2_0 *out) const {
+    // Read the file.  If the file can't be read temp will be empty string.
+    std::string data;
+
+    if (!cooling_devices_.readThermalFile(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.data());
+
+    out->type = type;
+    out->name = cooling_device.data();
+    out->value = std::stoi(data);
+
+    return true;
+}
+
+bool ThermalHelper::readTemperature(std::string_view 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;
+
+    if (!thermal_sensors_.readThermalFile(sensor_name, &temp)) {
+        LOG(ERROR) << "readTemperature: sensor not found: " << sensor_name;
+        return false;
+    }
+
+    if (temp.empty()) {
+        LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name;
+        return false;
+    }
+
+    const SensorInfo &sensor_info = sensor_info_map_.at(sensor_name.data());
+    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.data();
+    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(
+        std::string_view 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;
+
+    if (!thermal_sensors_.readThermalFile(sensor_name, &temp)) {
+        LOG(ERROR) << "readTemperature: sensor not found: " << sensor_name;
+        return false;
+    }
+
+    if (temp.empty()) {
+        LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name;
+        return false;
+    }
+
+    const auto &sensor_info = sensor_info_map_.at(sensor_name.data());
+    out->type = sensor_info.type;
+    out->name = sensor_name.data();
+    out->value = std::stof(temp) * sensor_info.multiplier;
+
+    std::pair<ThrottlingSeverity, ThrottlingSeverity> status =
+        std::make_pair(ThrottlingSeverity::NONE, ThrottlingSeverity::NONE);
+    // Only update status if the thermal sensor is being monitored
+    if (sensor_info.is_monitor) {
+        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.data()).prev_hot_severity;
+            prev_cold_severity = sensor_status_map_.at(sensor_name.data()).prev_cold_severity;
+        }
+        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);
+    }
+    if (throtting_status) {
+        *throtting_status = status;
+    }
+
+    out->throttlingStatus = static_cast<size_t>(status.first) > static_cast<size_t>(status.second)
+                                ? status.first
+                                : status.second;
+
+    return true;
+}
+
+bool ThermalHelper::readTemperatureThreshold(std::string_view 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.data())) {
+        LOG(ERROR) << __func__ << ": sensor not found: " << sensor_name;
+        return false;
+    }
+
+    const auto &sensor_info = sensor_info_map_.at(sensor_name.data());
+
+    out->type = sensor_info.type;
+    out->name = sensor_name.data();
+    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(const std::map<std::string, std::string> &path_map) {
+    for (const auto &sensor_info_pair : sensor_info_map_) {
+        std::string_view sensor_name = sensor_info_pair.first;
+        if (!path_map.count(sensor_name.data())) {
+            LOG(ERROR) << "Could not find " << sensor_name << " in sysfs";
+            continue;
+        }
+        std::string path = android::base::StringPrintf(
+                "%s/%s", path_map.at(sensor_name.data()).c_str(), kSensorTempSuffix.data());
+        if (!thermal_sensors_.addThermalFile(sensor_name, path)) {
+            LOG(ERROR) << "Could not add " << sensor_name << "to sensors map";
+        }
+    }
+    if (sensor_info_map_.size() == thermal_sensors_.getNumThermalFiles()) {
+        return true;
+    }
+    return false;
+}
+
+bool ThermalHelper::initializeCoolingDevices(const std::map<std::string, std::string> &path_map) {
+    for (const auto &cooling_device_info_pair : cooling_device_info_map_) {
+        std::string_view cooling_device_name = cooling_device_info_pair.first;
+        if (!path_map.count(cooling_device_name.data())) {
+            LOG(ERROR) << "Could not find " << cooling_device_name << " in sysfs";
+            continue;
+        }
+        std::string path = android::base::StringPrintf(
+                "%s/%s", path_map.at(cooling_device_name.data()).c_str(),
+                kCoolingDeviceCurStateSuffix.data());
+        if (!cooling_devices_.addThermalFile(cooling_device_name, path)) {
+            LOG(ERROR) << "Could not add " << cooling_device_name << "to cooling device map";
+            continue;
+        }
+    }
+
+    if (cooling_device_info_map_.size() == cooling_devices_.getNumThermalFiles()) {
+        return true;
+    }
+    return false;
+}
+
+bool ThermalHelper::initializeTrip(const std::map<std::string, std::string> &path_map) {
+    for (const auto &sensor_info : sensor_info_map_) {
+        if (sensor_info.second.is_monitor) {
+            std::string_view sensor_name = sensor_info.first;
+            std::string_view tz_path = path_map.at(sensor_name.data());
+            std::string tz_policy;
+            std::string path = android::base::StringPrintf("%s/%s", (tz_path.data()),
+                                                           kSensorPolicyFile.data());
+            if (!android::base::ReadFileToString(path, &tz_policy)) {
+                LOG(ERROR) << sensor_name << " could not open tz policy file:" << path;
+                return false;
+            }
+            // Check if thermal zone support uevent notify
+            tz_policy = android::base::Trim(tz_policy);
+            if (tz_policy != kUserSpaceSuffix) {
+                LOG(ERROR) << sensor_name << " does not support uevent notify";
+                return false;
+            }
+
+            // Update thermal zone trip point
+            for (size_t i = 0; i < kThrottlingSeverityCount; ++i) {
+                if (!std::isnan(sensor_info.second.hot_thresholds[i]) &&
+                    !std::isnan(sensor_info.second.hot_hysteresis[i])) {
+                    // Update trip_point_0_temp threshold
+                    std::string threshold = std::to_string(static_cast<int>(
+                            sensor_info.second.hot_thresholds[i] / sensor_info.second.multiplier));
+                    path = android::base::StringPrintf("%s/%s", (tz_path.data()),
+                                                       kSensorTripPointTempZeroFile.data());
+                    if (!android::base::WriteStringToFile(threshold, path)) {
+                        LOG(ERROR) << "fail to update " << sensor_name
+                                   << " trip point: " << threshold << path;
+                        return false;
+                    }
+                    // Update trip_point_0_hyst threshold
+                    threshold = std::to_string(static_cast<int>(
+                            sensor_info.second.hot_hysteresis[i] / sensor_info.second.multiplier));
+                    path = android::base::StringPrintf("%s/%s", (tz_path.data()),
+                                                       kSensorTripPointHystZeroFile.data());
+                    if (!android::base::WriteStringToFile(threshold, path)) {
+                        LOG(ERROR) << "fail to update " << sensor_name << "trip hyst" << threshold
+                                   << path;
+                        return false;
+                    }
+                    break;
+                } else if (i == kThrottlingSeverityCount - 1) {
+                    LOG(ERROR) << sensor_name << ":all thresholds are NAN";
+                    return false;
+                }
+            }
+        }
+    }
+    return true;
+}
+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
+// uevent_sensors is the set of sensors which trigger uevent from thermal core driver.
+bool ThermalHelper::thermalWatcherCallbackFunc(const std::set<std::string> &uevent_sensors) {
+    std::vector<Temperature_2_0> temps;
+    bool thermal_triggered = false;
+    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;
+        }
+        // If callback is triggered by uevent, only check the sensors within uevent_sensors
+        if (uevent_sensors.size() != 0 &&
+            uevent_sensors.find(name_status_pair.first) == uevent_sensors.end()) {
+            if (sensor_status.severity != ThrottlingSeverity::NONE) {
+                thermal_triggered = true;
+            }
+            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 (sensor_status.severity != ThrottlingSeverity::NONE) {
+            thermal_triggered = true;
+        }
+    }
+    if (!temps.empty() && cb_) {
+        cb_(temps);
+    }
+
+    return thermal_triggered;
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace thermal
+}  // namespace hardware
+}  // namespace android
diff --git a/hidl/thermal/thermal-helper.h b/hidl/thermal/thermal-helper.h
new file mode 100644 (file)
index 0000000..5eb389e
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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_THERMAL_HELPER_H__
+#define THERMAL_THERMAL_HELPER_H__
+
+#include <array>
+#include <chrono>
+#include <mutex>
+#include <shared_mutex>
+#include <string>
+#include <string_view>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <android/hardware/thermal/2.0/IThermal.h>
+
+#include "utils/config_parser.h"
+#include "utils/thermal_files.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_; }
+
+    // Read the temperature of a single sensor.
+    bool readTemperature(std::string_view sensor_name, Temperature_1_0 *out) const;
+    bool readTemperature(
+            std::string_view sensor_name, Temperature_2_0 *out,
+            std::pair<ThrottlingSeverity, ThrottlingSeverity> *throtting_status = nullptr) const;
+    bool readTemperatureThreshold(std::string_view sensor_name, TemperatureThreshold *out) const;
+    // Read the value of a single cooling device.
+    bool readCoolingDevice(std::string_view 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(const std::map<std::string, std::string> &path_map);
+    bool initializeCoolingDevices(const std::map<std::string, std::string> &path_map);
+    bool initializeTrip(const std::map<std::string, std::string> &path_map);
+
+    // For thermal_watcher_'s polling thread
+    bool thermalWatcherCallbackFunc(const std::set<std::string> &uevent_sensors);
+    // 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_;
+    ThermalFiles thermal_sensors_;
+    ThermalFiles 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_THERMAL_HELPER_H__
diff --git a/hidl/thermal/utils/config_parser.cpp b/hidl/thermal/utils/config_parser.cpp
new file mode 100644 (file)
index 0000000..d74fd49
--- /dev/null
@@ -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(std::string_view 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(std::string_view config_path) {
+    std::string json_doc;
+    std::map<std::string, SensorInfo> sensors_parsed;
+    if (!android::base::ReadFileToString(config_path.data(), &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(std::string_view config_path) {
+    std::string json_doc;
+    std::map<std::string, CoolingType> cooling_devices_parsed;
+    if (!android::base::ReadFileToString(config_path.data(), &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.data());
+        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/hidl/thermal/utils/config_parser.h b/hidl/thermal/utils/config_parser.h
new file mode 100644 (file)
index 0000000..fe43594
--- /dev/null
@@ -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 THERMAL_UTILS_CONFIG_PARSER_H__
+#define THERMAL_UTILS_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(std::string_view config_path);
+std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_path);
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace thermal
+}  // namespace hardware
+}  // namespace android
+
+#endif  // THERMAL_UTILS_CONFIG_PARSER_H__
diff --git a/hidl/thermal/utils/config_schema.json b/hidl/thermal/utils/config_schema.json
new file mode 100644 (file)
index 0000000..fd493e1
--- /dev/null
@@ -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/hidl/thermal/utils/thermal_files.cpp b/hidl/thermal/utils/thermal_files.cpp
new file mode 100644 (file)
index 0000000..c394816
--- /dev/null
@@ -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 <string_view>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include "thermal_files.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+std::string ThermalFiles::getThermalFilePath(std::string_view thermal_name) const {
+    auto sensor_itr = thermal_name_to_path_map_.find(thermal_name.data());
+    if (sensor_itr == thermal_name_to_path_map_.end()) {
+        return "";
+    }
+    return sensor_itr->second;
+}
+
+bool ThermalFiles::addThermalFile(std::string_view thermal_name, std::string_view path) {
+    return thermal_name_to_path_map_.emplace(thermal_name, path).second;
+}
+
+bool ThermalFiles::readThermalFile(std::string_view thermal_name, std::string *data) const {
+    std::string sensor_reading;
+    std::string file_path = getThermalFilePath(std::string_view(thermal_name));
+    *data = "";
+    if (file_path.empty()) {
+        return false;
+    }
+
+    if (!::android::base::ReadFileToString(file_path, &sensor_reading)) {
+        PLOG(WARNING) << "Failed to read sensor: " << thermal_name;
+        return false;
+    }
+
+    // Strip the newline.
+    *data = ::android::base::Trim(sensor_reading);
+    return true;
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace thermal
+}  // namespace hardware
+}  // namespace android
diff --git a/hidl/thermal/utils/thermal_files.h b/hidl/thermal/utils/thermal_files.h
new file mode 100644 (file)
index 0000000..d2c86dd
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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_UTILS_THERMAL_FILES_H_
+#define THERMAL_UTILS_THERMAL_FILES_H_
+
+#include <string>
+#include <unordered_map>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V2_0 {
+namespace implementation {
+
+class ThermalFiles {
+  public:
+    ThermalFiles() = default;
+    ~ThermalFiles() = default;
+    ThermalFiles(const ThermalFiles &) = delete;
+    void operator=(const ThermalFiles &) = delete;
+
+    std::string getThermalFilePath(std::string_view thermal_name) const;
+    // Returns true if add was successful, false otherwise.
+    bool addThermalFile(std::string_view thermal_name, std::string_view path);
+    // If thermal_name is not found in the thermal names to path map, this will set
+    // data to empty and return false. If the thermal_name is found and its content
+    // is read, this function will fill in data accordingly then return true.
+    bool readThermalFile(std::string_view thermal_name, std::string *data) const;
+    size_t getNumThermalFiles() const { return thermal_name_to_path_map_.size(); }
+
+  private:
+    std::unordered_map<std::string, std::string> thermal_name_to_path_map_;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace thermal
+}  // namespace hardware
+}  // namespace android
+
+#endif  // THERMAL_UTILS_THERMAL_FILES_H_
diff --git a/hidl/thermal/utils/thermal_watcher.cpp b/hidl/thermal/utils/thermal_watcher.cpp
new file mode 100644 (file)
index 0000000..19e7628
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * 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 <cutils/uevent.h>
+#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 "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::set<std::string> &sensors_to_watch,
+                                          const std::set<std::string> &cdev_to_watch,
+                                          bool uevent_monitor) {
+    int flags = O_RDONLY | O_CLOEXEC | O_BINARY;
+
+    for (const auto &path : cdev_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);
+    }
+    monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end());
+    if (!uevent_monitor) {
+        is_polling_ = true;
+        return;
+    }
+    uevent_fd_.reset((TEMP_FAILURE_RETRY(uevent_open_socket(64 * 1024, true))));
+    if (uevent_fd_.get() < 0) {
+        LOG(ERROR) << "failed to open uevent socket";
+        is_polling_ = true;
+        return;
+    }
+
+    fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
+
+    looper_->addFd(uevent_fd_.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr);
+    is_polling_ = false;
+    thermal_triggered_ = true;
+}
+
+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::parseUevent(std::set<std::string> *sensors_set) {
+    bool thermal_event = false;
+    constexpr int kUeventMsgLen = 2048;
+    char msg[kUeventMsgLen + 2];
+    char *cp;
+
+    while (true) {
+        int n = uevent_kernel_multicast_recv(uevent_fd_.get(), msg, kUeventMsgLen);
+        if (n <= 0) {
+            if (errno != EAGAIN && errno != EWOULDBLOCK) {
+                LOG(ERROR) << "Error reading from Uevent Fd";
+            }
+            break;
+        }
+
+        if (n >= kUeventMsgLen) {
+            LOG(ERROR) << "Uevent overflowed buffer, discarding";
+            continue;
+        }
+
+        msg[n] = '\0';
+        msg[n + 1] = '\0';
+
+        cp = msg;
+        while (*cp) {
+            std::string uevent = cp;
+            if (!thermal_event) {
+                if (uevent.find("SUBSYSTEM=") == 0) {
+                    if (uevent.find("SUBSYSTEM=thermal") != std::string::npos) {
+                        thermal_event = true;
+                    } else {
+                        break;
+                    }
+                }
+            } else {
+                auto start_pos = uevent.find("NAME=");
+                if (start_pos != std::string::npos) {
+                    start_pos += 5;
+                    std::string name = uevent.substr(start_pos);
+                    if (std::find(monitored_sensors_.begin(), monitored_sensors_.end(), name) !=
+                        monitored_sensors_.end()) {
+                        sensors_set->insert(name);
+                    }
+                    break;
+                }
+            }
+            while (*cp++) {
+            }
+        }
+    }
+}
+
+void ThermalWatcher::wake() {
+    looper_->wake();
+}
+
+bool ThermalWatcher::threadLoop() {
+    LOG(VERBOSE) << "ThermalWatcher polling...";
+    // Polling interval 2s
+    static constexpr int kMinPollIntervalMs = 2000;
+    // Max uevent timeout 5mins
+    static constexpr int kUeventPollTimeoutMs = 300000;
+    int fd;
+    std::set<std::string> sensors;
+
+    int timeout = (thermal_triggered_ || is_polling_) ? kMinPollIntervalMs : kUeventPollTimeoutMs;
+    if (looper_->pollOnce(timeout, &fd, nullptr, nullptr) >= 0) {
+        if (fd != uevent_fd_.get()) {
+            return true;
+        }
+        parseUevent(&sensors);
+        // Ignore cb_ if uevent is not from monitored sensors
+        if (sensors.size() == 0) {
+            return true;
+        }
+    }
+    thermal_triggered_ = cb_(sensors);
+    return true;
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace thermal
+}  // namespace hardware
+}  // namespace android
diff --git a/hidl/thermal/utils/thermal_watcher.h b/hidl/thermal/utils/thermal_watcher.h
new file mode 100644 (file)
index 0000000..45c2a73
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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_UTILS_THERMAL_WATCHER_H_
+#define THERMAL_UTILS_THERMAL_WATCHER_H_
+
+#include <chrono>
+#include <condition_variable>
+#include <future>
+#include <list>
+#include <mutex>
+#include <set>
+#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<bool(const std::set<std::string> &name)>;
+
+// A helper class for monitoring thermal files changes.
+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::set<std::string> &sensors_to_watch,
+                              const std::set<std::string> &cdev_to_watch, bool uevent_monitor);
+    // 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;
+
+    // Parse uevent message
+    void parseUevent(std::set<std::string> *sensor_name);
+
+    // 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 thermal uevent is seen.
+    // The function passed in should expect a string in the form (type).
+    // Where type is the name of the thermal zone that trigger a uevent notification.
+    // Callback will return thermal trigger status for next polling decision.
+    const WatcherCallback cb_;
+
+    sp<Looper> looper_;
+
+    // For uevent socket registration.
+    android::base::unique_fd uevent_fd_;
+    // Sensor list which monitor flag is enabled.
+    std::set<std::string> monitored_sensors_;
+    // Flag to point out if any sensor across the first threshold.
+    bool thermal_triggered_;
+    // Flag to point out if device can support uevent notify.
+    bool is_polling_;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace thermal
+}  // namespace hardware
+}  // namespace android
+
+#endif  // THERMAL_UTILS_THERMAL_WATCHER_H_