Fix lockscreen launch animations once and for all

In SysUI, make sure not to dismiss Keyguard multiple times when just
waiting for a deferred dismissal, so WindowManager doesn't get
multiple calls to keyguardGoingAway.

Change heuristics how notifying Keyguard about activity drawn works.
Always notify Keyguard after executing an app transition, and notify
it also when not doing a transition after a startActivity call.

For that to work, update AppWindowToken.startingDisplayed also when
the window is displayed, but force hidden because of Keyguard.

Further, handle the case correctly when a window gets added during
the Keyguard exit animation by overriding the start time for the
animation of that new window. Also don't apply a transition animation
for a window when executing keyguard exit animation, so by removing
a starting window we don't break this animation.

Last but not least, tell Keyguard to start exiting immediately if
animations for exiting are disabled, like when going to phone/camera
on lockscreen. Before, we always had a delay of 1 second because we
waited for the timeout.

Bug: 1599196
Bug: 18272544
Change-Id: I596b2489f814b934abd256e16079d3d3f326e209
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 172aaf6..7ca8fc1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -426,7 +426,9 @@
         }
 
         public void keyguardDone(boolean authenticated) {
-            KeyguardViewMediator.this.keyguardDone(authenticated, true);
+            if (!mKeyguardDonePending) {
+                KeyguardViewMediator.this.keyguardDone(authenticated, true);
+            }
         }
 
         public void keyguardDoneDrawing() {
@@ -1049,9 +1051,6 @@
     public void keyguardDone(boolean authenticated, boolean wakeup) {
         if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated + ")");
         EventLog.writeEvent(70000, 2);
-        synchronized (this) {
-            mKeyguardDonePending = false;
-        }
         Message msg = mHandler.obtainMessage(KEYGUARD_DONE, authenticated ? 1 : 0, wakeup ? 1 : 0);
         mHandler.sendMessage(msg);
     }
@@ -1122,6 +1121,9 @@
      */
     private void handleKeyguardDone(boolean authenticated, boolean wakeup) {
         if (DEBUG) Log.d(TAG, "handleKeyguardDone");
+        synchronized (this) {
+            mKeyguardDonePending = false;
+        }
 
         if (authenticated) {
             mUpdateMonitor.clearFailedUnlockAttempts();
@@ -1297,6 +1299,7 @@
     }
 
     private void handleOnActivityDrawn() {
+        if (DEBUG) Log.d(TAG, "handleOnActivityDrawn: mKeyguardDonePending=" + mKeyguardDonePending);
         if (mKeyguardDonePending) {
             mStatusBarKeyguardViewManager.onActivityDrawn();
         }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 0dae028..3a1fafe 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1538,9 +1538,6 @@
             ActivityOptions.abort(options);
             if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Top activity resumed " + next);
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-
-            // Make sure to notify Keyguard as well if it is waiting for an activity to be drawn.
-            mStackSupervisor.notifyActivityDrawnForKeyguard();
             return false;
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 099151f..120002e 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -656,7 +656,6 @@
 
     void reportActivityVisibleLocked(ActivityRecord r) {
         sendWaitingVisibleReportLocked(r);
-        notifyActivityDrawnForKeyguard();
     }
 
     void sendWaitingVisibleReportLocked(ActivityRecord r) {
@@ -1832,6 +1831,7 @@
                     final ActivityStack lastStack = getLastStack();
                     ActivityRecord curTop = lastStack == null?
                             null : lastStack.topRunningNonDelayedActivityLocked(notTop);
+                    boolean movedToFront = false;
                     if (curTop != null && (curTop.task != intentActivity.task ||
                             curTop.task != lastStack.topTask())) {
                         r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
@@ -1851,6 +1851,7 @@
                                 intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
                             }
                             options = null;
+                            movedToFront = true;
                         }
                     }
                     // If the caller has requested that the target task be
@@ -1865,6 +1866,12 @@
                         // sure we have correctly resumed the top activity.
                         if (doResume) {
                             resumeTopActivitiesLocked(targetStack, null, options);
+
+                            // Make sure to notify Keyguard as well if we are not running an app
+                            // transition later.
+                            if (!movedToFront) {
+                                notifyActivityDrawnForKeyguard();
+                            }
                         } else {
                             ActivityOptions.abort(options);
                         }
@@ -1956,6 +1963,11 @@
                         // sure we have correctly resumed the top activity.
                         if (doResume) {
                             targetStack.resumeTopActivityLocked(null, options);
+                            if (!movedToFront) {
+                                // Make sure to notify Keyguard as well if we are not running an app
+                                // transition later.
+                                notifyActivityDrawnForKeyguard();
+                            }
                         } else {
                             ActivityOptions.abort(options);
                         }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 82e4bb1..8af8578 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -21,6 +21,7 @@
 
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static com.android.server.wm.WindowManagerService.DEBUG_KEYGUARD;
 import static com.android.server.wm.WindowManagerService.LayoutFields.SET_UPDATE_ROTATION;
 import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPAPER_MAY_CHANGE;
@@ -241,6 +242,7 @@
                         winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f);
                         winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS);
                         winAnimator.mAnimationIsEntrance = false;
+                        winAnimator.mAnimationStartTime = -1;
                     }
                 } else {
                     if (DEBUG_KEYGUARD) Slog.d(TAG,
@@ -263,6 +265,7 @@
                 null : winShowWhenLocked.mAppToken;
 
         boolean wallpaperInUnForceHiding = false;
+        boolean startingInUnForceHiding = false;
         ArrayList<WindowStateAnimator> unForceHiding = null;
         WindowState wallpaper = null;
         for (int i = windows.size() - 1; i >= 0; i--) {
@@ -344,8 +347,13 @@
                         if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
                                 "Now policy hidden: " + win);
                     } else {
-                        if (!win.showLw(false, false)) {
-                            // Was already showing.
+                        boolean applyExistingExitAnimation = mPostKeyguardExitAnimation != null
+                                && !winAnimator.mKeyguardGoingAwayAnimation
+                                && win.hasDrawnLw();
+
+                        // If the window is already showing and we don't need to apply an existing
+                        // Keyguard exit animation, skip.
+                        if (!win.showLw(false, false) && !applyExistingExitAnimation) {
                             continue;
                         }
                         final boolean visibleNow = win.isVisibleNow();
@@ -364,11 +372,19 @@
                             if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
                                 wallpaperInUnForceHiding = true;
                             }
-                        } else if (mPostKeyguardExitAnimation != null) {
+                            if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
+                                startingInUnForceHiding = true;
+                            }
+                        } else if (applyExistingExitAnimation) {
                             // We're already in the middle of an animation. Use the existing
                             // animation to bring in this window.
-                            winAnimator.setAnimation(mPostKeyguardExitAnimation);
-                            winAnimator.keyguardGoingAwayAnimation = true;
+                            if (DEBUG_KEYGUARD) Slog.v(TAG,
+                                    "Applying existing Keyguard exit animation to new window: win="
+                                            + win);
+                            Animation a = mPolicy.createForceHideEnterAnimation(
+                                    false, mKeyguardGoingAwayToNotificationShade);
+                            winAnimator.setAnimation(a, mPostKeyguardExitAnimation.getStartTime());
+                            winAnimator.mKeyguardGoingAwayAnimation = true;
                         }
                         final WindowState currentFocus = mService.mCurrentFocus;
                         if (currentFocus == null || currentFocus.mLayer < win.mLayer) {
@@ -421,25 +437,34 @@
         } // end forall windows
 
         // If we have windows that are being show due to them no longer
-        // being force-hidden, apply the appropriate animation to them.
+        // being force-hidden, apply the appropriate animation to them if animations are not
+        // disabled.
         if (unForceHiding != null) {
-            // This only happens the first time that we detect the keyguard is animating out.
-            if (mKeyguardGoingAwayDisableWindowAnimations) {
-                if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: skipping anim for windows");
-            } else {
-                if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: created anim for windows="
-                        + unForceHiding);
-                mPostKeyguardExitAnimation = mPolicy.createForceHideEnterAnimation(
-                        wallpaperInUnForceHiding, mKeyguardGoingAwayToNotificationShade);
-            }
-            if (mPostKeyguardExitAnimation != null) {
+            if (!mKeyguardGoingAwayDisableWindowAnimations) {
+                boolean first = true;
                 for (int i=unForceHiding.size()-1; i>=0; i--) {
                     final WindowStateAnimator winAnimator = unForceHiding.get(i);
-                    winAnimator.setAnimation(mPostKeyguardExitAnimation);
-                    winAnimator.keyguardGoingAwayAnimation = true;
+                    Animation a = mPolicy.createForceHideEnterAnimation(
+                            wallpaperInUnForceHiding && !startingInUnForceHiding,
+                            mKeyguardGoingAwayToNotificationShade);
+                    if (a != null) {
+                        if (DEBUG_KEYGUARD) Slog.v(TAG,
+                                "Starting keyguard exit animation on window " + winAnimator.mWin);
+                        winAnimator.setAnimation(a);
+                        winAnimator.mKeyguardGoingAwayAnimation = true;
+                        if (first) {
+                            mPostKeyguardExitAnimation = a;
+                            mPostKeyguardExitAnimation.setStartTime(mCurrentTime);
+                            first = false;
+                        }
+                    }
                 }
+            } else if (mKeyguardGoingAway) {
+                mPolicy.startKeyguardExitAnimation(mCurrentTime, 0 /* duration */);
+                mKeyguardGoingAway = false;
             }
 
+
             // Wallpaper is going away in un-force-hide motion, animate it as well.
             if (!wallpaperInUnForceHiding && wallpaper != null
                     && !mKeyguardGoingAwayDisableWindowAnimations) {
@@ -459,8 +484,10 @@
                         mPostKeyguardExitAnimation.getStartOffset(),
                         mPostKeyguardExitAnimation.getDuration());
                 mKeyguardGoingAway = false;
-            } else if (mPostKeyguardExitAnimation.hasEnded()) {
+            } else if (mCurrentTime - mPostKeyguardExitAnimation.getStartTime()
+                    > mPostKeyguardExitAnimation.getDuration()) {
                 // Done with the animation, reset.
+                if (DEBUG_KEYGUARD) Slog.v(TAG, "Done with Keyguard exit animations.");
                 mPostKeyguardExitAnimation = null;
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6cb1e4a..1e492a5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1759,7 +1759,7 @@
             // wallpaper during the animation so it doesn't flicker out.
             final boolean hasWallpaper = (w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
                     || (w.mAppToken != null
-                            && w.mWinAnimator.keyguardGoingAwayAnimation);
+                            && w.mWinAnimator.mKeyguardGoingAwayAnimation);
             if (hasWallpaper && w.isOnScreen()
                     && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
                 if (DEBUG_WALLPAPER) Slog.v(TAG,
@@ -2541,8 +2541,8 @@
             }
             mInputMonitor.updateInputWindowsLw(false /*force*/);
 
-            if (true || localLOGV) Slog.v(TAG, "addWindow: New client " + client.asBinder()
-                    + ": window=" + win + " Callers=" + Debug.getCallers(5));
+            if (true || localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG, "addWindow: New client "
+                    + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));
 
             if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
                 reportNewConfig = true;
@@ -5411,7 +5411,7 @@
 
     public void notifyActivityDrawnForKeyguard() {
         if (DEBUG_KEYGUARD) Slog.d(TAG, "notifyActivityDrawnForKeyguard: waiting="
-                + mKeyguardWaitingForActivityDrawn);
+                + mKeyguardWaitingForActivityDrawn + " Callers=" + Debug.getCallers(5));
         synchronized (mWindowMap) {
             if (mKeyguardWaitingForActivityDrawn) {
                 mPolicy.notifyActivityDrawnForKeyguardLw();
@@ -9322,6 +9322,7 @@
             }
             updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, true /*updateInputWindows*/);
             mFocusMayChange = false;
+            notifyActivityDrawnForKeyguard();
         }
 
         return changes;
@@ -9809,7 +9810,8 @@
                                 atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
                                 atoken.startingDisplayed = false;
                             }
-                            if ((w.isOnScreen() || winAnimator.mAttrType == TYPE_BASE_APPLICATION)
+                            if ((w.isOnScreenIgnoringKeyguard()
+                                    || winAnimator.mAttrType == TYPE_BASE_APPLICATION)
                                     && !w.mExiting && !w.mDestroying) {
                                 if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
                                     Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f9efc80..021a6e4 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -935,7 +935,15 @@
      * being visible.
      */
     boolean isOnScreen() {
-        if (!mHasSurface || !mPolicyVisibility || mDestroying) {
+        return mPolicyVisibility && isOnScreenIgnoringKeyguard();
+    }
+
+    /**
+     * Like isOnScreen(), but ignores any force hiding of the window due
+     * to the keyguard.
+     */
+    boolean isOnScreenIgnoringKeyguard() {
+        if (!mHasSurface || mDestroying) {
             return false;
         }
         final AppWindowToken atoken = mAppToken;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 819ca50..e929065 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -97,6 +97,7 @@
     boolean mWasAnimating;      // Were we animating going into the most recent animation step?
     int mAnimLayer;
     int mLastLayer;
+    long mAnimationStartTime;
 
     SurfaceControl mSurfaceControl;
     SurfaceControl mPendingDestroySurface;
@@ -147,7 +148,7 @@
      * window is first added or shown, cleared when the callback has been made. */
     boolean mEnteringAnimation;
 
-    boolean keyguardGoingAwayAnimation;
+    boolean mKeyguardGoingAwayAnimation;
 
     /** This is set when there is no Surface */
     static final int NO_SURFACE = 0;
@@ -210,7 +211,7 @@
         mIsWallpaper = win.mIsWallpaper;
     }
 
-    public void setAnimation(Animation anim) {
+    public void setAnimation(Animation anim, long startTime) {
         if (localLOGV) Slog.v(TAG, "Setting animation in " + this + ": " + anim);
         mAnimating = false;
         mLocalAnimating = false;
@@ -221,6 +222,11 @@
         mTransformation.clear();
         mTransformation.setAlpha(mLastHidden ? 0 : 1);
         mHasLocalTransformation = true;
+        mAnimationStartTime = startTime;
+    }
+
+    public void setAnimation(Animation anim) {
+        setAnimation(anim, -1);
     }
 
     public void clearAnimation() {
@@ -229,7 +235,7 @@
             mLocalAnimating = false;
             mAnimation.cancel();
             mAnimation = null;
-            keyguardGoingAwayAnimation = false;
+            mKeyguardGoingAwayAnimation = false;
         }
     }
 
@@ -299,7 +305,9 @@
                     final DisplayInfo displayInfo = displayContent.getDisplayInfo();
                     mAnimDw = displayInfo.appWidth;
                     mAnimDh = displayInfo.appHeight;
-                    mAnimation.setStartTime(currentTime);
+                    mAnimation.setStartTime(mAnimationStartTime != -1
+                            ? mAnimationStartTime
+                            : currentTime);
                     mLocalAnimating = true;
                     mAnimating = true;
                 }
@@ -351,7 +359,7 @@
             + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false));
 
         mAnimating = false;
-        keyguardGoingAwayAnimation = false;
+        mKeyguardGoingAwayAnimation = false;
         mLocalAnimating = false;
         if (mAnimation != null) {
             mAnimation.cancel();
@@ -500,9 +508,6 @@
                 Slog.v(TAG, "Draw state now committed in " + mWin);
             }
             mDrawState = COMMIT_DRAW_PENDING;
-            if (startingWindow) {
-                mService.notifyActivityDrawnForKeyguard();
-            }
             return true;
         }
         return false;
@@ -1786,9 +1791,14 @@
      * @return true if an animation has been loaded.
      */
     boolean applyAnimationLocked(int transit, boolean isEntrance) {
-        if (mLocalAnimating && mAnimationIsEntrance == isEntrance) {
+        if ((mLocalAnimating && mAnimationIsEntrance == isEntrance)
+                || mKeyguardGoingAwayAnimation) {
             // If we are trying to apply an animation, but already running
-            // an animation of the same type, then just leave that one alone.
+            // an animation of the same type, or when we are playing the Keyguard dismissing
+            // animation, then just leave that one alone.
+
+            // TODO: if mKeyguardGoingAwayAnimation and this is a exiting starting window, modify
+            // existing animation to fade it out as well.
             return true;
         }