Add persist_previous_state option to LED configuration.

Google-Bug-Id:540653227
PiperOrigin-RevId: 976044718
Change-Id: Ibff6ddd18e73007e1b5601a3b163f7e27e8c095c
diff --git a/tlbmc/hal/led/led.cc b/tlbmc/hal/led/led.cc
index 34e3af4..b581780 100644
--- a/tlbmc/hal/led/led.cc
+++ b/tlbmc/hal/led/led.cc
@@ -32,12 +32,21 @@
       default_behavior_->state() == LED_STATE_UNSPECIFIED) {
     return absl::OkStatus();
   }
-  return SetBehavior(*default_behavior_, timestamp);
+  absl::MutexLock lock(mutex_);
+  if (persist_previous_state_ && last_update_time_.has_value()) {
+    return absl::OkStatus();
+  }
+  return SetBehaviorLocked(*default_behavior_, timestamp);
 }
 
 absl::Status Led::SetBehavior(const LedBehavior& behavior,
                               absl::Time timestamp) {
   absl::MutexLock lock(mutex_);
+  return SetBehaviorLocked(behavior, timestamp);
+}
+
+absl::Status Led::SetBehaviorLocked(const LedBehavior& behavior,
+                                    absl::Time timestamp) {
   if (last_update_time_.has_value() && timestamp <= *last_update_time_) {
     return absl::OkStatus();
   }
diff --git a/tlbmc/hal/led/led.h b/tlbmc/hal/led/led.h
index 63c55c6..5e7ec27 100644
--- a/tlbmc/hal/led/led.h
+++ b/tlbmc/hal/led/led.h
@@ -39,6 +39,10 @@
     return default_behavior_;
   }
 
+  // Returns whether the LED persists its previous state when setting default
+  // behavior.
+  bool persist_previous_state() const { return persist_previous_state_; }
+
   // Returns the last update timestamp of the LED.
   std::optional<absl::Time> GetLastUpdateTime() const {
     absl::MutexLock lock(mutex_);
@@ -46,6 +50,8 @@
   }
 
   // Sets the LED to its default behavior.
+  // If `persist_previous_state` is true and the LED has already been updated,
+  // this is a no-op and preserves the previous state.
   absl::Status SetToDefaultBehavior(absl::Time timestamp);
 
   // Applies the given behavior (OFF / ON / BLINK) to the LED.
@@ -71,6 +77,7 @@
         default_behavior_(config.has_default_behavior()
                               ? std::make_optional(config.default_behavior())
                               : std::nullopt),
+        persist_previous_state_(config.persist_previous_state()),
         last_update_time_(std::nullopt) {}
 
   // Hardware-specific implementation of setting the LED behavior.
@@ -80,8 +87,14 @@
 
   const std::string name_;
   const std::optional<LedBehavior> default_behavior_;
+  const bool persist_previous_state_;
   mutable absl::Mutex mutex_;
   std::optional<absl::Time> last_update_time_ ABSL_GUARDED_BY(mutex_);
+
+ private:
+  absl::Status SetBehaviorLocked(const LedBehavior& behavior,
+                                 absl::Time timestamp)
+      ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
 };
 
 }  // namespace milotic_tlbmc
diff --git a/tlbmc/led_config.proto b/tlbmc/led_config.proto
index 458c807..5708bb1 100644
--- a/tlbmc/led_config.proto
+++ b/tlbmc/led_config.proto
@@ -37,9 +37,10 @@
   string name = 1;
   // Hardware output interface (sysfs or GPIO).
   LedOutput output = 2;
-  // Default behavior applied on start / no rules matched
-  // set to LED_STATE_UNSPECIFIED to persist the previous state
+  // Default behavior applied on start / no rules matched.
   LedBehavior default_behavior = 3;
+  // If true, persists the previous state when no rules match.
+  bool persist_previous_state = 4 [default = false];
 }
 
 message LedPresetMember {