health: handle charger in health HAL

Test: manual in charger mode
Bug: 203246116

Change-Id: Id02077d78fa8b1489ea4fba5b10e32f961c4cea0
diff --git a/health/aidl/default/Android.bp b/health/aidl/default/Android.bp
index cb78e77..af4aaf0 100644
--- a/health/aidl/default/Android.bp
+++ b/health/aidl/default/Android.bp
@@ -35,6 +35,52 @@
     ],
 }
 
+// Dependency to libhealthd_charger_ui. No UI in recovery.
+cc_defaults {
+    name: "libhealth_aidl_charger_defaults",
+    shared_libs: [
+        // common
+        "android.hardware.health-V1-ndk",
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libutils",
+
+        // charger UI only
+        "libpng",
+    ],
+
+    static_libs: [
+        // common
+        "libbatterymonitor",
+        "libhealthloop",
+
+        // charger UI only
+        "libhealthd_draw",
+        "libhealthd_charger_ui",
+        "libminui",
+        "libsuspend",
+    ],
+
+    target: {
+        recovery: {
+            // No UI and libsuspend for recovery charger.
+            cflags: [
+                "-DCHARGER_FORCE_NO_UI=1",
+            ],
+            exclude_shared_libs: [
+                "libpng",
+            ],
+            exclude_static_libs: [
+                "libhealthd_draw",
+                "libhealthd_charger_ui",
+                "libminui",
+                "libsuspend",
+            ],
+        },
+    },
+}
+
 // AIDL version of libhealth2impl.
 // A helper library for health HAL implementation.
 // HAL implementations can link to this library and extend the Health class.
@@ -42,12 +88,14 @@
     name: "libhealth_aidl_impl",
     defaults: [
         "libhealth_aidl_common_defaults",
+        "libhealth_aidl_charger_defaults",
     ],
     export_include_dirs: ["include"],
     export_static_lib_headers: [
         "libbatterymonitor",
     ],
     srcs: [
+        "ChargerUtils.cpp",
         "health-convert.cpp",
         "HalHealthLoop.cpp",
         "Health.cpp",
@@ -57,6 +105,13 @@
         ":__subpackages__",
         "//hardware/interfaces/tests/extension/health:__subpackages__",
     ],
+    target: {
+        recovery: {
+            exclude_srcs: [
+                "ChargerUtils.cpp",
+            ],
+        },
+    },
 }
 
 // AIDL version of android.hardware.health@2.1-service.
@@ -68,9 +123,13 @@
     vintf_fragments: ["android.hardware.health-service.example.xml"],
     defaults: [
         "libhealth_aidl_common_defaults",
+        "libhealth_aidl_charger_defaults",
     ],
     static_libs: [
         "libhealth_aidl_impl",
     ],
     srcs: ["main.cpp"],
+    overrides: [
+        "charger",
+    ],
 }
diff --git a/health/aidl/default/ChargerUtils.cpp b/health/aidl/default/ChargerUtils.cpp
new file mode 100644
index 0000000..f8e208b
--- /dev/null
+++ b/health/aidl/default/ChargerUtils.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <health-impl/ChargerUtils.h>
+
+namespace aidl::android::hardware::health::charger {
+
+std::optional<bool> ChargerCallback::ChargerShouldKeepScreenOn() {
+    return service_->ShouldKeepScreenOn();
+}
+
+bool ChargerCallback::ChargerIsOnline() {
+    auto hal_health_loop_sp = hal_health_loop_.lock();
+    if (hal_health_loop_sp == nullptr) {
+        // Assume no charger
+        return false;
+    }
+    return hal_health_loop_sp->charger_online();
+}
+
+void ChargerCallback::ChargerInitConfig(healthd_config* config) {
+    auto hal_health_loop_sp = hal_health_loop_.lock();
+    if (hal_health_loop_sp == nullptr) {
+        return;
+    }
+    return service_->OnInit(hal_health_loop_sp.get(), config);
+}
+
+int ChargerCallback::ChargerRegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
+    auto hal_health_loop_sp = hal_health_loop_.lock();
+    if (hal_health_loop_sp == nullptr) {
+        return -1;
+    }
+    return hal_health_loop_sp->RegisterEvent(fd, func, wakeup);
+}
+
+void ChargerCallback::set_hal_health_loop(const std::weak_ptr<HalHealthLoop>& hal_health_loop) {
+    hal_health_loop_ = std::move(hal_health_loop);
+}
+
+// Implements HalHealthLoopCallback for AIDL charger
+// Adapter of (Charger, Health) ->  HalHealthLoopCallback
+class LoopCallback : public HalHealthLoopCallback {
+  public:
+    LoopCallback(const std::shared_ptr<Health>& service, ChargerCallback* charger_callback)
+        : service_(service), charger_(std::make_unique<::android::Charger>(charger_callback)) {}
+
+    void OnHeartbeat() override {
+        service_->OnHeartbeat();
+        charger_->OnHeartbeat();
+    }
+    // Return the minimum timeout. Negative values are treated as no values.
+    int OnPrepareToWait() override {
+        int timeout1 = service_->OnPrepareToWait();
+        int timeout2 = charger_->OnPrepareToWait();
+
+        if (timeout1 < 0) return timeout2;
+        if (timeout2 < 0) return timeout1;
+        return std::min(timeout1, timeout2);
+    }
+
+    void OnInit(HalHealthLoop*, struct healthd_config* config) override {
+        // Charger::OnInit calls ChargerInitConfig, which calls into the real Health::OnInit.
+        charger_->OnInit(config);
+    }
+
+    void OnHealthInfoChanged(const HealthInfo& health_info) override {
+        charger_->OnHealthInfoChanged(::android::ChargerHealthInfo{
+                .battery_level = health_info.batteryLevel,
+                .battery_status = health_info.batteryStatus,
+        });
+        service_->OnHealthInfoChanged(health_info);
+    }
+
+  private:
+    std::shared_ptr<Health> service_;
+    std::unique_ptr<::android::Charger> charger_;
+};
+
+int ChargerModeMain(const std::shared_ptr<Health>& binder,
+                    const std::shared_ptr<ChargerCallback>& charger_callback) {
+    LOG(INFO) << "Starting charger mode.";
+    //   parent stack ==========================================
+    //   current stack                                         ||
+    //    ||                                                   ||
+    //    V                                                    V
+    // hal_health_loop => loop_callback => charger --(raw)--> charger_callback
+    //    ^----------------(weak)---------------------------------'
+    auto loop_callback = std::make_shared<LoopCallback>(binder, charger_callback.get());
+    auto hal_health_loop = std::make_shared<HalHealthLoop>(binder, std::move(loop_callback));
+    charger_callback->set_hal_health_loop(hal_health_loop);
+    return hal_health_loop->StartLoop();
+}
+
+}  // namespace aidl::android::hardware::health::charger
diff --git a/health/aidl/default/android.hardware.health-service.example.rc b/health/aidl/default/android.hardware.health-service.example.rc
index b393c58..dee3d11 100644
--- a/health/aidl/default/android.hardware.health-service.example.rc
+++ b/health/aidl/default/android.hardware.health-service.example.rc
@@ -4,3 +4,13 @@
     group system
     capabilities WAKE_ALARM BLOCK_SUSPEND
     file /dev/kmsg w
+
+service vendor.charger-default /vendor/bin/hw/android.hardware.health-service.example --charger
+    class charger
+    user system
+    group system wakelock input
+    capabilities SYS_BOOT
+    file /dev/kmsg w
+    file /sys/fs/pstore/console-ramoops-0 r
+    file /sys/fs/pstore/console-ramoops r
+    file /proc/last_kmsg r
diff --git a/health/aidl/default/include/health-impl/ChargerUtils.h b/health/aidl/default/include/health-impl/ChargerUtils.h
new file mode 100644
index 0000000..8f94181
--- /dev/null
+++ b/health/aidl/default/include/health-impl/ChargerUtils.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <optional>
+#include <type_traits>
+
+#include <android/binder_auto_utils.h>
+#include <charger/healthd_mode_charger.h>
+#include <health-impl/Health.h>
+
+#pragma once
+
+namespace aidl::android::hardware::health::charger {
+
+// Implements ChargerHalHealthLoopInterface for AIDL charger
+// Adapter of (Health, HalHealthLoop) -> ChargerHalHealthLoopInterface
+class ChargerCallback : public ::android::ChargerConfigurationInterface {
+  public:
+    ChargerCallback(const std::shared_ptr<Health>& service) : service_(service) {}
+    std::optional<bool> ChargerShouldKeepScreenOn() override;
+    bool ChargerIsOnline() override;
+    void ChargerInitConfig(healthd_config* config) override;
+    int ChargerRegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) override;
+
+    // Override to return true to replace `ro.charger.enable_suspend=true`
+    bool ChargerEnableSuspend() override { return false; }
+
+    void set_hal_health_loop(const std::weak_ptr<HalHealthLoop>& hal_health_loop);
+
+  private:
+    std::shared_ptr<Health> service_;
+    std::weak_ptr<HalHealthLoop> hal_health_loop_;
+};
+
+int ChargerModeMain(const std::shared_ptr<Health>& binder,
+                    const std::shared_ptr<ChargerCallback>& charger_callback);
+
+}  // namespace aidl::android::hardware::health::charger
diff --git a/health/aidl/default/main.cpp b/health/aidl/default/main.cpp
index 014ae34..76c6ba0 100644
--- a/health/aidl/default/main.cpp
+++ b/health/aidl/default/main.cpp
@@ -19,17 +19,46 @@
 #include <health-impl/Health.h>
 #include <health/utils.h>
 
+#ifndef CHARGER_FORCE_NO_UI
+#define CHARGER_FORCE_NO_UI 0
+#endif
+
+#if !CHARGER_FORCE_NO_UI
+#include <health-impl/ChargerUtils.h>
+#endif
+
 using aidl::android::hardware::health::HalHealthLoop;
 using aidl::android::hardware::health::Health;
 
-static constexpr const char* gInstanceName = "default";
+#if !CHARGER_FORCE_NO_UI
+using aidl::android::hardware::health::charger::ChargerCallback;
+using aidl::android::hardware::health::charger::ChargerModeMain;
+#endif
 
-int main() {
-    // TODO(b/203246116): handle charger
+static constexpr const char* gInstanceName = "default";
+static constexpr std::string_view gChargerArg{"--charger"};
+
+int main(int argc, char** argv) {
     // make a default health service
     auto config = std::make_unique<healthd_config>();
     ::android::hardware::health::InitHealthdConfig(config.get());
     auto binder = ndk::SharedRefBase::make<Health>(gInstanceName, std::move(config));
+
+    if (argc >= 2 && argv[1] == gChargerArg) {
+        android::base::InitLogging(argv, &android::base::KernelLogger);
+
+#if !CHARGER_FORCE_NO_UI
+        // If charger shouldn't have UI for your device, simply drop the line below
+        // for your service implementation. This corresponds to
+        // ro.charger.no_ui=true
+        return ChargerModeMain(binder, std::make_shared<ChargerCallback>(binder));
+#endif
+
+        LOG(INFO) << "Starting charger mode without UI.";
+    } else {
+        LOG(INFO) << "Starting health HAL.";
+    }
+
     auto hal_health_loop = std::make_shared<HalHealthLoop>(binder, binder);
     return hal_health_loop->StartLoop();
 }