Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2eb61d88 authored by Sam Mortimer's avatar Sam Mortimer Committed by Michael Bestas
Browse files

frameworks/base: Battery and Notification Lights

A reworked implementation based on code in the
commits listed further below.

Key changes from the original implementation:

*) Settings observation and most lineage lights
   feature specific code has been moved to the
   lineage-sdk.

*) Battery and notification frameworks services
   call out to the sdk to allow our features
   to make changes to lights values.

Original commits:

Author: DvTonder <david.vantonder@gmail.com>
Author: Ricardo Cerqueira <cyanogenmod@cerqueira.org>
Date:   Mon Nov 4 00:57:51 2013 +0000
Framework: Port CM9 features to CM10
Change-Id: Ibd63116df90b06f6ce6adb8a0343059bbb999bfb

Author: Pawit Pornkitprasan <p.pawit@gmail.com>
Date:   Sun Dec 8 15:24:41 2013 +0700
BatteryService: fix FC on boot until battery stat is present
updateLightsLocked() can be called from CM's added SettingsObserver
when battery stat is not present, causing an FC and a loop until
battery stat is present.
Change-Id: Ic4438fe50e98f1aa05ae1d0d26240bf9410fd92f

Author: Sam Mortimer <sam@mortimer.me.uk>
Date:   Tue Dec 31 16:22:05 2013 -0800
[2/2] Framework: instant led test
Adds support a new notification extra boolean
EXTRA_FORCE_SHOW_LIGHTS.  Used by settings
notification light picker to force lights on
when the screen is on.
Change-Id: If0a42d32b28fe8c02ef5f7dd148db7eb478fac17

Author: Michael Bestas <mikeioannina@gmail.com>
Date:   Mon Aug 18 04:56:28 2014 +0300
Add support for single color notification LED (1/2)
Change-Id: I367af77036da9e87c6dd0df552ce4c56d945a44d

Author: Danesh M <daneshm90@gmail.com>
Date:   Thu, 12 Nov 2015 10:52:11 -0800
Framework : Move System settings to CMSettings
Change-Id: I4e9fb06db7b9ba05e4a7bbe11916bb2271af4727

Author: Adnan Begovic <adnan@cyngn.com>
Date:   Mon, 9 Nov 2015 16:26:00 -0800
fw: Move battery light settings to CMSettings.
Change-Id: I28e60473356b2a9af152df82d34ad7abc9918682

Author: Steve Kondik <steve@cyngn.com>
Date:   Thu Sep 24 11:27:59 2015 -0700
lights: Automatically generate an LED color for notifications
Change-Id: I7288e52499819a6a6c75ed9d9ba7cfa1b13c5669
nms: Only generate LED colors if the device has a multicolored LED
* Check the overlay value before doing any of this stuff.
Change-Id: Iedfceba6bfc86b2761d8af57ecab51026bfa4c19
Change-Id: I7288e52499819a6a6c75ed9d9ba7cfa1b13c5669

Author: Adrian DC <radian.dc@gmail.com>
Date:   Sat Oct 14 23:08:47 2017 +0200
fw: Rebrand to LineageOS and cleanup for Android Oreo
Change-Id: I21d424433bb52a17eea7974c4ea29a3a16fe1be5

Author: AdrianDC <radian.dc@gmail.com>
Date:   Sat Jul 18 12:20:51 2015 +0200
Lights with Screen On [1/2]: Optional allowment of lights
Change-Id: I2071147d8ddab80ba0e1e7310e785ac3e03b3c7c
Lights with screen on: Don't disable leds after the lockscreen
Change-Id: If8f5b867a34be09fb061bb7ad040b16730f4e263
Framework : Move System settings to CMSettings
Change-Id: I4e9fb06db7b9ba05e4a7bbe11916bb2271af4727
fw: Rebrand to LineageOS and port for Android Oreo
Change-Id: I65cfeb659fe516ef43aa84b1c6d2eb6876df202a
Change-Id: If8f5b867a34be09fb061bb7ad040b16730f4e263

Author: Michael W <baddaemon87@gmail.com>
Date:   Mon Oct 9 22:04:00 2017 +0200
Core: Battery warning levels are inclusive, not exclusive
Change-Id: Ib35b154b6117f7e26b4a3a5aee9254dda3adda12

Author: Adrian DC <radian.dc@gmail.com>
Date:   Sat Oct 14 23:08:47 2017 +0200
fw: Rebrand to LineageOS and cleanup for Android Oreo
Change-Id: I845f866891386aee808a4e7e80f4ab7c3ad48830

Author: Sam Mortimer <sam@mortimer.me.uk>
Date:   Tue Nov 21 23:18:16 2017 -0800
frameworks/base: Improve interface to LineageNotificationLights
Change-Id: I43af52dd236e802d232a4cf96ccd6f69af6b26b7

Author: Sam Mortimer <sam@mortimer.me.uk>
Date:   Wed Dec 13 11:54:23 2017 -0800
frameworks/base: prevent lights service calls when battery led does not exist
Change-Id: I9eefff1f587c978c0aa2b31d03e664dc7ccf42de

Author: Sam Mortimer <sam@mortimer.me.uk>
Date:   Fri Feb 9 14:04:35 2018 -0800
frameworks/base: disable warning about mLineageBatteryLights not being ready
Change-Id: I96852f83a739924ac47260de511ddbea81465c52

Author: Luca Stefani <luca.stefani.ge1@gmail.com>
Date:   Sat Jul 14 10:34:53 2018 -0700
    fw/b lights: Allow black notification color
    * Color 0 is used to mean "lineage-sdk should pick a default"
    * Explictly requesting black made the led blue (eg lineageparts light picker), fix that
    Change-Id: Ia03f898c1b6cd0f77af8bb155139b587664f47af

Author: Sam Mortimer <sam@mortimer.me.uk>
Date:   Fri Feb 16 00:51:38 2018 -0800
    frameworks/base lights: Let Lineage lights decide if notification led is on/off
    *) Allows us to cater for the case where the notification led is turned off
       and yet the user wants to adjust battery light settings (which requires
       posting led notifications to illustrate the change).
    Change-Id: Iea52feb907f99ecf7925aff1fc1baf008642f7d7

Author: Sam Mortimer <sam@mortimer.me.uk>
Date:   Sat Feb 10 22:33:51 2018 -0800
    frameworks/base lights: Always allow LineageNotificationLights set the default color
    *) Oreo sdk onwards deprecates notification DEFAULT_LIGHTS.  However,
       LineageNotificationLights currently uses this flag to determine
       if we should generate an app specific color.
    *) Use a light color of 0 to indicate that the app (or channel) has not
       set an explicit preference and that lineage lights should set a
       default.
    Change-Id: I1f655aa17cb97c5a042a424ad1e8bd9d5ddec9a2

Change-Id: I564fdb6ca6c5bbcf890afa4b9a2f9fa7bf8b9604
parent 0772b15a
Loading
Loading
Loading
Loading
+46 −21
Original line number Diff line number Diff line
@@ -69,6 +69,9 @@ import com.android.server.am.BatteryStatsService;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;

import org.lineageos.internal.notification.LedValues;
import org.lineageos.internal.notification.LineageBatteryLights;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
@@ -192,6 +195,8 @@ public final class BatteryService extends SystemService {

    private MetricsLogger mMetricsLogger;

    private LineageBatteryLights mLineageBatteryLights;

    public BatteryService(Context context) {
        super(context);

@@ -261,6 +266,16 @@ public final class BatteryService extends SystemService {
                        false, obs, UserHandle.USER_ALL);
                updateBatteryWarningLevelLocked();
            }
        } else if (phase == PHASE_BOOT_COMPLETED) {
            mLineageBatteryLights = new LineageBatteryLights(mContext,
                    new LineageBatteryLights.LedUpdater() {
                public void update() {
                    updateLedPulse();
                }
            });

            // Update light state now that mLineageBatteryLights has been initialized.
            updateLedPulse();
        }
    }

@@ -1062,6 +1077,10 @@ public final class BatteryService extends SystemService {
        Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
    }

    private synchronized void updateLedPulse() {
        mLed.updateLightsLocked();
    }

    private final class Led {
        private final Light mBatteryLight;

@@ -1090,29 +1109,35 @@ public final class BatteryService extends SystemService {
         * Synchronize on BatteryService.
         */
        public void updateLightsLocked() {
            final int level = mHealthInfo.batteryLevel;
            final int status = mHealthInfo.batteryStatus;
            if (level < mLowBatteryWarningLevel) {
                if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
                    // Solid red when battery is charging
                    mBatteryLight.setColor(mBatteryLowARGB);
                } else {
                    // Flash red when battery is low and not charging
                    mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,
                            mBatteryLedOn, mBatteryLedOff);
                }
            } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
                    || status == BatteryManager.BATTERY_STATUS_FULL) {
                if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
                    // Solid green when full or charging and nearly full
                    mBatteryLight.setColor(mBatteryFullARGB);
                } else {
                    // Solid orange when charging and halfway full
                    mBatteryLight.setColor(mBatteryMediumARGB);
            // mHealthInfo could be null on startup (called by SettingsObserver)
            if (mHealthInfo == null) {
                Slog.w(TAG, "updateLightsLocked: mHealthInfo is null; skipping");
                return;
            }
            } else {
                // No lights if not charging and not low
            // mLineageBatteryLights is initialized during PHASE_BOOT_COMPLETED
            // This means we don't have Lineage battery settings yet so skip.
            if (mLineageBatteryLights == null) {
                if (DEBUG) {
                    Slog.w(TAG, "updateLightsLocked: mLineageBatteryLights is not yet ready; "
                            + "skipping");
                }
                return;
            }
            if (!mLineageBatteryLights.isSupported()) {
                return;
            }

            LedValues ledValues = new LedValues(0 /* color */, mBatteryLedOn, mBatteryLedOff);
            mLineageBatteryLights.calcLights(ledValues, mHealthInfo.batteryLevel,
                    mHealthInfo.batteryStatus, mHealthInfo.batteryLevel <= mLowBatteryWarningLevel);

            if (!ledValues.isEnabled()) {
                mBatteryLight.turnOff();
            } else if (ledValues.isPulsed()) {
                mBatteryLight.setFlashing(ledValues.getColor(), Light.LIGHT_FLASH_TIMED,
                        ledValues.getOnMs(), ledValues.getOffMs());
            } else {
                mBatteryLight.setColor(ledValues.getColor());
            }
        }
    }
+51 −19
Original line number Diff line number Diff line
/*
 * Copyright (C) 2007 The Android Open Source Project
 * Copyright (C) 2015 The CyanogenMod Project
 * Copyright (C) 2017 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.
@@ -243,6 +245,9 @@ import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;

import org.lineageos.internal.notification.LedValues;
import org.lineageos.internal.notification.LineageNotificationLights;

import libcore.io.IoUtils;

import org.json.JSONException;
@@ -469,6 +474,8 @@ public class NotificationManagerService extends SystemService {

    private final SavePolicyFileRunnable mSavePolicyFile = new SavePolicyFileRunnable();

    private LineageNotificationLights mLineageNotificationLights;

    private static class Archive {
        final int mBufferSize;
        final ArrayDeque<StatusBarNotification> mBuffer;
@@ -1126,9 +1133,12 @@ public class NotificationManagerService extends SystemService {
    @GuardedBy("mNotificationLock")
    private void clearLightsLocked() {
        // light
        // clear only if lockscreen is not active
        if (!mLineageNotificationLights.isKeyguardLocked()) {
            mLights.clear();
            updateLightsLocked();
        }
    }

    protected final BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
        @Override
@@ -1333,7 +1343,10 @@ public class NotificationManagerService extends SystemService {
                }
            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
                // turn off LED when user passes through lock screen
                // if lights with screen on is disabled.
                if (!mLineageNotificationLights.showLightsScreenOn()) {
                    mNotificationLight.turnOff();
                }
            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
                mUserProfiles.updateCache(context);
@@ -1690,6 +1703,7 @@ public class NotificationManagerService extends SystemService {
                        new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
                                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
                        UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
                mLineageNotificationLights.setZenMode(mZenModeHelper.getZenMode());
                synchronized (mNotificationLock) {
                    updateInterruptionFilterLocked();
                }
@@ -1869,8 +1883,16 @@ public class NotificationManagerService extends SystemService {
        IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
        getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter);

        mLineageNotificationLights = new LineageNotificationLights(getContext(),
                 new LineageNotificationLights.LedUpdater() {
            public void update() {
                updateNotificationPulse();
            }
        });

        publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
                DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
        publishLocalService(NotificationManagerInternal.class, mInternalService);
    }

@@ -5953,6 +5975,12 @@ public class NotificationManagerService extends SystemService {
        if (!mHasLight) {
            return false;
        }
        // Forced on
        // Used by LineageParts light picker
        // eg to allow selecting battery light color when notification led is turned off.
        if (isLedForcedOn(record)) {
            return true;
        }
        // user turned lights off globally
        if (!mNotificationPulseEnabled) {
            return false;
@@ -5965,10 +5993,6 @@ public class NotificationManagerService extends SystemService {
        if (!aboveThreshold) {
            return false;
        }
        // suppressed due to DND
        if ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) != 0) {
            return false;
        }
        // Suppressed because it's a silent update
        final Notification notification = record.getNotification();
        if (record.isUpdate && (notification.flags & FLAG_ONLY_ALERT_ONCE) != 0) {
@@ -5978,10 +6002,6 @@ public class NotificationManagerService extends SystemService {
        if (record.sbn.isGroup() && record.getNotification().suppressAlertingDueToGrouping()) {
            return false;
        }
        // not if in call or the screen's on
        if (isInCall() || mScreenOn) {
            return false;
        }

        return true;
    }
@@ -7079,17 +7099,29 @@ public class NotificationManagerService extends SystemService {
            }
        }

        // Don't flash while we are in a call or screen is on
        if (ledNotification == null || isInCall() || mScreenOn) {
        NotificationRecord.Light light = ledNotification != null ?
                ledNotification.getLight() : null;
        if (ledNotification == null || mLineageNotificationLights == null || light == null) {
            mNotificationLight.turnOff();
            return;
        }

        LedValues ledValues = new LedValues(light.color, light.onMs, light.offMs);
        mLineageNotificationLights.calcLights(ledValues, ledNotification.sbn.getPackageName(),
                ledNotification.sbn.getNotification(), mScreenOn || isInCall(),
                ledNotification.getSuppressedVisualEffects());

        if (!ledValues.isEnabled()) {
            mNotificationLight.turnOff();
        } else {
            NotificationRecord.Light light = ledNotification.getLight();
            if (light != null && mNotificationPulseEnabled) {
                // pulse repeatedly
                mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
                        light.onMs, light.offMs);
            mNotificationLight.setFlashing(ledValues.getColor(), Light.LIGHT_FLASH_TIMED,
                    ledValues.getOnMs(), ledValues.getOffMs());
        }
    }

    private boolean isLedForcedOn(NotificationRecord nr) {
        return nr != null ?
                mLineageNotificationLights.isForcedOn(nr.sbn.getNotification()) : false;
    }

    @GuardedBy("mNotificationLock")
+7 −2
Original line number Diff line number Diff line
@@ -248,8 +248,8 @@ public final class NotificationRecord {
    }

    private Light calculateLights() {
        int defaultLightColor = mContext.getResources().getColor(
                com.android.internal.R.color.config_defaultNotificationColor);
        // Lineage lights will set the default color later
        int defaultLightColor = 0;
        int defaultLightOn = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_defaultNotificationLedOn);
        int defaultLightOff = mContext.getResources().getInteger(
@@ -269,6 +269,11 @@ public final class NotificationRecord {
                if ((notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
                    light = new Light(defaultLightColor, defaultLightOn,
                            defaultLightOff);
                } else if (light.color == 0) {
                    // User has requested color 0.  However, lineage-sdk interprets
                    // color 0 as "supply a default" therefore adjust alpha to make
                    // the color still black but non-zero.
                    light = new Light(0x01000000, light.onMs, light.offMs);
                }
            } else {
                light = null;