AOD: Prolong AOD2 after interaction, turn off AOD when prox covered

Turns off AOD 1 and 2 when the proximity sensor is covered.
Also extends the AOD2 duration when the screen is touched or
the lift gesture is triggered.

Also fixes some issues with the fingerprint unlock transition
from AOD where the doze state for the NotificationPanelView
was cleared too early.

Also hides the wallpaper while we're dozing.

Test: runtest -x packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
Fixes: 36893539
Fixes: 36893538
Fixes: 36033906
Fixes: 37327153
Change-Id: I3fccae1515a0daf2ff99589ed78ec947687e6262
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index f3fb1ef..ec56e15 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -33,6 +33,8 @@
     boolean isPulsingBlocked();
 
     void startPendingIntentDismissingKeyguard(PendingIntent intent);
+    void abortPulsing();
+    void extendPulse();
 
     interface Callback {
         default void onNotificationHeadsUp() {}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index f27521e..1cc5fb9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -58,12 +58,15 @@
         /** Pulse is done showing. Followed by transition to DOZE or DOZE_AOD. */
         DOZE_PULSE_DONE,
         /** Doze is done. DozeService is finished. */
-        FINISH;
+        FINISH,
+        /** AOD, but the display is temporarily off. */
+        DOZE_AOD_PAUSED;
 
         boolean canPulse() {
             switch (this) {
                 case DOZE:
                 case DOZE_AOD:
+                case DOZE_AOD_PAUSED:
                     return true;
                 default:
                     return false;
@@ -85,6 +88,7 @@
                 case UNINITIALIZED:
                 case INITIALIZED:
                 case DOZE:
+                case DOZE_AOD_PAUSED:
                     return Display.STATE_OFF;
                 case DOZE_PULSING:
                 case DOZE_AOD:
@@ -241,6 +245,11 @@
         if (mState == State.FINISH) {
             return State.FINISH;
         }
+        if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD || mState == State.DOZE)
+                && requestedState == State.DOZE_PULSE_DONE) {
+            Log.i(TAG, "Dropping pulse done because current state is already done: " + mState);
+            return mState;
+        }
         if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) {
             Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState);
             return mState;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 2ac0657..73f5222 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -22,6 +22,8 @@
 import android.content.Context;
 import android.database.ContentObserver;
 import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.hardware.TriggerEvent;
 import android.hardware.TriggerEventListener;
@@ -40,6 +42,7 @@
 
 import java.io.PrintWriter;
 import java.util.List;
+import java.util.function.Consumer;
 
 public class DozeSensors {
 
@@ -55,18 +58,22 @@
     private final DozeParameters mDozeParameters;
     private final AmbientDisplayConfiguration mConfig;
     private final WakeLock mWakeLock;
+    private final Consumer<Boolean> mProxCallback;
     private final Callback mCallback;
 
     private final Handler mHandler = new Handler();
+    private final ProxSensor mProxSensor;
 
 
     public DozeSensors(Context context, SensorManager sensorManager, DozeParameters dozeParameters,
-            AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback) {
+            AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback,
+            Consumer<Boolean> proxCallback) {
         mContext = context;
         mSensorManager = sensorManager;
         mDozeParameters = dozeParameters;
         mConfig = config;
         mWakeLock = wakeLock;
+        mProxCallback = proxCallback;
         mResolver = mContext.getContentResolver();
 
         mSensors = new TriggerSensor[] {
@@ -86,6 +93,8 @@
                         true /* configured */,
                         DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP)
         };
+
+        mProxSensor = new ProxSensor();
         mCallback = callback;
     }
 
@@ -129,6 +138,10 @@
         }
     }
 
+    public void setProxListening(boolean listen) {
+        mProxSensor.setRegistered(listen);
+    }
+
     private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
         @Override
         public void onChange(boolean selfChange, Uri uri, int userId) {
@@ -152,6 +165,43 @@
         }
     }
 
+    private class ProxSensor implements SensorEventListener {
+
+        boolean mRegistered;
+        Boolean mCurrentlyFar;
+
+        void setRegistered(boolean register) {
+            if (mRegistered == register) {
+                // Send an update even if we don't re-register.
+                mHandler.post(() -> {
+                    if (mCurrentlyFar != null) {
+                        mProxCallback.accept(mCurrentlyFar);
+                    }
+                });
+                return;
+            }
+            if (register) {
+                mRegistered = mSensorManager.registerListener(this,
+                        mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY),
+                        SensorManager.SENSOR_DELAY_NORMAL, mHandler);
+            } else {
+                mSensorManager.unregisterListener(this);
+                mRegistered = false;
+                mCurrentlyFar = null;
+            }
+        }
+
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange();
+            mProxCallback.accept(mCurrentlyFar);
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        }
+    }
+
     private class TriggerSensor extends TriggerEventListener {
         final Sensor mSensor;
         final boolean mConfigured;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 1b9bf73..9b3593b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -46,6 +46,7 @@
 public class DozeTriggers implements DozeMachine.Part {
 
     private static final String TAG = "DozeTriggers";
+    private static final boolean DEBUG = DozeService.DEBUG;
 
     /** adb shell am broadcast -a com.android.systemui.doze.pulse com.android.systemui */
     private static final String PULSE_ACTION = "com.android.systemui.doze.pulse";
@@ -81,7 +82,7 @@
         mWakeLock = wakeLock;
         mAllowPulseTriggers = allowPulseTriggers;
         mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters, config,
-                wakeLock, this::onSensor);
+                wakeLock, this::onSensor, this::onProximityFar);
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
     }
 
@@ -113,6 +114,22 @@
         }
     }
 
+    private void onProximityFar(boolean far) {
+        final boolean near = !far;
+        DozeMachine.State state = mMachine.getState();
+        if (near && state == DozeMachine.State.DOZE_PULSING) {
+            if (DEBUG) Log.i(TAG, "Prox NEAR, ending pulse");
+            mMachine.requestState(DozeMachine.State.DOZE_PULSE_DONE);
+        }
+        if (far && state == DozeMachine.State.DOZE_AOD_PAUSED) {
+            if (DEBUG) Log.i(TAG, "Prox FAR, unpausing AOD");
+            mMachine.requestState(DozeMachine.State.DOZE_AOD);
+        } else if (near && state == DozeMachine.State.DOZE_AOD) {
+            if (DEBUG) Log.i(TAG, "Prox NEAR, pausing AOD");
+            mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSED);
+        }
+    }
+
     private void onCarMode() {
         mMachine.requestState(DozeMachine.State.FINISH);
     }
@@ -131,15 +148,21 @@
                 break;
             case DOZE:
             case DOZE_AOD:
+            case DOZE_AOD_PAUSED:
+                mDozeSensors.setProxListening(newState != DozeMachine.State.DOZE);
                 mDozeSensors.setListening(true);
                 if (oldState != DozeMachine.State.INITIALIZED) {
                     mDozeSensors.reregisterAllSensors();
                 }
                 break;
+            case DOZE_PULSING:
+                mDozeSensors.setProxListening(true);
+                break;
             case FINISH:
                 mBroadcastReceiver.unregister(mContext);
                 mDozeHost.removeCallback(mHostCallback);
                 mDozeSensors.setListening(false);
+                mDozeSensors.setProxListening(false);
                 break;
             default:
         }
@@ -156,6 +179,7 @@
 
     private void requestPulse(final int reason, boolean performedProxCheck) {
         Assert.isMainThread();
+        mDozeHost.extendPulse();
         if (mPulsePending || !mAllowPulseTriggers || !canPulse()) {
             return;
         }
@@ -286,6 +310,8 @@
     }
 
     private class TriggerReceiver extends BroadcastReceiver {
+        private boolean mRegistered;
+
         @Override
         public void onReceive(Context context, Intent intent) {
             if (PULSE_ACTION.equals(intent.getAction())) {
@@ -301,14 +327,22 @@
         }
 
         public void register(Context context) {
+            if (mRegistered) {
+                return;
+            }
             IntentFilter filter = new IntentFilter(PULSE_ACTION);
             filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
             filter.addAction(Intent.ACTION_USER_SWITCHED);
             context.registerReceiver(this, filter);
+            mRegistered = true;
         }
 
         public void unregister(Context context) {
+            if (!mRegistered) {
+                return;
+            }
             context.unregisterReceiver(this);
+            mRegistered = false;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index f577654..6098a20 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -75,11 +75,14 @@
                 scheduleTimeTick();
                 break;
             case DOZE:
+            case DOZE_AOD_PAUSED:
                 unscheduleTimeTick();
                 break;
             case DOZE_REQUEST_PULSE:
                 pulseWhileDozing(DozeLog.PULSE_REASON_NOTIFICATION /* TODO */);
                 break;
+            case DOZE_PULSE_DONE:
+                mHost.abortPulsing();
             case INITIALIZED:
                 mHost.startDozing();
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 7b2e997..1a00d7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -158,6 +158,10 @@
         return sPickupSubtypePerformsProxMatcher.isIn(subType);
     }
 
+    public int getPulseVisibleDurationExtended() {
+        return 2 * getPulseVisibleDuration();
+    }
+
 
     /**
      * Parses a spec of the form `1,2,3,!5,*`. The resulting object will match numbers that are
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index b78f748..c5f23c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -30,6 +30,7 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
+import com.android.systemui.doze.DozeTriggers;
 
 /**
  * Controller which handles all the doze animations of the scrims.
@@ -43,8 +44,6 @@
     private final ScrimController mScrimController;
 
     private final Context mContext;
-    private final View mStackScroller;
-    private final NotificationPanelView mNotificationPanelView;
 
     private boolean mDozing;
     private DozeHost.PulseCallback mPulseCallback;
@@ -53,24 +52,22 @@
     private Animator mBehindAnimator;
     private float mInFrontTarget;
     private float mBehindTarget;
+    private boolean mDozingAborted;
 
-    public DozeScrimController(ScrimController scrimController, Context context,
-            View stackScroller, NotificationPanelView notificationPanelView) {
+    public DozeScrimController(ScrimController scrimController, Context context) {
         mContext = context;
-        mStackScroller = stackScroller;
         mScrimController = scrimController;
         mDozeParameters = new DozeParameters(context);
-        mNotificationPanelView = notificationPanelView;
     }
 
     public void setDozing(boolean dozing, boolean animate) {
         if (mDozing == dozing) return;
         mDozing = dozing;
         if (mDozing) {
+            mDozingAborted = false;
             abortAnimations();
             mScrimController.setDozeBehindAlpha(1f);
             mScrimController.setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? 0f : 1f);
-            mNotificationPanelView.setDark(true);
         } else {
             cancelPulsing();
             if (animate) {
@@ -85,8 +82,6 @@
                 mScrimController.setDozeBehindAlpha(0f);
                 mScrimController.setDozeInFrontAlpha(0f);
             }
-            // TODO: animate
-            mNotificationPanelView.setDark(false);
         }
     }
 
@@ -116,10 +111,19 @@
         cancelPulsing();
         if (mDozing) {
             mScrimController.setDozeBehindAlpha(1f);
-            mScrimController.setDozeInFrontAlpha(1f);
+            mScrimController.setDozeInFrontAlpha(
+                    mDozeParameters.getAlwaysOn() && !mDozingAborted ? 0f : 1f);
         }
     }
 
+    /**
+     * Aborts dozing immediately.
+     */
+    public void abortDoze() {
+        mDozingAborted = true;
+        abortPulsing();
+    }
+
     public void onScreenTurnedOn() {
         if (isPulsing()) {
             final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
@@ -139,12 +143,17 @@
         return mDozing;
     }
 
+    public void extendPulse() {
+        mHandler.removeCallbacks(mPulseOut);
+    }
+
     private void cancelPulsing() {
         if (DEBUG) Log.d(TAG, "Cancel pulsing");
 
         if (mPulseCallback != null) {
             mHandler.removeCallbacks(mPulseIn);
             mHandler.removeCallbacks(mPulseOut);
+            mHandler.removeCallbacks(mPulseOutExtended);
             pulseFinished();
         }
     }
@@ -271,12 +280,23 @@
             if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing);
             if (!mDozing) return;
             mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
+            mHandler.postDelayed(mPulseOutExtended,
+                    mDozeParameters.getPulseVisibleDurationExtended());
+        }
+    };
+
+    private final Runnable mPulseOutExtended = new Runnable() {
+        @Override
+        public void run() {
+            mHandler.removeCallbacks(mPulseOut);
+            mPulseOut.run();
         }
     };
 
     private final Runnable mPulseOut = new Runnable() {
         @Override
         public void run() {
+            mHandler.removeCallbacks(mPulseOutExtended);
             if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
             if (!mDozing) return;
             startScrimAnimation(true /* inFront */, mDozeParameters.getAlwaysOn() ? 0 : 1,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 2bb3cbc..9206914 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -221,7 +221,7 @@
             case MODE_WAKE_AND_UNLOCK:
                 Trace.beginSection("MODE_WAKE_AND_UNLOCK");
                 mStatusBarWindowManager.setStatusBarFocusable(false);
-                mDozeScrimController.abortPulsing();
+                mDozeScrimController.abortDoze();
                 mKeyguardViewMediator.onWakeAndUnlocking();
                 mScrimController.setWakeAndUnlocking();
                 if (mStatusBar.getNavigationBarView() != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 472af65..4156325 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1112,8 +1112,7 @@
         }
         mHeadsUpManager.addListener(mScrimController);
         mStackScroller.setScrimController(mScrimController);
-        mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller,
-                mNotificationPanel);
+        mDozeScrimController = new DozeScrimController(mScrimController, context);
 
         // Other icons
         mVolumeComponent = getComponent(VolumeComponent.class);
@@ -4330,6 +4329,7 @@
         mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
         mScrimController.setDozing(mDozing);
         mKeyguardIndicationController.setDozing(mDozing);
+        mNotificationPanel.setDark(mDozing);
 
         // Immediately abort the dozing from the doze scrim controller in case of wake-and-unlock
         // for pulsing so the Keyguard fade-out animation scrim can take over.
@@ -4956,6 +4956,7 @@
         mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD
                 || mFingerprintUnlockController.getMode()
                         == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+        mStatusBarWindowManager.setDozing(mDozing);
         updateDozingState();
         Trace.endSection();
     }
@@ -5063,6 +5064,16 @@
             StatusBar.this.startPendingIntentDismissingKeyguard(intent);
         }
 
+        @Override
+        public void abortPulsing() {
+            mDozeScrimController.abortPulsing();
+        }
+
+        @Override
+        public void extendPulse() {
+            mDozeScrimController.extendPulse();
+        }
+
     }
 
     // Begin Extra BaseStatusBar methods.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index deb0070..0326e40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -118,7 +118,7 @@
             mLpChanged.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
         }
 
-        if (state.keyguardShowing && !state.backdropShowing) {
+        if (state.keyguardShowing && !state.backdropShowing && !state.dozing) {
             mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
         } else {
             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -357,6 +357,11 @@
         apply(mCurrentState);
     }
 
+    public void setDozing(boolean dozing) {
+        mCurrentState.dozing = dozing;
+        apply(mCurrentState);
+    }
+
     public void setBarHeight(int barHeight) {
         mBarHeight = barHeight;
         apply(mCurrentState);
@@ -404,6 +409,7 @@
 
         boolean remoteInputActive;
         boolean forcePluginOpen;
+        boolean dozing;
 
         private boolean isKeyguardShowingAndNotOccluded() {
             return keyguardShowing && !keyguardOccluded;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 1848d4e..1a09d75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -255,6 +255,9 @@
         if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
             mStackScrollLayout.closeControlsIfOutsideTouch(ev);
         }
+        if (mService.isDozing()) {
+            mService.mDozeScrimController.extendPulse();
+        }
 
         return super.dispatchTouchEvent(ev);
     }