From 1f32c65697c22f423c2888cf4c53da1c95d602c1 Mon Sep 17 00:00:00 2001 From: Christoph Studer Date: Wed, 26 Nov 2014 15:32:20 +0100 Subject: [PATCH] NoMan/SysUI: Clear LEDs only when entering the shade Don't clear notification LEDs when seeing notifications on the lockscreen. Also fix a bug where the LED didn't continue flashing after the screen turned off. For devices with doze capability, ensure that the LED continuing to flash after screen off doesn't cause an immediate pulses, but delay the first pulse by 10s. Bug: 15449039 Change-Id: Id34d51a2c91ceaf069e49add1ab690bb855f9638 --- .../internal/statusbar/IStatusBarService.aidl | 4 +- .../com/android/systemui/doze/DozeHost.java | 1 + .../android/systemui/doze/DozeService.java | 42 +++++++++++++++---- .../systemui/statusbar/BaseStatusBar.java | 6 ++- .../statusbar/phone/PhoneStatusBar.java | 19 +++++++++ .../notification/NotificationDelegate.java | 3 +- .../NotificationManagerService.java | 25 +++++++---- .../statusbar/StatusBarManagerService.java | 21 +++++++--- 8 files changed, 96 insertions(+), 25 deletions(-) diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 5e610ed32d006..40c009f3a4771 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -40,8 +40,10 @@ interface IStatusBarService // You need the STATUS_BAR_SERVICE permission void registerStatusBar(IStatusBar callbacks, out StatusBarIconList iconList, out int[] switches, out List binders); - void onPanelRevealed(); + void onPanelRevealed(boolean clearNotificationEffects); void onPanelHidden(); + // Mark current notifications as "seen" and stop ringing, vibrating, blinking. + void clearNotificationEffects(); void onNotificationClick(String key); void onNotificationActionClick(String key, int actionIndex); void onNotificationError(String pkg, String tag, int id, diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java index e92f9888ac998..7070220cd4f04 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java @@ -28,6 +28,7 @@ public interface DozeHost { void pulseWhileDozing(@NonNull PulseCallback callback); void stopDozing(); boolean isPowerSaveActive(); + boolean isNotificationLightOn(); public interface Callback { void onNewNotifications(); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index 89b3e5bd6e2f0..f309e69553e49 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -56,6 +56,17 @@ public class DozeService extends DreamService { private static final String NOTIFICATION_PULSE_ACTION = ACTION_BASE + ".notification_pulse"; private static final String EXTRA_INSTANCE = "instance"; + /** + * Earliest time we pulse due to a notification light after the service started. + * + *

Incoming notification light events during the blackout period are + * delayed to the earliest time defined by this constant.

+ * + *

This delay avoids a pulse immediately after screen off, at which + * point the notification light is re-enabled again by NoMan.

+ */ + private static final int EARLIEST_LIGHT_PULSE_AFTER_START_MS = 10 * 1000; + private final String mTag = String.format(TAG + ".%08x", hashCode()); private final Context mContext = this; private final DozeParameters mDozeParameters = new DozeParameters(mContext); @@ -77,6 +88,7 @@ public class DozeService extends DreamService { private boolean mPowerSaveActive; private boolean mCarMode; private long mNotificationPulseTime; + private long mEarliestPulseDueToLight; private int mScheduleResetsRemaining; public DozeService() { @@ -159,8 +171,9 @@ public class DozeService extends DreamService { } mDreaming = true; - listenForPulseSignals(true); rescheduleNotificationPulse(false /*predicate*/); // cancel any pending pulse alarms + mEarliestPulseDueToLight = System.currentTimeMillis() + EARLIEST_LIGHT_PULSE_AFTER_START_MS; + listenForPulseSignals(true); // Ask the host to get things ready to start dozing. // Once ready, we call startDozing() at which point the CPU may suspend @@ -295,6 +308,12 @@ public class DozeService extends DreamService { if (listen) { resetNotificationResets(); mHost.addCallback(mHostCallback); + + // Continue to pulse for existing LEDs. + mNotificationLightOn = mHost.isNotificationLightOn(); + if (mNotificationLightOn) { + updateNotificationPulseDueToLight(); + } } else { mHost.removeCallback(mHostCallback); } @@ -305,21 +324,26 @@ public class DozeService extends DreamService { mScheduleResetsRemaining = mDozeParameters.getPulseScheduleResets(); } - private void updateNotificationPulse() { - if (DEBUG) Log.d(mTag, "updateNotificationPulse"); + private void updateNotificationPulseDueToLight() { + long timeMs = System.currentTimeMillis(); + timeMs = Math.max(timeMs, mEarliestPulseDueToLight); + updateNotificationPulse(timeMs); + } + + private void updateNotificationPulse(long notificationTimeMs) { + if (DEBUG) Log.d(mTag, "updateNotificationPulse notificationTimeMs=" + notificationTimeMs); if (!mDozeParameters.getPulseOnNotifications()) return; if (mScheduleResetsRemaining <= 0) { if (DEBUG) Log.d(mTag, "No more schedule resets remaining"); return; } - final long now = System.currentTimeMillis(); - if ((now - mNotificationPulseTime) < mDozeParameters.getPulseDuration()) { + if ((notificationTimeMs - mNotificationPulseTime) < mDozeParameters.getPulseDuration()) { if (DEBUG) Log.d(mTag, "Recently updated, not resetting schedule"); return; } mScheduleResetsRemaining--; if (DEBUG) Log.d(mTag, "mScheduleResetsRemaining = " + mScheduleResetsRemaining); - mNotificationPulseTime = now; + mNotificationPulseTime = notificationTimeMs; rescheduleNotificationPulse(true /*predicate*/); } @@ -401,14 +425,14 @@ public class DozeService extends DreamService { private final DozeHost.Callback mHostCallback = new DozeHost.Callback() { @Override public void onNewNotifications() { - if (DEBUG) Log.d(mTag, "onNewNotifications"); + if (DEBUG) Log.d(mTag, "onNewNotifications (noop)"); // noop for now } @Override public void onBuzzBeepBlinked() { if (DEBUG) Log.d(mTag, "onBuzzBeepBlinked"); - updateNotificationPulse(); + updateNotificationPulse(System.currentTimeMillis()); } @Override @@ -417,7 +441,7 @@ public class DozeService extends DreamService { if (mNotificationLightOn == on) return; mNotificationLightOn = on; if (mNotificationLightOn) { - updateNotificationPulse(); + updateNotificationPulseDueToLight(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 4a16f8d8aa0fc..416fdec2ea4ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -1642,7 +1642,11 @@ public abstract class BaseStatusBar extends SystemUI implements protected void handleVisibleToUserChanged(boolean visibleToUser) { try { if (visibleToUser) { - mBarService.onPanelRevealed(); + // Only stop blinking, vibrating, ringing when the user went into the shade + // manually (SHADE or SHADE_LOCKED). + boolean clearNotificationEffects = + (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED); + mBarService.onPanelRevealed(clearNotificationEffects); } else { mBarService.onPanelHidden(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 54eb18c2249cc..56645482f76c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -3807,6 +3807,16 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, * @param state The {@link StatusBarState} to set. */ public void setBarState(int state) { + // If we're visible and switched to SHADE_LOCKED (the user dragged down + // on the lockscreen), clear notification LED, vibration, ringing. + // Other transitions are covered in handleVisibleToUserChanged(). + if (mVisible && mState != state && state == StatusBarState.SHADE_LOCKED) { + try { + mBarService.clearNotificationEffects(); + } catch (RemoteException e) { + // Ignore. + } + } mState = state; mStatusBarWindowManager.setStatusBarState(state); } @@ -4138,6 +4148,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private final ArrayList mCallbacks = new ArrayList(); private final H mHandler = new H(); + // Keeps the last reported state by fireNotificationLight. + private boolean mNotificationLightOn; + @Override public String toString() { return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]"; @@ -4156,6 +4169,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public void fireNotificationLight(boolean on) { + mNotificationLightOn = on; for (Callback callback : mCallbacks) { callback.onNotificationLight(on); } @@ -4197,6 +4211,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return mBatteryController != null && mBatteryController.isPowerSave(); } + @Override + public boolean isNotificationLightOn() { + return mNotificationLightOn; + } + private void handleStartDozing(@NonNull Runnable ready) { if (!mDozing) { mDozing = true; diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java index 24fc45508bc1b..fdb443e1753d9 100644 --- a/services/core/java/com/android/server/notification/NotificationDelegate.java +++ b/services/core/java/com/android/server/notification/NotificationDelegate.java @@ -26,8 +26,9 @@ public interface NotificationDelegate { void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id, int uid, int initialPid, String message, int userId); - void onPanelRevealed(); + void onPanelRevealed(boolean clearEffects); void onPanelHidden(); + void clearEffects(); void onNotificationVisibilityChanged( String[] newlyVisibleKeys, String[] noLongerVisibleKeys); void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 70d0e6a4287df..4e1640faadfb3 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -122,7 +122,7 @@ import java.util.Objects; /** {@hide} */ public class NotificationManagerService extends SystemService { static final String TAG = "NotificationService"; - static final boolean DBG = false; + static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); static final int MAX_PACKAGE_NOTIFICATIONS = 50; @@ -564,9 +564,23 @@ public class NotificationManagerService extends SystemService { } @Override - public void onPanelRevealed() { + public void onPanelRevealed(boolean clearEffects) { EventLogTags.writeNotificationPanelRevealed(); + if (clearEffects) { + clearEffects(); + } + } + + @Override + public void onPanelHidden() { + EventLogTags.writeNotificationPanelHidden(); + } + + @Override + public void clearEffects() { synchronized (mNotificationList) { + if (DBG) Slog.d(TAG, "clearEffects"); + // sound mSoundNotification = null; @@ -597,11 +611,6 @@ public class NotificationManagerService extends SystemService { } } - @Override - public void onPanelHidden() { - EventLogTags.writeNotificationPanelHidden(); - } - @Override public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id, int uid, int initialPid, String message, int userId) { @@ -740,8 +749,10 @@ public class NotificationManagerService extends SystemService { // Keep track of screen on/off state, but do not turn off the notification light // until user passes through the lock screen or views the notification. mScreenOn = true; + updateNotificationPulse(); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { mScreenOn = false; + updateNotificationPulse(); } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE)); diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 9828cd4a30d6f..cf2ed07425f66 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -26,7 +26,6 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.util.Slog; -import android.view.WindowManager; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.IStatusBarService; @@ -495,16 +494,26 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } /** - * The status bar service should call this each time the user brings the panel from - * invisible to visible in order to clear the notification light. + * @param clearNotificationEffects whether to consider notifications as "shown" and stop + * LED, vibration, and ringing */ @Override - public void onPanelRevealed() { + public void onPanelRevealed(boolean clearNotificationEffects) { enforceStatusBarService(); long identity = Binder.clearCallingIdentity(); try { - // tell the notification manager to turn off the lights. - mNotificationDelegate.onPanelRevealed(); + mNotificationDelegate.onPanelRevealed(clearNotificationEffects); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void clearNotificationEffects() throws RemoteException { + enforceStatusBarService(); + long identity = Binder.clearCallingIdentity(); + try { + mNotificationDelegate.clearEffects(); } finally { Binder.restoreCallingIdentity(identity); }