Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ee8da1ae authored by Robin Lee's avatar Robin Lee
Browse files

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
parent 049923ce
Loading
Loading
Loading
Loading
+40 −0
Original line number Diff line number Diff line
@@ -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.
     *
     * <p>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
+0 −7
Original line number Diff line number Diff line
@@ -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");
        }
+63 −58
Original line number Diff line number Diff line
@@ -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<IBinder, IRemoteTransition> mStartedTransitions = new ArrayMap<>();
    private final ArrayMap<IBinder, StartedTransition> 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.
+11 −3
Original line number Diff line number Diff line
@@ -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.
+9 −2
Original line number Diff line number Diff line
@@ -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<SurfaceControl, SurfaceControl> 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();
Loading