Updated ledflasher sample to use trait/component model
 - light schema onOff trait
 - dynamically add components based on available LEDs from lights HAL
 - custom trait _ledInfo to save lights HAL LED names

BUG:26798554
Change-Id: I4c1dd0cdb2f3479703e9082fea152a713178e65c
diff --git a/src/common/aidl/brillo/examples/ledflasher/ILEDService.aidl b/src/common/aidl/brillo/examples/ledflasher/ILEDService.aidl
index 5ec561b..84a5092 100644
--- a/src/common/aidl/brillo/examples/ledflasher/ILEDService.aidl
+++ b/src/common/aidl/brillo/examples/ledflasher/ILEDService.aidl
@@ -17,7 +17,10 @@
 package brillo.examples.ledflasher;
 
 interface ILEDService {
+  int getLEDCount();
   void setLED(int ledIndex, boolean on);
   boolean getLED(int ledIndex);
   boolean[] getAllLEDs();
+  String[] getAllLEDNames();
+  void setAllLEDs(boolean on);
 }
diff --git a/src/ledflasher/animation.cpp b/src/ledflasher/animation.cpp
index 34513d2..b76589e 100644
--- a/src/ledflasher/animation.cpp
+++ b/src/ledflasher/animation.cpp
@@ -25,6 +25,10 @@
     android::sp<brillo::examples::ledflasher::ILEDService> led_service,
     const base::TimeDelta& step_duration)
   : led_service_{led_service}, step_duration_{step_duration} {
+  int led_count;
+  led_service_->getLEDCount(&led_count);
+  num_leds = static_cast<size_t>(led_count);
+  step_duration_ /= num_leds;
 }
 
 Animation::~Animation() {
diff --git a/src/ledflasher/animation.h b/src/ledflasher/animation.h
index 190e9a6..30a3b40 100644
--- a/src/ledflasher/animation.h
+++ b/src/ledflasher/animation.h
@@ -39,9 +39,9 @@
       const std::string& type,
       const base::TimeDelta& duration);
 
-  static const size_t num_leds = 4;
 
  protected:
+  size_t num_leds;
   virtual void DoAnimationStep() = 0;
 
   bool GetLED(size_t index) const;
diff --git a/src/ledflasher/animation_marquee.cpp b/src/ledflasher/animation_marquee.cpp
index 77df265..37674e2 100644
--- a/src/ledflasher/animation_marquee.cpp
+++ b/src/ledflasher/animation_marquee.cpp
@@ -20,8 +20,7 @@
     android::sp<brillo::examples::ledflasher::ILEDService> led_service,
     const base::TimeDelta& duration,
     Direction direction)
-  : Animation{led_service, duration / num_leds}, direction_{direction} {
-}
+    : Animation{led_service, duration}, direction_{direction} {}
 
 void AnimationMarquee::DoAnimationStep() {
   SetAllLEDs(false);
diff --git a/src/ledflasher/etc/weaved/traits/ledflasher.json b/src/ledflasher/etc/weaved/traits/ledflasher.json
index ff4ac68..0d5974a 100644
--- a/src/ledflasher/etc/weaved/traits/ledflasher.json
+++ b/src/ledflasher/etc/weaved/traits/ledflasher.json
@@ -1,27 +1,6 @@
 {
   "_ledflasher": {
     "commands": {
-      "set": {
-        "minimalRole": "user",
-        "parameters": {
-          "led": {
-            "type": "integer",
-            "minimum": 1,
-            "maximum": 4
-          },
-          "on": { "type": "boolean" }
-        }
-      },
-      "toggle": {
-        "minimalRole": "user",
-        "parameters": {
-          "led": {
-            "type": "integer",
-            "minimum": 1,
-            "maximum": 4
-          }
-        }
-      },
       "animate": {
         "minimalRole": "user",
         "parameters": {
@@ -41,10 +20,34 @@
       "status": {
         "type": "string",
         "enum": [ "idle", "animating" ]
-      },
-      "leds": {
-        "type": "array",
-        "items": { "type": "boolean" }
+      }
+    }
+  },
+  "onOff": {
+    "commands": {
+      "setConfig": {
+        "minimalRole": "user",
+        "parameters": {
+          "state": {
+            "type": "string",
+            "enum": [ "on", "off" ]
+          }
+        }
+      }
+    },
+    "state": {
+      "state": {
+        "isRequired": true,
+        "type": "string",
+        "enum": [ "on", "off" ]
+      }
+    }
+  },
+  "_ledInfo": {
+    "state": {
+      "name": {
+        "isRequired": true,
+        "type": "string"
       }
     }
   }
diff --git a/src/ledflasher/ledflasher.cpp b/src/ledflasher/ledflasher.cpp
index de495f3..bce3420 100644
--- a/src/ledflasher/ledflasher.cpp
+++ b/src/ledflasher/ledflasher.cpp
@@ -31,11 +31,16 @@
 #include "binder_constants.h"
 #include "brillo/examples/ledflasher/ILEDService.h"
 
+using android::String16;
+
 namespace {
-const char kWeaveComponent[] = "ledflasher";
-const char kWeaveTrait[] = "_ledflasher";
+const char kLedFlasherComponent[] = "ledflasher";
+const char kLedFlasherTrait[] = "_ledflasher";
 const char kBaseComponent[] = "base";
 const char kBaseTrait[] = "base";
+const char kLedComponentPrefix[] = "led";
+const char kOnOffTrait[] = "onOff";
+const char kLedInfoTrait[] = "_ledInfo";
 }  // anonymous namespace
 
 using brillo::examples::ledflasher::ILEDService;
@@ -52,10 +57,10 @@
   void ConnectToLEDService();
   void OnLEDServiceDisconnected();
   void OnPairingInfoChanged(const weaved::Service::PairingInfo* pairing_info);
+  void CreateLedComponentsIfNeeded();
 
   // Particular command handlers for various commands.
-  void OnSet(std::unique_ptr<weaved::Command> command);
-  void OnToggle(std::unique_ptr<weaved::Command> command);
+  void OnSetConfig(size_t led_index, std::unique_ptr<weaved::Command> command);
   void OnAnimate(std::unique_ptr<weaved::Command> command);
   void OnIdentify(std::unique_ptr<weaved::Command> command);
 
@@ -80,6 +85,8 @@
   brillo::BinderWatcher binder_watcher_;
   std::unique_ptr<weaved::Service::Subscription> weave_service_subscription_;
 
+  bool led_components_added_{false};
+
   base::WeakPtrFactory<Daemon> weak_ptr_factory_{this};
   DISALLOW_COPY_AND_ASSIGN(Daemon);
 };
@@ -111,15 +118,12 @@
   if (!weave_service)
     return;
 
-  weave_service->AddComponent(kWeaveComponent, {kWeaveTrait}, nullptr);
+  weave_service->AddComponent(
+      kLedFlasherComponent, {kLedFlasherTrait}, nullptr);
   weave_service->AddCommandHandler(
-      kWeaveComponent, kWeaveTrait, "set",
-      base::Bind(&Daemon::OnSet, weak_ptr_factory_.GetWeakPtr()));
-  weave_service->AddCommandHandler(
-      kWeaveComponent, kWeaveTrait, "toggle",
-      base::Bind(&Daemon::OnToggle, weak_ptr_factory_.GetWeakPtr()));
-  weave_service->AddCommandHandler(
-      kWeaveComponent, kWeaveTrait, "animate",
+      kLedFlasherComponent,
+      kLedFlasherTrait,
+      "animate",
       base::Bind(&Daemon::OnAnimate, weak_ptr_factory_.GetWeakPtr()));
   weave_service->AddCommandHandler(
       kBaseComponent, kBaseTrait, "identify",
@@ -129,6 +133,8 @@
       base::Bind(&Daemon::OnPairingInfoChanged,
                  weak_ptr_factory_.GetWeakPtr()));
 
+  led_components_added_ = false;
+  CreateLedComponentsIfNeeded();
   UpdateDeviceState();
 }
 
@@ -147,61 +153,92 @@
       base::Bind(&Daemon::OnLEDServiceDisconnected,
                  weak_ptr_factory_.GetWeakPtr()));
   led_service_ = android::interface_cast<ILEDService>(binder);
+  CreateLedComponentsIfNeeded();
   UpdateDeviceState();
 }
 
+void Daemon::CreateLedComponentsIfNeeded() {
+  if (led_components_added_ || !led_service_.get())
+    return;
+
+  auto weave_service = weave_service_.lock();
+  if (!weave_service)
+    return;
+
+  std::vector<bool> leds;
+  std::vector<String16> ledNames;
+  if (!led_service_->getAllLEDs(&leds).isOk())
+    return;
+
+  if (!led_service_->getAllLEDNames(&ledNames).isOk())
+    return;
+
+  for (size_t led_index = 0; led_index < leds.size(); led_index++) {
+    std::string led_name = android::String8{ledNames[led_index]}.string();
+    std::string component_name =
+        kLedComponentPrefix + std::to_string(led_index + 1);
+    if (weave_service->AddComponent(
+            component_name, {kOnOffTrait, kLedInfoTrait}, nullptr)) {
+      weave_service->AddCommandHandler(
+          component_name,
+          kOnOffTrait,
+          "setConfig",
+          base::Bind(
+              &Daemon::OnSetConfig, weak_ptr_factory_.GetWeakPtr(), led_index));
+
+      weave_service->SetStateProperty(
+          component_name,
+          kOnOffTrait,
+          "state",
+          *brillo::ToValue(leds[led_index] ? "on" : "off"),
+          nullptr);
+
+      weave_service->SetStateProperty(component_name,
+                                      kLedInfoTrait,
+                                      "name",
+                                      *brillo::ToValue(led_name),
+                                      nullptr);
+    }
+  }
+  led_components_added_ = true;
+}
+
 void Daemon::OnLEDServiceDisconnected() {
   animation_.reset();
   led_service_ = nullptr;
   ConnectToLEDService();
 }
 
-void Daemon::OnSet(std::unique_ptr<weaved::Command> command) {
+void Daemon::OnSetConfig(size_t led_index,
+                         std::unique_ptr<weaved::Command> command) {
   if (!led_service_.get()) {
     command->Abort("_system_error", "ledservice unavailable", nullptr);
     return;
   }
 
-  int index = command->GetParameter<int>("led");
-  if(index < 1 || index > 4) {
-    command->Abort("_invalid_parameter", "Invalid parameter value", nullptr);
-    return;
-  }
-  bool on = command->GetParameter<bool>("on");
-  android::binder::Status status = led_service_->setLED(index - 1, on);
+  auto state = command->GetParameter<std::string>("state");
+  bool on = (state == "on");
+  android::binder::Status status = led_service_->setLED(led_index, on);
   if (!status.isOk()) {
     command->AbortWithCustomError(status, nullptr);
     return;
   }
-  animation_.reset();
-  status_ = "idle";
-  UpdateDeviceState();
-  command->Complete({}, nullptr);
-}
-
-void Daemon::OnToggle(std::unique_ptr<weaved::Command> command) {
-  if (!led_service_.get()) {
-    command->Abort("_system_error", "ledservice unavailable", nullptr);
-    return;
+  if (animation_) {
+    animation_.reset();
+    status_ = "idle";
+    UpdateDeviceState();
   }
 
-  int index = command->GetParameter<int>("led");
-  if(index < 1 || index > 4) {
-    command->Abort("_invalid_parameter", "Invalid parameter value", nullptr);
-    return;
+  auto weave_service = weave_service_.lock();
+  if (weave_service) {
+    std::string component_name =
+        kLedComponentPrefix + std::to_string(led_index + 1);
+    weave_service->SetStateProperty(component_name,
+                                    kOnOffTrait,
+                                    "state",
+                                    *brillo::ToValue(on ? "on" : "off"),
+                                    nullptr);
   }
-  index--;
-  bool on = false;
-  android::binder::Status status = led_service_->getLED(index, &on);
-  if (status.isOk())
-    status = led_service_->setLED(index, !on);
-  if(!status.isOk()) {
-    command->AbortWithCustomError(status, nullptr);
-    return;
-  }
-  animation_.reset();
-  status_ = "idle";
-  UpdateDeviceState();
   command->Complete({}, nullptr);
 }
 
@@ -263,21 +300,15 @@
 }
 
 void Daemon::UpdateDeviceState() {
-  if (!led_service_.get())
-    return;
-
-  std::vector<bool> leds;
-  if (!led_service_->getAllLEDs(&leds).isOk())
-    return;
-
   auto weave_service = weave_service_.lock();
   if (!weave_service)
     return;
 
-  base::DictionaryValue state_change;
-  state_change.SetString("_ledflasher.status", status_);
-  state_change.Set("_ledflasher.leds", brillo::ToValue(leds).release());
-  weave_service->SetStateProperties(kWeaveComponent, state_change, nullptr);
+  weave_service->SetStateProperty(kLedFlasherComponent,
+                                  kLedFlasherTrait,
+                                  "status",
+                                  *brillo::ToValue(status_),
+                                  nullptr);
 }
 
 int main(int argc, char* argv[]) {
diff --git a/src/ledservice/ledservice.cpp b/src/ledservice/ledservice.cpp
index cdc2924..065606a 100644
--- a/src/ledservice/ledservice.cpp
+++ b/src/ledservice/ledservice.cpp
@@ -29,8 +29,15 @@
 #include "brillo/examples/ledflasher/BnLEDService.h"
 #include "ledstatus.h"
 
+using android::String16;
+
 class LEDService : public brillo::examples::ledflasher::BnLEDService {
  public:
+  android::binder::Status getLEDCount(int32_t* count) override {
+    *count = leds_.GetLedCount();
+    return android::binder::Status::ok();
+  }
+
   android::binder::Status setLED(int32_t ledIndex, bool on) override {
     leds_.SetLedStatus(ledIndex, on);
     return android::binder::Status::ok();
@@ -46,6 +53,19 @@
     return android::binder::Status::ok();
   }
 
+  android::binder::Status getAllLEDNames(std::vector<String16>* leds) override {
+    std::vector<std::string> ledNames = leds_.GetNames();
+    for (const std::string& name : ledNames) {
+      leds->push_back(String16{name.c_str()});
+    }
+    return android::binder::Status::ok();
+  }
+
+  android::binder::Status setAllLEDs(bool on) override {
+    leds_.SetAllLeds(on);
+    return android::binder::Status::ok();
+  }
+
  private:
   LedStatus leds_;
 };
diff --git a/src/ledservice/ledstatus.cpp b/src/ledservice/ledstatus.cpp
index 6ce65c6..f62c77c 100644
--- a/src/ledservice/ledstatus.cpp
+++ b/src/ledservice/ledstatus.cpp
@@ -30,7 +30,6 @@
 namespace {
 
 brillo::StreamPtr GetLEDDataStream(size_t index, bool write) {
-  CHECK(index < LedStatus::num_leds);
   std::string led_path;
   if( index == 3 ) {
       led_path = "/sys/class/leds/boot/brightness";
@@ -48,7 +47,7 @@
 }  // anonymous namespace
 
 LedStatus::LedStatus() {
-  // Try to open the lights hal.
+  // Try to open the lights HAL.
   int ret = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, &lights_hal_);
   if (ret) {
     LOG(ERROR) << "Failed to load the lights HAL.";
@@ -57,14 +56,12 @@
   CHECK(lights_hal_);
   LOG(INFO) << "Loaded lights HAL.";
 
-  // If we can open the hal, then we map each number from 1 - 4 to one of the
-  // leds available on the board. Note, multiple numbers could be mapped to the
-  // same led.
+  // If we can open the HAL, then we map each number to one of the LEDs
+  // available on the board.
   const std::initializer_list<const char*> kLogicalLights = {
     LIGHT_ID_BACKLIGHT, LIGHT_ID_KEYBOARD, LIGHT_ID_BUTTONS, LIGHT_ID_BATTERY,
     LIGHT_ID_NOTIFICATIONS, LIGHT_ID_ATTENTION, LIGHT_ID_BLUETOOTH,
     LIGHT_ID_WIFI};
-  size_t led_index = 0;
   for (const char* light_name : kLogicalLights) {
     light_device_t* light_device = nullptr;
     ret = lights_hal_->methods->open(
@@ -74,41 +71,39 @@
     if (ret || !light_device) {
       continue;
     }
-    hal_led_map_.emplace(led_index, light_name);
-    led_index++;
-    if (led_index == num_leds) {
-      // We have already mapped all num_leds LEDs.
-      break;
-    }
+    hal_leds_.push_back(light_name);
+    hal_led_status_.push_back(false);
   }
 
-  // If the size of the map is zero, then the lights hal doesn't have any valid
+  // If the size of the map is zero, then the lights HAL doesn't have any valid
   // leds.
-  if (hal_led_map_.empty()) {
-    LOG(ERROR) << "Unable to open any light devices using the hal.";
+  if (hal_leds_.empty()) {
+    LOG(INFO) << "Unable to open any light devices using the HAL.";
     lights_hal_ = nullptr;
     return;
   }
+}
 
-  // If not all 4 numbers have been mapped, then we map them to the first led
-  // mapped.
-  for (size_t i = hal_led_map_.size(); i < num_leds; i++) {
-    hal_led_map_.emplace(i, hal_led_map_[0]);
-  }
-  hal_led_status_.resize(num_leds);
+size_t LedStatus::GetLedCount() const {
+  return lights_hal_ ? hal_leds_.size() : 1;
 }
 
 std::vector<bool> LedStatus::GetStatus() const {
   if (lights_hal_)
     return hal_led_status_;
 
-  std::vector<bool> leds(num_leds);
-  for (size_t index = 0; index < num_leds; index++)
+  std::vector<bool> leds(GetLedCount());
+  for (size_t index = 0; index < GetLedCount(); index++)
     leds[index] = IsLedOn(index);
   return leds;
 }
 
+std::vector<std::string> LedStatus::GetNames() const {
+  return lights_hal_ ? hal_leds_ : std::vector<std::string>(GetLedCount());
+}
+
 bool LedStatus::IsLedOn(size_t index) const {
+  CHECK(index < GetLedCount());
   if (lights_hal_)
     return hal_led_status_[index];
 
@@ -132,6 +127,7 @@
 }
 
 void LedStatus::SetLedStatus(size_t index, bool on) {
+  CHECK(index < GetLedCount());
   if (lights_hal_) {
     light_state_t state = {};
     state.color = on;
@@ -141,23 +137,24 @@
     state.brightnessMode = BRIGHTNESS_MODE_USER;
     light_device_t* light_device = nullptr;
     int rc = lights_hal_->methods->open(
-        lights_hal_, hal_led_map_[index].c_str(),
+        lights_hal_,
+        hal_leds_[index].c_str(),
         reinterpret_cast<hw_device_t**>(&light_device));
     if (rc) {
-      LOG(ERROR) << "Unable to open " << hal_led_map_[index];
+      LOG(ERROR) << "Unable to open " << hal_leds_[index];
       return;
     }
     CHECK(light_device);
     rc = light_device->set_light(light_device, &state);
     if (rc) {
-      LOG(ERROR) << "Unable to set " << hal_led_map_[index];
+      LOG(ERROR) << "Unable to set " << hal_leds_[index];
       return;
     }
     hal_led_status_[index] = on;
     light_device->common.close(
         reinterpret_cast<hw_device_t*>(light_device));
     if (rc) {
-      LOG(ERROR) << "Unable to close " << hal_led_map_[index];
+      LOG(ERROR) << "Unable to close " << hal_leds_[index];
       return;
     }
     return;
@@ -170,3 +167,8 @@
   std::string brightness = on ? "255" : "0";
   stream->WriteAllBlocking(brightness.data(), brightness.size(), nullptr);
 }
+
+void LedStatus::SetAllLeds(bool on) {
+  for (size_t i = 0; i < GetLedCount(); i++)
+    SetLedStatus(i, on);
+}
diff --git a/src/ledservice/ledstatus.h b/src/ledservice/ledstatus.h
index dc9a472..8e9c26a 100644
--- a/src/ledservice/ledstatus.h
+++ b/src/ledservice/ledstatus.h
@@ -17,7 +17,7 @@
 #ifndef LEDFLASHER_SRC_LEDSERVICE_LEDSTATUS_H_
 #define LEDFLASHER_SRC_LEDSERVICE_LEDSTATUS_H_
 
-#include <map>
+#include <string>
 #include <vector>
 
 #include <base/macros.h>
@@ -28,17 +28,17 @@
   LedStatus();
 
   std::vector<bool> GetStatus() const;
+  std::vector<std::string> GetNames() const;
   bool IsLedOn(size_t index) const;
   void SetLedStatus(size_t index, bool on);
-
-  static const size_t num_leds = 4;
+  void SetAllLeds(bool on);
+  size_t GetLedCount() const;
 
  private:
   const hw_module_t* lights_hal_{nullptr};
-  // Maps the lights available to the hal to numbers 1 - 4. It is possible for
-  // multiple numbers to be mapped to the same led on the board.
-  std::map<size_t, std::string> hal_led_map_;
-  // Since the hal doesn't have a way to track the led status, we maintain that
+  // Contains the names of LEDs in the HAL for each of supported LEDs.
+  std::vector<std::string> hal_leds_;
+  // Since the HAL doesn't have a way to track the led status, we maintain that
   // info here.
   std::vector<bool> hal_led_status_;