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

Commit 3a11a943 authored by Robin Lee's avatar Robin Lee Committed by Automerger Merge Worker
Browse files

Merge "Inject a handler for Keyguard transitions" into udc-dev am: 983bf1c5

parents 7478bab7 983bf1c5
Loading
Loading
Loading
Loading
+24 −0
Original line number Original line Diff line number Diff line
@@ -77,6 +77,8 @@ import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.keyguard.KeyguardTransitions;
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.recents.RecentsTransitionHandler;
@@ -561,6 +563,28 @@ public abstract class WMShellBaseModule {
        return new TaskViewTransitions(transitions);
        return new TaskViewTransitions(transitions);
    }
    }


    //
    // Keyguard transitions (optional feature)
    //

    @WMSingleton
    @Provides
    static KeyguardTransitionHandler provideKeyguardTransitionHandler(
            ShellInit shellInit,
            Transitions transitions,
            @ShellMainThread Handler mainHandler,
            @ShellMainThread ShellExecutor mainExecutor) {
        return new KeyguardTransitionHandler(
                    shellInit, transitions, mainHandler, mainExecutor);
    }

    @WMSingleton
    @Provides
    static KeyguardTransitions provideKeyguardTransitions(
            KeyguardTransitionHandler handler) {
        return handler.asKeyguardTransitions();
    }

    //
    //
    // Display areas
    // Display areas
    //
    //
+3 −1
Original line number Original line Diff line number Diff line
@@ -60,6 +60,7 @@ import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler;
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler;
import com.android.wm.shell.freeform.FreeformTaskTransitionObserver;
import com.android.wm.shell.freeform.FreeformTaskTransitionObserver;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.Pip;
@@ -532,9 +533,10 @@ public abstract class WMShellModule {
            Optional<SplitScreenController> splitScreenOptional,
            Optional<SplitScreenController> splitScreenOptional,
            Optional<PipTouchHandler> pipTouchHandlerOptional,
            Optional<PipTouchHandler> pipTouchHandlerOptional,
            Optional<RecentsTransitionHandler> recentsTransitionHandler,
            Optional<RecentsTransitionHandler> recentsTransitionHandler,
            KeyguardTransitionHandler keyguardTransitionHandler,
            Transitions transitions) {
            Transitions transitions) {
        return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
        return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
                pipTouchHandlerOptional, recentsTransitionHandler);
                pipTouchHandlerOptional, recentsTransitionHandler, keyguardTransitionHandler);
    }
    }


    @WMSingleton
    @WMSingleton
+274 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

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.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;

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.util.ArrayMap;
import android.util.Log;
import android.view.SurfaceControl;
import android.window.IRemoteTransition;
import android.window.IRemoteTransitionFinishedCallback;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;

import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
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.
 *
 * <p>This takes the highest priority.
 */
public class KeyguardTransitionHandler implements Transitions.TransitionHandler {
    private static final String TAG = "KeyguardTransition";

    private final Transitions mTransitions;
    private final Handler mMainHandler;
    private final ShellExecutor mMainExecutor;

    private final Map<IBinder, IRemoteTransition> mStartedTransitions = new ArrayMap<>();

    /**
     * Local IRemoteTransition implementations registered by the keyguard service.
     * @see KeyguardTransitions
     */
    private IRemoteTransition mExitTransition = null;
    private IRemoteTransition mOccludeTransition = null;
    private IRemoteTransition mOccludeByDreamTransition = null;
    private IRemoteTransition mUnoccludeTransition = null;

    public KeyguardTransitionHandler(
            @NonNull ShellInit shellInit,
            @NonNull Transitions transitions,
            @NonNull Handler mainHandler,
            @NonNull ShellExecutor mainExecutor) {
        mTransitions = transitions;
        mMainHandler = mainHandler;
        mMainExecutor = mainExecutor;
        shellInit.addInitCallback(this::onInit, this);
    }

    private void onInit() {
        mTransitions.addHandler(this);
    }

    /**
     * Interface for SystemUI implementations to set custom Keyguard exit/occlude handlers.
     */
    @ExternalThread
    public KeyguardTransitions asKeyguardTransitions() {
        return new KeyguardTransitionsImpl();
    }

    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;
    }

    @Override
    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull TransitionFinishCallback finishCallback) {
        if (!handles(info)) {
            return false;
        }

        boolean hasOpeningOcclude = 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 (isOpeningType(change.getMode())) {
                if (change.hasFlags(FLAG_OCCLUDES_KEYGUARD)) {
                    hasOpeningOcclude = true;
                }
                if (change.getTaskInfo() != null
                        && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_DREAM) {
                    hasOpeningDream = true;
                }
            } else if (isClosingType(change.getMode())) {
                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 (hasOpeningDream) {
                return startAnimation(mOccludeByDreamTransition,
                        "occlude-by-dream",
                        transition, info, startTransaction, finishTransaction, finishCallback);
            } else {
                return startAnimation(mOccludeTransition,
                        "occlude",
                        transition, info, startTransaction, finishTransaction, finishCallback);
            }
        } else if (hasClosingApp || info.getType() == TRANSIT_KEYGUARD_UNOCCLUDE) {
             return startAnimation(mUnoccludeTransition,
                    "unocclude",
                    transition, info, startTransaction, finishTransaction, finishCallback);
        } else {
            Log.wtf(TAG, "Failed to play: " + info);
            return false;
        }
    }

    private boolean startAnimation(IRemoteTransition remoteHandler, String description,
            @NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull TransitionFinishCallback finishCallback) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                "start keyguard %s transition, info = %s", description, info);

        try {
            remoteHandler.startAnimation(transition, info, startTransaction,
                    new IRemoteTransitionFinishedCallback.Stub() {
                        @Override
                        public void onTransitionFinished(
                                WindowContainerTransaction wct, SurfaceControl.Transaction sct) {
                            mMainExecutor.execute(() -> {
                                finishCallback.onTransitionFinished(wct, null);
                            });
                        }
                    });
            mStartedTransitions.put(transition, remoteHandler);
        } catch (RemoteException e) {
            Log.wtf(TAG, "RemoteException thrown from local IRemoteTransition", e);
            return false;
        }
        startTransaction.clear();
        return true;
    }

    @Override
    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);

        if (playing == null) {
            ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, 
                    "unknown keyguard transition %s", currentTransition);
            return;
        }

        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 {
            finishAnimationImmediately(currentTransition);
        }
    }

    @Override
    public void onTransitionConsumed(IBinder transition, boolean aborted,
            SurfaceControl.Transaction finishTransaction) {
        finishAnimationImmediately(transition);
    }

    @Nullable
    @Override
    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
            @NonNull TransitionRequestInfo request) {
        return null;
    }

    private void finishAnimationImmediately(IBinder transition) {
        final IRemoteTransition playing = mStartedTransitions.get(transition);

        if (playing != null) {
            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);
            } 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);
            }
        }
    }

    private static class FakeFinishCallback extends IRemoteTransitionFinishedCallback.Stub {
        @Override
        public void onTransitionFinished(
                WindowContainerTransaction wct, SurfaceControl.Transaction t) {
            return;
        }
    }

    @ExternalThread
    private final class KeyguardTransitionsImpl implements KeyguardTransitions {
        @Override
        public void register(
                IRemoteTransition exitTransition,
                IRemoteTransition occludeTransition,
                IRemoteTransition occludeByDreamTransition,
                IRemoteTransition unoccludeTransition) {
            mMainExecutor.execute(() -> {
                mExitTransition = exitTransition;
                mOccludeTransition = occludeTransition;
                mOccludeByDreamTransition = occludeByDreamTransition;
                mUnoccludeTransition = unoccludeTransition;
            });
        }
    }
}
+41 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.wm.shell.keyguard;

import android.annotation.NonNull;
import android.window.IRemoteTransition;

import com.android.wm.shell.common.annotations.ExternalThread;

/**
 * Interface exposed to SystemUI Keyguard to register handlers for running
 * animations on keyguard visibility changes.
 *
 * TODO(b/274954192): Merge the occludeTransition and occludeByDream handlers and just let the
 * keyguard handler make the decision on which version it wants to play.
 */
@ExternalThread
public interface KeyguardTransitions {
    /**
     * Registers a set of remote transitions for Keyguard.
     */
    default void register(
            @NonNull IRemoteTransition unlockTransition,
            @NonNull IRemoteTransition occludeTransition,
            @NonNull IRemoteTransition occludeByDreamTransition,
            @NonNull IRemoteTransition unoccludeTransition) {}
}
+29 −1
Original line number Original line Diff line number Diff line
@@ -40,6 +40,7 @@ import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransactionCallback;
import android.window.WindowContainerTransactionCallback;


import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -63,6 +64,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
    private PipTransitionController mPipHandler;
    private PipTransitionController mPipHandler;
    private RecentsTransitionHandler mRecentsHandler;
    private RecentsTransitionHandler mRecentsHandler;
    private StageCoordinator mSplitHandler;
    private StageCoordinator mSplitHandler;
    private final KeyguardTransitionHandler mKeyguardHandler;


    private static class MixedTransition {
    private static class MixedTransition {
        static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
        static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
@@ -76,6 +78,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
        /** Recents transition while split-screen foreground. */
        /** Recents transition while split-screen foreground. */
        static final int TYPE_RECENTS_DURING_SPLIT = 4;
        static final int TYPE_RECENTS_DURING_SPLIT = 4;


        /** Keyguard exit/occlude/unocclude transition. */
        static final int TYPE_KEYGUARD = 5;

        /** The default animation for this mixed transition. */
        /** The default animation for this mixed transition. */
        static final int ANIM_TYPE_DEFAULT = 0;
        static final int ANIM_TYPE_DEFAULT = 0;


@@ -126,8 +131,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
    public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player,
    public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player,
            Optional<SplitScreenController> splitScreenControllerOptional,
            Optional<SplitScreenController> splitScreenControllerOptional,
            Optional<PipTouchHandler> pipTouchHandlerOptional,
            Optional<PipTouchHandler> pipTouchHandlerOptional,
            Optional<RecentsTransitionHandler> recentsHandlerOptional) {
            Optional<RecentsTransitionHandler> recentsHandlerOptional,
            KeyguardTransitionHandler keyguardHandler) {
        mPlayer = player;
        mPlayer = player;
        mKeyguardHandler = keyguardHandler;
        if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent()
        if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent()
                && splitScreenControllerOptional.isPresent()) {
                && splitScreenControllerOptional.isPresent()) {
            // Add after dependencies because it is higher priority
            // Add after dependencies because it is higher priority
@@ -263,12 +270,26 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
            @NonNull Transitions.TransitionFinishCallback finishCallback) {

        MixedTransition mixed = null;
        MixedTransition mixed = null;
        for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
        for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
            if (mActiveTransitions.get(i).mTransition != transition) continue;
            if (mActiveTransitions.get(i).mTransition != transition) continue;
            mixed = mActiveTransitions.get(i);
            mixed = mActiveTransitions.get(i);
            break;
            break;
        }
        }

        // Offer Keyguard the opportunity to take over lock transitions - ideally we could know by
        // the time of handleRequest, but we need more information than is available at that time.
        if (KeyguardTransitionHandler.handles(info)) {
            if (mixed != null && mixed.mType != MixedTransition.TYPE_KEYGUARD) {
                ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                        "Converting mixed transition into a keyguard transition");
                onTransitionConsumed(transition, false, null);
            }
            mixed = new MixedTransition(MixedTransition.TYPE_KEYGUARD, transition);
            mActiveTransitions.add(mixed);
        }

        if (mixed == null) return false;
        if (mixed == null) return false;


        if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
        if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
@@ -282,6 +303,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
            return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction,
            return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction,
                    finishCallback);
                    finishCallback);
        } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
            return mKeyguardHandler.startAnimation(
                    transition, info, startTransaction, finishTransaction, finishCallback);
        } else {
        } else {
            mActiveTransitions.remove(mixed);
            mActiveTransitions.remove(mixed);
            throw new IllegalStateException("Starting mixed animation without a known mixed type? "
            throw new IllegalStateException("Starting mixed animation without a known mixed type? "
@@ -574,6 +598,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
                }
                }
                mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
                mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
                        finishCallback);
                        finishCallback);
            } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
                mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
            } else {
            } else {
                throw new IllegalStateException("Playing a mixed transition with unknown type? "
                throw new IllegalStateException("Playing a mixed transition with unknown type? "
                        + mixed.mType);
                        + mixed.mType);
@@ -597,6 +623,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
            mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
            mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
        } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
        } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
            mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
            mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
        } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
            mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT);
        }
        }
    }
    }
}
}
Loading