diff --git a/device.mk b/device.mk
index 168daff0bac119208dd344d199e33233b07dd0bc..d1e13dfc3969e14bc6ff2150f9f0f6f9ad2cfdd0 100644
--- a/device.mk
+++ b/device.mk
@@ -228,9 +228,7 @@ PRODUCT_COPY_FILES += \
 
 # Lights
 PRODUCT_PACKAGES += \
-    android.hardware.light@2.0-impl \
-    android.hardware.light@2.0-service \
-    lights.msm8953
+    android.hardware.light@2.0-service.onclite
 
 # LiveDisplay
 PRODUCT_PACKAGES += \
diff --git a/light/Android.bp b/light/Android.bp
new file mode 100644
index 0000000000000000000000000000000000000000..ee181d69e10068f4d8e785447e5ff7a459a8d45c
--- /dev/null
+++ b/light/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2018 The LineageOS 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.
+
+cc_binary {
+    relative_install_path: "hw",
+    defaults: ["hidl_defaults"],
+    name: "android.hardware.light@2.0-service.onclite",
+    proprietary: true,
+    init_rc: ["android.hardware.light@2.0-service.onclite.rc"],
+    srcs: ["service.cpp", "Light.cpp"],
+    shared_libs: [
+        "libhardware",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libhwbinder",
+        "libutils",
+        "android.hardware.light@2.0",
+    ],
+}
diff --git a/light/Light.cpp b/light/Light.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b8fbb693126633d3d2fe03dd9ac0dae86e479fdc
--- /dev/null
+++ b/light/Light.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2018 The LineageOS 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.
+ */
+
+#define LOG_TAG "LightService"
+
+#include <log/log.h>
+
+#include "Light.h"
+
+#include <fstream>
+
+#define LEDS            "/sys/class/leds/"
+
+#define LCD_LED         LEDS "lcd-backlight/"
+#define WHITE_LED       LEDS "red/"
+
+#define BLINK           "blink"
+#define BRIGHTNESS      "brightness"
+#define MAX_BRIGHTNESS  "max_brightness"
+#define DUTY_PCTS       "duty_pcts"
+#define PAUSE_HI        "pause_hi"
+#define PAUSE_LO        "pause_lo"
+#define RAMP_STEP_MS    "ramp_step_ms"
+#define START_IDX       "start_idx"
+
+/*
+ * 15 duty percent steps.
+ */
+#define RAMP_STEPS 15
+/*
+ * Each step will stay on for 150ms by default.
+ */
+#define RAMP_STEP_DURATION 150
+/*
+ * Each value represents a duty percent (0 - 100) for the led pwm.
+ */
+static int32_t BRIGHTNESS_RAMP[RAMP_STEPS] = {0, 12, 25, 37, 50, 72, 85, 100, 85, 72, 50, 37, 25, 12, 0};
+
+namespace {
+/*
+ * Write value to path and close file.
+ */
+static void set(std::string path, std::string value) {
+    std::ofstream file(path);
+
+    if (!file.is_open()) {
+        ALOGW("failed to write %s to %s", value.c_str(), path.c_str());
+        return;
+    }
+
+    file << value;
+}
+
+static void set(std::string path, int value) {
+    set(path, std::to_string(value));
+}
+
+static int get(std::string path) {
+    std::ifstream file(path);
+    int value;
+
+    if (!file.is_open()) {
+        ALOGW("failed to read from %s", path.c_str());
+        return 0;
+    }
+
+    file >> value;
+    return value;
+}
+
+static int getMaxBrightness(std::string path) {
+    int value = get(path);
+    ALOGW("Got max brightness %d", value);
+    return value;
+}
+
+static uint32_t getBrightness(const LightState& state) {
+    uint32_t alpha, red, green, blue;
+
+    /*
+     * Extract brightness from AARRGGBB.
+     */
+    alpha = (state.color >> 24) & 0xFF;
+    red = (state.color >> 16) & 0xFF;
+    green = (state.color >> 8) & 0xFF;
+    blue = state.color & 0xFF;
+
+    /*
+     * Scale RGB brightness if Alpha brightness is not 0xFF.
+     */
+    if (alpha != 0xFF) {
+        red = red * alpha / 0xFF;
+        green = green * alpha / 0xFF;
+        blue = blue * alpha / 0xFF;
+    }
+
+    return (77 * red + 150 * green + 29 * blue) >> 8;
+}
+
+static inline uint32_t scaleBrightness(uint32_t brightness, uint32_t maxBrightness) {
+    return brightness * maxBrightness / 0xFF;
+}
+
+static inline uint32_t getScaledBrightness(const LightState& state, uint32_t maxBrightness) {
+    return scaleBrightness(getBrightness(state), maxBrightness);
+}
+
+static void handleBacklight(const LightState& state) {
+    uint32_t brightness = getScaledBrightness(state, getMaxBrightness(LCD_LED MAX_BRIGHTNESS));
+    set(LCD_LED BRIGHTNESS, brightness);
+}
+
+/*
+ * Scale each value of the brightness ramp according to the
+ * brightness of the color.
+ */
+static std::string getScaledRamp(uint32_t brightness) {
+    std::string ramp, pad;
+
+    for (auto const& step : BRIGHTNESS_RAMP) {
+        ramp += pad + std::to_string(step * brightness / 0xFF);
+        pad = ",";
+    }
+
+    return ramp;
+}
+
+static void handleNotification(const LightState& state) {
+    uint32_t whiteBrightness = getScaledBrightness(state, getMaxBrightness(WHITE_LED MAX_BRIGHTNESS));
+
+    /* Disable blinking */
+    set(WHITE_LED BLINK, 0);
+
+    if (state.flashMode == Flash::TIMED) {
+        /*
+         * If the flashOnMs duration is not long enough to fit ramping up
+         * and down at the default step duration, step duration is modified
+         * to fit.
+         */
+        int32_t stepDuration = RAMP_STEP_DURATION;
+        int32_t pauseHi = state.flashOnMs - (stepDuration * RAMP_STEPS * 2);
+        int32_t pauseLo = state.flashOffMs;
+
+        if (pauseHi < 0) {
+            //stepDuration = state.flashOnMs / (RAMP_STEPS * 2);
+            pauseHi = 0;
+        }
+
+        /* White */
+        set(WHITE_LED START_IDX, 0 * RAMP_STEPS);
+        set(WHITE_LED DUTY_PCTS, getScaledRamp(whiteBrightness));
+        set(WHITE_LED PAUSE_LO, pauseLo);
+        set(WHITE_LED PAUSE_HI, pauseHi);
+        set(WHITE_LED RAMP_STEP_MS, stepDuration);
+
+        /* Enable blinking */
+        set(WHITE_LED BLINK, 1);
+    } else {
+        set(WHITE_LED BRIGHTNESS, whiteBrightness);
+    }
+}
+
+static inline bool isLit(const LightState& state) {
+    return state.color & 0x00ffffff;
+}
+
+/* Keep sorted in the order of importance. */
+static std::vector<LightBackend> backends = {
+    { Type::ATTENTION, handleNotification },
+    { Type::NOTIFICATIONS, handleNotification },
+    { Type::BATTERY, handleNotification },
+    { Type::BACKLIGHT, handleBacklight },
+};
+
+}  // anonymous namespace
+
+namespace android {
+namespace hardware {
+namespace light {
+namespace V2_0 {
+namespace implementation {
+
+Return<Status> Light::setLight(Type type, const LightState& state) {
+    LightStateHandler handler;
+    bool handled = false;
+
+    /* Lock global mutex until light state is updated. */
+    std::lock_guard<std::mutex> lock(globalLock);
+
+    /* Update the cached state value for the current type. */
+    for (LightBackend& backend : backends) {
+        if (backend.type == type) {
+            backend.state = state;
+            handler = backend.handler;
+        }
+    }
+
+    /* If no handler has been found, then the type is not supported. */
+    if (!handler) {
+        return Status::LIGHT_NOT_SUPPORTED;
+    }
+
+    /* Light up the type with the highest priority that matches the current handler. */
+    for (LightBackend& backend : backends) {
+        if (handler == backend.handler && isLit(backend.state)) {
+            handler(backend.state);
+            handled = true;
+            break;
+        }
+    }
+
+    /* If no type has been lit up, then turn off the hardware. */
+    if (!handled) {
+        handler(state);
+    }
+
+    return Status::SUCCESS;
+}
+
+Return<void> Light::getSupportedTypes(getSupportedTypes_cb _hidl_cb) {
+    std::vector<Type> types;
+
+    for (const LightBackend& backend : backends) {
+        types.push_back(backend.type);
+    }
+
+    _hidl_cb(types);
+
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace light
+}  // namespace hardware
+}  // namespace android
\ No newline at end of file
diff --git a/light/Light.h b/light/Light.h
new file mode 100644
index 0000000000000000000000000000000000000000..6196e541fea118128ba5fefe0fa8b5523c960ef9
--- /dev/null
+++ b/light/Light.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The LineageOS 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.
+ */
+
+#ifndef ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H
+#define ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H
+
+#include <android/hardware/light/2.0/ILight.h>
+#include <hardware/lights.h>
+#include <hidl/Status.h>
+#include <map>
+#include <mutex>
+#include <vector>
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::light::V2_0::Flash;
+using ::android::hardware::light::V2_0::ILight;
+using ::android::hardware::light::V2_0::LightState;
+using ::android::hardware::light::V2_0::Status;
+using ::android::hardware::light::V2_0::Type;
+
+typedef void (*LightStateHandler)(const LightState&);
+
+struct LightBackend {
+    Type type;
+    LightState state;
+    LightStateHandler handler;
+
+    LightBackend(Type type, LightStateHandler handler) : type(type), handler(handler) {
+        this->state.color = 0xff000000;
+    }
+};
+
+namespace android {
+namespace hardware {
+namespace light {
+namespace V2_0 {
+namespace implementation {
+
+class Light : public ILight {
+  public:
+    Return<Status> setLight(Type type, const LightState& state) override;
+    Return<void> getSupportedTypes(getSupportedTypes_cb _hidl_cb) override;
+
+  private:
+    std::mutex globalLock;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace light
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H
\ No newline at end of file
diff --git a/light/android.hardware.light@2.0-service.onclite.rc b/light/android.hardware.light@2.0-service.onclite.rc
new file mode 100644
index 0000000000000000000000000000000000000000..deba2f3fadd25dba831e24b03cce506feb8236d0
--- /dev/null
+++ b/light/android.hardware.light@2.0-service.onclite.rc
@@ -0,0 +1,22 @@
+on boot
+    # leds light
+    chown system system /sys/class/leds/red/brightness
+    chown system system /sys/class/leds/red/blink
+    chown system system /sys/class/leds/red/duty_pcts
+    chown system system /sys/class/leds/red/pause_hi
+    chown system system /sys/class/leds/red/pause_lo
+    chown system system /sys/class/leds/red/ramp_step_ms
+    chown system system /sys/class/leds/red/start_idx
+
+    chmod 660 /sys/class/leds/red/blink
+    chmod 660 /sys/class/leds/red/duty_pcts
+    chmod 660 /sys/class/leds/red/pause_hi
+    chmod 660 /sys/class/leds/red/pause_lo
+    chmod 660 /sys/class/leds/red/ramp_step_ms
+    chmod 660 /sys/class/leds/red/start_idx
+
+service light-hal-2-0 /vendor/bin/hw/android.hardware.light@2.0-service.onclite
+    class hal
+    user system
+    group system
+    shutdown critical
diff --git a/light/service.cpp b/light/service.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..63b522ecbc40e7ddd07ea2a4fcbd87c80a65a252
--- /dev/null
+++ b/light/service.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The LineageOS 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.
+ */
+
+#define LOG_TAG "android.hardware.light@2.0-service.onclite"
+
+#include <hidl/HidlTransportSupport.h>
+
+#include "Light.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+using android::hardware::light::V2_0::ILight;
+using android::hardware::light::V2_0::implementation::Light;
+
+using android::OK;
+using android::sp;
+using android::status_t;
+
+int main() {
+    sp<ILight> service = new Light();
+
+    configureRpcThreadpool(1, true);
+
+    status_t status = service->registerAsService();
+    if (status != OK) {
+        ALOGE("Cannot register Light HAL service.");
+        return 1;
+    }
+
+    ALOGI("Light HAL service ready.");
+
+    joinRpcThreadpool();
+
+    ALOGI("Light HAL service failed to join thread pool.");
+    return 1;
+}
diff --git a/rootdir/init.qcom.rc b/rootdir/init.qcom.rc
index 82002b00419a847e02bdba87aca4fa22c26f75d3..21a605d37e34c177c84db2bba788f3f2bbe7edb4 100644
--- a/rootdir/init.qcom.rc
+++ b/rootdir/init.qcom.rc
@@ -182,14 +182,6 @@ on boot
     # bond0 used by FST Manager
     chown wifi wifi /sys/class/net/bond0/bonding/queue_id
 
-    # Notification LED
-    chown system system /sys/class/leds/red/blink
-    chown system system /sys/class/leds/green/blink
-    chown system system /sys/class/leds/blue/blink
-    chown system system /sys/class/leds/red/brightness
-    chown system system /sys/class/leds/green/brightness
-    chown system system /sys/class/leds/blue/brightness
-
     # Allow access to emmc rawdump block partition and dload sysfs node
     chown root system /dev/block/bootdevice/by-name/rawdump
     chmod 0660 /dev/block/bootdevice/by-name/rawdump
diff --git a/sepolicy/file_contexts b/sepolicy/file_contexts
index 1a290203cdb14f32b34a3867e39d2b897e59379a..fe2d994fe0e42d090ca79c5db680e27294894854 100644
--- a/sepolicy/file_contexts
+++ b/sepolicy/file_contexts
@@ -1,6 +1,7 @@
 # HALs
 /(vendor|system/vendor)/bin/hw/android\.hardware\.drm@1\.0-service\.widevine         u:object_r:hal_drm_widevine_exec:s0
 /(vendor|system/vendor)/bin/hw/android\.hardware\.drm@1\.1-service\.clearkey         u:object_r:hal_drm_clearkey_exec:s0
+/(vendor|system/vendor)/bin/hw/android\.hardware\.light@2\.0-service\.onclite	     u:object_r:hal_light_default_exec:s0
 
 # Input devices
 /(vendor|system/vendor)/usr/idc(/.*)?                         u:object_r:idc_file:s0