From ee8da1aefceb470a25bf206f3d732193957f9866 Mon Sep 17 00:00:00 2001 From: Robin Lee Date: Thu, 1 Jun 2023 19:18:08 +0200 Subject: [PATCH 1/2] Transition flags for Keyguard un/occlude/cancel Final keyguard state is computed in KeyguardController and passed through Transition flags to SystemUI for occlude status as well as (previously) visibility during unlock. The occlude state cannot be derived only from ChangeInfo items in the transition. Example 1: b/284096414 where an app launches two activities, one opaque and the second translucent. Closing/unoccluding the translucent activity leaves the opaque activity on screen. This should not be recorded as an unocclude, but all Keyguard sees in the Change diff is an OCCLUDE activity closing and nothing new opening. Example 2: b/282672298 in which a shared-element return transition from Photos to Camera generates two separate transitions: one for re-opening Camera, and a second for closing Photos. Because the final transition was for a closing OCCLUDE activity on its own, all Keyguard could interpret this as was a request to unocclude the keyguard and play the return-to-keyguard animation. These cases are fixed by the final state being sent after the transition, but the intermediate animation was wrong and showed the lock screen views for a few hundred milliseconds. We also introduce one more flag in opposition to KEYGUARD_GOING_AWAY, which is the flag KEYGUARD_APPEARING. This is used to wrap up swipe-to-unlock interactions which end with the device locked, since swipe-to-unlock optimistically generates a full transition for the unlock and to avoid jank we have to merge an inverse transition back in. Bug: 284096414 Bug: 282672298 Fix: 282302169 Fix: 282291585 Change-Id: Ib2c7acdace63729dd52711e47c8cc481eac45ebe --- core/java/android/view/WindowManager.java | 40 ++++++ core/java/android/window/TransitionInfo.java | 7 - .../keyguard/KeyguardTransitionHandler.java | 121 +++++++++--------- .../shell/transition/DefaultMixedHandler.java | 14 +- .../systemui/keyguard/KeyguardService.java | 11 +- .../KeyguardUnlockAnimationController.kt | 1 + .../keyguard/KeyguardViewMediator.java | 15 +-- .../com/android/server/wm/DisplayContent.java | 11 +- .../android/server/wm/KeyguardController.java | 22 +++- .../com/android/server/wm/Transition.java | 4 - .../server/wm/TransitionController.java | 8 +- .../server/wm/WallpaperController.java | 14 +- 12 files changed, 165 insertions(+), 103 deletions(-) diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e40f8e78dafcf..d0562b7b8c0dc 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -435,11 +435,15 @@ public interface WindowManager extends ViewManager { int TRANSIT_KEYGUARD_GOING_AWAY = 7; /** * A window is appearing above a locked keyguard. + * @deprecated use {@link #TRANSIT_TO_FRONT} + {@link #TRANSIT_FLAG_KEYGUARD_OCCLUDING} for + * keyguard occluding with Shell transition. * @hide */ int TRANSIT_KEYGUARD_OCCLUDE = 8; /** * A window is made invisible revealing a locked keyguard. + * @deprecated use {@link #TRANSIT_TO_BACK} + {@link #TRANSIT_FLAG_KEYGUARD_UNOCCLUDING} for + * keyguard occluding with Shell transition. * @hide */ int TRANSIT_KEYGUARD_UNOCCLUDE = 9; @@ -561,6 +565,25 @@ public interface WindowManager extends ViewManager { */ int TRANSIT_FLAG_INVISIBLE = (1 << 10); // 0x400 + /** + * Transition flag: Indicates that keyguard will be showing (locked) with this transition, + * which is the opposite of {@link #TRANSIT_FLAG_KEYGUARD_GOING_AWAY}. + * @hide + */ + int TRANSIT_FLAG_KEYGUARD_APPEARING = (1 << 11); // 0x800 + + /** + * Transition flag: Indicates that keyguard is becoming hidden by an app + * @hide + */ + int TRANSIT_FLAG_KEYGUARD_OCCLUDING = (1 << 12); // 0x1000 + + /** + * Transition flag: Indicates that keyguard is being revealed after an app was occluding it. + * @hide + */ + int TRANSIT_FLAG_KEYGUARD_UNOCCLUDING = (1 << 13); // 0x2000 + /** * @hide */ @@ -576,10 +599,27 @@ public interface WindowManager extends ViewManager { TRANSIT_FLAG_KEYGUARD_GOING_AWAY, TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT, TRANSIT_FLAG_INVISIBLE, + TRANSIT_FLAG_KEYGUARD_APPEARING, + TRANSIT_FLAG_KEYGUARD_OCCLUDING, + TRANSIT_FLAG_KEYGUARD_UNOCCLUDING }) @Retention(RetentionPolicy.SOURCE) @interface TransitionFlags {} + /** + * Transit flags used to signal keyguard visibility is changing for animations. + * + *

These roughly correspond to CLOSE, OPEN, TO_BACK, and TO_FRONT on a hypothetical Keyguard + * container. Since Keyguard isn't a container we can't include it in changes and need to send + * this information in its own channel. + * @hide + */ + int KEYGUARD_VISIBILITY_TRANSIT_FLAGS = + (TRANSIT_FLAG_KEYGUARD_GOING_AWAY + | TRANSIT_FLAG_KEYGUARD_APPEARING + | TRANSIT_FLAG_KEYGUARD_OCCLUDING + | TRANSIT_FLAG_KEYGUARD_UNOCCLUDING); + /** * Remove content mode: Indicates remove content mode is currently not defined. * @hide diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index a92ca8a4d3f26..59238b40e0c8e 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -99,9 +99,6 @@ public final class TransitionInfo implements Parcelable { /** The container is the display. */ public static final int FLAG_IS_DISPLAY = 1 << 5; - /** The container can show on top of lock screen. */ - public static final int FLAG_OCCLUDES_KEYGUARD = 1 << 6; - /** * Only for IS_DISPLAY containers. Is set if the display has system alert windows. This is * used to prevent seamless rotation. @@ -175,7 +172,6 @@ public final class TransitionInfo implements Parcelable { FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT, FLAG_IS_VOICE_INTERACTION, FLAG_IS_DISPLAY, - FLAG_OCCLUDES_KEYGUARD, FLAG_DISPLAY_HAS_ALERT_WINDOWS, FLAG_IS_INPUT_METHOD, FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY, @@ -457,9 +453,6 @@ public final class TransitionInfo implements Parcelable { if ((flags & FLAG_IS_DISPLAY) != 0) { sb.append(sb.length() == 0 ? "" : "|").append("IS_DISPLAY"); } - if ((flags & FLAG_OCCLUDES_KEYGUARD) != 0) { - sb.append(sb.length() == 0 ? "" : "|").append("OCCLUDES_KEYGUARD"); - } if ((flags & FLAG_DISPLAY_HAS_ALERT_WINDOWS) != 0) { sb.append(sb.length() == 0 ? "" : "|").append("DISPLAY_HAS_ALERT_WINDOWS"); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java index 513638eeb9607..cef7e16663312 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java @@ -17,31 +17,25 @@ package com.android.wm.shell.keyguard; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; -import static android.view.WindowManager.TRANSIT_CLOSE; -import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; -import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; -import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; -import static android.view.WindowManager.TRANSIT_NONE; -import static android.view.WindowManager.TRANSIT_OPEN; -import static android.view.WindowManager.TRANSIT_SLEEP; -import static android.view.WindowManager.TRANSIT_TO_BACK; -import static android.view.WindowManager.TRANSIT_TO_FRONT; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; +import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; -import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_OCCLUDING; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING; +import static android.view.WindowManager.TRANSIT_SLEEP; import static com.android.wm.shell.util.TransitionUtil.isOpeningType; -import static com.android.wm.shell.util.TransitionUtil.isClosingType; import android.annotation.NonNull; import android.annotation.Nullable; -import android.os.RemoteException; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import android.view.SurfaceControl; +import android.view.WindowManager; import android.window.IRemoteTransition; import android.window.IRemoteTransitionFinishedCallback; import android.window.TransitionInfo; @@ -56,8 +50,6 @@ import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions.TransitionFinishCallback; -import java.util.Map; - /** * The handler for Keyguard enter/exit and occlude/unocclude animations. * @@ -70,7 +62,7 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler private final Handler mMainHandler; private final ShellExecutor mMainExecutor; - private final Map mStartedTransitions = new ArrayMap<>(); + private final ArrayMap mStartedTransitions = new ArrayMap<>(); /** * Local IRemoteTransition implementations registered by the keyguard service. @@ -81,6 +73,18 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler private IRemoteTransition mOccludeByDreamTransition = null; private IRemoteTransition mUnoccludeTransition = null; + private final class StartedTransition { + final TransitionInfo mInfo; + final SurfaceControl.Transaction mFinishT; + final IRemoteTransition mPlayer; + + public StartedTransition(TransitionInfo info, + SurfaceControl.Transaction finishT, IRemoteTransition player) { + mInfo = info; + mFinishT = finishT; + mPlayer = player; + } + } public KeyguardTransitionHandler( @NonNull ShellInit shellInit, @NonNull Transitions transitions, @@ -105,10 +109,7 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler } public static boolean handles(TransitionInfo info) { - return (info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0 - || (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0 - || info.getType() == TRANSIT_KEYGUARD_OCCLUDE - || info.getType() == TRANSIT_KEYGUARD_UNOCCLUDE; + return (info.getFlags() & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0; } @Override @@ -120,39 +121,14 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler return false; } - boolean hasOpeningOcclude = false; - boolean hasClosingOcclude = false; - boolean hasOpeningDream = false; - boolean hasClosingApp = false; - - // Check for occluding/dream/closing apps - for (int i = info.getChanges().size() - 1; i >= 0; i--) { - final TransitionInfo.Change change = info.getChanges().get(i); - if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) { - continue; - } else if (isOpeningType(change.getMode())) { - hasOpeningOcclude |= change.hasFlags(FLAG_OCCLUDES_KEYGUARD); - hasOpeningDream |= (change.getTaskInfo() != null - && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_DREAM); - } else if (isClosingType(change.getMode())) { - hasClosingOcclude |= change.hasFlags(FLAG_OCCLUDES_KEYGUARD); - hasClosingApp = true; - } - } - // Choose a transition applicable for the changes and keyguard state. if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) { return startAnimation(mExitTransition, "going-away", transition, info, startTransaction, finishTransaction, finishCallback); } - if (hasOpeningOcclude || info.getType() == TRANSIT_KEYGUARD_OCCLUDE) { - if (hasClosingOcclude) { - // Transitions between apps on top of the keyguard can use the default handler. - // WM sends a final occlude status update after the transition is finished. - return false; - } - if (hasOpeningDream) { + if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) != 0) { + if (hasOpeningDream(info)) { return startAnimation(mOccludeByDreamTransition, "occlude-by-dream", transition, info, startTransaction, finishTransaction, finishCallback); @@ -161,12 +137,12 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler "occlude", transition, info, startTransaction, finishTransaction, finishCallback); } - } else if (hasClosingApp || info.getType() == TRANSIT_KEYGUARD_UNOCCLUDE) { + } else if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) { return startAnimation(mUnoccludeTransition, "unocclude", transition, info, startTransaction, finishTransaction, finishCallback); } else { - Log.w(TAG, "Failed to play: " + info); + Log.i(TAG, "Refused to play keyguard transition: " + info); return false; } } @@ -194,7 +170,8 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler }); } }); - mStartedTransitions.put(transition, remoteHandler); + mStartedTransitions.put(transition, + new StartedTransition(info, finishTransaction, remoteHandler)); } catch (RemoteException e) { Log.wtf(TAG, "RemoteException thrown from local IRemoteTransition", e); return false; @@ -207,20 +184,35 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler public void mergeAnimation(@NonNull IBinder nextTransition, @NonNull TransitionInfo nextInfo, @NonNull SurfaceControl.Transaction nextT, @NonNull IBinder currentTransition, @NonNull TransitionFinishCallback nextFinishCallback) { - final IRemoteTransition playing = mStartedTransitions.get(currentTransition); - + final StartedTransition playing = mStartedTransitions.get(currentTransition); if (playing == null) { ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "unknown keyguard transition %s", currentTransition); return; } - - if (nextInfo.getType() == TRANSIT_SLEEP) { + if ((nextInfo.getFlags() & WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING) != 0 + && (playing.mInfo.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) { + // Keyguard unlocking has been canceled. Merge the unlock and re-lock transitions to + // avoid a flicker where we flash one frame with the screen fully unlocked. + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "canceling keyguard exit transition %s", currentTransition); + playing.mFinishT.merge(nextT); + try { + playing.mPlayer.mergeAnimation(nextTransition, nextInfo, nextT, currentTransition, + new FakeFinishCallback()); + } catch (RemoteException e) { + // There is no good reason for this to happen because the player is a local object + // implementing an AIDL interface. + Log.wtf(TAG, "RemoteException thrown from KeyguardService transition", e); + } + nextFinishCallback.onTransitionFinished(null, null); + } else if (nextInfo.getType() == TRANSIT_SLEEP) { // An empty SLEEP transition comes in as a signal to abort transitions whenever a sleep // token is held. In cases where keyguard is showing, we are running the animation for // the device sleeping/waking, so it's best to ignore this and keep playing anyway. return; - } else { + } else if (handles(nextInfo)) { + // In all other cases, fast-forward to let the next queued transition start playing. finishAnimationImmediately(currentTransition, playing); } } @@ -228,7 +220,7 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler @Override public void onTransitionConsumed(IBinder transition, boolean aborted, SurfaceControl.Transaction finishTransaction) { - final IRemoteTransition playing = mStartedTransitions.remove(transition); + final StartedTransition playing = mStartedTransitions.remove(transition); if (playing != null) { finishAnimationImmediately(transition, playing); } @@ -241,13 +233,26 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler return null; } - private void finishAnimationImmediately(IBinder transition, IRemoteTransition playing) { + private static boolean hasOpeningDream(@NonNull TransitionInfo info) { + for (int i = info.getChanges().size() - 1; i >= 0; i--) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (isOpeningType(change.getMode()) + && change.getTaskInfo() != null + && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_DREAM) { + return true; + } + } + return false; + } + + private void finishAnimationImmediately(IBinder transition, StartedTransition playing) { final IBinder fakeTransition = new Binder(); final TransitionInfo fakeInfo = new TransitionInfo(TRANSIT_SLEEP, 0x0); final SurfaceControl.Transaction fakeT = new SurfaceControl.Transaction(); final FakeFinishCallback fakeFinishCb = new FakeFinishCallback(); try { - playing.mergeAnimation(fakeTransition, fakeInfo, fakeT, transition, fakeFinishCb); + playing.mPlayer.mergeAnimation( + fakeTransition, fakeInfo, fakeT, transition, fakeFinishCb); } catch (RemoteException e) { // There is no good reason for this to happen because the player is a local object // implementing an AIDL interface. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 64571e067a832..3ec433e55da96 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -31,6 +31,7 @@ import static com.android.wm.shell.util.TransitionUtil.isOpeningType; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.IBinder; +import android.util.Log; import android.util.Pair; import android.view.SurfaceControl; import android.view.WindowManager; @@ -566,11 +567,18 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { - boolean consumed = mKeyguardHandler.startAnimation( - mixed.mTransition, info, startTransaction, finishTransaction, finishCallback); - if (!consumed) { + final Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> { + mixed.mInFlightSubAnimations--; + if (mixed.mInFlightSubAnimations == 0) { + mActiveTransitions.remove(mixed); + finishCallback.onTransitionFinished(wct, wctCB); + } + }; + if (!mKeyguardHandler.startAnimation( + mixed.mTransition, info, startTransaction, finishTransaction, finishCB)) { return false; } + mixed.mInFlightSubAnimations++; // Sync pip state. if (mPipHandler != null) { // We don't know when to apply `startTransaction` so use a separate transaction here. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 10db9050caf50..94227bccfced5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -19,6 +19,7 @@ package com.android.systemui.keyguard; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; @@ -182,8 +183,8 @@ public class KeyguardService extends Service { // Wrap Keyguard going away animation. // Note: Also used for wrapping occlude by Dream animation. It works (with some redundancy). - public static IRemoteTransition wrap(IRemoteAnimationRunner runner, - boolean lockscreenLiveWallpaperEnabled) { + public static IRemoteTransition wrap(final KeyguardViewMediator keyguardViewMediator, + final IRemoteAnimationRunner runner, final boolean lockscreenLiveWallpaperEnabled) { return new IRemoteTransition.Stub() { private final ArrayMap mLeashMap = new ArrayMap<>(); @@ -239,6 +240,12 @@ public class KeyguardService extends Service { SurfaceControl.Transaction candidateT, IBinder currentTransition, IRemoteTransitionFinishedCallback candidateFinishCallback) throws RemoteException { + if ((candidateInfo.getFlags() & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0) { + keyguardViewMediator.setPendingLock(true); + keyguardViewMediator.cancelKeyguardExitAnimation(); + return; + } + try { synchronized (mLeashMap) { runner.onAnimationCancelled(); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index fe804661914dd..28845a3bb52d3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -649,6 +649,7 @@ class KeyguardUnlockAnimationController @Inject constructor( @VisibleForTesting fun unlockToLauncherWithInWindowAnimations() { + surfaceBehindAlpha = 1f setSurfaceBehindAppearAmount(1f, wallpapers = false) try { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 992add63e23ca..6680a431487e2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1352,12 +1352,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, setShowingLocked(false /* showing */, true /* forceCallbacks */); } - boolean isLockscreenLwpEnabled = getWallpaperManager().isLockscreenLiveWallpaperEnabled(); + boolean isLLwpEnabled = getWallpaperManager().isLockscreenLiveWallpaperEnabled(); mKeyguardTransitions.register( - KeyguardService.wrap(getExitAnimationRunner(), isLockscreenLwpEnabled), - KeyguardService.wrap(getOccludeAnimationRunner(), isLockscreenLwpEnabled), - KeyguardService.wrap(getOccludeByDreamAnimationRunner(), isLockscreenLwpEnabled), - KeyguardService.wrap(getUnoccludeAnimationRunner(), isLockscreenLwpEnabled)); + KeyguardService.wrap(this, getExitAnimationRunner(), isLLwpEnabled), + KeyguardService.wrap(this, getOccludeAnimationRunner(), isLLwpEnabled), + KeyguardService.wrap(this, getOccludeByDreamAnimationRunner(), isLLwpEnabled), + KeyguardService.wrap(this, getUnoccludeAnimationRunner(), isLLwpEnabled)); final ContentResolver cr = mContext.getContentResolver(); @@ -2906,6 +2906,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // re-locking. We should just end the surface-behind animation without exiting the // keyguard. The pending lock will be handled by onFinishedGoingToSleep(). finishSurfaceBehindRemoteAnimation(true); + maybeHandlePendingLock(); } else { Log.d(TAG, "#handleCancelKeyguardExitAnimation: keyguard exit animation cancelled. " + "No pending lock, we should end up unlocked with the app/launcher visible."); @@ -3261,8 +3262,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, /** * Cancel the keyguard exit animation, usually because we were swiping to unlock but WM starts * a new remote animation before finishing the keyguard exit animation. - * - * This will dismiss the keyguard. */ public void cancelKeyguardExitAnimation() { Trace.beginSection("KeyguardViewMediator#cancelKeyguardExitAnimation"); @@ -3435,7 +3434,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } } - private void setPendingLock(boolean hasPendingLock) { + public void setPendingLock(boolean hasPendingLock) { mPendingLock = hasPendingLock; Trace.traceCounter(Trace.TRACE_TAG_APP, "pendingLock", mPendingLock ? 1 : 0); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 6aec96988a772..7fc86b0fdc016 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5602,17 +5602,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ void requestTransitionAndLegacyPrepare(@WindowManager.TransitionType int transit, @WindowManager.TransitionFlags int flags) { - prepareAppTransition(transit, flags); - mTransitionController.requestTransitionIfNeeded(transit, flags, - null /* trigger */, this); + requestTransitionAndLegacyPrepare(transit, flags, null /* trigger */); } /** @see #requestTransitionAndLegacyPrepare(int, int) */ void requestTransitionAndLegacyPrepare(@WindowManager.TransitionType int transit, - @Nullable WindowContainer trigger) { - prepareAppTransition(transit); - mTransitionController.requestTransitionIfNeeded(transit, 0 /* flags */, - trigger, this); + @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger) { + prepareAppTransition(transit, flags); + mTransitionController.requestTransitionIfNeeded(transit, flags, trigger, this); } void executeAppTransition() { diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 99878a3d0ffe5..ad9c3b2742673 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -19,17 +19,21 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_OCCLUDING; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT; @@ -171,10 +175,11 @@ class KeyguardController { final KeyguardDisplayState state = getDisplayState(displayId); final boolean aodChanged = aodShowing != state.mAodShowing; final boolean aodRemoved = state.mAodShowing && !aodShowing; + final boolean goingAwayRemoved = state.mKeyguardGoingAway && keyguardShowing; // If keyguard is going away, but SystemUI aborted the transition, need to reset state. // Do not reset keyguardChanged status when only AOD is removed. final boolean keyguardChanged = (keyguardShowing != state.mKeyguardShowing) - || (state.mKeyguardGoingAway && keyguardShowing && !aodRemoved); + || (goingAwayRemoved && !aodRemoved); if (aodRemoved) { updateDeferTransitionForAod(false /* waiting */); } @@ -214,6 +219,15 @@ class KeyguardController { if (keyguardShowing) { state.mDismissalRequested = false; } + if (goingAwayRemoved) { + // Keyguard dismiss is canceled. Send a transition to undo the changes and clean up + // before holding the sleep token again. + final DisplayContent dc = mRootWindowContainer.getDefaultDisplay(); + dc.requestTransitionAndLegacyPrepare( + TRANSIT_TO_FRONT, TRANSIT_FLAG_KEYGUARD_APPEARING); + dc.mWallpaperController.showWallpaperInTransition(false /* showHome */); + mWindowManager.executeAppTransition(); + } } // Update the sleep token first such that ensureActivitiesVisible has correct sleep token @@ -269,7 +283,7 @@ class KeyguardController { updateKeyguardSleepToken(); // Make the home wallpaper visible - dc.mWallpaperController.showHomeWallpaperInTransition(); + dc.mWallpaperController.showWallpaperInTransition(true /* showHome */); // Some stack visibility might change (e.g. docked stack) mRootWindowContainer.resumeFocusedTasksTopActivities(); mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); @@ -413,10 +427,12 @@ class KeyguardController { if (isDisplayOccluded(DEFAULT_DISPLAY)) { mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare( TRANSIT_KEYGUARD_OCCLUDE, + TRANSIT_FLAG_KEYGUARD_OCCLUDING, topActivity != null ? topActivity.getRootTask() : null); } else { mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare( - TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */); + TRANSIT_KEYGUARD_UNOCCLUDE, + TRANSIT_FLAG_KEYGUARD_UNOCCLUDING); } updateKeyguardSleepToken(DEFAULT_DISPLAY); mWindowManager.executeAppTransition(); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index b938b9e4ec51e..9a5f766989ca8 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -52,7 +52,6 @@ import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; import static android.window.TransitionInfo.FLAG_NO_ANIMATION; -import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD; import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static android.window.TransitionInfo.FLAG_TASK_LAUNCHING_BEHIND; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; @@ -2820,9 +2819,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } } - if (occludesKeyguard(wc)) { - flags |= FLAG_OCCLUDES_KEYGUARD; - } if ((mFlags & FLAG_CHANGE_NO_ANIMATION) != 0 && (mFlags & FLAG_CHANGE_YES_ANIMATION) == 0) { flags |= FLAG_NO_ANIMATION; diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 1c6a412e54503..0cb6f14b38f2c 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -16,10 +16,10 @@ package com.android.server.wm; +import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; @@ -619,9 +619,9 @@ class TransitionController { } // Make the collecting transition wait until this request is ready. mCollectingTransition.setReady(readyGroupRef, false); - if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) { - // Add keyguard flag to dismiss keyguard - mCollectingTransition.addFlag(flags); + if ((flags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0) { + // Add keyguard flags to affect keyguard visibility + mCollectingTransition.addFlag(flags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS); } } else { newTransition = requestStartTransition(createTransition(type, flags), diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 20ce98ca2faee..0e928ead2f39e 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -339,12 +339,12 @@ class WallpaperController { } /** - * Change the visibility if wallpaper is home screen only. + * Make one wallpaper visible, according to {@attr showHome}. * This is called during the keyguard unlocking transition - * (see {@link KeyguardController#keyguardGoingAway(int, int)}) and thus assumes that if the - * system wallpaper is shared with lock, then it needs no animation. + * (see {@link KeyguardController#keyguardGoingAway(int, int)}), + * or when a keyguard unlock is cancelled (see {@link KeyguardController}) */ - public void showHomeWallpaperInTransition() { + public void showWallpaperInTransition(boolean showHome) { updateWallpaperWindowsTarget(mFindResults); if (!mFindResults.hasTopShowWhenLockedWallpaper()) { @@ -357,9 +357,9 @@ class WallpaperController { // Shared wallpaper, ensure its visibility showWhenLocked.mToken.asWallpaperToken().updateWallpaperWindows(true); } else { - // Separate lock and home wallpapers: show home wallpaper and hide lock - hideWhenLocked.mToken.asWallpaperToken().updateWallpaperWindowsInTransition(true); - showWhenLocked.mToken.asWallpaperToken().updateWallpaperWindowsInTransition(false); + // Separate lock and home wallpapers: show the correct wallpaper in transition + hideWhenLocked.mToken.asWallpaperToken().updateWallpaperWindowsInTransition(showHome); + showWhenLocked.mToken.asWallpaperToken().updateWallpaperWindowsInTransition(!showHome); } } From 209d5a532831bb71e1c259b2b3a02bab315e5aa6 Mon Sep 17 00:00:00 2001 From: Robin Lee Date: Thu, 1 Jun 2023 21:13:49 +0000 Subject: [PATCH 2/2] Don't let expanding notification shade cancel unlock At the end of unlock, views start resetting themselves. This includes the notification shade which goes from 0f height to 1f height (covering the keyguard again). Since notification shade height is 1 minus keyguard height, and the full unlock finalisation happens in a handler to avoid jank, this can cause a second hit to dismissAmountThresholdsReached which cancels the unlock. Test: android.platform.test.scenario.sysui.foldable Bug: 284096414 Bug: 282672298 Change-Id: Ic9469c015c47aade323710a877923e8b0c8960b9 --- .../KeyguardUnlockAnimationController.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 28845a3bb52d3..57c7b09759a5f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -205,6 +205,12 @@ class KeyguardUnlockAnimationController @Inject constructor( */ var playingCannedUnlockAnimation = false + /** + * Whether we reached the swipe gesture threshold to dismiss keyguard, or restore it, once + * and should ignore any future changes to the dismiss amount before the animation finishes. + */ + var dismissAmountThresholdsReached = false + /** * Remote callback provided by Launcher that allows us to control the Launcher's unlock * animation and smartspace. @@ -763,6 +769,10 @@ class KeyguardUnlockAnimationController @Inject constructor( return } + if (dismissAmountThresholdsReached) { + return + } + if (!keyguardStateController.isShowing) { return } @@ -794,6 +804,11 @@ class KeyguardUnlockAnimationController @Inject constructor( return } + // no-op if we alreaddy reached a threshold. + if (dismissAmountThresholdsReached) { + return + } + // no-op if animation is not requested yet. if (!keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() || !keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) { @@ -808,6 +823,7 @@ class KeyguardUnlockAnimationController @Inject constructor( !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture && dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)) { setSurfaceBehindAppearAmount(1f) + dismissAmountThresholdsReached = true keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation( false /* cancelled */) } @@ -942,6 +958,7 @@ class KeyguardUnlockAnimationController @Inject constructor( wallpaperTargets = null playingCannedUnlockAnimation = false + dismissAmountThresholdsReached = false willUnlockWithInWindowLauncherAnimations = false willUnlockWithSmartspaceTransition = false