diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 28368ef370618..74ef57e4baaea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -77,6 +77,8 @@ import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipUiEventLogger; 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.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; @@ -561,6 +563,28 @@ public abstract class WMShellBaseModule { 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 // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index f3130d358ec1e..be0288e238cef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -60,6 +60,7 @@ import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.freeform.FreeformTaskTransitionHandler; 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.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; @@ -532,9 +533,10 @@ public abstract class WMShellModule { Optional splitScreenOptional, Optional pipTouchHandlerOptional, Optional recentsTransitionHandler, + KeyguardTransitionHandler keyguardTransitionHandler, Transitions transitions) { return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional, - pipTouchHandlerOptional, recentsTransitionHandler); + pipTouchHandlerOptional, recentsTransitionHandler, keyguardTransitionHandler); } @WMSingleton 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 new file mode 100644 index 0000000000000..4d8075a9b56c4 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java @@ -0,0 +1,274 @@ +/* + * 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. + * + *

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 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; + }); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java new file mode 100644 index 0000000000000..b4b327f0eff24 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java @@ -0,0 +1,41 @@ +/* + * 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) {} +} 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 6fa1861a23e26..42633b722649e 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 @@ -40,6 +40,7 @@ import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; 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.phone.PipTouchHandler; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -63,6 +64,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, private PipTransitionController mPipHandler; private RecentsTransitionHandler mRecentsHandler; private StageCoordinator mSplitHandler; + private final KeyguardTransitionHandler mKeyguardHandler; private static class MixedTransition { 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. */ 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. */ 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, Optional splitScreenControllerOptional, Optional pipTouchHandlerOptional, - Optional recentsHandlerOptional) { + Optional recentsHandlerOptional, + KeyguardTransitionHandler keyguardHandler) { mPlayer = player; + mKeyguardHandler = keyguardHandler; if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent() && splitScreenControllerOptional.isPresent()) { // 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 finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + MixedTransition mixed = null; for (int i = mActiveTransitions.size() - 1; i >= 0; --i) { if (mActiveTransitions.get(i).mTransition != transition) continue; mixed = mActiveTransitions.get(i); 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.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) { return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction, finishCallback); + } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { + return mKeyguardHandler.startAnimation( + transition, info, startTransaction, finishTransaction, finishCallback); } else { mActiveTransitions.remove(mixed); 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, finishCallback); + } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { + mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); } else { throw new IllegalStateException("Playing a mixed transition with unknown type? " + mixed.mType); @@ -597,6 +623,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); + } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { + mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java index ef2a511177b24..a242c72db8b33 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java @@ -94,8 +94,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { - if (!Transitions.SHELL_TRANSITIONS_ROTATION && TransitionUtil.hasDisplayChange(info) - && !TransitionUtil.alwaysReportToKeyguard(info)) { + if (!Transitions.SHELL_TRANSITIONS_ROTATION && TransitionUtil.hasDisplayChange(info)) { // Note that if the remote doesn't have permission ACCESS_SURFACE_FLINGER, some // operations of the start transaction may be ignored. mRequestedRemotes.remove(transition); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index ab27c55f20dc8..f33b0778a1b2f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -71,6 +71,7 @@ import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ExternalThread; +import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; @@ -686,7 +687,11 @@ public class Transitions implements RemoteCallable, active.mToken, info, active.mStartT, active.mFinishT); } - if (info.getRootCount() == 0 && !TransitionUtil.alwaysReportToKeyguard(info)) { + /* + * Some transitions we always need to report to keyguard even if they are empty. + * TODO (b/274954192): Remove this once keyguard dispatching fully moves to Shell. + */ + if (info.getRootCount() == 0 && !KeyguardTransitionHandler.handles(info)) { // No root-leashes implies that the transition is empty/no-op, so just do // housekeeping and return. ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots in %s so" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java index 402b0ce7c87cb..ef0c7a8a755bc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java @@ -85,23 +85,6 @@ public class TransitionUtil { return false; } - /** - * Some transitions we always need to report to keyguard even if they are empty. - * TODO (b/274954192): Remove this once keyguard dispatching moves to Shell. - */ - public static boolean alwaysReportToKeyguard(TransitionInfo info) { - // occlusion status of activities can change while screen is off so there will be no - // visibility change but we still need keyguardservice to be notified. - if (info.getType() == TRANSIT_KEYGUARD_UNOCCLUDE) return true; - - // It's possible for some activities to stop with bad timing (esp. since we can't yet - // queue activity transitions initiated by apps) that results in an empty transition for - // keyguard going-away. In general, we should should always report Keyguard-going-away. - if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) return true; - - return false; - } - /** Returns `true` if `change` is a wallpaper. */ public static boolean isWallpaper(TransitionInfo.Change change) { return (change.getTaskInfo() == null) diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java index 632fcdc162592..2b468cf7c16b0 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java @@ -27,6 +27,7 @@ import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dagger.WMComponent; import com.android.systemui.util.InitializationChecker; import com.android.wm.shell.dagger.WMShellConcurrencyModule; +import com.android.wm.shell.keyguard.KeyguardTransitions; import com.android.wm.shell.sysui.ShellInterface; import com.android.wm.shell.transition.ShellTransitions; @@ -93,6 +94,7 @@ public abstract class SystemUIInitializer { .setBubbles(mWMComponent.getBubbles()) .setTaskViewFactory(mWMComponent.getTaskViewFactory()) .setTransitions(mWMComponent.getTransitions()) + .setKeyguardTransitions(mWMComponent.getKeyguardTransitions()) .setStartingSurface(mWMComponent.getStartingSurface()) .setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper()) .setRecentTasks(mWMComponent.getRecentTasks()) @@ -113,6 +115,7 @@ public abstract class SystemUIInitializer { .setBubbles(Optional.ofNullable(null)) .setTaskViewFactory(Optional.ofNullable(null)) .setTransitions(new ShellTransitions() {}) + .setKeyguardTransitions(new KeyguardTransitions() {}) .setDisplayAreaHelper(Optional.ofNullable(null)) .setStartingSurface(Optional.ofNullable(null)) .setRecentTasks(Optional.ofNullable(null)) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 1a0fcea6ca877..52355f34a71df 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -42,6 +42,7 @@ import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; +import com.android.wm.shell.keyguard.KeyguardTransitions; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasks; @@ -103,6 +104,9 @@ public interface SysUIComponent { @BindsInstance Builder setTransitions(ShellTransitions t); + @BindsInstance + Builder setKeyguardTransitions(KeyguardTransitions k); + @BindsInstance Builder setStartingSurface(Optional s); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index 17d2332a4dac5..b71871ebdb55e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -31,6 +31,7 @@ import com.android.wm.shell.dagger.WMShellModule; import com.android.wm.shell.dagger.WMSingleton; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; +import com.android.wm.shell.keyguard.KeyguardTransitions; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasks; @@ -98,6 +99,9 @@ public interface WMComponent { @WMSingleton ShellTransitions getTransitions(); + @WMSingleton + KeyguardTransitions getKeyguardTransitions(); + @WMSingleton Optional getStartingSurface(); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 8b6bd24861173..6e77dcbd75cf1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -21,7 +21,6 @@ import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; 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; @@ -37,7 +36,6 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.TransitionFlags; import static android.view.WindowManager.TransitionOldType; import static android.view.WindowManager.TransitionType; -import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD; import android.app.ActivityManager; import android.app.ActivityTaskManager; @@ -67,8 +65,6 @@ import android.view.WindowManager; import android.view.WindowManagerPolicyConstants; import android.window.IRemoteTransition; import android.window.IRemoteTransitionFinishedCallback; -import android.window.RemoteTransition; -import android.window.TransitionFilter; import android.window.TransitionInfo; import com.android.internal.policy.IKeyguardDismissCallback; @@ -178,7 +174,7 @@ public class KeyguardService extends Service { // Wrap Keyguard going away animation. // Note: Also used for wrapping occlude by Dream animation. It works (with some redundancy). - private static IRemoteTransition wrap(IRemoteAnimationRunner runner) { + public static IRemoteTransition wrap(IRemoteAnimationRunner runner) { return new IRemoteTransition.Stub() { final ArrayMap mFinishCallbacks = new ArrayMap<>(); @@ -273,7 +269,8 @@ public class KeyguardService extends Service { if (mShellTransitions == null || !Transitions.ENABLE_SHELL_TRANSITIONS) { RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); final RemoteAnimationAdapter exitAnimationAdapter = - new RemoteAnimationAdapter(mExitAnimationRunner, 0, 0); + new RemoteAnimationAdapter( + mKeyguardViewMediator.getExitAnimationRunner(), 0, 0); definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, exitAnimationAdapter); definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER, @@ -297,92 +294,7 @@ public class KeyguardService extends Service { unoccludeAnimationAdapter); ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay( mDisplayTracker.getDefaultDisplayId(), definition); - return; } - Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_GOING_AWAY"); - TransitionFilter f = new TransitionFilter(); - f.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY; - mShellTransitions.registerRemote(f, new RemoteTransition( - wrap(mExitAnimationRunner), getIApplicationThread(), "ExitKeyguard")); - - Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_(UN)OCCLUDE"); - // Register for occluding - final RemoteTransition occludeTransition = new RemoteTransition( - mOccludeAnimation, getIApplicationThread(), "KeyguardOcclude"); - f = new TransitionFilter(); - f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED; - f.mRequirements = new TransitionFilter.Requirement[]{ - new TransitionFilter.Requirement(), new TransitionFilter.Requirement()}; - // First require at-least one app showing that occludes. - f.mRequirements[0].mMustBeIndependent = false; - f.mRequirements[0].mFlags = FLAG_OCCLUDES_KEYGUARD; - f.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; - // Then require that we aren't closing any occludes (because this would mean a - // regular task->task or activity->activity animation not involving keyguard). - f.mRequirements[1].mNot = true; - f.mRequirements[1].mMustBeIndependent = false; - f.mRequirements[1].mFlags = FLAG_OCCLUDES_KEYGUARD; - f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK}; - mShellTransitions.registerRemote(f, occludeTransition); - - // Now register for un-occlude. - final RemoteTransition unoccludeTransition = new RemoteTransition( - mUnoccludeAnimation, getIApplicationThread(), "KeyguardUnocclude"); - f = new TransitionFilter(); - f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED; - f.mRequirements = new TransitionFilter.Requirement[]{ - new TransitionFilter.Requirement(), new TransitionFilter.Requirement()}; - // First require at-least one app going-away (doesn't need occlude flag - // as that is implicit by it having been visible and we don't want to exclude - // cases where we are un-occluding because the app removed its showWhenLocked - // capability at runtime). - f.mRequirements[1].mMustBeIndependent = false; - f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK}; - f.mRequirements[1].mMustBeTask = true; - // Then require that we aren't opening any occludes (otherwise we'd remain - // occluded). - f.mRequirements[0].mNot = true; - f.mRequirements[0].mMustBeIndependent = false; - f.mRequirements[0].mFlags = FLAG_OCCLUDES_KEYGUARD; - f.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; - mShellTransitions.registerRemote(f, unoccludeTransition); - - // Register for specific transition type. - // Above filter cannot fulfill all conditions. - // E.g. close top activity while screen off but next activity is occluded, this should - // an occluded transition, but since the activity is invisible, the condition would - // match unoccluded transition. - // But on the contrary, if we add above condition in occluded transition, then when user - // trying to dismiss occluded activity when unlock keyguard, the condition would match - // occluded transition. - f = new TransitionFilter(); - f.mTypeSet = new int[]{TRANSIT_KEYGUARD_OCCLUDE}; - mShellTransitions.registerRemote(f, occludeTransition); - - f = new TransitionFilter(); - f.mTypeSet = new int[]{TRANSIT_KEYGUARD_UNOCCLUDE}; - mShellTransitions.registerRemote(f, unoccludeTransition); - - Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_OCCLUDE for DREAM"); - // Register for occluding by Dream - f = new TransitionFilter(); - f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED; - f.mRequirements = new TransitionFilter.Requirement[]{ - new TransitionFilter.Requirement(), new TransitionFilter.Requirement()}; - // First require at-least one app of type DREAM showing that occludes. - f.mRequirements[0].mActivityType = WindowConfiguration.ACTIVITY_TYPE_DREAM; - f.mRequirements[0].mMustBeIndependent = false; - f.mRequirements[0].mFlags = FLAG_OCCLUDES_KEYGUARD; - f.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; - // Then require that we aren't closing any occludes (because this would mean a - // regular task->task or activity->activity animation not involving keyguard). - f.mRequirements[1].mNot = true; - f.mRequirements[1].mMustBeIndependent = false; - f.mRequirements[1].mFlags = FLAG_OCCLUDES_KEYGUARD; - f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK}; - mShellTransitions.registerRemote(f, new RemoteTransition( - wrap(mKeyguardViewMediator.getOccludeByDreamAnimationRunner()), - getIApplicationThread(), "KeyguardOccludeByDream")); } @Override @@ -402,27 +314,6 @@ public class KeyguardService extends Service { } } - private final IRemoteAnimationRunner.Stub mExitAnimationRunner = - new IRemoteAnimationRunner.Stub() { - @Override // Binder interface - public void onAnimationStart(@WindowManager.TransitionOldType int transit, - RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, - RemoteAnimationTarget[] nonApps, - IRemoteAnimationFinishedCallback finishedCallback) { - Trace.beginSection("mExitAnimationRunner.onAnimationStart#startKeyguardExitAnimation"); - checkPermission(); - mKeyguardViewMediator.startKeyguardExitAnimation(transit, apps, wallpapers, - nonApps, finishedCallback); - Trace.endSection(); - } - - @Override // Binder interface - public void onAnimationCancelled() { - mKeyguardViewMediator.cancelKeyguardExitAnimation(); - } - }; - final IRemoteTransition mOccludeAnimation = new IRemoteTransition.Stub() { @Override public void startAnimation(IBinder transition, TransitionInfo info, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 46ff0d9ded55c..04af0d42f62db 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -94,6 +94,7 @@ import android.view.WindowManager; import android.view.WindowManagerPolicyConstants; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import android.window.IRemoteTransition; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -154,12 +155,14 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.DeviceConfigProxy; +import com.android.wm.shell.keyguard.KeyguardTransitions; import dagger.Lazy; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Optional; import java.util.concurrent.Executor; /** @@ -962,7 +965,26 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } }; - private IRemoteAnimationRunner mOccludeAnimationRunner = + private final IRemoteAnimationRunner.Stub mExitAnimationRunner = + new IRemoteAnimationRunner.Stub() { + @Override // Binder interface + public void onAnimationStart(@WindowManager.TransitionOldType int transit, + RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) { + Trace.beginSection("mExitAnimationRunner.onAnimationStart#startKeyguardExitAnimation"); + startKeyguardExitAnimation(transit, apps, wallpapers, nonApps, finishedCallback); + Trace.endSection(); + } + + @Override // Binder interface + public void onAnimationCancelled() { + cancelKeyguardExitAnimation(); + } + }; + + private final IRemoteAnimationRunner mOccludeAnimationRunner = new OccludeActivityLaunchRemoteAnimationRunner(mOccludeAnimationController); private final IRemoteAnimationRunner mOccludeByDreamAnimationRunner = @@ -1187,6 +1209,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private final InteractionJankMonitor mInteractionJankMonitor; private boolean mWallpaperSupportsAmbientMode; private ScreenOnCoordinator mScreenOnCoordinator; + private final KeyguardTransitions mKeyguardTransitions; private Lazy mActivityLaunchAnimator; private Lazy mScrimControllerLazy; @@ -1222,6 +1245,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, ScreenOffAnimationController screenOffAnimationController, Lazy notificationShadeDepthController, ScreenOnCoordinator screenOnCoordinator, + KeyguardTransitions keyguardTransitions, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, Lazy shadeControllerLazy, @@ -1249,6 +1273,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, dumpManager.registerDumpable(getClass().getName(), this); mDeviceConfig = deviceConfig; mScreenOnCoordinator = screenOnCoordinator; + mKeyguardTransitions = keyguardTransitions; mNotificationShadeWindowControllerLazy = notificationShadeWindowControllerLazy; mShowHomeOverLockscreen = mDeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, @@ -1324,6 +1349,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, setShowingLocked(false /* showing */, true /* forceCallbacks */); } + mKeyguardTransitions.register( + KeyguardService.wrap(getExitAnimationRunner()), + KeyguardService.wrap(getOccludeAnimationRunner()), + KeyguardService.wrap(getOccludeByDreamAnimationRunner()), + KeyguardService.wrap(getUnoccludeAnimationRunner())); + final ContentResolver cr = mContext.getContentResolver(); mDeviceInteractive = mPM.isInteractive(); @@ -1859,6 +1890,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, Trace.endSection(); } + public IRemoteAnimationRunner getExitAnimationRunner() { + return mExitAnimationRunner; + } + public IRemoteAnimationRunner getOccludeAnimationRunner() { return mOccludeAnimationRunner; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index deb8f5d96cbdf..255556c801210 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -64,6 +64,7 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.DeviceConfigProxy; +import com.android.wm.shell.keyguard.KeyguardTransitions; import dagger.Lazy; import dagger.Module; @@ -119,6 +120,7 @@ public class KeyguardModule { ScreenOffAnimationController screenOffAnimationController, Lazy notificationShadeDepthController, ScreenOnCoordinator screenOnCoordinator, + KeyguardTransitions keyguardTransitions, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, Lazy shadeController, @@ -152,6 +154,7 @@ public class KeyguardModule { screenOffAnimationController, notificationShadeDepthController, screenOnCoordinator, + keyguardTransitions, interactionJankMonitor, dreamOverlayStateController, shadeController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 83c89f1e6855c..ca4e68ac03008 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -95,6 +95,7 @@ import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.DeviceConfigProxyFake; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; +import com.android.wm.shell.keyguard.KeyguardTransitions; import org.junit.Before; import org.junit.Test; @@ -135,6 +136,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock ScreenOffAnimationController mScreenOffAnimationController; private @Mock InteractionJankMonitor mInteractionJankMonitor; private @Mock ScreenOnCoordinator mScreenOnCoordinator; + private @Mock KeyguardTransitions mKeyguardTransitions; private @Mock ShadeController mShadeController; private NotificationShadeWindowController mNotificationShadeWindowController; private @Mock DreamOverlayStateController mDreamOverlayStateController; @@ -614,6 +616,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mScreenOffAnimationController, () -> mNotificationShadeDepthController, mScreenOnCoordinator, + mKeyguardTransitions, mInteractionJankMonitor, mDreamOverlayStateController, () -> mShadeController,