rmad: Setup for sending power cable status signal

BUG=b:209387919
TEST=emerge-octopus rmad

Change-Id: I6a1b03d5ccb86c766a4b8f30d13f54c9eff85db8
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/3317267
Reviewed-by: Gene Chang <genechang@google.com>
Commit-Queue: Cheng-Han Yang <chenghan@chromium.org>
Tested-by: Cheng-Han Yang <chenghan@chromium.org>
diff --git a/rmad/dbus_service.cc b/rmad/dbus_service.cc
index 38c4fa8..49af755 100644
--- a/rmad/dbus_service.cc
+++ b/rmad/dbus_service.cc
@@ -465,6 +465,10 @@
       std::make_unique<base::RepeatingCallback<bool(const FinalizeStatus&)>>(
           base::BindRepeating(&DBusService::SendFinalizeProgressSignal,
                               base::Unretained(this))));
+  rmad_interface_->RegisterSignalSender(
+      RmadState::StateCase::kRepairComplete,
+      std::make_unique<base::RepeatingCallback<bool(bool)>>(base::BindRepeating(
+          &DBusService::SendPowerCableStateSignal, base::Unretained(this))));
 }
 
 bool DBusService::HandleIsRmaRequiredMethod() {
diff --git a/rmad/dbus_service_test.cc b/rmad/dbus_service_test.cc
index bef2381..9b7c0a9 100644
--- a/rmad/dbus_service_test.cc
+++ b/rmad/dbus_service_test.cc
@@ -199,7 +199,7 @@
     return dbus_service_->SendHardwareWriteProtectionStateSignal(enabled);
   }
 
-  bool SignalPowerCable(bool plugged_in) {
+  bool SignalPowerCableState(bool plugged_in) {
     return dbus_service_->SendPowerCableStateSignal(plugged_in);
   }
 
@@ -500,7 +500,7 @@
   EXPECT_TRUE(SignalHardwareWriteProtection(true));
 }
 
-TEST_F(DBusServiceTest, SignalPowerCable) {
+TEST_F(DBusServiceTest, SignalPowerCableState) {
   SetUpDBusService(true, RoVerificationStatus::NOT_TRIGGERED, true);
   EXPECT_CALL(*GetMockExportedObject(), SendSignal(_))
       .WillRepeatedly(Invoke([](dbus::Signal* signal) {
@@ -511,7 +511,7 @@
         EXPECT_TRUE(reader.PopBool(&plugged_in));
         EXPECT_TRUE(plugged_in);
       }));
-  EXPECT_TRUE(SignalPowerCable(true));
+  EXPECT_TRUE(SignalPowerCableState(true));
 }
 
 }  // namespace rmad
diff --git a/rmad/state_handler/repair_complete_state_handler.cc b/rmad/state_handler/repair_complete_state_handler.cc
index 438ebb1..1dd4fd7 100644
--- a/rmad/state_handler/repair_complete_state_handler.cc
+++ b/rmad/state_handler/repair_complete_state_handler.cc
@@ -56,9 +56,16 @@
   if (!state_.has_repair_complete() && !RetrieveState()) {
     state_.set_allocated_repair_complete(new RepairCompleteState);
   }
+  power_cable_timer_.Start(
+      FROM_HERE, kReportPowerCableInterval, this,
+      &RepairCompleteStateHandler::SendPowerCableStateSignal);
   return RMAD_ERROR_OK;
 }
 
+void RepairCompleteStateHandler::CleanUpState() {
+  power_cable_timer_.Stop();
+}
+
 BaseStateHandler::GetNextStateCaseReply
 RepairCompleteStateHandler::GetNextStateCase(const RmadState& state) {
   if (!state.has_repair_complete()) {
@@ -102,20 +109,20 @@
     switch (state.repair_complete().shutdown()) {
       case RepairCompleteState::RMAD_REPAIR_COMPLETE_REBOOT:
         // Wait for a while before reboot.
-        timer_.Start(FROM_HERE, kShutdownDelay, this,
-                     &RepairCompleteStateHandler::Reboot);
+        action_timer_.Start(FROM_HERE, kShutdownDelay, this,
+                            &RepairCompleteStateHandler::Reboot);
         return {.error = RMAD_ERROR_EXPECT_REBOOT,
                 .state_case = GetStateCase()};
       case RepairCompleteState::RMAD_REPAIR_COMPLETE_SHUTDOWN:
         // Wait for a while before shutdown.
-        timer_.Start(FROM_HERE, kShutdownDelay, this,
-                     &RepairCompleteStateHandler::Shutdown);
+        action_timer_.Start(FROM_HERE, kShutdownDelay, this,
+                            &RepairCompleteStateHandler::Shutdown);
         return {.error = RMAD_ERROR_EXPECT_SHUTDOWN,
                 .state_case = GetStateCase()};
       case RepairCompleteState::RMAD_REPAIR_COMPLETE_BATTERY_CUTOFF:
         // Wait for a while before cutoff.
-        timer_.Start(FROM_HERE, kShutdownDelay, this,
-                     &RepairCompleteStateHandler::Cutoff);
+        action_timer_.Start(FROM_HERE, kShutdownDelay, this,
+                            &RepairCompleteStateHandler::Cutoff);
         return {.error = RMAD_ERROR_EXPECT_SHUTDOWN,
                 .state_case = GetStateCase()};
       default:
@@ -134,8 +141,8 @@
       LOG(ERROR) << "Failed to request powerwash";
       return NextStateCaseWrapper(RMAD_ERROR_POWERWASH_FAILED);
     }
-    timer_.Start(FROM_HERE, kShutdownDelay, this,
-                 &RepairCompleteStateHandler::Reboot);
+    action_timer_.Start(FROM_HERE, kShutdownDelay, this,
+                        &RepairCompleteStateHandler::Reboot);
     return NextStateCaseWrapper(GetStateCase(), RMAD_ERROR_EXPECT_REBOOT,
                                 AdditionalActivity::REBOOT);
   }
@@ -170,4 +177,10 @@
   }
 }
 
+void RepairCompleteStateHandler::SendPowerCableStateSignal() {
+  // TODO(chenghan): This is currently fake.
+  CHECK(power_cable_signal_sender_);
+  power_cable_signal_sender_->Run(true);
+}
+
 }  // namespace rmad
diff --git a/rmad/state_handler/repair_complete_state_handler.h b/rmad/state_handler/repair_complete_state_handler.h
index b34d30f..3ebfb0f 100644
--- a/rmad/state_handler/repair_complete_state_handler.h
+++ b/rmad/state_handler/repair_complete_state_handler.h
@@ -8,6 +8,7 @@
 #include "rmad/state_handler/base_state_handler.h"
 
 #include <memory>
+#include <utility>
 
 #include <base/files/file_path.h>
 #include <base/timer/timer.h>
@@ -22,6 +23,9 @@
   // Wait for 5 seconds before reboot/shutdown/cutoff.
   static constexpr base::TimeDelta kShutdownDelay =
       base::TimeDelta::FromSeconds(5);
+  // Report power cable state every second.
+  static constexpr base::TimeDelta kReportPowerCableInterval =
+      base::TimeDelta::FromSeconds(1);
 
   explicit RepairCompleteStateHandler(scoped_refptr<JsonStore> json_store);
   // Used to inject |working_dir_path_| and mocked |power_manager_client_|,
@@ -35,7 +39,13 @@
   ASSIGN_STATE(RmadState::StateCase::kRepairComplete);
   SET_UNREPEATABLE;
 
+  void RegisterSignalSender(
+      std::unique_ptr<base::RepeatingCallback<bool(bool)>> callback) override {
+    power_cable_signal_sender_ = std::move(callback);
+  }
+
   RmadErrorCode InitializeState() override;
+  void CleanUpState() override;
   GetNextStateCaseReply GetNextStateCase(const RmadState& state) override;
 
  protected:
@@ -45,11 +55,15 @@
   void Reboot();
   void Shutdown();
   void Cutoff();
+  void SendPowerCableStateSignal();
 
   base::FilePath working_dir_path_;
   std::unique_ptr<PowerManagerClient> power_manager_client_;
   std::unique_ptr<MetricsUtils> metrics_utils_;
-  base::OneShotTimer timer_;
+  std::unique_ptr<base::RepeatingCallback<bool(bool)>>
+      power_cable_signal_sender_;
+  base::OneShotTimer action_timer_;
+  base::RepeatingTimer power_cable_timer_;
 };
 
 namespace fake {
diff --git a/rmad/state_handler/repair_complete_state_handler_test.cc b/rmad/state_handler/repair_complete_state_handler_test.cc
index e1945dd..c08e186 100644
--- a/rmad/state_handler/repair_complete_state_handler_test.cc
+++ b/rmad/state_handler/repair_complete_state_handler_test.cc
@@ -29,6 +29,12 @@
 
 class RepairCompleteStateHandlerTest : public StateHandlerTest {
  public:
+  // Helper class to mock the callback function to send signal.
+  class SignalSender {
+   public:
+    MOCK_METHOD(bool, SendPowerCableStateSignal, (bool), (const));
+  };
+
   scoped_refptr<RepairCompleteStateHandler> CreateStateHandler(
       bool* reboot_called,
       bool* shutdown_called,
@@ -54,9 +60,17 @@
     ON_CALL(*mock_metrics_utils, Record(_, _))
         .WillByDefault(DoAll(Assign(metrics_called, true),
                              Return(record_metrics_success)));
-    return base::MakeRefCounted<RepairCompleteStateHandler>(
+    auto handler = base::MakeRefCounted<RepairCompleteStateHandler>(
         json_store_, GetTempDirPath(), std::move(mock_power_manager_client),
         std::move(mock_metrics_utils));
+    auto callback = std::make_unique<base::RepeatingCallback<bool(bool)>>(
+        base::BindRepeating(&SignalSender::SendPowerCableStateSignal,
+                            base::Unretained(&signal_sender_)));
+    handler->RegisterSignalSender(std::move(callback));
+
+    ON_CALL(signal_sender_, SendPowerCableStateSignal(_))
+        .WillByDefault(Return(true));
+    return handler;
   }
 
   base::FilePath GetPowerwashRequestFilePath() const {
@@ -76,6 +90,8 @@
   }
 
  protected:
+  NiceMock<SignalSender> signal_sender_;
+
   // Variables for TaskRunner.
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
@@ -533,6 +549,9 @@
   EXPECT_FALSE(base::PathExists(GetPowerwashRequestFilePath()));
   EXPECT_FALSE(base::PathExists(GetCutoffRequestFilePath()));
 
+  // Stop the polling timer.
+  handler->CleanUpState();
+
   // Check that the power cycle won't be called if the state file cannot be
   // cleared.
   task_environment_.FastForwardUntilNoTasksRemain();