Doze: Follow the notification light.

 - Send the notification light info up to SystemUI from NoMan.
 - Doze mode will now periodically pulse the display (once)
   if the notification light is active.
 - Change "tease" terminology to "pulse", which is the singular.
   Maintain the multi-pulse on buzz-beep-blink for now as extra
   emphasis.
 - Scrim controller now always takes number of pulses as an arg,
   to support a single pulse (used for notification light, and
   eventually pickup once available).
 - Dial down the display brightness when pulsing.

Bug:15863249
Change-Id: Ifb208a27e82b66cff1d0c04e5b7f758098ea29cf
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 230f4af..a4576bb 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -162,11 +162,17 @@
     <!-- Doze: does this device support STATE_DOZE and STATE_DOZE_SUSPEND?  -->
     <bool name="doze_display_state_supported">false</bool>
 
-    <!-- Doze: should the significant motion sensor be used as a tease signal? -->
-    <bool name="doze_tease_on_significant_motion">false</bool>
+    <!-- Doze: should the significant motion sensor be used as a pulse signal? -->
+    <bool name="doze_pulse_on_significant_motion">false</bool>
 
-    <!-- Doze: maximum brightness to use when teasing -->
-    <integer name="doze_tease_brightness">80</integer>
+    <!-- Doze: maximum brightness to use when pulsing -->
+    <integer name="doze_pulse_brightness">40</integer>
+
+    <!-- Doze: number of pulses when doing multiple pulses in quick succession -->
+    <integer name="doze_multipulse_count">3</integer>
+
+    <!-- Doze: interval between pulses when following the notification light -->
+    <integer name="doze_notification_pulse_interval">30000</integer>
 
     <!-- Volume: time to delay dismissing the volume panel after a click is performed -->
     <integer name="volume_panel_dismiss_delay">200</integer>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 13c15f5..943a294 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -19,6 +19,8 @@
 import static android.os.PowerManager.BRIGHTNESS_OFF;
 import static android.os.PowerManager.BRIGHTNESS_ON;
 
+import android.app.AlarmManager;
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -42,12 +44,16 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Date;
 
 public class DozeService extends DreamService {
     private static final String TAG = "DozeService";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final String TEASE_ACTION = "com.android.systemui.doze.tease";
+    private static final String ACTION_BASE = "com.android.systemui.doze";
+    private static final String PULSE_ACTION = ACTION_BASE + ".pulse";
+    private static final String NOTIFICATION_PULSE_ACTION = ACTION_BASE + ".notification_pulse";
+    private static final String EXTRA_PULSES = "pulses";
 
     private final String mTag = String.format(TAG + ".%08x", hashCode());
     private final Context mContext = this;
@@ -58,13 +64,18 @@
     private Sensor mSigMotionSensor;
     private PowerManager mPowerManager;
     private PowerManager.WakeLock mWakeLock;
+    private AlarmManager mAlarmManager;
     private int mMaxBrightness;
     private boolean mDreaming;
-    private boolean mTeaseReceiverRegistered;
+    private boolean mBroadcastReceiverRegistered;
     private boolean mSigMotionConfigured;
     private boolean mSigMotionEnabled;
     private boolean mDisplayStateSupported;
     private int mDisplayStateWhenOn;
+    private boolean mNotificationLightOn;
+    private PendingIntent mNotificationPulseIntent;
+    private int mMultipulseCount;
+    private int mNotificationPulseInterval;
 
     public DozeService() {
         if (DEBUG) Log.d(mTag, "new DozeService()");
@@ -75,12 +86,15 @@
     protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) {
         super.dumpOnHandler(fd, pw, args);
         pw.print("  mDreaming: "); pw.println(mDreaming);
-        pw.print("  mTeaseReceiverRegistered: "); pw.println(mTeaseReceiverRegistered);
+        pw.print("  mBroadcastReceiverRegistered: "); pw.println(mBroadcastReceiverRegistered);
         pw.print("  mSigMotionSensor: "); pw.println(mSigMotionSensor);
         pw.print("  mSigMotionConfigured: "); pw.println(mSigMotionConfigured);
         pw.print("  mSigMotionEnabled: "); pw.println(mSigMotionEnabled);
         pw.print("  mMaxBrightness: "); pw.println(mMaxBrightness);
         pw.print("  mDisplayStateSupported: "); pw.println(mDisplayStateSupported);
+        pw.print("  mNotificationLightOn: "); pw.println(mNotificationLightOn);
+        pw.print("  mMultipulseCount: "); pw.println(mMultipulseCount);
+        pw.print("  mNotificationPulseInterval: "); pw.println(mNotificationPulseInterval);
     }
 
     @Override
@@ -99,14 +113,21 @@
         mSigMotionSensor = mSensors.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
+        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         final Resources res = mContext.getResources();
-        mSigMotionConfigured = SystemProperties.getBoolean("doze.tease.sigmotion",
-                res.getBoolean(R.bool.doze_tease_on_significant_motion));
+        mSigMotionConfigured = SystemProperties.getBoolean("doze.pulse.sigmotion",
+                res.getBoolean(R.bool.doze_pulse_on_significant_motion));
         mDisplayStateSupported = SystemProperties.getBoolean("doze.display.supported",
                 res.getBoolean(R.bool.doze_display_state_supported));
-        mMaxBrightness = MathUtils.constrain(res.getInteger(R.integer.doze_tease_brightness),
+        mMaxBrightness = MathUtils.constrain(res.getInteger(R.integer.doze_pulse_brightness),
                 BRIGHTNESS_OFF, BRIGHTNESS_ON);
-
+        mNotificationPulseIntent = PendingIntent.getBroadcast(mContext, 0,
+                new Intent(NOTIFICATION_PULSE_ACTION).setPackage(getPackageName()),
+                PendingIntent.FLAG_CANCEL_CURRENT);
+        mMultipulseCount = SystemProperties.getInt("doze.multipulses",
+                res.getInteger(R.integer.doze_multipulse_count));
+        mNotificationPulseInterval = SystemProperties.getInt("doze.notification.pulse",
+                res.getInteger(R.integer.doze_notification_pulse_interval));
         mDisplayStateWhenOn = mDisplayStateSupported ? Display.STATE_DOZE : Display.STATE_ON;
         setDozeScreenState(mDisplayStateWhenOn);
     }
@@ -122,7 +143,7 @@
         super.onDreamingStarted();
         if (DEBUG) Log.d(mTag, "onDreamingStarted canDoze=" + canDoze());
         mDreaming = true;
-        listenForTeaseSignals(true);
+        listenForPulseSignals(true);
         requestDoze();
     }
 
@@ -160,7 +181,7 @@
         if (mWakeLock.isHeld()) {
             mWakeLock.release();
         }
-        listenForTeaseSignals(false);
+        listenForPulseSignals(false);
         stopDozing();
         dozingStopped();
     }
@@ -187,9 +208,17 @@
         }
     }
 
-    private void requestTease() {
+    private void requestMultipulse() {
+        requestPulse(mMultipulseCount);
+    }
+
+    private void requestPulse() {
+        requestPulse(1);
+    }
+
+    private void requestPulse(int pulses) {
         if (mHost != null) {
-            mHost.requestTease(this);
+            mHost.requestPulse(pulses, this);
         }
     }
 
@@ -199,10 +228,10 @@
         }
     }
 
-    private void listenForTeaseSignals(boolean listen) {
-        if (DEBUG) Log.d(mTag, "listenForTeaseSignals: " + listen);
+    private void listenForPulseSignals(boolean listen) {
+        if (DEBUG) Log.d(mTag, "listenForPulseSignals: " + listen);
         listenForSignificantMotion(listen);
-        listenForBroadcast(listen);
+        listenForBroadcasts(listen);
         listenForNotifications(listen);
     }
 
@@ -216,15 +245,17 @@
         }
     }
 
-    private void listenForBroadcast(boolean listen) {
+    private void listenForBroadcasts(boolean listen) {
         if (listen) {
-            mContext.registerReceiver(mTeaseReceiver, new IntentFilter(TEASE_ACTION));
-            mTeaseReceiverRegistered = true;
+            final IntentFilter filter = new IntentFilter(PULSE_ACTION);
+            filter.addAction(NOTIFICATION_PULSE_ACTION);
+            mContext.registerReceiver(mBroadcastReceiver, filter);
+            mBroadcastReceiverRegistered = true;
         } else {
-            if (mTeaseReceiverRegistered) {
-                mContext.unregisterReceiver(mTeaseReceiver);
+            if (mBroadcastReceiverRegistered) {
+                mContext.unregisterReceiver(mBroadcastReceiver);
             }
-            mTeaseReceiverRegistered = false;
+            mBroadcastReceiverRegistered = false;
         }
     }
 
@@ -237,6 +268,15 @@
         }
     }
 
+    private void rescheduleNotificationPulse() {
+        mAlarmManager.cancel(mNotificationPulseIntent);
+        if (mNotificationLightOn) {
+            final long time = System.currentTimeMillis() + mNotificationPulseInterval;
+            if (DEBUG) Log.d(TAG, "Scheduling pulse for " + new Date(time));
+            mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, time, mNotificationPulseIntent);
+        }
+    }
+
     private static String triggerEventToString(TriggerEvent event) {
         if (event == null) return null;
         final StringBuilder sb = new StringBuilder("TriggerEvent[")
@@ -269,16 +309,23 @@
                     v.vibrate(1000);
                 }
             }
-            requestTease();
+            requestPulse();
             listenForSignificantMotion(true);  // reregister, this sensor only fires once
         }
     };
 
-    private final BroadcastReceiver mTeaseReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (DEBUG) Log.d(mTag, "Received tease intent");
-            requestTease();
+            if (PULSE_ACTION.equals(intent.getAction())) {
+                if (DEBUG) Log.d(mTag, "Received pulse intent");
+                requestPulse(intent.getIntExtra(EXTRA_PULSES, mMultipulseCount));
+            }
+            if (NOTIFICATION_PULSE_ACTION.equals(intent.getAction())) {
+                if (DEBUG) Log.d(mTag, "Received notification pulse intent");
+                requestPulse();
+                rescheduleNotificationPulse();
+            }
         }
     };
 
@@ -288,10 +335,19 @@
             if (DEBUG) Log.d(mTag, "onNewNotifications");
             // noop for now
         }
+
         @Override
         public void onBuzzBeepBlinked() {
             if (DEBUG) Log.d(mTag, "onBuzzBeepBlinked");
-            requestTease();
+            requestMultipulse();
+        }
+
+        @Override
+        public void onNotificationLight(boolean on) {
+            if (DEBUG) Log.d(mTag, "onNotificationLight on=" + on);
+            if (mNotificationLightOn == on) return;
+            mNotificationLightOn = on;
+            rescheduleNotificationPulse();
         }
     };
 
@@ -299,12 +355,13 @@
         void addCallback(Callback callback);
         void removeCallback(Callback callback);
         void requestDoze(DozeService dozeService);
-        void requestTease(DozeService dozeService);
+        void requestPulse(int pulses, DozeService dozeService);
         void dozingStopped(DozeService dozeService);
 
         public interface Callback {
             void onNewNotifications();
             void onBuzzBeepBlinked();
+            void onNotificationLight(boolean on);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index a82c907..9107790 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -55,6 +55,8 @@
     private static final int MSG_SHOW_RECENT_APPS           = 14 << MSG_SHIFT;
     private static final int MSG_HIDE_RECENT_APPS           = 15 << MSG_SHIFT;
     private static final int MSG_BUZZ_BEEP_BLINKED          = 16 << MSG_SHIFT;
+    private static final int MSG_NOTIFICATION_LIGHT_OFF     = 17 << MSG_SHIFT;
+    private static final int MSG_NOTIFICATION_LIGHT_PULSE   = 18 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -95,6 +97,8 @@
         public void hideSearchPanel();
         public void setWindowState(int window, int state);
         public void buzzBeepBlinked();
+        public void notificationLightOff();
+        public void notificationLightPulse(int argb, int onMillis, int offMillis);
     }
 
     public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -230,6 +234,19 @@
         }
     }
 
+    public void notificationLightOff() {
+        synchronized (mList) {
+            mHandler.sendEmptyMessage(MSG_NOTIFICATION_LIGHT_OFF);
+        }
+    }
+
+    public void notificationLightPulse(int argb, int onMillis, int offMillis) {
+        synchronized (mList) {
+            mHandler.obtainMessage(MSG_NOTIFICATION_LIGHT_PULSE, onMillis, offMillis, argb)
+                    .sendToTarget();
+        }
+    }
+
     private final class H extends Handler {
         public void handleMessage(Message msg) {
             final int what = msg.what & MSG_MASK;
@@ -306,7 +323,12 @@
                 case MSG_BUZZ_BEEP_BLINKED:
                     mCallbacks.buzzBeepBlinked();
                     break;
-
+                case MSG_NOTIFICATION_LIGHT_OFF:
+                    mCallbacks.notificationLightOff();
+                    break;
+                case MSG_NOTIFICATION_LIGHT_PULSE:
+                    mCallbacks.notificationLightPulse((Integer) msg.obj, msg.arg1, msg.arg2);
+                    break;
             }
         }
     }
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 e38bada..99b0259 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2336,6 +2336,20 @@
         }
     }
 
+    @Override
+    public void notificationLightOff() {
+        if (mDozeServiceHost != null) {
+            mDozeServiceHost.fireNotificationLight(false);
+        }
+    }
+
+    @Override
+    public void notificationLightPulse(int argb, int onMillis, int offMillis) {
+        if (mDozeServiceHost != null) {
+            mDozeServiceHost.fireNotificationLight(true);
+        }
+    }
+
     @Override // CommandQueue
     public void setSystemUiVisibility(int vis, int mask) {
         final int oldVal = mSystemUiVisibility;
@@ -3799,6 +3813,12 @@
             }
         }
 
+        public void fireNotificationLight(boolean on) {
+            for (Callback callback : mCallbacks) {
+                callback.onNotificationLight(on);
+            }
+        }
+
         public void fireNewNotifications() {
             for (Callback callback : mCallbacks) {
                 callback.onNewNotifications();
@@ -3823,10 +3843,10 @@
         }
 
         @Override
-        public void requestTease(DozeService dozeService) {
+        public void requestPulse(int pulses, DozeService dozeService) {
             if (dozeService == null) return;
             dozeService.stayAwake(PROCESSING_TIME);
-            mHandler.obtainMessage(H.REQUEST_TEASE, dozeService).sendToTarget();
+            mHandler.obtainMessage(H.REQUEST_PULSE, pulses, 0, dozeService).sendToTarget();
         }
 
         @Override
@@ -3845,9 +3865,9 @@
             mCurrentDozeService.startDozing();
         }
 
-        private void handleRequestTease(DozeService dozeService) {
+        private void handleRequestPulse(int pulses, DozeService dozeService) {
             if (!dozeService.equals(mCurrentDozeService)) return;
-            final long stayAwake = mScrimController.tease();
+            final long stayAwake = mScrimController.pulse(pulses);
             mCurrentDozeService.stayAwake(stayAwake);
         }
 
@@ -3863,15 +3883,15 @@
 
         private final class H extends Handler {
             private static final int REQUEST_DOZE = 1;
-            private static final int REQUEST_TEASE = 2;
+            private static final int REQUEST_PULSE = 2;
             private static final int DOZING_STOPPED = 3;
 
             @Override
             public void handleMessage(Message msg) {
                 if (msg.what == REQUEST_DOZE) {
                     handleRequestDoze((DozeService) msg.obj);
-                } else if (msg.what == REQUEST_TEASE) {
-                    handleRequestTease((DozeService) msg.obj);
+                } else if (msg.what == REQUEST_PULSE) {
+                    handleRequestPulse(msg.arg1, (DozeService) msg.obj);
                 } else if (msg.what == DOZING_STOPPED) {
                     handleDozingStopped((DozeService) msg.obj);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 7bce664..cbd66aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -43,14 +43,13 @@
     private static final long ANIMATION_DURATION = 220;
     private static final int TAG_KEY_ANIM = R.id.scrim;
 
-    private static final int NUM_TEASES = 3;
-    private static final long TEASE_IN_ANIMATION_DURATION = 1000;
-    private static final long TEASE_VISIBLE_DURATION = 2000;
-    private static final long TEASE_OUT_ANIMATION_DURATION = 1000;
-    private static final long TEASE_INVISIBLE_DURATION = 1000;
-    private static final long TEASE_DURATION = TEASE_IN_ANIMATION_DURATION
-            + TEASE_VISIBLE_DURATION + TEASE_OUT_ANIMATION_DURATION + TEASE_INVISIBLE_DURATION;
-    private static final long PRE_TEASE_DELAY = 1000;
+    private static final long PULSE_IN_ANIMATION_DURATION = 1000;
+    private static final long PULSE_VISIBLE_DURATION = 2000;
+    private static final long PULSE_OUT_ANIMATION_DURATION = 1000;
+    private static final long PULSE_INVISIBLE_DURATION = 1000;
+    private static final long PULSE_DURATION = PULSE_IN_ANIMATION_DURATION
+            + PULSE_VISIBLE_DURATION + PULSE_OUT_ANIMATION_DURATION + PULSE_INVISIBLE_DURATION;
+    private static final long PRE_PULSE_DELAY = 1000;
 
     private final View mScrimBehind;
     private final View mScrimInFront;
@@ -70,7 +69,7 @@
     private Runnable mOnAnimationFinished;
     private boolean mAnimationStarted;
     private boolean mDozing;
-    private int mTeasesRemaining;
+    private int mPulsesRemaining;
     private final Interpolator mInterpolator = new DecelerateInterpolator();
 
     public ScrimController(View scrimBehind, View scrimInFront) {
@@ -126,23 +125,23 @@
         if (mDozing == dozing) return;
         mDozing = dozing;
         if (!mDozing) {
-            cancelTeasing();
+            cancelPulsing();
         }
         scheduleUpdate();
     }
 
     /** When dozing, fade screen contents in and out a few times using the front scrim. */
-    public long tease() {
+    public long pulse(int pulses) {
         if (!mDozing) return 0;
-        mTeasesRemaining = NUM_TEASES;
-        mScrimInFront.postDelayed(mTeaseIn, PRE_TEASE_DELAY);
-        return PRE_TEASE_DELAY + NUM_TEASES * TEASE_DURATION;
+        mPulsesRemaining = Math.max(pulses, mPulsesRemaining);
+        mScrimInFront.postDelayed(mPulseIn, PRE_PULSE_DELAY);
+        return PRE_PULSE_DELAY + mPulsesRemaining * PULSE_DURATION;
     }
 
-    private void cancelTeasing() {
-        mTeasesRemaining = 0;
-        mScrimInFront.removeCallbacks(mTeaseIn);
-        mScrimInFront.removeCallbacks(mTeaseOut);
+    private void cancelPulsing() {
+        mPulsesRemaining = 0;
+        mScrimInFront.removeCallbacks(mPulseIn);
+        mScrimInFront.removeCallbacks(mPulseOut);
     }
 
     private void scheduleUpdate() {
@@ -285,49 +284,49 @@
         return true;
     }
 
-    private final Runnable mTeaseIn = new Runnable() {
+    private final Runnable mPulseIn = new Runnable() {
         @Override
         public void run() {
-            if (DEBUG) Log.d(TAG, "Tease in, mDozing=" + mDozing
-                    + " mTeasesRemaining=" + mTeasesRemaining);
-            if (!mDozing || mTeasesRemaining == 0) return;
-            mTeasesRemaining--;
-            mDurationOverride = TEASE_IN_ANIMATION_DURATION;
+            if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing
+                    + " mPulsesRemaining=" + mPulsesRemaining);
+            if (!mDozing || mPulsesRemaining == 0) return;
+            mPulsesRemaining--;
+            mDurationOverride = PULSE_IN_ANIMATION_DURATION;
             mAnimationDelay = 0;
             mAnimateChange = true;
-            mOnAnimationFinished = mTeaseInFinished;
+            mOnAnimationFinished = mPulseInFinished;
             setScrimColor(mScrimInFront, 0);
         }
     };
 
-    private final Runnable mTeaseInFinished = new Runnable() {
+    private final Runnable mPulseInFinished = new Runnable() {
         @Override
         public void run() {
-            if (DEBUG) Log.d(TAG, "Tease in finished, mDozing=" + mDozing);
+            if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing);
             if (!mDozing) return;
-            mScrimInFront.postDelayed(mTeaseOut, TEASE_VISIBLE_DURATION);
+            mScrimInFront.postDelayed(mPulseOut, PULSE_VISIBLE_DURATION);
         }
     };
 
-    private final Runnable mTeaseOut = new Runnable() {
+    private final Runnable mPulseOut = new Runnable() {
         @Override
         public void run() {
-            if (DEBUG) Log.d(TAG, "Tease in finished, mDozing=" + mDozing);
+            if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
             if (!mDozing) return;
-            mDurationOverride = TEASE_OUT_ANIMATION_DURATION;
+            mDurationOverride = PULSE_OUT_ANIMATION_DURATION;
             mAnimationDelay = 0;
             mAnimateChange = true;
-            mOnAnimationFinished = mTeaseOutFinished;
+            mOnAnimationFinished = mPulseOutFinished;
             setScrimColor(mScrimInFront, 1);
         }
     };
 
-    private final Runnable mTeaseOutFinished = new Runnable() {
+    private final Runnable mPulseOutFinished = new Runnable() {
         @Override
         public void run() {
-            if (DEBUG) Log.d(TAG, "Tease out finished, mTeasesRemaining=" + mTeasesRemaining);
-            if (mTeasesRemaining > 0) {
-                mScrimInFront.postDelayed(mTeaseIn, TEASE_INVISIBLE_DURATION);
+            if (DEBUG) Log.d(TAG, "Pulse out finished, mPulsesRemaining=" + mPulsesRemaining);
+            if (mPulsesRemaining > 0) {
+                mScrimInFront.postDelayed(mPulseIn, PULSE_INVISIBLE_DURATION);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index a123bf7..049add5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -99,6 +99,14 @@
     public void buzzBeepBlinked() {
     }
 
+    @Override // CommandQueue
+    public void notificationLightOff() {
+    }
+
+    @Override // CommandQueue
+    public void notificationLightPulse(int argb, int onMillis, int offMillis) {
+    }
+
     @Override
     protected WindowManager.LayoutParams getSearchLayoutParams(
             LayoutParams layoutParams) {