Inject a handler for Keyguard transitions

Keyguard transitions absolutely must not fail because Keyguard, not
being a window itself (more like a mode for SystemUI views), uses the
signal from the transition system to control its visibility.

We're putting a KeyguardTransitionHandler at the top of the list of
handlers so that it has the first opportunity to run anything related
to occlusion or keyguard-going-away flags.

As of now the KeyguardViewMediator is still using the IRemoteTransition
interface to keep the change small and safe-ish. This can be improved.
For example, there's no real need for both sides to have a special
concept of "occludeByDream".

Test: atest ShellTransitionTests
Bug: 274954192
Change-Id: I54e4ab66840c47e686071cab3c22f4c351dc875e
This commit is contained in:
Robin Lee
2023-05-09 21:42:20 +02:00
parent 90c01a2a47
commit 675402855f
15 changed files with 434 additions and 135 deletions

View File

@@ -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
//

View File

@@ -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<SplitScreenController> splitScreenOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
Optional<RecentsTransitionHandler> recentsTransitionHandler,
KeyguardTransitionHandler keyguardTransitionHandler,
Transitions transitions) {
return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
pipTouchHandlerOptional, recentsTransitionHandler);
pipTouchHandlerOptional, recentsTransitionHandler, keyguardTransitionHandler);
}
@WMSingleton

View File

@@ -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.
*
* <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;
});
}
}
}

View File

@@ -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) {}
}

View File

@@ -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<SplitScreenController> splitScreenControllerOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
Optional<RecentsTransitionHandler> recentsHandlerOptional) {
Optional<RecentsTransitionHandler> 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);
}
}
}

View File

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

View File

@@ -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<Transitions>,
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"

View File

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

View File

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

View File

@@ -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<StartingSurface> s);

View File

@@ -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<StartingSurface> getStartingSurface();

View File

@@ -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<IBinder, IRemoteTransitionFinishedCallback> 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,

View File

@@ -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<ActivityLaunchAnimator> mActivityLaunchAnimator;
private Lazy<ScrimController> mScrimControllerLazy;
@@ -1222,6 +1245,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
ScreenOffAnimationController screenOffAnimationController,
Lazy<NotificationShadeDepthController> notificationShadeDepthController,
ScreenOnCoordinator screenOnCoordinator,
KeyguardTransitions keyguardTransitions,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
Lazy<ShadeController> 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;
}

View File

@@ -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> notificationShadeDepthController,
ScreenOnCoordinator screenOnCoordinator,
KeyguardTransitions keyguardTransitions,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
Lazy<ShadeController> shadeController,
@@ -152,6 +154,7 @@ public class KeyguardModule {
screenOffAnimationController,
notificationShadeDepthController,
screenOnCoordinator,
keyguardTransitions,
interactionJankMonitor,
dreamOverlayStateController,
shadeController,

View File

@@ -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,