ledflasher: Use lights HAL if it is available.

Change the ledflasher to use the lights hal if it is available. If the
lights hal isn't available, then it defaults back to using sysfs.

Remove a CHECK from ledflasher.cpp as a short term fix. Added a TODO
there for a longer term solution.

BUG=24846780
BUG=25498156
TEST=manual test on both the edison and dragonboard

Change-Id: I6b2d57ff61bde7a2c7ec31fc744871240ad9f83d
diff --git a/src/ledflasher/ledflasher.cpp b/src/ledflasher/ledflasher.cpp
index 3c5fed1..4d43df2 100644
--- a/src/ledflasher/ledflasher.cpp
+++ b/src/ledflasher/ledflasher.cpp
@@ -189,7 +189,9 @@
     {"_ledflasher._status", status_},
     {"_ledflasher._leds", leds},
   };
-  CHECK(device_->SetStateProperties(state_change, nullptr));
+  // TODO: Come up with a design for ledflasher.cpp such that this call never
+  // fails.
+  device_->SetStateProperties(state_change, nullptr);
 }
 
 int main(int argc, char* argv[]) {
diff --git a/src/ledservice/Android.mk b/src/ledservice/Android.mk
index e739232..492211c 100644
--- a/src/ledservice/Android.mk
+++ b/src/ledservice/Android.mk
@@ -32,9 +32,11 @@
 	libchrome \
 	libchrome-dbus \
 	libdbus \
+	libhardware \
+	libutils \
 
 LOCAL_C_INCLUDES := external/gtest/include
-LOCAL_CFLAGS := -Wall -Werror -Wno-sign-promo
+LOCAL_CFLAGS := -Wall -Werror -Wno-sign-promo -Wno-missing-field-initializers
 LOCAL_RTTI_FLAG := -frtti
 
 include $(BUILD_EXECUTABLE)
diff --git a/src/ledservice/ledstatus.cpp b/src/ledservice/ledstatus.cpp
index 5fac42c..a51ea2a 100644
--- a/src/ledservice/ledstatus.cpp
+++ b/src/ledservice/ledstatus.cpp
@@ -47,7 +47,61 @@
 
 }  // anonymous namespace
 
+LedStatus::LedStatus() {
+  // 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.";
+    return;
+  }
+  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.
+  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(
+        lights_hal_, light_name,
+        reinterpret_cast<hw_device_t**>(&light_device));
+    // If a given light device couldn't be opened, don't map it to a number.
+    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;
+    }
+  }
+
+  // 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.";
+    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);
+}
+
 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++)
     leds[index] = IsLedOn(index);
@@ -75,6 +129,37 @@
 }
 
 void LedStatus::SetLedStatus(size_t index, bool on) {
+  if (lights_hal_) {
+    light_state_t state = {};
+    state.color = on;
+    state.flashMode = LIGHT_FLASH_NONE;
+    state.flashOnMS = 0;
+    state.flashOffMS = 0;
+    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(),
+        reinterpret_cast<hw_device_t**>(&light_device));
+    if (rc) {
+      LOG(ERROR) << "Unable to open " << hal_led_map_[index];
+      return;
+    }
+    CHECK(light_device);
+    rc = light_device->set_light(light_device, &state);
+    if (rc) {
+      LOG(ERROR) << "Unable to set " << hal_led_map_[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];
+      return;
+    }
+    return;
+  }
+
   brillo::StreamPtr stream = GetLEDDataStream(index, true);
   if (!stream)
     return;
diff --git a/src/ledservice/ledstatus.h b/src/ledservice/ledstatus.h
index f12e958..dc9a472 100644
--- a/src/ledservice/ledstatus.h
+++ b/src/ledservice/ledstatus.h
@@ -17,13 +17,15 @@
 #ifndef LEDFLASHER_SRC_LEDSERVICE_LEDSTATUS_H_
 #define LEDFLASHER_SRC_LEDSERVICE_LEDSTATUS_H_
 
+#include <map>
 #include <vector>
 
 #include <base/macros.h>
+#include <hardware/lights.h>
 
 class LedStatus final {
  public:
-  LedStatus() = default;
+  LedStatus();
 
   std::vector<bool> GetStatus() const;
   bool IsLedOn(size_t index) const;
@@ -32,6 +34,13 @@
   static const size_t num_leds = 4;
 
  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
+  // info here.
+  std::vector<bool> hal_led_status_;
 
   DISALLOW_COPY_AND_ASSIGN(LedStatus);
 };