diff options
author | lumark <lumark@google.com> | 2018-07-20 18:53:54 +0800 |
---|---|---|
committer | lumark <lumark@google.com> | 2018-11-01 21:10:37 +0800 |
commit | 588a3e86a9b2a2c62202eaf61afd98bc0bbf0fd7 (patch) | |
tree | f9261d8ac37e0c46a1a8acb1fe2cb8e3f1346221 | |
parent | 101f907ddb6b3edc259792a841d8e10c58c0f613 (diff) |
Each displays can have individual app transition.
Include below refectoring items to support per display AppTransition:
WMS / AM refectoring parts:
- Move AppTransition related stuff from WMS into DisplayContent.
- Move WMS.prepareAppTransition into DisplayWindowController.
- Move WMS.executeAppTransition to DisplayWindowController.
- Move ATM.isNextTransitionForward to DisplayWindowController.
- Move WMS.getPendingAppTransition to DisplayWindowController.
- Move WMS.overrideAppTransition like APIs to DisplayWindowController.
- Move ActivityRecord.applyOptionsLocked to AppContainerController.
- Support tracing all display's AppTransition status for
DisplayContent.pendingLayoutChanges & window hierachy update.
- Modify logics for AppTransition related caller parts.
- Move WindowSurfacePlacer.handleAppTransitionReadyLocked related
stuffs into added class AppTransitionController.
WM unit test parts:
- Add test case for verifying app transition state per display:
- AppTransitionTests.testAppTransitionStateForMultiDisplay
- AppTransitionTests.testCleanAppTransitionWhenTaskStackReparent
- Rename WindowSurfacePlacerTest to AppTransitionControllerTest since
the test is related handle AppTransition flow.
Bug: 111362605
Test: go/wm-smoke
Test: atest ActivityManagerTransitionSelectionTests
Test: atest ActivityManagerMultiDisplayTests
Test: atest FrameworksServicesTests for DisplayContent / AppTransition
related tests.
Change-Id: Ic1793aa794eb161bec31fda57847a6ba2ff4f84f
43 files changed, 1505 insertions, 1179 deletions
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt index 2d5103875bd1..c312aa9f8c63 100644 --- a/config/boot-image-profile.txt +++ b/config/boot-image-profile.txt @@ -13145,7 +13145,7 @@ HSPLandroid/app/ActivityManagerInternal;->notifyAppTransitionCancelled()V HSPLandroid/app/ActivityManagerInternal;->notifyAppTransitionFinished()V HSPLandroid/app/ActivityManagerInternal;->notifyAppTransitionStarting(Landroid/util/SparseIntArray;J)V HSPLandroid/app/ActivityManagerInternal;->notifyDockedStackMinimizedChanged(Z)V -HSPLandroid/app/ActivityManagerInternal;->notifyKeyguardFlagsChanged(Ljava/lang/Runnable;)V +HSPLandroid/app/ActivityManagerInternal;->notifyKeyguardFlagsChanged(Ljava/lang/Runnable;I)V HSPLandroid/app/ActivityManagerInternal;->notifyKeyguardTrustedChanged()V HSPLandroid/app/ActivityManagerInternal;->notifyNetworkPolicyRulesUpdated(IJ)V HSPLandroid/app/ActivityManagerInternal;->onLocalVoiceInteractionStarted(Landroid/os/IBinder;Landroid/service/voice/IVoiceInteractionSession;Lcom/android/internal/app/IVoiceInteractor;)V @@ -32750,15 +32750,8 @@ HSPLandroid/view/IWindowManager;->isViewServerRunning()Z HSPLandroid/view/IWindowManager;->isWindowTraceEnabled()Z HSPLandroid/view/IWindowManager;->lockNow(Landroid/os/Bundle;)V HSPLandroid/view/IWindowManager;->openSession(Landroid/view/IWindowSessionCallback;Lcom/android/internal/view/IInputMethodClient;Lcom/android/internal/view/IInputContext;)Landroid/view/IWindowSession; -HSPLandroid/view/IWindowManager;->overridePendingAppTransition(Ljava/lang/String;IILandroid/os/IRemoteCallback;)V -HSPLandroid/view/IWindowManager;->overridePendingAppTransitionAspectScaledThumb(Landroid/graphics/GraphicBuffer;IIIILandroid/os/IRemoteCallback;Z)V -HSPLandroid/view/IWindowManager;->overridePendingAppTransitionClipReveal(IIII)V -HSPLandroid/view/IWindowManager;->overridePendingAppTransitionInPlace(Ljava/lang/String;I)V -HSPLandroid/view/IWindowManager;->overridePendingAppTransitionMultiThumb([Landroid/view/AppTransitionAnimationSpec;Landroid/os/IRemoteCallback;Landroid/os/IRemoteCallback;Z)V HSPLandroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z)V HSPLandroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)V -HSPLandroid/view/IWindowManager;->overridePendingAppTransitionScaleUp(IIII)V -HSPLandroid/view/IWindowManager;->overridePendingAppTransitionThumb(Landroid/graphics/GraphicBuffer;IILandroid/os/IRemoteCallback;Z)V HSPLandroid/view/IWindowManager;->prepareAppTransition(IZ)V HSPLandroid/view/IWindowManager;->reenableKeyguard(Landroid/os/IBinder;)V HSPLandroid/view/IWindowManager;->refreshScreenCaptureDisabled(I)V diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index bbc3f35aa05a..e6257b3583a0 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -1418,7 +1418,6 @@ Landroid/view/IWindowManager;->getBaseDisplaySize(ILandroid/graphics/Point;)V Landroid/view/IWindowManager;->getDockedStackSide()I Landroid/view/IWindowManager;->getInitialDisplayDensity(I)I Landroid/view/IWindowManager;->getInitialDisplaySize(ILandroid/graphics/Point;)V -Landroid/view/IWindowManager;->getPendingAppTransition()I Landroid/view/IWindowManager;->hasNavigationBar()Z Landroid/view/IWindowManager;->isKeyguardLocked()Z Landroid/view/IWindowManager;->isKeyguardSecure()Z diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 0c3a2957e1bc..f8bdfe214233 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -88,29 +88,6 @@ interface IWindowManager void addWindowToken(IBinder token, int type, int displayId); void removeWindowToken(IBinder token, int displayId); void prepareAppTransition(int transit, boolean alwaysKeepCurrent); - int getPendingAppTransition(); - void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim, - IRemoteCallback startedCallback); - void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth, - int startHeight); - void overridePendingAppTransitionClipReveal(int startX, int startY, - int startWidth, int startHeight); - void overridePendingAppTransitionThumb(in GraphicBuffer srcThumb, int startX, int startY, - IRemoteCallback startedCallback, boolean scaleUp); - void overridePendingAppTransitionAspectScaledThumb(in GraphicBuffer srcThumb, int startX, - int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback, - boolean scaleUp); - /** - * Overrides animation for app transition that exits from an application to a multi-window - * environment and allows specifying transition animation parameters for each window. - * - * @param specs Array of transition animation descriptions for entering windows. - * - * @hide - */ - void overridePendingAppTransitionMultiThumb(in AppTransitionAnimationSpec[] specs, - IRemoteCallback startedCallback, IRemoteCallback finishedCallback, boolean scaleUp); - void overridePendingAppTransitionInPlace(String packageName, int anim); /** * Like overridePendingAppTransitionMultiThumb, but uses a future to supply the specs. This is diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index c1c86f088873..b0dbaa03760e 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -45,7 +45,6 @@ message WindowManagerServiceDumpProto { optional bool display_frozen = 6; optional int32 rotation = 7; optional int32 last_orientation = 8; - optional AppTransitionProto app_transition = 9; } /* represents RootWindowContainer object */ @@ -159,6 +158,7 @@ message DisplayContentProto { optional DisplayFramesProto display_frames = 13; optional int32 surface_size = 14; optional string focused_app = 15; + optional AppTransitionProto app_transition = 16; } /* represents DisplayFrames */ diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 865c774a2267..47b4f4700049 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -18,16 +18,7 @@ package com.android.server.am; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX; -import static android.app.ActivityOptions.ANIM_CLIP_REVEAL; -import static android.app.ActivityOptions.ANIM_CUSTOM; -import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; -import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION; -import static android.app.ActivityOptions.ANIM_SCALE_UP; import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; -import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN; -import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP; -import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; -import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; import static android.app.ActivityTaskManager.INVALID_STACK_ID; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE; @@ -156,7 +147,6 @@ import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; -import android.graphics.GraphicBuffer; import android.graphics.Rect; import android.os.Binder; import android.os.Build; @@ -177,8 +167,6 @@ import android.util.MergedConfiguration; import android.util.Slog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; -import android.view.AppTransitionAnimationSpec; -import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IApplicationToken; import android.view.RemoteAnimationDefinition; import android.view.WindowManager.LayoutParams; @@ -1502,93 +1490,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo void applyOptionsLocked() { if (pendingOptions != null && pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) { - final int animationType = pendingOptions.getAnimationType(); - switch (animationType) { - case ANIM_CUSTOM: - service.mWindowManager.overridePendingAppTransition( - pendingOptions.getPackageName(), - pendingOptions.getCustomEnterResId(), - pendingOptions.getCustomExitResId(), - pendingOptions.getOnAnimationStartListener()); - break; - case ANIM_CLIP_REVEAL: - service.mWindowManager.overridePendingAppTransitionClipReveal( - pendingOptions.getStartX(), pendingOptions.getStartY(), - pendingOptions.getWidth(), pendingOptions.getHeight()); - if (intent.getSourceBounds() == null) { - intent.setSourceBounds(new Rect(pendingOptions.getStartX(), - pendingOptions.getStartY(), - pendingOptions.getStartX()+pendingOptions.getWidth(), - pendingOptions.getStartY()+pendingOptions.getHeight())); - } - break; - case ANIM_SCALE_UP: - service.mWindowManager.overridePendingAppTransitionScaleUp( - pendingOptions.getStartX(), pendingOptions.getStartY(), - pendingOptions.getWidth(), pendingOptions.getHeight()); - if (intent.getSourceBounds() == null) { - intent.setSourceBounds(new Rect(pendingOptions.getStartX(), - pendingOptions.getStartY(), - pendingOptions.getStartX()+pendingOptions.getWidth(), - pendingOptions.getStartY()+pendingOptions.getHeight())); - } - break; - case ANIM_THUMBNAIL_SCALE_UP: - case ANIM_THUMBNAIL_SCALE_DOWN: - final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP); - final GraphicBuffer buffer = pendingOptions.getThumbnail(); - service.mWindowManager.overridePendingAppTransitionThumb(buffer, - pendingOptions.getStartX(), pendingOptions.getStartY(), - pendingOptions.getOnAnimationStartListener(), - scaleUp); - if (intent.getSourceBounds() == null && buffer != null) { - intent.setSourceBounds(new Rect(pendingOptions.getStartX(), - pendingOptions.getStartY(), - pendingOptions.getStartX() + buffer.getWidth(), - pendingOptions.getStartY() + buffer.getHeight())); - } - break; - case ANIM_THUMBNAIL_ASPECT_SCALE_UP: - case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: - final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs(); - final IAppTransitionAnimationSpecsFuture specsFuture = - pendingOptions.getSpecsFuture(); - if (specsFuture != null) { - service.mWindowManager.overridePendingAppTransitionMultiThumbFuture( - specsFuture, pendingOptions.getOnAnimationStartListener(), - animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP); - } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN - && specs != null) { - service.mWindowManager.overridePendingAppTransitionMultiThumb( - specs, pendingOptions.getOnAnimationStartListener(), - pendingOptions.getAnimationFinishedListener(), false); - } else { - service.mWindowManager.overridePendingAppTransitionAspectScaledThumb( - pendingOptions.getThumbnail(), - pendingOptions.getStartX(), pendingOptions.getStartY(), - pendingOptions.getWidth(), pendingOptions.getHeight(), - pendingOptions.getOnAnimationStartListener(), - (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP)); - if (intent.getSourceBounds() == null) { - intent.setSourceBounds(new Rect(pendingOptions.getStartX(), - pendingOptions.getStartY(), - pendingOptions.getStartX() + pendingOptions.getWidth(), - pendingOptions.getStartY() + pendingOptions.getHeight())); - } - } - break; - case ANIM_OPEN_CROSS_PROFILE_APPS: - service.mWindowManager.overridePendingAppTransitionStartCrossProfileApps(); - break; - case ANIM_REMOTE_ANIMATION: - service.mWindowManager.overridePendingAppTransitionRemote( - pendingOptions.getRemoteAnimationAdapter()); - break; - default: - Slog.e(TAG, "applyOptionsLocked: Unknown animationType=" + animationType); - break; - } - + mWindowContainerController.applyOptionsLocked(pendingOptions, intent); if (task == null) { clearOptionsLocked(false /* withAbort */); } else { @@ -2768,7 +2670,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo preserveWindow); final ActivityLifecycleItem lifecycleItem; if (andResume) { - lifecycleItem = ResumeActivityItem.obtain(service.isNextTransitionForward()); + lifecycleItem = ResumeActivityItem.obtain( + getDisplay().getWindowContainerController().isNextTransitionForward()); } else { lifecycleItem = PauseActivityItem.obtain(); } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 026c5cc3017d..b8d85a9d0f37 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -156,6 +156,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerService.ItemMatcher; import com.android.server.wm.ConfigurationContainer; +import com.android.server.wm.DisplayWindowController; import com.android.server.wm.StackWindowController; import com.android.server.wm.StackWindowListener; import com.android.server.wm.WindowManagerService; @@ -2615,17 +2616,18 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // that the previous one will be hidden soon. This way it can know // to ignore it when computing the desired screen orientation. boolean anim = true; + final DisplayWindowController dwc = getDisplay().getWindowContainerController(); if (prev != null) { if (prev.finishing) { if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare close transition: prev=" + prev); if (mStackSupervisor.mNoAnimActivities.contains(prev)) { anim = false; - mWindowManager.prepareAppTransition(TRANSIT_NONE, false); + dwc.prepareAppTransition(TRANSIT_NONE, false); } else { - mWindowManager.prepareAppTransition(prev.getTask() == next.getTask() - ? TRANSIT_ACTIVITY_CLOSE - : TRANSIT_TASK_CLOSE, false); + dwc.prepareAppTransition( + prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_CLOSE + : TRANSIT_TASK_CLOSE, false); } prev.setVisibility(false); } else { @@ -2633,22 +2635,21 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai "Prepare open transition: prev=" + prev); if (mStackSupervisor.mNoAnimActivities.contains(next)) { anim = false; - mWindowManager.prepareAppTransition(TRANSIT_NONE, false); + dwc.prepareAppTransition(TRANSIT_NONE, false); } else { - mWindowManager.prepareAppTransition(prev.getTask() == next.getTask() - ? TRANSIT_ACTIVITY_OPEN - : next.mLaunchTaskBehind - ? TRANSIT_TASK_OPEN_BEHIND - : TRANSIT_TASK_OPEN, false); + dwc.prepareAppTransition( + prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_OPEN + : next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND + : TRANSIT_TASK_OPEN, false); } } } else { if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous"); if (mStackSupervisor.mNoAnimActivities.contains(next)) { anim = false; - mWindowManager.prepareAppTransition(TRANSIT_NONE, false); + dwc.prepareAppTransition(TRANSIT_NONE, false); } else { - mWindowManager.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false); + dwc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false); } } @@ -2777,7 +2778,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai next.clearOptionsLocked(); transaction.setLifecycleStateRequest( ResumeActivityItem.obtain(next.app.getReportedProcState(), - mService.isNextTransitionForward())); + getDisplay().getWindowContainerController() + .isNextTransitionForward())); mService.getLifecycleManager().scheduleTransaction(transaction); if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed " @@ -2984,10 +2986,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai task.setFrontOfTask(); if (!isHomeOrRecentsStack() || numActivities() > 0) { + final DisplayWindowController dwc = getDisplay().getWindowContainerController(); if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: starting " + r); if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { - mWindowManager.prepareAppTransition(TRANSIT_NONE, keepCurTransition); + dwc.prepareAppTransition(TRANSIT_NONE, keepCurTransition); mStackSupervisor.mNoAnimActivities.add(r); } else { int transit = TRANSIT_ACTIVITY_OPEN; @@ -3006,7 +3009,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai transit = TRANSIT_TASK_OPEN; } } - mWindowManager.prepareAppTransition(transit, keepCurTransition); + dwc.prepareAppTransition(transit, keepCurTransition); mStackSupervisor.mNoAnimActivities.remove(r); } boolean doShow = true; @@ -3635,8 +3638,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai int taskNdx = mTaskHistory.indexOf(finishedTask); final TaskRecord task = finishedTask; int activityNdx = task.mActivities.indexOf(r); - mWindowManager.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, - false /* alwaysKeepCurrent */); + getDisplay().getWindowContainerController().prepareAppTransition( + TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); finishedTask = task; // Also terminate any activities below it that aren't yet @@ -3801,7 +3804,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mService.getTaskChangeNotificationController().notifyTaskRemovalStarted( task.taskId); } - mWindowManager.prepareAppTransition(transit, false); + getDisplay().getWindowContainerController().prepareAppTransition(transit, false); // Tell window manager to prepare for this one to be removed. r.setVisibility(false); @@ -3856,9 +3859,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } private void prepareActivityHideTransitionAnimation(ActivityRecord r, int transit) { - mWindowManager.prepareAppTransition(transit, false); + final DisplayWindowController dwc = getDisplay().getWindowContainerController(); + dwc.prepareAppTransition(transit, false); r.setVisibility(false); - mWindowManager.executeAppTransition(); + dwc.executeAppTransition(); if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) { mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(r); } @@ -4602,7 +4606,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai ActivityOptions.abort(options); } } - mWindowManager.prepareAppTransition(transit, false); + getDisplay().getWindowContainerController().prepareAppTransition(transit, false); } private void updateTaskMovement(TaskRecord task, boolean toFront) { @@ -4671,7 +4675,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr); if (noAnimation) { - mWindowManager.prepareAppTransition(TRANSIT_NONE, false); + getDisplay().getWindowContainerController().prepareAppTransition( + TRANSIT_NONE, false); if (r != null) { mStackSupervisor.mNoAnimActivities.add(r); } @@ -4753,7 +4758,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mTaskHistory.add(0, tr); updateTaskMovement(tr, false); - mWindowManager.prepareAppTransition(TRANSIT_TASK_TO_BACK, false); + getDisplay().getWindowContainerController().prepareAppTransition( + TRANSIT_TASK_TO_BACK, false); moveToBack("moveTaskToBackLocked", tr); if (inPinnedWindowingMode()) { @@ -5077,8 +5083,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai + r.intent.getComponent().flattenToShortString()); // Force the destroy to skip right to removal. r.app = null; - mWindowManager.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, - false /* alwaysKeepCurrent */); + getDisplay().getWindowContainerController().prepareAppTransition( + TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false, "handleAppCrashedLocked"); } @@ -5410,7 +5416,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } void executeAppTransition(ActivityOptions options) { - mWindowManager.executeAppTransition(); + getDisplay().getWindowContainerController().executeAppTransition(); ActivityOptions.abort(options); } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 17454b42524c..8f5e02d146e9 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1072,6 +1072,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return foundResumed; } + private void executeAppTransitionForAllDisplay() { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + display.getWindowContainerController().executeAppTransition(); + } + } + /** * Pause all activities in either all of the stacks or just the back stacks. * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving(). @@ -1421,6 +1428,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Create activity launch transaction. final ClientTransaction clientTransaction = ClientTransaction.obtain( proc.getThread(), r.appToken); + + final DisplayWindowController dwc = r.getDisplay().getWindowContainerController(); clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, // TODO: Have this take the merged configuration instead of separate global @@ -1429,12 +1438,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mergedConfiguration.getOverrideConfiguration(), r.compat, r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(), r.icicle, r.persistentState, results, newIntents, - mService.isNextTransitionForward(), profilerInfo)); + dwc.isNextTransitionForward(), profilerInfo)); // Set desired final state. final ActivityLifecycleItem lifecycleItem; if (andResume) { - lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward()); + lifecycleItem = ResumeActivityItem.obtain(dwc.isNextTransitionForward()); } else { lifecycleItem = PauseActivityItem.obtain(); } @@ -3520,7 +3529,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } if (stack.getDisplay().allResumedActivitiesComplete()) { ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - mWindowManager.executeAppTransition(); + // Make sure activity & window visibility should be identical + // for all displays in this stage. + executeAppTransitionForAllDisplay(); return true; } return false; @@ -4618,6 +4629,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // not run into issues where we still need to draw the task in recents but the // docked stack is already created. deferUpdateRecentsHomeStackBounds(); + // TODO(multi-display): currently recents animation only support default display. mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false); } diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index fa227a27dced..37ddaf94c0a0 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1502,7 +1502,7 @@ class ActivityStarter { mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); // Go ahead and tell window manager to execute app transition for this activity // since the app transition will not be triggered through the resume channel. - mService.mWindowManager.executeAppTransition(); + mTargetStack.getDisplay().getWindowContainerController().executeAppTransition(); } else { // If the target stack was not previously focusable (previous top running activity // on that stack was not visible) then any prior calls to move the stack to the diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java index 5b0a4a9b0596..078ed0ed509c 100644 --- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java @@ -65,11 +65,8 @@ import static android.provider.Settings.System.FONT_SCALE; import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; -import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE; -import static android.view.WindowManager.TRANSIT_TASK_OPEN; -import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR; import static com.android.server.am.ActivityManagerService.MY_PID; @@ -259,6 +256,7 @@ import com.android.server.pm.UserManagerService; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; +import com.android.server.wm.DisplayWindowController; import com.android.server.wm.PinnedStackWindowController; import com.android.server.wm.WindowManagerService; @@ -1622,8 +1620,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (self.isState( ActivityStack.ActivityState.RESUMED, ActivityStack.ActivityState.PAUSING)) { - mWindowManager.overridePendingAppTransition(packageName, - enterAnim, exitAnim, null); + self.getDisplay().getWindowContainerController().overridePendingAppTransition( + packageName, enterAnim, exitAnim, null); } Binder.restoreCallingIdentity(origId); @@ -2941,10 +2939,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { throw new IllegalArgumentException("Expected in-place ActivityOption " + "with valid animation"); } - mWindowManager.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false); - mWindowManager.overridePendingAppTransitionInPlace(activityOptions.getPackageName(), - activityOptions.getCustomInPlaceResId()); - mWindowManager.executeAppTransition(); + // Get top display of front most application. + final ActivityStack focusedStack = getTopDisplayFocusedStack(); + if (focusedStack != null) { + final DisplayWindowController dwc = + focusedStack.getDisplay().getWindowContainerController(); + dwc.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false); + dwc.overridePendingAppTransitionInPlace(activityOptions.getPackageName(), + activityOptions.getCustomInPlaceResId()); + dwc.executeAppTransition(); + } } @Override @@ -4384,13 +4388,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mKeyguardController.isKeyguardLocked(); } - boolean isNextTransitionForward() { - int transit = mWindowManager.getPendingAppTransition(); - return transit == TRANSIT_ACTIVITY_OPEN - || transit == TRANSIT_TASK_OPEN - || transit == TRANSIT_TASK_TO_FRONT; - } - void dumpLastANRLocked(PrintWriter pw) { pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)"); if (mLastANRState == null) { @@ -5701,23 +5698,23 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void notifyKeyguardFlagsChanged(@Nullable Runnable callback) { + public void notifyKeyguardFlagsChanged(@Nullable Runnable callback, int displayId) { synchronized (mGlobalLock) { // We might change the visibilities here, so prepare an empty app transition which // might be overridden later if we actually change visibilities. - final boolean wasTransitionSet = - mWindowManager.getPendingAppTransition() != TRANSIT_NONE; + final DisplayWindowController dwc = mStackSupervisor.getActivityDisplay(displayId) + .getWindowContainerController(); + final boolean wasTransitionSet = dwc.getPendingAppTransition() != TRANSIT_NONE; if (!wasTransitionSet) { - mWindowManager.prepareAppTransition(TRANSIT_NONE, - false /* alwaysKeepCurrent */); + dwc.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */); } mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); // If there was a transition set already we don't want to interfere with it as we // might be starting it too early. if (!wasTransitionSet) { - mWindowManager.executeAppTransition(); + dwc.executeAppTransition(); } } if (callback != null) { diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java index 28b2a42522eb..9c41c77342e5 100644 --- a/services/core/java/com/android/server/am/KeyguardController.java +++ b/services/core/java/com/android/server/am/KeyguardController.java @@ -48,6 +48,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.ActivityTaskManagerInternal.SleepToken; +import com.android.server.wm.DisplayWindowController; import com.android.server.wm.WindowManagerService; import java.io.PrintWriter; @@ -160,9 +161,10 @@ class KeyguardController { mWindowManager.deferSurfaceLayout(); try { setKeyguardGoingAway(true); - mWindowManager.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, - false /* alwaysKeepCurrent */, convertTransitFlags(flags), - false /* forceOverride */); + mStackSupervisor.getDefaultDisplay().getWindowContainerController() + .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, + false /* alwaysKeepCurrent */, convertTransitFlags(flags), + false /* forceOverride */); updateKeyguardSleepToken(); // Some stack visibility might change (e.g. docked stack) @@ -285,8 +287,10 @@ class KeyguardController { if (isKeyguardLocked()) { mWindowManager.deferSurfaceLayout(); try { - mWindowManager.prepareAppTransition(resolveOccludeTransit(), - false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */); + mStackSupervisor.getDefaultDisplay().getWindowContainerController() + .prepareAppTransition(resolveOccludeTransit(), + false /* alwaysKeepCurrent */, 0 /* flags */, + true /* forceOverride */); updateKeyguardSleepToken(); mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); mWindowManager.executeAppTransition(); @@ -310,10 +314,12 @@ class KeyguardController { // If we are about to unocclude the Keyguard, but we can dismiss it without security, // we immediately dismiss the Keyguard so the activity gets shown without a flicker. + final DisplayWindowController dwc = + mStackSupervisor.getDefaultDisplay().getWindowContainerController(); if (mKeyguardShowing && canDismissKeyguard() - && mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) { - mWindowManager.prepareAppTransition(mBeforeUnoccludeTransit, - false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */); + && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) { + dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */, + 0 /* flags */, true /* forceOverride */); mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); mWindowManager.executeAppTransition(); } @@ -332,8 +338,10 @@ class KeyguardController { } private int resolveOccludeTransit() { + final DisplayWindowController dwc = + mStackSupervisor.getDefaultDisplay().getWindowContainerController(); if (mBeforeUnoccludeTransit != TRANSIT_UNSET - && mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE + && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE // TODO(b/113840485): Handle app transition for individual display. && isDisplayOccluded(DEFAULT_DISPLAY)) { @@ -344,7 +352,7 @@ class KeyguardController { } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) { // Save transit in case we dismiss/occlude Keyguard shortly after. - mBeforeUnoccludeTransit = mWindowManager.getPendingAppTransition(); + mBeforeUnoccludeTransit = dwc.getPendingAppTransition(); return TRANSIT_KEYGUARD_UNOCCLUDE; } else { return TRANSIT_KEYGUARD_OCCLUDE; diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java index d5f2d34bde01..bcebaaab6aa9 100644 --- a/services/core/java/com/android/server/am/LockTaskController.java +++ b/services/core/java/com/android/server/am/LockTaskController.java @@ -580,7 +580,10 @@ public class LockTaskController { mSupervisor.findTaskToMoveToFront(task, 0, null, reason, lockTaskModeState != LOCK_TASK_MODE_NONE); mSupervisor.resumeFocusedStacksTopActivitiesLocked(); - mWindowManager.executeAppTransition(); + final ActivityStack stack = task.getStack(); + if (stack != null) { + stack.getDisplay().getWindowContainerController().executeAppTransition(); + } } else if (lockTaskModeState != LOCK_TASK_MODE_NONE) { mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, task.getStack(), true /* forceNonResizable */); diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java index c5586bbc3e82..41d488babd92 100644 --- a/services/core/java/com/android/server/am/RecentsAnimation.java +++ b/services/core/java/com/android/server/am/RecentsAnimation.java @@ -41,6 +41,8 @@ import android.os.RemoteException; import android.os.Trace; import android.util.Slog; import android.view.IRecentsAnimationRunner; + +import com.android.server.wm.DisplayWindowController; import com.android.server.wm.RecentsAnimationController; import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks; import com.android.server.wm.WindowManagerService; @@ -85,10 +87,13 @@ class RecentsAnimation implements RecentsAnimationCallbacks, + " assistDataReceiver=" + assistDataReceiver); Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity"); + // TODO(multi-display) currently only support recents animation in default display. + final DisplayWindowController dwc = + mStackSupervisor.getDefaultDisplay().getWindowContainerController(); if (!mWindowManager.canStartRecentsAnimation()) { notifyAnimationCancelBeforeStart(recentsAnimationRunner); if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition=" - + mWindowManager.getPendingAppTransition()); + + dwc.getPendingAppTransition()); return; } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index b011da60b210..ae47c27268bc 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -229,8 +229,9 @@ public abstract class ActivityTaskManagerInternal { * @param callback Callback to run after activity visibilities have been reevaluated. This can * be used from window manager so that when the callback is called, it's * guaranteed that all apps have their visibility updated accordingly. + * @param displayId The id of the display where the keyguard flags changed. */ - public abstract void notifyKeyguardFlagsChanged(@Nullable Runnable callback); + public abstract void notifyKeyguardFlagsChanged(@Nullable Runnable callback, int displayId); /** * Called when the trusted state of Keyguard has changed. diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index a9d09781d223..10a1be52b71e 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -166,6 +166,7 @@ public class AppTransition implements Dump { private final Context mContext; private final WindowManagerService mService; + private final DisplayContent mDisplayContent; private @TransitionType int mNextAppTransition = TRANSIT_UNSET; private @TransitionFlags int mNextAppTransitionFlags = 0; @@ -257,10 +258,11 @@ public class AppTransition implements Dump { final Handler mHandler; final Runnable mHandleAppTransitionTimeoutRunnable = () -> handleAppTransitionTimeout(); - AppTransition(Context context, WindowManagerService service) { + AppTransition(Context context, WindowManagerService service, DisplayContent displayContent) { mContext = context; mService = service; mHandler = new Handler(service.mH.getLooper()); + mDisplayContent = displayContent; mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, com.android.internal.R.interpolator.linear_out_slow_in); mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, @@ -426,7 +428,7 @@ public class AppTransition implements Dump { ? topOpeningAnim.getStatusBarTransitionsStartTime() : SystemClock.uptimeMillis(), AnimationAdapter.STATUS_BAR_TRANSITION_DURATION); - mService.getDefaultDisplayContentLocked().getDockedDividerController() + mDisplayContent.getDockedDividerController() .notifyAppTransitionStarting(openingApps, transit); if (mRemoteAnimationController != null) { @@ -2142,7 +2144,8 @@ public class AppTransition implements Dump { + " transit=" + appTransitionToString(transit) + " " + this + " alwaysKeepCurrent=" + alwaysKeepCurrent - + " Callers=" + Debug.getCallers(3)); + + " displayId=" + mDisplayContent.getDisplayId() + + " Callers=" + Debug.getCallers(5)); final boolean allowSetCrashing = !isKeyguardTransit(mNextAppTransition) && transit == TRANSIT_CRASHING_ACTIVITY_CLOSE; if (forceOverride || isKeyguardTransit(transit) || !isTransitionSet() @@ -2218,14 +2221,18 @@ public class AppTransition implements Dump { private void handleAppTransitionTimeout() { synchronized (mService.mWindowMap) { - if (isTransitionSet() || !mService.mOpeningApps.isEmpty() - || !mService.mClosingApps.isEmpty()) { + final DisplayContent dc = mDisplayContent; + if (dc == null) { + return; + } + if (isTransitionSet() || !dc.mOpeningApps.isEmpty() || !dc.mClosingApps.isEmpty()) { if (DEBUG_APP_TRANSITIONS) { Slog.v(TAG_WM, "*** APP TRANSITION TIMEOUT." + + " displayId=" + dc.getDisplayId() + " isTransitionSet()=" - + mService.mAppTransition.isTransitionSet() - + " mOpeningApps.size()=" + mService.mOpeningApps.size() - + " mClosingApps.size()=" + mService.mClosingApps.size()); + + dc.mAppTransition.isTransitionSet() + + " mOpeningApps.size()=" + dc.mOpeningApps.size() + + " mClosingApps.size()=" + dc.mClosingApps.size()); } setTimeout(); mService.mWindowPlacerLocked.performSurfacePlacement(); diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java new file mode 100644 index 000000000000..94a47dd2aa60 --- /dev/null +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -0,0 +1,631 @@ +/* + * Copyright (C) 2018 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.server.wm; + +import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; +import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; +import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER; +import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_TASK_CLOSE; +import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE; +import static android.view.WindowManager.TRANSIT_TASK_OPEN; +import static android.view.WindowManager.TRANSIT_TASK_TO_BACK; +import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; +import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE; +import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE; +import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN; +import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN; + +import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; +import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; +import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT; +import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN; +import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN; +import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; +import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING; + +import android.app.WindowConfiguration; +import android.os.Trace; +import android.util.ArraySet; +import android.util.Slog; +import android.util.SparseIntArray; +import android.view.Display; +import android.view.RemoteAnimationAdapter; +import android.view.RemoteAnimationDefinition; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.view.animation.Animation; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.function.Predicate; + + +/** + * Checks for app transition readiness, resolves animation attributes and performs visibility + * change for apps that animate as part of an app transition. + */ +public class AppTransitionController { + private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransitionController" : TAG_WM; + private final WindowManagerService mService; + private final DisplayContent mDisplayContent; + private final WallpaperController mWallpaperControllerLocked; + + private final SparseIntArray mTempTransitionReasons = new SparseIntArray(); + + AppTransitionController(WindowManagerService service, DisplayContent displayContent) { + mService = service; + mDisplayContent = displayContent; + mWallpaperControllerLocked = new WallpaperController(mService); + } + + /** + * Handle application transition for given display. + */ + void handleAppTransitionReady() { + final int appsCount = mDisplayContent.mOpeningApps.size(); + if (!transitionGoodToGo(appsCount, mTempTransitionReasons)) { + return; + } + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady"); + + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); + int transit = mDisplayContent.mAppTransition.getAppTransition(); + if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) { + transit = WindowManager.TRANSIT_UNSET; + } + mDisplayContent.mSkipAppTransitionAnimation = false; + mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear(); + + mDisplayContent.mAppTransition.removeAppTransitionTimeoutCallbacks(); + + mService.mRoot.mWallpaperMayChange = false; + + int i; + for (i = 0; i < appsCount; i++) { + final AppWindowToken wtoken = mDisplayContent.mOpeningApps.valueAt(i); + // Clearing the mAnimatingExit flag before entering animation. It's set to true if app + // window is removed, or window relayout to invisible. This also affects window + // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the + // transition selection depends on wallpaper target visibility. + wtoken.clearAnimatingFlags(); + } + + // Adjust wallpaper before we pull the lower/upper target, since pending changes + // (like the clearAnimatingFlags() above) might affect wallpaper target result. + // Or, the opening app window should be a wallpaper target. + mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(mDisplayContent, + mDisplayContent.mOpeningApps); + + // Determine if closing and opening app token sets are wallpaper targets, in which case + // special animations are needed. + final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null; + final boolean openingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mOpeningApps) + && hasWallpaperTarget; + final boolean closingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mClosingApps) + && hasWallpaperTarget; + + transit = maybeUpdateTransitToTranslucentAnim(transit); + transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper, + closingAppHasWallpaper); + + // Find the layout params of the top-most application window in the tokens, which is + // what will control the animation theme. If all closing windows are obscured, then there is + // no need to do an animation. This is the case, for example, when this transition is being + // done behind a dream window. + final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps, + mDisplayContent.mClosingApps); + final boolean allowAnimations = mService.mPolicy.allowAppAnimationsLw(); + final AppWindowToken animLpToken = allowAnimations + ? findAnimLayoutParamsToken(transit, activityTypes) + : null; + final AppWindowToken topOpeningApp = allowAnimations + ? getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */) + : null; + final AppWindowToken topClosingApp = allowAnimations + ? getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */) + : null; + final WindowManager.LayoutParams animLp = getAnimLp(animLpToken); + overrideWithRemoteAnimationIfSet(animLpToken, transit, activityTypes); + + final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps) + || containsVoiceInteraction(mDisplayContent.mOpeningApps); + + final int layoutRedo; + mService.mSurfaceAnimationRunner.deferStartingAnimations(); + try { + processApplicationsAnimatingInPlace(transit); + + handleClosingApps(transit, animLp, voiceInteraction); + handleOpeningApps(transit, animLp, voiceInteraction); + + mDisplayContent.mAppTransition.setLastAppTransition(transit, topOpeningApp, + topClosingApp); + + final int flags = mDisplayContent.mAppTransition.getTransitFlags(); + layoutRedo = mDisplayContent.mAppTransition.goodToGo(transit, topOpeningApp, + topClosingApp, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps); + handleNonAppWindowsInTransition(transit, flags); + mDisplayContent.mAppTransition.postAnimationCallback(); + mDisplayContent.mAppTransition.clear(); + } finally { + mService.mSurfaceAnimationRunner.continueStartingAnimations(); + } + + mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent); + + mDisplayContent.mOpeningApps.clear(); + mDisplayContent.mClosingApps.clear(); + mDisplayContent.mUnknownAppVisibilityController.clear(); + + // This has changed the visibility of windows, so perform + // a new layout to get them all up-to-date. + mDisplayContent.setLayoutNeeded(); + + mDisplayContent.computeImeTarget(true /* updateImeTarget */); + + mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, + mTempTransitionReasons.clone()).sendToTarget(); + + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); + + mDisplayContent.pendingLayoutChanges |= + layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG; + } + + private static WindowManager.LayoutParams getAnimLp(AppWindowToken wtoken) { + final WindowState mainWindow = wtoken != null ? wtoken.findMainWindow() : null; + return mainWindow != null ? mainWindow.mAttrs : null; + } + + /** + * Overrides the pending transition with the remote animation defined for the transition in the + * set of defined remote animations in the app window token. + */ + private void overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit, + ArraySet<Integer> activityTypes) { + if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) { + // The crash transition has higher priority than any involved remote animations. + return; + } + if (animLpToken == null) { + return; + } + final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition(); + if (definition == null) { + return; + } + final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes); + if (adapter != null) { + animLpToken.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote( + adapter); + } + } + + /** + * @return The window token that determines the animation theme. + */ + private AppWindowToken findAnimLayoutParamsToken(@WindowManager.TransitionType int transit, + ArraySet<Integer> activityTypes) { + AppWindowToken result; + final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps; + final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps; + + // Remote animations always win, but fullscreen tokens override non-fullscreen tokens. + result = lookForHighestTokenWithFilter(closingApps, openingApps, + w -> w.getRemoteAnimationDefinition() != null + && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes)); + if (result != null) { + return result; + } + result = lookForHighestTokenWithFilter(closingApps, openingApps, + w -> w.fillsParent() && w.findMainWindow() != null); + if (result != null) { + return result; + } + return lookForHighestTokenWithFilter(closingApps, openingApps, + w -> w.findMainWindow() != null); + } + + /** + * @return The set of {@link WindowConfiguration.ActivityType}s contained in the set of apps in + * {@code array1} and {@code array2}. + */ + private static ArraySet<Integer> collectActivityTypes(ArraySet<AppWindowToken> array1, + ArraySet<AppWindowToken> array2) { + final ArraySet<Integer> result = new ArraySet<>(); + for (int i = array1.size() - 1; i >= 0; i--) { + result.add(array1.valueAt(i).getActivityType()); + } + for (int i = array2.size() - 1; i >= 0; i--) { + result.add(array2.valueAt(i).getActivityType()); + } + return result; + } + + private static AppWindowToken lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1, + ArraySet<AppWindowToken> array2, Predicate<AppWindowToken> filter) { + final int array1count = array1.size(); + final int count = array1count + array2.size(); + int bestPrefixOrderIndex = Integer.MIN_VALUE; + AppWindowToken bestToken = null; + for (int i = 0; i < count; i++) { + final AppWindowToken wtoken = i < array1count + ? array1.valueAt(i) + : array2.valueAt(i - array1count); + final int prefixOrderIndex = wtoken.getPrefixOrderIndex(); + if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) { + bestPrefixOrderIndex = prefixOrderIndex; + bestToken = wtoken; + } + } + return bestToken; + } + + private boolean containsVoiceInteraction(ArraySet<AppWindowToken> apps) { + for (int i = apps.size() - 1; i >= 0; i--) { + if (apps.valueAt(i).mVoiceInteraction) { + return true; + } + } + return false; + } + + private void handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction) { + final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps; + final int appsCount = openingApps.size(); + for (int i = 0; i < appsCount; i++) { + AppWindowToken wtoken = openingApps.valueAt(i); + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); + + if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)) { + // This token isn't going to be animating. Add it to the list of tokens to + // be notified of app transition complete since the notification will not be + // sent be the app window animator. + mDisplayContent.mNoAnimationNotifyOnTransitionFinished.add(wtoken.token); + } + wtoken.updateReportedVisibilityLocked(); + wtoken.waitingToShow = false; + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, + ">>> OPEN TRANSACTION handleAppTransitionReady()"); + mService.openSurfaceTransaction(); + try { + wtoken.showAllWindowsLocked(); + } finally { + mService.closeSurfaceTransaction("handleAppTransitionReady"); + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, + "<<< CLOSE TRANSACTION handleAppTransitionReady()"); + } + + if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailUp()) { + wtoken.attachThumbnailAnimation(); + } else if (mDisplayContent.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) { + wtoken.attachCrossProfileAppsThumbnailAnimation(); + } + } + } + + private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction) { + final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps; + final int appsCount = closingApps.size(); + for (int i = 0; i < appsCount; i++) { + AppWindowToken wtoken = closingApps.valueAt(i); + + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); + // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not + // animating? + wtoken.setVisibility(animLp, false, transit, false, voiceInteraction); + wtoken.updateReportedVisibilityLocked(); + // Force the allDrawn flag, because we want to start + // this guy's animations regardless of whether it's + // gotten drawn. + wtoken.allDrawn = true; + wtoken.deferClearAllDrawn = false; + // Ensure that apps that are mid-starting are also scheduled to have their + // starting windows removed after the animation is complete + if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit + && wtoken.getController() != null) { + wtoken.getController().removeStartingWindow(); + } + + if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) { + wtoken.attachThumbnailAnimation(); + } + } + } + + private void handleNonAppWindowsInTransition(int transit, int flags) { + if (transit == TRANSIT_KEYGUARD_GOING_AWAY) { + if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0 + && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0) { + Animation anim = mService.mPolicy.createKeyguardWallpaperExit( + (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0); + if (anim != null) { + mDisplayContent.mWallpaperController.startWallpaperAnimation(anim); + } + } + } + if (transit == TRANSIT_KEYGUARD_GOING_AWAY + || transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) { + mDisplayContent.startKeyguardExitOnNonAppWindows( + transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER, + (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0); + } + } + + private boolean transitionGoodToGo(int appsCount, SparseIntArray outReasons) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "Checking " + appsCount + " opening apps (frozen=" + + mService.mDisplayFrozen + " timeout=" + + mDisplayContent.mAppTransition.isTimeout() + ")..."); + final ScreenRotationAnimation screenRotationAnimation = + mService.mAnimator.getScreenRotationAnimationLocked( + Display.DEFAULT_DISPLAY); + + outReasons.clear(); + if (!mDisplayContent.mAppTransition.isTimeout()) { + // Imagine the case where we are changing orientation due to an app transition, but a + // previous orientation change is still in progress. We won't process the orientation + // change for our transition because we need to wait for the rotation animation to + // finish. + // If we start the app transition at this point, we will interrupt it halfway with a + // new rotation animation after the old one finally finishes. It's better to defer the + // app transition. + if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() && + mService.getDefaultDisplayContentLocked().rotationNeedsUpdate()) { + if (DEBUG_APP_TRANSITIONS) { + Slog.v(TAG, "Delaying app transition for screen rotation animation to finish"); + } + return false; + } + for (int i = 0; i < appsCount; i++) { + AppWindowToken wtoken = mDisplayContent.mOpeningApps.valueAt(i); + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "Check opening app=" + wtoken + ": allDrawn=" + + wtoken.allDrawn + " startingDisplayed=" + + wtoken.startingDisplayed + " startingMoved=" + + wtoken.startingMoved + " isRelaunching()=" + + wtoken.isRelaunching() + " startingWindow=" + + wtoken.startingWindow); + + + final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching(); + if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) { + return false; + } + final int windowingMode = wtoken.getWindowingMode(); + if (allDrawn) { + outReasons.put(windowingMode, APP_TRANSITION_WINDOWS_DRAWN); + } else { + outReasons.put(windowingMode, + wtoken.startingData instanceof SplashScreenStartingData + ? APP_TRANSITION_SPLASH_SCREEN + : APP_TRANSITION_SNAPSHOT); + } + } + + // We also need to wait for the specs to be fetched, if needed. + if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "isFetchingAppTransitionSpecs=true"); + return false; + } + + if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) { + if (DEBUG_APP_TRANSITIONS) { + Slog.v(TAG, "unknownApps is not empty: " + + mDisplayContent.mUnknownAppVisibilityController.getDebugMessage()); + } + return false; + } + + // If the wallpaper is visible, we need to check it's ready too. + boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() || + mWallpaperControllerLocked.wallpaperTransitionReady(); + if (wallpaperReady) { + return true; + } + return false; + } + return true; + } + + private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper, + boolean closingAppHasWallpaper) { + // Given no app transition pass it through instead of a wallpaper transition. + // Never convert the crashing transition. + // Never update the transition for the wallpaper if we are just docking from recents + if (transit == TRANSIT_NONE || transit == TRANSIT_CRASHING_ACTIVITY_CLOSE + || transit == TRANSIT_DOCK_TASK_FROM_RECENTS) { + return transit; + } + + final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); + final boolean showWallpaper = wallpaperTarget != null + && (wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0; + // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set, + // don't consider upgrading to wallpaper transition. + final WindowState oldWallpaper = + (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper) + ? null + : wallpaperTarget; + final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps; + final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps; + final AppWindowToken topOpeningApp = getTopApp(mDisplayContent.mOpeningApps, + false /* ignoreHidden */); + final AppWindowToken topClosingApp = getTopApp(mDisplayContent.mClosingApps, + true /* ignoreHidden */); + + boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps); + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New wallpaper target=" + wallpaperTarget + + ", oldWallpaper=" + oldWallpaper + + ", openingApps=" + openingApps + + ", closingApps=" + closingApps); + + if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) { + transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New transit: " + AppTransition.appTransitionToString(transit)); + } + // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic + // relies on the fact that we always execute a Keyguard transition after preparing one. + else if (!isKeyguardGoingAwayTransit(transit)) { + if (closingAppHasWallpaper && openingAppHasWallpaper) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!"); + switch (transit) { + case TRANSIT_ACTIVITY_OPEN: + case TRANSIT_TASK_OPEN: + case TRANSIT_TASK_TO_FRONT: + transit = TRANSIT_WALLPAPER_INTRA_OPEN; + break; + case TRANSIT_ACTIVITY_CLOSE: + case TRANSIT_TASK_CLOSE: + case TRANSIT_TASK_TO_BACK: + transit = TRANSIT_WALLPAPER_INTRA_CLOSE; + break; + } + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New transit: " + AppTransition.appTransitionToString(transit)); + } else if (oldWallpaper != null && !mDisplayContent.mOpeningApps.isEmpty() + && !openingApps.contains(oldWallpaper.mAppToken) + && closingApps.contains(oldWallpaper.mAppToken) + && topClosingApp == oldWallpaper.mAppToken) { + // We are transitioning from an activity with a wallpaper to one without. + transit = TRANSIT_WALLPAPER_CLOSE; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: " + + AppTransition.appTransitionToString(transit)); + } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() + && openingApps.contains(wallpaperTarget.mAppToken) + && topOpeningApp == wallpaperTarget.mAppToken + && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) { + // We are transitioning from an activity without + // a wallpaper to now showing the wallpaper + transit = TRANSIT_WALLPAPER_OPEN; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: " + + AppTransition.appTransitionToString(transit)); + } + } + return transit; + } + + /** + * There are cases where we open/close a new task/activity, but in reality only a translucent + * activity on top of existing activities is opening/closing. For that one, we have a different + * animation because non of the task/activity animations actually work well with translucent + * apps. + * + * @param transit The current transition type. + * @return The current transition type or + * {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE}/ + * {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the + * situation. + */ + @VisibleForTesting + int maybeUpdateTransitToTranslucentAnim(int transit) { + final boolean taskOrActivity = AppTransition.isTaskTransit(transit) + || AppTransition.isActivityTransit(transit); + boolean allOpeningVisible = true; + boolean allTranslucentOpeningApps = !mDisplayContent.mOpeningApps.isEmpty(); + for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) { + final AppWindowToken token = mDisplayContent.mOpeningApps.valueAt(i); + if (!token.isVisible()) { + allOpeningVisible = false; + if (token.fillsParent()) { + allTranslucentOpeningApps = false; + } + } + } + boolean allTranslucentClosingApps = !mDisplayContent.mClosingApps.isEmpty(); + for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) { + if (mDisplayContent.mClosingApps.valueAt(i).fillsParent()) { + allTranslucentClosingApps = false; + break; + } + } + + if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) { + return TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE; + } + if (taskOrActivity && allTranslucentOpeningApps && mDisplayContent.mClosingApps.isEmpty()) { + return TRANSIT_TRANSLUCENT_ACTIVITY_OPEN; + } + return transit; + } + + private boolean canBeWallpaperTarget(ArraySet<AppWindowToken> apps) { + for (int i = apps.size() - 1; i >= 0; i--) { + if (apps.valueAt(i).windowsCanBeWallpaperTarget()) { + return true; + } + } + return false; + } + + /** + * Finds the top app in a list of apps, using its {@link AppWindowToken#getPrefixOrderIndex} to + * compare z-order. + * + * @param apps The list of apps to search. + * @param ignoreHidden If set to true, ignores apps that are {@link AppWindowToken#isHidden}. + * @return The top {@link AppWindowToken}. + */ + private AppWindowToken getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden) { + int topPrefixOrderIndex = Integer.MIN_VALUE; + AppWindowToken topApp = null; + for (int i = apps.size() - 1; i >= 0; i--) { + final AppWindowToken app = apps.valueAt(i); + if (ignoreHidden && app.isHidden()) { + continue; + } + final int prefixOrderIndex = app.getPrefixOrderIndex(); + if (prefixOrderIndex > topPrefixOrderIndex) { + topPrefixOrderIndex = prefixOrderIndex; + topApp = app; + } + } + return topApp; + } + + private void processApplicationsAnimatingInPlace(int transit) { + if (transit == TRANSIT_TASK_IN_PLACE) { + // Find the focused window + final WindowState win = mDisplayContent.findFocusedWindow(); + if (win != null) { + final AppWindowToken wtoken = win.mAppToken; + if (DEBUG_APP_TRANSITIONS) + Slog.v(TAG, "Now animating app in place " + wtoken); + wtoken.cancelAnimation(); + wtoken.applyAnimationLocked(null, transit, false, false); + wtoken.updateReportedVisibilityLocked(); + wtoken.showAllWindowsLocked(); + } + } + } +} diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index 330c54ca4c1d..7435ea5e532f 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -16,6 +16,15 @@ package com.android.server.wm; +import static android.app.ActivityOptions.ANIM_CLIP_REVEAL; +import static android.app.ActivityOptions.ANIM_CUSTOM; +import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; +import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION; +import static android.app.ActivityOptions.ANIM_SCALE_UP; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; @@ -30,14 +39,20 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.app.ActivityManager.TaskSnapshot; +import android.app.ActivityOptions; +import android.content.Intent; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; +import android.graphics.GraphicBuffer; +import android.graphics.Rect; import android.os.Debug; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.util.Slog; +import android.view.AppTransitionAnimationSpec; +import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IApplicationToken; import android.view.RemoteAnimationDefinition; import android.view.WindowManager; @@ -324,6 +339,7 @@ public class AppWindowContainerController } final AppWindowToken wtoken = mContainer; + final AppTransition appTransition = mContainer.getDisplayContent().mAppTransition; // Don't set visibility to false if we were already not visible. This prevents WM from // adding the app to the closing app list which doesn't make sense for something that is @@ -344,12 +360,13 @@ public class AppWindowContainerController } if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility(" - + mToken + ", visible=" + visible + "): " + mService.mAppTransition + + mToken + ", visible=" + visible + "): " + appTransition + " hidden=" + wtoken.isHidden() + " hiddenRequested=" + wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6)); - mService.mOpeningApps.remove(wtoken); - mService.mClosingApps.remove(wtoken); + final DisplayContent displayContent = mContainer.getDisplayContent(); + displayContent.mOpeningApps.remove(wtoken); + displayContent.mClosingApps.remove(wtoken); wtoken.waitingToShow = false; wtoken.hiddenRequested = !visible; wtoken.mDeferHidingClient = deferHidingClient; @@ -360,12 +377,12 @@ public class AppWindowContainerController // if made visible again. wtoken.removeDeadWindows(); } else { - if (!mService.mAppTransition.isTransitionSet() - && mService.mAppTransition.isReady()) { + if (!appTransition.isTransitionSet() + && appTransition.isReady()) { // Add the app mOpeningApps if transition is unset but ready. This means // we're doing a screen freeze, and the unfreeze will wait for all opening // apps to be ready. - mService.mOpeningApps.add(wtoken); + displayContent.mOpeningApps.add(wtoken); } wtoken.startingMoved = false; // If the token is currently hidden (should be the common case), or has been @@ -395,16 +412,16 @@ public class AppWindowContainerController // If we are preparing an app transition, then delay changing // the visibility of this token until we execute that transition. - if (wtoken.okToAnimate() && mService.mAppTransition.isTransitionSet()) { + if (wtoken.okToAnimate() && appTransition.isTransitionSet()) { wtoken.inPendingTransaction = true; if (visible) { - mService.mOpeningApps.add(wtoken); + displayContent.mOpeningApps.add(wtoken); wtoken.mEnteringAnimation = true; } else { - mService.mClosingApps.add(wtoken); + displayContent.mClosingApps.add(wtoken); wtoken.mEnteringAnimation = false; } - if (mService.mAppTransition.getAppTransition() + if (appTransition.getAppTransition() == WindowManager.TRANSIT_TASK_OPEN_BEHIND) { // We're launchingBehind, add the launching activity to mOpeningApps. final WindowState win = mContainer.getDisplayContent().findFocusedWindow(); @@ -415,7 +432,7 @@ public class AppWindowContainerController + " adding " + focusedToken + " to mOpeningApps"); // Force animation to be loaded. focusedToken.setHidden(true); - mService.mOpeningApps.add(focusedToken); + displayContent.mOpeningApps.add(focusedToken); } } } @@ -434,7 +451,8 @@ public class AppWindowContainerController public void notifyUnknownVisibilityLaunched() { synchronized(mWindowMap) { if (mContainer != null) { - mService.mUnknownAppVisibilityController.notifyLaunched(mContainer); + mContainer.getDisplayContent().mUnknownAppVisibilityController.notifyLaunched( + mContainer); } } } @@ -547,7 +565,8 @@ public class AppWindowContainerController private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents, TaskSnapshot snapshot) { - if (mService.mAppTransition.getAppTransition() == TRANSIT_DOCK_TASK_FROM_RECENTS) { + if (mContainer.getDisplayContent().mAppTransition.getAppTransition() + == TRANSIT_DOCK_TASK_FROM_RECENTS) { // TODO(b/34099271): Remove this statement to add back the starting window and figure // out why it causes flickering, the starting window appears over the thumbnail while // the docked from recents transition occurs @@ -753,6 +772,104 @@ public class AppWindowContainerController } /** + * Apply override app transition base on options & animation type. + */ + public void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) { + synchronized (mWindowMap) { + final int animationType = pendingOptions.getAnimationType(); + final DisplayContent displayContent = mContainer.getDisplayContent(); + switch (animationType) { + case ANIM_CUSTOM: + displayContent.mAppTransition.overridePendingAppTransition( + pendingOptions.getPackageName(), + pendingOptions.getCustomEnterResId(), + pendingOptions.getCustomExitResId(), + pendingOptions.getOnAnimationStartListener()); + break; + case ANIM_CLIP_REVEAL: + displayContent.mAppTransition.overridePendingAppTransitionClipReveal( + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getWidth(), pendingOptions.getHeight()); + if (intent.getSourceBounds() == null) { + intent.setSourceBounds(new Rect(pendingOptions.getStartX(), + pendingOptions.getStartY(), + pendingOptions.getStartX() + pendingOptions.getWidth(), + pendingOptions.getStartY() + pendingOptions.getHeight())); + } + break; + case ANIM_SCALE_UP: + displayContent.mAppTransition.overridePendingAppTransitionScaleUp( + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getWidth(), pendingOptions.getHeight()); + if (intent.getSourceBounds() == null) { + intent.setSourceBounds(new Rect(pendingOptions.getStartX(), + pendingOptions.getStartY(), + pendingOptions.getStartX() + pendingOptions.getWidth(), + pendingOptions.getStartY() + pendingOptions.getHeight())); + } + break; + case ANIM_THUMBNAIL_SCALE_UP: + case ANIM_THUMBNAIL_SCALE_DOWN: + final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP); + final GraphicBuffer buffer = pendingOptions.getThumbnail(); + displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer, + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getOnAnimationStartListener(), + scaleUp); + if (intent.getSourceBounds() == null && buffer != null) { + intent.setSourceBounds(new Rect(pendingOptions.getStartX(), + pendingOptions.getStartY(), + pendingOptions.getStartX() + buffer.getWidth(), + pendingOptions.getStartY() + buffer.getHeight())); + } + break; + case ANIM_THUMBNAIL_ASPECT_SCALE_UP: + case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: + final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs(); + final IAppTransitionAnimationSpecsFuture specsFuture = + pendingOptions.getSpecsFuture(); + if (specsFuture != null) { + // TODO(multidisplay): Shouldn't be really used anymore from next CL. + displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture( + specsFuture, pendingOptions.getOnAnimationStartListener(), + animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP); + } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN + && specs != null) { + displayContent.mAppTransition.overridePendingAppTransitionMultiThumb( + specs, pendingOptions.getOnAnimationStartListener(), + pendingOptions.getAnimationFinishedListener(), false); + } else { + displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb( + pendingOptions.getThumbnail(), + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getWidth(), pendingOptions.getHeight(), + pendingOptions.getOnAnimationStartListener(), + (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP)); + if (intent.getSourceBounds() == null) { + intent.setSourceBounds(new Rect(pendingOptions.getStartX(), + pendingOptions.getStartY(), + pendingOptions.getStartX() + pendingOptions.getWidth(), + pendingOptions.getStartY() + pendingOptions.getHeight())); + } + } + break; + case ANIM_OPEN_CROSS_PROFILE_APPS: + displayContent.mAppTransition + .overridePendingAppTransitionStartCrossProfileApps(); + break; + case ANIM_REMOTE_ANIMATION: + // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL. + displayContent.mAppTransition.overridePendingAppTransitionRemote( + pendingOptions.getRemoteAnimationAdapter()); + break; + default: + Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType); + break; + } + } + } + + /** * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP. * This information helps AWT know that the app is in the process of pausing before it gets the * signal on the WM side. diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java index ad92f81f4dde..729f89bb2611 100644 --- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java +++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java @@ -96,7 +96,7 @@ class AppWindowThumbnail implements Animatable { anim.scaleCurrentDuration(mAppToken.mService.getTransitionAnimationScaleLocked()); mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter( new WindowAnimationSpec(anim, position, - mAppToken.mService.mAppTransition.canSkipFirstFrame()), + mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame()), mAppToken.mService.mSurfaceAnimationRunner), false /* hidden */); } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index e38e22909957..9baafcb73279 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -111,6 +111,7 @@ import com.android.server.wm.WindowManagerService.H; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.function.Consumer; class AppTokenList extends ArrayList<AppWindowToken> { } @@ -500,14 +501,14 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree setClientHidden(!visible); } - if (!mService.mClosingApps.contains(this) && !mService.mOpeningApps.contains(this)) { + if (!getDisplayContent().mClosingApps.contains(this) + && !getDisplayContent().mOpeningApps.contains(this)) { // The token is not closing nor opening, so even if there is an animation set, that // doesn't mean that it goes through the normal app transition cycle so we have // to inform the docked controller about visibility change. // TODO(multi-display): notify docked divider on all displays where visibility was // affected. - mService.getDefaultDisplayContentLocked().getDockedDividerController() - .notifyAppVisibilityChanged(); + getDisplayContent().getDockedDividerController().notifyAppVisibilityChanged(); // Take the screenshot before possibly hiding the WSA, otherwise the screenshot // will not be taken. @@ -524,7 +525,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // no animation but there will still be a transition set. // We still need to delay hiding the surface such that it // can be synchronized with showing the next surface in the transition. - if (isHidden() && !delayed && !mService.mAppTransition.isTransitionSet()) { + if (isHidden() && !delayed && !getDisplayContent().mAppTransition.isTransitionSet()) { SurfaceControl.openTransaction(); for (int i = mChildren.size() - 1; i >= 0; i--) { mChildren.get(i).mWinAnimator.hide("immediately hidden"); @@ -630,14 +631,14 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction); - mService.mOpeningApps.remove(this); - mService.mUnknownAppVisibilityController.appRemovedOrHidden(this); + getDisplayContent().mOpeningApps.remove(this); + getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this); mService.mTaskSnapshotController.onAppRemoved(this); waitingToShow = false; - if (mService.mClosingApps.contains(this)) { + if (getDisplayContent().mClosingApps.contains(this)) { delayed = true; - } else if (mService.mAppTransition.isTransitionSet()) { - mService.mClosingApps.add(this); + } else if (getDisplayContent().mAppTransition.isTransitionSet()) { + getDisplayContent().mClosingApps.add(this); delayed = true; } @@ -652,10 +653,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } // If this window was animating, then we need to ensure that the app transition notifies - // that animations have completed in WMS.handleAnimatingStoppedAndTransitionLocked(), so - // add to that list now + // that animations have completed in DisplayContent.handleAnimatingStoppedAndTransition(), + // so add to that list now if (isSelfAnimating()) { - mService.mNoAnimationNotifyOnTransitionFinished.add(token); + getDisplayContent().mNoAnimationNotifyOnTransitionFinished.add(token); } final TaskStack stack = getStack(); @@ -795,7 +796,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (task == null) { // It is possible we have been marked as a closing app earlier. We must remove ourselves // from this list so we do not participate in any future animations. - mService.mClosingApps.remove(this); + getDisplayContent().mClosingApps.remove(this); } else if (mLastParent != null && mLastParent.mStack != null) { task.mStack.mExitingAppTokens.remove(this); } @@ -1219,7 +1220,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (tStartingWindow != null && fromToken.startingSurface != null) { // In this case, the starting icon has already been displayed, so start // letting windows get shown immediately without any more transitions. - mService.mSkipAppTransitionAnimation = true; + getDisplayContent().mSkipAppTransitionAnimation = true; if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Moving existing starting " + tStartingWindow + " from " + fromToken + " to " + this); @@ -1269,7 +1270,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // When transferring an animation, we no longer need to apply an animation to the // the token we transfer the animation over. Thus, remove the animation from // pending opening apps. - mService.mOpeningApps.remove(this); + getDisplayContent().mOpeningApps.remove(this); mService.updateFocusedWindowLocked( UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/); @@ -1323,8 +1324,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // The {@link AppWindowToken} should only specify an orientation when it is not closing or // going to the bottom. Allowing closing {@link AppWindowToken} to participate can lead to // an Activity in another task being started in the wrong orientation during the transition. - if (!(sendingToBottom || mService.mClosingApps.contains(this)) - && (isVisible() || mService.mOpeningApps.contains(this))) { + if (!(sendingToBottom || getDisplayContent().mClosingApps.contains(this)) + && (isVisible() || getDisplayContent().mOpeningApps.contains(this))) { return mOrientation; } @@ -1398,7 +1399,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "checkAppWindowsReadyToShow"); // We can now show all of the drawn windows! - if (!mService.mOpeningApps.contains(this) && canShowWindows()) { + if (!getDisplayContent().mOpeningApps.contains(this) && canShowWindows()) { showAllWindowsLocked(); } } @@ -1572,6 +1573,11 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return forAllWindowsUnchecked(callback, traverseTopToBottom); } + @Override + void forAllAppWindows(Consumer<AppWindowToken> callback) { + callback.accept(this); + } + boolean forAllWindowsUnchecked(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) { return super.forAllWindows(callback, traverseTopToBottom); @@ -1629,7 +1635,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final boolean containsShowWhenLocked = containsShowWhenLockedWindow(); if (containsDismissKeyguard != mLastContainsDismissKeyguardWindow || containsShowWhenLocked != mLastContainsShowWhenLockedWindow) { - mService.notifyKeyguardFlagsChanged(null /* callback */); + mService.notifyKeyguardFlagsChanged(null /* callback */, + getDisplayContent().getDisplayId()); } mLastContainsDismissKeyguardWindow = containsDismissKeyguard; mLastContainsShowWhenLockedWindow = containsShowWhenLocked; @@ -1787,19 +1794,20 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree getAnimationBounds(mTmpPoint, mTmpRect); // Delaying animation start isn't compatible with remote animations at all. - if (mService.mAppTransition.getRemoteAnimationController() != null + if (getDisplayContent().mAppTransition.getRemoteAnimationController() != null && !mSurfaceAnimator.isAnimationStartDelayed()) { - adapter = mService.mAppTransition.getRemoteAnimationController() + adapter = getDisplayContent().mAppTransition.getRemoteAnimationController() .createAnimationAdapter(this, mTmpPoint, mTmpRect); } else { - final int appStackClipMode = mService.mAppTransition.getAppStackClipMode(); + final int appStackClipMode = + getDisplayContent().mAppTransition.getAppStackClipMode(); mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM); final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction); if (a != null) { adapter = new LocalAnimationAdapter( new WindowAnimationSpec(a, mTmpPoint, mTmpRect, - mService.mAppTransition.canSkipFirstFrame(), + getDisplayContent().mAppTransition.canSkipFirstFrame(), appStackClipMode, true /* isAppAnimation */), mService.mSurfaceAnimationRunner); @@ -1807,7 +1815,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mNeedsZBoost = true; } mTransit = transit; - mTransitFlags = mService.mAppTransition.getTransitFlags(); + mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags(); } else { adapter = null; } @@ -1877,7 +1885,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree + " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets); final Configuration displayConfig = displayContent.getConfiguration(); - final Animation a = mService.mAppTransition.loadAnimation(lp, transit, enter, + final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter, displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets, surfaceInsets, stableInsets, isVoiceInteraction, freeform, getTask().mTaskId); if (a != null) { @@ -2017,7 +2025,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final ArrayList<WindowState> children = new ArrayList<>(mChildren); children.forEach(WindowState::onExitAnimationDone); - mService.mAppTransition.notifyAppTransitionFinishedLocked(token); + getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token); scheduleAnimation(); } @@ -2048,8 +2056,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } boolean isWaitingForTransitionStart() { - return mService.mAppTransition.isTransitionSet() - && (mService.mOpeningApps.contains(this) || mService.mClosingApps.contains(this)); + return getDisplayContent().mAppTransition.isTransitionSet() + && (getDisplayContent().mOpeningApps.contains(this) + || getDisplayContent().mClosingApps.contains(this)); } public int getTransit() { @@ -2066,7 +2075,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } final int taskId = getTask().mTaskId; final GraphicBuffer thumbnailHeader = - mService.mAppTransition.getAppTransitionThumbnailHeader(taskId); + getDisplayContent().mAppTransition.getAppTransitionThumbnailHeader(taskId); if (thumbnailHeader == null) { if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "No thumbnail header bitmap for: " + taskId); return; @@ -2095,14 +2104,14 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree ? R.drawable.ic_account_circle : R.drawable.ic_corp_badge; final GraphicBuffer thumbnail = - mService.mAppTransition + getDisplayContent().mAppTransition .createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame); if (thumbnail == null) { return; } mThumbnail = new AppWindowThumbnail(getPendingTransaction(), this, thumbnail); final Animation animation = - mService.mAppTransition.createCrossProfileAppsThumbnailAnimationLocked( + getDisplayContent().mAppTransition.createCrossProfileAppsThumbnailAnimationLocked( win.getFrameLw()); mThumbnail.startAnimation(getPendingTransaction(), animation, new Point(frame.left, frame.top)); @@ -2119,7 +2128,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight); final Rect insets = win != null ? win.getContentInsets() : null; final Configuration displayConfig = mDisplayContent.getConfiguration(); - return mService.mAppTransition.createThumbnailAspectScaleAnimationLocked( + return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked( appRect, insets, thumbnailHeader, getTask().mTaskId, displayConfig.uiMode, displayConfig.orientation); } @@ -2357,4 +2366,11 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return forAllWindows(ws -> ws.mAttrs.getColorMode() != COLOR_MODE_DEFAULT, true /* topToBottom */); } + + void removeFromPendingTransition() { + if (isWaitingForTransitionStart() && mDisplayContent != null) { + mDisplayContent.mOpeningApps.remove(this); + mDisplayContent.mClosingApps.remove(this); + } + } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index fa9ae529c6e1..ba030340ef36 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -65,6 +65,7 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_C import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.DisplayContentProto.ABOVE_APP_WINDOWS; +import static com.android.server.wm.DisplayContentProto.APP_TRANSITION; import static com.android.server.wm.DisplayContentProto.BELOW_APP_WINDOWS; import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES; import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO; @@ -80,6 +81,7 @@ import static com.android.server.wm.DisplayContentProto.STACKS; import static com.android.server.wm.DisplayContentProto.SURFACE_SIZE; import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS; @@ -119,6 +121,7 @@ import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP; import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; +import android.animation.AnimationHandler; import android.annotation.CallSuper; import android.annotation.IntDef; import android.annotation.NonNull; @@ -155,6 +158,7 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; import android.view.WindowManagerPolicyConstants.PointerEventListener; +import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; @@ -228,6 +232,21 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private boolean mTmpInitial; private int mMaxUiWidth; + final AppTransition mAppTransition; + final AppTransitionController mAppTransitionController; + boolean mSkipAppTransitionAnimation = false; + + final ArraySet<AppWindowToken> mOpeningApps = new ArraySet<>(); + final ArraySet<AppWindowToken> mClosingApps = new ArraySet<>(); + final UnknownAppVisibilityController mUnknownAppVisibilityController; + BoundsAnimationController mBoundsAnimationController; + + /** + * List of clients without a transtiton animation that we notify once we are done + * transitioning since they won't be notified through the app window animator. + */ + final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>(); + // Mapping from a token IBinder to a WindowToken object on this display. private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap(); @@ -821,6 +840,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mDividerControllerLocked = new DockedStackDividerController(service, this); mPinnedStackControllerLocked = new PinnedStackController(service, this); + mAppTransition = new AppTransition(service.mContext, service, this); + mAppTransition.registerListenerLocked(service.mActivityManagerAppTransitionNotifier); + mAppTransitionController = new AppTransitionController(service, this); + mUnknownAppVisibilityController = new UnknownAppVisibilityController(service, this); + + AnimationHandler animationHandler = new AnimationHandler(); + mBoundsAnimationController = new BoundsAnimationController(service.mContext, + mAppTransition, SurfaceAnimationThread.getHandler(), animationHandler); + // We use this as our arbitrary surface size for buffer-less parents // that don't impose cropping on their children. It may need to be larger // than the display size because fullscreen windows can be shifted offscreen @@ -2135,6 +2163,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo + " to its current displayId=" + mDisplayId); } + // Clean up all pending transitions when stack reparent to another display. + stack.forAllAppWindows(AppWindowToken::removeFromPendingTransition); + prevDc.mTaskStackContainers.removeChild(stack); mTaskStackContainers.addStackToDisplay(stack, onTop); } @@ -2294,6 +2325,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo void removeImmediately() { mRemovingDisplay = true; try { + // Clear all transitions & screen frozen states when removing display. + mOpeningApps.clear(); + mClosingApps.clear(); + mUnknownAppVisibilityController.clear(); + mAppTransition.removeAppTransitionTimeoutCallbacks(); + handleAnimatingStoppedAndTransition(); + mService.stopFreezingDisplayLocked(); super.removeImmediately(); if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this); if (mPointerEventDispatcher != null && mTapDetector != null) { @@ -2514,6 +2552,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION); } mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES); + mAppTransition.writeToProto(proto, APP_TRANSITION); proto.write(SURFACE_SIZE, mSurfaceSize); if (mFocusedApp != null) { mFocusedApp.writeNameToProto(proto, FOCUSED_APP); @@ -2998,11 +3037,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } if (highestTarget != null) { - final AppTransition appTransition = mService.mAppTransition; - if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, appTransition + " " + highestTarget + if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, mAppTransition + " " + highestTarget + " animating=" + highestTarget.isAnimating()); - if (appTransition.isTransitionSet()) { + if (mAppTransition.isTransitionSet()) { // If we are currently setting up for an animation, hold everything until we // can find out what will happen. setInputMethodTarget(highestTarget, true); @@ -3093,6 +3131,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo pw.println(); } } + + if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty()) { + pw.println(); + if (mOpeningApps.size() > 0) { + pw.print(" mOpeningApps="); pw.println(mOpeningApps); + } + if (mClosingApps.size() > 0) { + pw.print(" mClosingApps="); pw.println(mClosingApps); + } + } + + mUnknownAppVisibilityController.dump(pw, " "); } void dumpWindowAnimators(PrintWriter pw, String subPrefix) { @@ -3989,7 +4039,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens; for (int j = appTokens.size() - 1; j >= 0; --j) { final AppWindowToken token = appTokens.get(j); - if (!token.hasVisible && !mService.mClosingApps.contains(token) + if (!token.hasVisible && !mClosingApps.contains(token) && (!token.mIsExiting || token.isEmpty())) { // Make sure there is no animation running on this token, so any windows // associated with it will be removed as soon as their animations are @@ -4287,8 +4337,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Only allow force setting the orientation when all unknown visibilities have been // resolved, as otherwise we just may be starting another occluding activity. final boolean isUnoccluding = - mService.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE - && mService.mUnknownAppVisibilityController.allResolved(); + mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE + && mUnknownAppVisibilityController.allResolved(); if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) { return mLastKeyguardForcedOrientation; } @@ -4528,4 +4578,56 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mPointerEventDispatcher.unregisterInputEventListener(listener); } } + + void prepareAppTransition(@WindowManager.TransitionType int transit, + boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags, + boolean forceOverride) { + final boolean prepared = mAppTransition.prepareAppTransitionLocked( + transit, alwaysKeepCurrent, flags, forceOverride); + if (prepared && okToAnimate()) { + mSkipAppTransitionAnimation = false; + } + } + + void executeAppTransition() { + if (mAppTransition.isTransitionSet()) { + if (DEBUG_APP_TRANSITIONS) { + Slog.w(TAG_WM, "Execute app transition: " + mAppTransition + ", displayId: " + + mDisplayId + " Callers=" + Debug.getCallers(5)); + } + mAppTransition.setReady(); + mService.mWindowPlacerLocked.requestTraversal(); + } + } + + /** + * Update pendingLayoutChanges after app transition has finished. + */ + void handleAnimatingStoppedAndTransition() { + int changes = 0; + + mAppTransition.setIdle(); + + for (int i = mNoAnimationNotifyOnTransitionFinished.size() - 1; i >= 0; i--) { + final IBinder token = mNoAnimationNotifyOnTransitionFinished.get(i); + mAppTransition.notifyAppTransitionFinishedLocked(token); + } + mNoAnimationNotifyOnTransitionFinished.clear(); + + mWallpaperController.hideDeferredWallpapersIfNeeded(); + + onAppTransitionDone(); + + changes |= FINISH_LAYOUT_REDO_LAYOUT; + if (DEBUG_WALLPAPER_LIGHT) { + Slog.v(TAG_WM, "Wallpaper layer changed: assigning layers + relayout"); + } + computeImeTarget(true /* updateImeTarget */); + mService.mRoot.mWallpaperMayChange = true; + // Since the window list has been rebuilt, focus might have to be recomputed since the + // actual order of windows might have changed again. + mService.mFocusMayChange = true; + + pendingLayoutChanges |= changes; + } } diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java index 3282b1c8848a..01d556a699e8 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowController.java +++ b/services/core/java/com/android/server/wm/DisplayWindowController.java @@ -16,6 +16,10 @@ package com.android.server.wm; +import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_TASK_OPEN; +import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; + import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; @@ -23,10 +27,17 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import android.content.res.Configuration; +import android.graphics.GraphicBuffer; import android.os.Binder; import android.os.IBinder; +import android.os.IRemoteCallback; import android.util.Slog; +import android.view.AppTransitionAnimationSpec; import android.view.Display; +import android.view.WindowManager; +import android.view.WindowManager.TransitionType; + +import com.android.internal.annotations.VisibleForTesting; /** * Controller for the display container. This is created by activity manager to link activity @@ -56,6 +67,12 @@ public class DisplayWindowController } } + @VisibleForTesting + public DisplayWindowController(Display display, WindowManagerService service) { + super(null, service); + mDisplayId = display.getDisplayId(); + } + @Override public void removeContainer() { synchronized (mWindowMap) { @@ -179,6 +196,124 @@ public class DisplayWindowController } } + public void prepareAppTransition(@WindowManager.TransitionType int transit, + boolean alwaysKeepCurrent) { + prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */); + } + + /** + * @param transit What kind of transition is happening. Use one of the constants + * AppTransition.TRANSIT_*. + * @param alwaysKeepCurrent If true and a transition is already set, new transition will NOT + * be set. + * @param flags Additional flags for the app transition, Use a combination of the constants + * AppTransition.TRANSIT_FLAG_*. + * @param forceOverride Always override the transit, not matter what was set previously. + */ + public void prepareAppTransition(@WindowManager.TransitionType int transit, + boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags, + boolean forceOverride) { + synchronized (mWindowMap) { + mRoot.getDisplayContent(mDisplayId).prepareAppTransition(transit, alwaysKeepCurrent, + flags, forceOverride); + } + } + + public void executeAppTransition() { + synchronized (mWindowMap) { + mRoot.getDisplayContent(mDisplayId).executeAppTransition(); + } + } + + public void overridePendingAppTransition(String packageName, + int enterAnim, int exitAnim, IRemoteCallback startedCallback) { + synchronized (mWindowMap) { + mRoot.getDisplayContent(mDisplayId).mAppTransition.overridePendingAppTransition( + packageName, enterAnim, exitAnim, startedCallback); + } + } + + public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth, + int startHeight) { + synchronized (mWindowMap) { + mRoot.getDisplayContent(mDisplayId).mAppTransition.overridePendingAppTransitionScaleUp( + startX, startY, startWidth, startHeight); + } + } + + public void overridePendingAppTransitionClipReveal(int startX, int startY, + int startWidth, int startHeight) { + synchronized (mWindowMap) { + mRoot.getDisplayContent(mDisplayId) + .mAppTransition.overridePendingAppTransitionClipReveal(startX, startY, + startWidth, startHeight); + } + } + + public void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX, + int startY, IRemoteCallback startedCallback, boolean scaleUp) { + synchronized (mWindowMap) { + mRoot.getDisplayContent(mDisplayId) + .mAppTransition.overridePendingAppTransitionThumb(srcThumb, startX, startY, + startedCallback, scaleUp); + } + } + + public void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX, + int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback, + boolean scaleUp) { + synchronized (mWindowMap) { + mRoot.getDisplayContent(mDisplayId) + .mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX, + startY, targetWidth, targetHeight, startedCallback, scaleUp); + } + } + + public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs, + IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback, + boolean scaleUp) { + synchronized (mWindowMap) { + mRoot.getDisplayContent(mDisplayId) + .mAppTransition.overridePendingAppTransitionMultiThumb(specs, + onAnimationStartedCallback, onAnimationFinishedCallback, scaleUp); + } + } + + public void overridePendingAppTransitionStartCrossProfileApps() { + synchronized (mWindowMap) { + mRoot.getDisplayContent(mDisplayId) + .mAppTransition.overridePendingAppTransitionStartCrossProfileApps(); + } + } + + public void overridePendingAppTransitionInPlace(String packageName, int anim) { + synchronized (mWindowMap) { + mRoot.getDisplayContent(mDisplayId) + .mAppTransition.overrideInPlaceAppTransition(packageName, anim); + } + } + + /** + * Get Pending App transition of display. + * + * @return The pending app transition of the display. + */ + public @TransitionType int getPendingAppTransition() { + synchronized (mWindowMap) { + return mRoot.getDisplayContent(mDisplayId).mAppTransition.getAppTransition(); + } + } + + /** + * Check if pending app transition is for activity / task launch. + */ + public boolean isNextTransitionForward() { + final int transit = getPendingAppTransition(); + return transit == TRANSIT_ACTIVITY_OPEN + || transit == TRANSIT_TASK_OPEN + || transit == TRANSIT_TASK_TO_FRONT; + } + @Override public String toString() { return "{DisplayWindowController displayId=" + mDisplayId + "}"; diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index ac9384872d92..8ed29a9b27d4 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -531,7 +531,7 @@ public class DockedStackDividerController { final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility(); final long transitionDuration = isAnimationMaximizing() - ? mService.mAppTransition.getLastClipRevealTransitionDuration() + ? mDisplayContent.mAppTransition.getLastClipRevealTransitionDuration() : DEFAULT_APP_TRANSITION_DURATION; mAnimationDuration = (long) (transitionDuration * mService.getTransitionAnimationScaleLocked()); @@ -950,7 +950,7 @@ public class DockedStackDividerController { return naturalAmount; } final int minimizeDistance = stack.getMinimizeDistance(); - float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation() + final float startPrime = mDisplayContent.mAppTransition.getLastClipRevealMaxTranslation() / (float) minimizeDistance; final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime; final float t2 = Math.min(t / mMaximizeMeetFraction, 1); @@ -963,12 +963,12 @@ public class DockedStackDividerController { */ private float getClipRevealMeetFraction(TaskStack stack) { if (!isAnimationMaximizing() || stack == null || - !mService.mAppTransition.hadClipRevealAnimation()) { + !mDisplayContent.mAppTransition.hadClipRevealAnimation()) { return 1f; } final int minimizeDistance = stack.getMinimizeDistance(); - final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation()) - / (float) minimizeDistance; + final float fraction = Math.abs(mDisplayContent.mAppTransition + .getLastClipRevealMaxTranslation()) / (float) minimizeDistance; final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN) / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN))); return CLIP_REVEAL_MEET_EARLIEST diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java index 02fbfba9d332..1807eeb0ed52 100644 --- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java +++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java @@ -117,11 +117,12 @@ public class PinnedStackWindowController extends StackWindowController { final Rect finalToBounds = toBounds; final @SchedulePipModeChangedState int finalSchedulePipModeChangedState = schedulePipModeChangedState; - mService.mBoundsAnimationController.getHandler().post(() -> { + final DisplayContent displayContent = mContainer.getDisplayContent(); + displayContent.mBoundsAnimationController.getHandler().post(() -> { if (mContainer == null) { return; } - mService.mBoundsAnimationController.animateBounds(mContainer, fromBounds, + displayContent.mBoundsAnimationController.animateBounds(mContainer, fromBounds, finalToBounds, animationDuration, finalSchedulePipModeChangedState, fromFullscreen, toFullscreen); }); diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 5c80759c6998..c4fbee931df3 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -467,7 +467,8 @@ public class RecentsAnimationController implements DeathRecipient { // so if we are actually transitioning there, notify again here if (mTargetAppToken != null) { if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) { - mService.mAppTransition.notifyAppTransitionFinishedLocked(mTargetAppToken.token); + mService.mRoot.getDisplayContent(mDisplayId) + .mAppTransition.notifyAppTransitionFinishedLocked(mTargetAppToken.token); } } } diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 8ec0a014e4a9..0ec4baf06099 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -317,7 +317,7 @@ class RemoteAnimationController implements DeathRecipient { } private int getMode() { - if (mService.mOpeningApps.contains(mAppWindowToken)) { + if (mAppWindowToken.getDisplayContent().mOpeningApps.contains(mAppWindowToken)) { return RemoteAnimationTarget.MODE_OPENING; } else { return RemoteAnimationTarget.MODE_CLOSING; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index c8977bede325..62078f722434 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -576,17 +576,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { mSustainedPerformanceModeCurrent = false; mService.mTransactionSequence++; - // TODO(multi-display): + // TODO(multi-display): recents animation & wallpaper need support multi-display. final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked(); - final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo(); - final int defaultDw = defaultInfo.logicalWidth; - final int defaultDh = defaultInfo.logicalHeight; + final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked; if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces"); mService.openSurfaceTransaction(); try { - applySurfaceChangesTransaction(recoveringMemory, defaultDw, defaultDh); + applySurfaceChangesTransaction(recoveringMemory); } catch (RuntimeException e) { Slog.wtf(TAG, "Unhandled exception in Window Manager", e); } finally { @@ -594,39 +592,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces"); } - mService.mAnimator.executeAfterPrepareSurfacesRunnables(); - final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked; - - // If we are ready to perform an app transition, check through all of the app tokens to be - // shown and see if they are ready to go. - if (mService.mAppTransition.isReady()) { - // This needs to be split into two expressions, as handleAppTransitionReadyLocked may - // modify dc.pendingLayoutChanges, which would get lost when writing - // defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked() - final int layoutChanges = surfacePlacer.handleAppTransitionReadyLocked(); - defaultDisplay.pendingLayoutChanges |= layoutChanges; - if (DEBUG_LAYOUT_REPEATS) - surfacePlacer.debugLayoutRepeats("after handleAppTransitionReadyLocked", - defaultDisplay.pendingLayoutChanges); - } - - if (!isAppAnimating() && mService.mAppTransition.isRunning()) { - // We have finished the animation of an app transition. To do this, we have delayed a - // lot of operations like showing and hiding apps, moving apps in Z-order, etc. The app - // token list reflects the correct Z-order, but the window list may now be out of sync - // with it. So here we will just rebuild the entire app window list. Fun! - defaultDisplay.pendingLayoutChanges |= - mService.handleAnimatingStoppedAndTransitionLocked(); - if (DEBUG_LAYOUT_REPEATS) - surfacePlacer.debugLayoutRepeats("after handleAnimStopAndXitionLock", - defaultDisplay.pendingLayoutChanges); - } + checkAppTransitionReady(surfacePlacer); // Defer starting the recents animation until the wallpaper has drawn final RecentsAnimationController recentsAnimationController = - mService.getRecentsAnimationController(); + mService.getRecentsAnimationController(); if (recentsAnimationController != null) { recentsAnimationController.checkAnimationReady(mWallpaperController); } @@ -732,8 +704,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { mUpdateRotation = updateRotationUnchecked(); } - if (mService.mWaitingForDrawnCallback != null || - (mOrientationChangeComplete && !defaultDisplay.isLayoutNeeded() + if (mService.mWaitingForDrawnCallback != null + || (mOrientationChangeComplete && !isLayoutNeeded() && !mUpdateRotation)) { mService.checkDrawnWindowsLocked(); } @@ -741,7 +713,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { final int N = mService.mPendingRemove.size(); if (N > 0) { if (mService.mPendingRemoveTmp.length < N) { - mService.mPendingRemoveTmp = new WindowState[N+10]; + mService.mPendingRemoveTmp = new WindowState[N + 10]; } mService.mPendingRemove.toArray(mService.mPendingRemoveTmp); mService.mPendingRemove.clear(); @@ -783,12 +755,47 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating()); } - private void applySurfaceChangesTransaction(boolean recoveringMemory, int defaultDw, - int defaultDh) { + private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) { + // Trace all displays app transition by Z-order for pending layout change. + for (int i = mChildren.size() - 1; i >= 0; --i) { + final DisplayContent curDisplay = mChildren.get(i); + + // If we are ready to perform an app transition, check through all of the app tokens + // to be shown and see if they are ready to go. + if (curDisplay.mAppTransition.isReady()) { + // handleAppTransitionReady may modify curDisplay.pendingLayoutChanges. + curDisplay.mAppTransitionController.handleAppTransitionReady(); + if (DEBUG_LAYOUT_REPEATS) { + surfacePlacer.debugLayoutRepeats("after handleAppTransitionReady", + curDisplay.pendingLayoutChanges); + } + } + + if (!curDisplay.isAppAnimating() && curDisplay.mAppTransition.isRunning()) { + // We have finished the animation of an app transition. To do this, we have + // delayed a lot of operations like showing and hiding apps, moving apps in + // Z-order, etc. + // The app token list reflects the correct Z-order, but the window list may now + // be out of sync with it. So here we will just rebuild the entire app window + // list. Fun! + curDisplay.handleAnimatingStoppedAndTransition(); + if (DEBUG_LAYOUT_REPEATS) { + surfacePlacer.debugLayoutRepeats("after handleAnimStopAndXitionLock", + curDisplay.pendingLayoutChanges); + } + } + } + } + + private void applySurfaceChangesTransaction(boolean recoveringMemory) { mHoldScreenWindow = null; mObscuringWindow = null; // TODO(multi-display): Support these features on secondary screens. + final DisplayContent defaultDc = mService.getDefaultDisplayContentLocked(); + final DisplayInfo defaultInfo = defaultDc.getDisplayInfo(); + final int defaultDw = defaultInfo.logicalWidth; + final int defaultDh = defaultInfo.logicalHeight; if (mService.mWatermark != null) { mService.mWatermark.positionSurface(defaultDw, defaultDh); } diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java index 1fd2c0fd19c5..6ac63a1dd14a 100644 --- a/services/core/java/com/android/server/wm/StackWindowController.java +++ b/services/core/java/com/android/server/wm/StackWindowController.java @@ -140,10 +140,11 @@ public class StackWindowController } mContainer.positionChildAt(POSITION_TOP, childTask, includingParents); - if (mService.mAppTransition.isTransitionSet()) { + final DisplayContent displayContent = mContainer.getDisplayContent(); + if (displayContent.mAppTransition.isTransitionSet()) { childTask.setSendingToBottom(false); } - mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); + displayContent.layoutAndAssignWindowLayersIfNeeded(); } } @@ -162,7 +163,7 @@ public class StackWindowController } mContainer.positionChildAt(POSITION_BOTTOM, childTask, includingParents); - if (mService.mAppTransition.isTransitionSet()) { + if (mContainer.getDisplayContent().mAppTransition.isTransitionSet()) { childTask.setSendingToBottom(true); } mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index ef63b9b70b3b..0d5469bd4847 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -129,8 +129,8 @@ class TaskSnapshotController { mPersister.start(); } - void onTransitionStarting() { - handleClosingApps(mService.mClosingApps); + void onTransitionStarting(DisplayContent displayContent) { + handleClosingApps(displayContent.mClosingApps); } /** diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 00cacebe2960..81507fa8ed51 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -1638,7 +1638,7 @@ public class TaskStack extends WindowContainer<Task> implements return; } - mService.mBoundsAnimationController.onAllWindowsDrawn(); + getDisplayContent().mBoundsAnimationController.onAllWindowsDrawn(); } @Override // AnimatesBounds diff --git a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java index eb751fa9749f..01abcab45760 100644 --- a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java +++ b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java @@ -60,8 +60,11 @@ class UnknownAppVisibilityController { private final WindowManagerService mService; - UnknownAppVisibilityController(WindowManagerService service) { + private final DisplayContent mDisplayContent; + + UnknownAppVisibilityController(WindowManagerService service, DisplayContent displayContent) { mService = service; + mDisplayContent = displayContent; } boolean allResolved() { @@ -128,7 +131,8 @@ class UnknownAppVisibilityController { int state = mUnknownApps.get(appWindow); if (state == UNKNOWN_STATE_WAITING_RELAYOUT) { mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_VISIBILITY_UPDATE); - mService.notifyKeyguardFlagsChanged(this::notifyVisibilitiesUpdated); + mService.notifyKeyguardFlagsChanged(this::notifyVisibilitiesUpdated, + appWindow.getDisplayContent().getDisplayId()); } } diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index a448f97306f0..942cdb9c1559 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -16,16 +16,14 @@ package com.android.server.wm; -import com.android.internal.util.ToBooleanFunction; - import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; @@ -50,6 +48,8 @@ import android.view.SurfaceControl; import android.view.WindowManager; import android.view.animation.Animation; +import com.android.internal.util.ToBooleanFunction; + import java.io.PrintWriter; import java.util.ArrayList; @@ -263,7 +263,8 @@ class WallpaperController { && (mWallpaperTarget != winGoingAway || mPrevWallpaperTarget != null)) { return; } - if (mService.mAppTransition.isRunning()) { + if (mWallpaperTarget != null + && mWallpaperTarget.getDisplayContent().mAppTransition.isRunning()) { // Defer hiding the wallpaper when app transition is running until the animations // are done. mDeferredHideWallpaper = winGoingAway; @@ -549,9 +550,9 @@ class WallpaperController { // is not. If they're both hidden, still use the new target. mWallpaperTarget = prevWallpaperTarget; } else if (newTargetHidden == oldTargetHidden - && !mService.mOpeningApps.contains(wallpaperTarget.mAppToken) - && (mService.mOpeningApps.contains(prevWallpaperTarget.mAppToken) - || mService.mClosingApps.contains(prevWallpaperTarget.mAppToken))) { + && !dc.mOpeningApps.contains(wallpaperTarget.mAppToken) + && (dc.mOpeningApps.contains(prevWallpaperTarget.mAppToken) + || dc.mClosingApps.contains(prevWallpaperTarget.mAppToken))) { // If they're both hidden (or both not hidden), prefer the one that's currently in // opening or closing app list, this allows transition selection logic to better // determine the wallpaper status of opening/closing apps. diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 46999a2a847e..abc382656db3 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -826,6 +826,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< wrapper.release(); } + void forAllAppWindows(Consumer<AppWindowToken> callback) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + mChildren.get(i).forAllAppWindows(callback); + } + } + /** * For all tasks at or below this container call the callback. * diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 3aad73c28f78..96fc2e241a48 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -67,12 +67,10 @@ import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN; import static com.android.server.LockGuard.INDEX_WINDOW; import static com.android.server.LockGuard.installLock; -import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.KeyguardDisableHandler.KEYGUARD_POLICY_CHANGED; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; @@ -86,7 +84,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; @@ -96,7 +93,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_VERBOSE_TRANSA import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import static com.android.server.wm.WindowManagerServiceDumpProto.APP_TRANSITION; import static com.android.server.wm.WindowManagerServiceDumpProto.DISPLAY_FROZEN; import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_APP; import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_WINDOW; @@ -108,7 +104,6 @@ import static com.android.server.wm.WindowManagerServiceDumpProto.ROTATION; import android.Manifest; import android.Manifest.permission; -import android.animation.AnimationHandler; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.NonNull; @@ -134,7 +129,6 @@ import android.content.pm.PackageManagerInternal; import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.Bitmap; -import android.graphics.GraphicBuffer; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; @@ -188,7 +182,6 @@ import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.TypedValue; import android.util.proto.ProtoOutputStream; -import android.view.AppTransitionAnimationSpec; import android.view.Display; import android.view.DisplayCutout; import android.view.DisplayInfo; @@ -221,7 +214,6 @@ import android.view.View; import android.view.WindowContentFrameStats; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; -import android.view.WindowManager.TransitionFlags; import android.view.WindowManager.TransitionType; import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicyConstants.PointerEventListener; @@ -266,7 +258,6 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; -import java.util.List; /** {@hide} */ public class WindowManagerService extends IWindowManager.Stub @@ -612,14 +603,6 @@ public class WindowManagerService extends IWindowManager.Stub // changes the orientation. private final PowerManager.WakeLock mScreenFrozenLock; - final AppTransition mAppTransition; - boolean mSkipAppTransitionAnimation = false; - - final ArraySet<AppWindowToken> mOpeningApps = new ArraySet<>(); - final ArraySet<AppWindowToken> mClosingApps = new ArraySet<>(); - - final UnknownAppVisibilityController mUnknownAppVisibilityController = - new UnknownAppVisibilityController(this); final TaskSnapshotController mTaskSnapshotController; boolean mIsTouchDevice; @@ -751,7 +734,6 @@ public class WindowManagerService extends IWindowManager.Stub * up when the animation finishes. */ final ArrayMap<AnimationAdapter, SurfaceAnimator> mAnimationTransferMap = new ArrayMap<>(); - final BoundsAnimationController mBoundsAnimationController; private WindowContentFrameStats mTempWindowRenderStats; @@ -779,10 +761,6 @@ public class WindowManagerService extends IWindowManager.Stub // For example, when this flag is true, there will be no wallpaper service. final boolean mOnlyCore; - // List of clients without a transtiton animation that we notify once we are done transitioning - // since they won't be notified through the app window animator. - final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>(); - static WindowManagerThreadPriorityBooster sThreadPriorityBooster = new WindowManagerThreadPriorityBooster(); @@ -975,13 +953,6 @@ public class WindowManagerService extends IWindowManager.Stub PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN"); mScreenFrozenLock.setReferenceCounted(false); - mAppTransition = new AppTransition(context, this); - mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier); - - final AnimationHandler animationHandler = new AnimationHandler(); - mBoundsAnimationController = new BoundsAnimationController(context, mAppTransition, - AnimationThread.getHandler(), animationHandler); - mActivityManager = ActivityManager.getService(); mActivityTaskManager = ActivityTaskManager.getService(); mAmInternal = LocalServices.getService(ActivityManagerInternal.class); @@ -1577,11 +1548,13 @@ public class WindowManagerService extends IWindowManager.Stub Rect frame = replacedWindow.getVisibleFrameLw(); // We treat this as if this activity was opening, so we can trigger the app transition // animation and piggy-back on existing transition animation infrastructure. - mOpeningApps.add(atoken); - prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT); - mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top, + final DisplayContent dc = atoken.getDisplayContent(); + dc.mOpeningApps.add(atoken); + dc.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT, + 0 /* flags */, false /* forceOverride */); + dc.mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top, frame.width(), frame.height()); - executeAppTransition(); + dc.executeAppTransition(); return true; } @@ -1590,10 +1563,12 @@ public class WindowManagerService extends IWindowManager.Stub // unfreeze wait for the apps to be drawn. // Note that if the display unfroze already because app unfreeze timed out, // we don't set up the transition anymore and just let it go. - if (mDisplayFrozen && !mOpeningApps.contains(atoken) && atoken.isRelaunching()) { - mOpeningApps.add(atoken); - prepareAppTransition(WindowManager.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT); - executeAppTransition(); + final DisplayContent dc = atoken.getDisplayContent(); + if (mDisplayFrozen && !dc.mOpeningApps.contains(atoken) && atoken.isRelaunching()) { + dc.mOpeningApps.add(atoken); + dc.prepareAppTransition(WindowManager.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT, 0 /* flags */, + false /* forceOverride */); + dc.executeAppTransition(); } } @@ -2087,7 +2062,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (win.mAppToken != null) { - mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken); + dc.mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken); } Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, @@ -2445,6 +2420,9 @@ public class WindowManagerService extends IWindowManager.Stub long ident = Binder.clearCallingIdentity(); try { final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + return false; + } final int req = dc.getOrientation(); if (req != dc.getLastOrientation() || forceUpdate) { dc.setLastOrientation(req); @@ -2475,109 +2453,15 @@ public class WindowManagerService extends IWindowManager.Stub } } + // TODO(multi-display): remove when no default display use case. + // (i.e. KeyguardController / RecentsAnimation) @Override public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent) { - prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */); - } - - /** - * @param transit What kind of transition is happening. Use one of the constants - * AppTransition.TRANSIT_*. - * @param alwaysKeepCurrent If true and a transition is already set, new transition will NOT - * be set. - * @param flags Additional flags for the app transition, Use a combination of the constants - * AppTransition.TRANSIT_FLAG_*. - * @param forceOverride Always override the transit, not matter what was set previously. - */ - public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent, - @TransitionFlags int flags, boolean forceOverride) { if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - synchronized(mWindowMap) { - boolean prepared = mAppTransition.prepareAppTransitionLocked(transit, alwaysKeepCurrent, - flags, forceOverride); - // TODO (multidisplay): associate app transitions with displays - final DisplayContent dc = mRoot.getDisplayContent(DEFAULT_DISPLAY); - if (prepared && dc != null && dc.okToAnimate()) { - mSkipAppTransitionAnimation = false; - } - } - } - - @Override - public @TransitionType int getPendingAppTransition() { - return mAppTransition.getAppTransition(); - } - - @Override - public void overridePendingAppTransition(String packageName, - int enterAnim, int exitAnim, IRemoteCallback startedCallback) { - synchronized(mWindowMap) { - mAppTransition.overridePendingAppTransition(packageName, enterAnim, exitAnim, - startedCallback); - } - } - - @Override - public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth, - int startHeight) { - synchronized(mWindowMap) { - mAppTransition.overridePendingAppTransitionScaleUp(startX, startY, startWidth, - startHeight); - } - } - - @Override - public void overridePendingAppTransitionClipReveal(int startX, int startY, - int startWidth, int startHeight) { - synchronized(mWindowMap) { - mAppTransition.overridePendingAppTransitionClipReveal(startX, startY, startWidth, - startHeight); - } - } - - @Override - public void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX, - int startY, IRemoteCallback startedCallback, boolean scaleUp) { - synchronized(mWindowMap) { - mAppTransition.overridePendingAppTransitionThumb(srcThumb, startX, startY, - startedCallback, scaleUp); - } - } - - @Override - public void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX, - int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback, - boolean scaleUp) { - synchronized(mWindowMap) { - mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX, startY, - targetWidth, targetHeight, startedCallback, scaleUp); - } - } - - @Override - public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs, - IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback, - boolean scaleUp) { - synchronized (mWindowMap) { - mAppTransition.overridePendingAppTransitionMultiThumb(specs, onAnimationStartedCallback, - onAnimationFinishedCallback, scaleUp); - - } - } - - public void overridePendingAppTransitionStartCrossProfileApps() { - synchronized (mWindowMap) { - mAppTransition.overridePendingAppTransitionStartCrossProfileApps(); - } - } - - @Override - public void overridePendingAppTransitionInPlace(String packageName, int anim) { - synchronized(mWindowMap) { - mAppTransition.overrideInPlaceAppTransition(packageName, anim); - } + getDefaultDisplayContentLocked().prepareAppTransition(transit, + alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */); } @Override @@ -2585,8 +2469,10 @@ public class WindowManagerService extends IWindowManager.Stub IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback, boolean scaleUp) { synchronized(mWindowMap) { - mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture, callback, - scaleUp); + // TODO(multi-display): sysui using this api only support default display. + mRoot.getDisplayContent(DEFAULT_DISPLAY) + .mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture, + callback, scaleUp); } } @@ -2598,7 +2484,9 @@ public class WindowManagerService extends IWindowManager.Stub "Requires CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission"); } synchronized (mWindowMap) { - mAppTransition.overridePendingAppTransitionRemote(remoteAnimationAdapter); + // TODO(multi-display): sysui using this api only support default display. + mRoot.getDisplayContent(DEFAULT_DISPLAY) + .mAppTransition.overridePendingAppTransitionRemote(remoteAnimationAdapter); } } @@ -2607,20 +2495,14 @@ public class WindowManagerService extends IWindowManager.Stub // TODO: Remove once clients are updated. } + // TODO(multi-display): remove when no default display use case. + // (i.e. KeyguardController / RecentsAnimation) @Override public void executeAppTransition() { if (!checkCallingPermission(MANAGE_APP_TOKENS, "executeAppTransition()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - - synchronized(mWindowMap) { - if (DEBUG_APP_TRANSITIONS) Slog.w(TAG_WM, "Execute app transition: " + mAppTransition - + " Callers=" + Debug.getCallers(5)); - if (mAppTransition.isTransitionSet()) { - mAppTransition.setReady(); - mWindowPlacerLocked.requestTraversal(); - } - } + getDefaultDisplayContentLocked().executeAppTransition(); } public void initializeRecentsAnimation(int targetActivityType, @@ -2630,7 +2512,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { mRecentsAnimationController = new RecentsAnimationController(this, recentsAnimationRunner, callbacks, displayId); - mAppTransition.updateBooster(); + mRoot.getDisplayContent(displayId).mAppTransition.updateBooster(); mRecentsAnimationController.initialize(targetActivityType, recentTaskIds); } } @@ -2650,7 +2532,8 @@ public class WindowManagerService extends IWindowManager.Stub */ public boolean canStartRecentsAnimation() { synchronized (mWindowMap) { - if (mAppTransition.isTransitionSet()) { + // TODO(multi-display): currently only default display support recent activity + if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) { return false; } return true; @@ -2677,7 +2560,8 @@ public class WindowManagerService extends IWindowManager.Stub final RecentsAnimationController controller = mRecentsAnimationController; mRecentsAnimationController = null; controller.cleanupAnimation(reorderMode); - mAppTransition.updateBooster(); + // TODO(mult-display): currently only default display support recents animation. + getDefaultDisplayContentLocked().mAppTransition.updateBooster(); } } } @@ -2747,7 +2631,8 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void notifyShowingDreamChanged() { - notifyKeyguardFlagsChanged(null /* callback */); + // TODO(multi-display): support show dream in multi-display. + notifyKeyguardFlagsChanged(null /* callback */, DEFAULT_DISPLAY); } @Override @@ -2822,11 +2707,12 @@ public class WindowManagerService extends IWindowManager.Stub * reevaluate the visibilities of the activities. * @param callback Runnable to be called when activity manager is done reevaluating visibilities */ - void notifyKeyguardFlagsChanged(@Nullable Runnable callback) { + void notifyKeyguardFlagsChanged(@Nullable Runnable callback, int displayId) { final Runnable wrappedCallback = callback != null ? () -> { synchronized (mWindowMap) { callback.run(); } } : null; - mH.obtainMessage(H.NOTIFY_KEYGUARD_FLAGS_CHANGED, wrappedCallback).sendToTarget(); + mH.obtainMessage(H.NOTIFY_KEYGUARD_FLAGS_CHANGED, displayId, 0, + wrappedCallback).sendToTarget(); } public boolean isKeyguardTrusted() { @@ -3215,7 +3101,6 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { mCurrentUserId = newUserId; mCurrentProfileIds = currentProfileIds; - mAppTransition.setCurrentUser(newUserId); mPolicy.setCurrentUserLw(newUserId); // If keyguard was disabled, re-enable it @@ -3234,6 +3119,8 @@ public class WindowManagerService extends IWindowManager.Stub displayContent.mDividerControllerLocked.notifyDockedStackExistsChanged( stack != null && stack.hasTaskForUser(newUserId)); + mRoot.forAllDisplays(dc -> dc.mAppTransition.setCurrentUser(newUserId)); + // If the display is already prepared, update the density. // Otherwise, we'll update it when it's prepared. if (mDisplayReady) { @@ -4895,7 +4782,7 @@ public class WindowManagerService extends IWindowManager.Stub } break; case NOTIFY_KEYGUARD_FLAGS_CHANGED: { - mAtmInternal.notifyKeyguardFlagsChanged((Runnable) msg.obj); + mAtmInternal.notifyKeyguardFlagsChanged((Runnable) msg.obj, msg.arg1); } break; case NOTIFY_KEYGUARD_TRUSTED_CHANGED: { @@ -5314,39 +5201,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - /** - * @return bitmap indicating if another pass through layout must be made. - */ - int handleAnimatingStoppedAndTransitionLocked() { - int changes = 0; - - mAppTransition.setIdle(); - - for (int i = mNoAnimationNotifyOnTransitionFinished.size() - 1; i >= 0; i--) { - final IBinder token = mNoAnimationNotifyOnTransitionFinished.get(i); - mAppTransition.notifyAppTransitionFinishedLocked(token); - } - mNoAnimationNotifyOnTransitionFinished.clear(); - - // TODO: multi-display. - final DisplayContent dc = getDefaultDisplayContentLocked(); - - dc.mWallpaperController.hideDeferredWallpapersIfNeeded(); - - dc.onAppTransitionDone(); - - changes |= FINISH_LAYOUT_REDO_LAYOUT; - if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG_WM, - "Wallpaper layer changed: assigning layers + relayout"); - dc.computeImeTarget(true /* updateImeTarget */); - mRoot.mWallpaperMayChange = true; - // Since the window list has been rebuilt, focus might have to be recomputed since the - // actual order of windows might have changed again. - mFocusMayChange = true; - - return changes; - } - void checkDrawnWindowsLocked() { if (mWaitingForDrawn.isEmpty() || mWaitingForDrawnCallback == null) { return; @@ -5459,8 +5313,8 @@ public class WindowManagerService extends IWindowManager.Stub mInputManagerCallback.freezeInputDispatchingLw(); - if (mAppTransition.isTransitionSet()) { - mAppTransition.freeze(); + if (displayContent.mAppTransition.isTransitionSet()) { + displayContent.mAppTransition.freeze(); } if (PROFILE_ORIENTATION) { @@ -5495,15 +5349,16 @@ public class WindowManagerService extends IWindowManager.Stub return; } + final DisplayContent dc = mRoot.getDisplayContent(mFrozenDisplayId); if (mWaitingForConfig || mAppsFreezingScreen > 0 || mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE - || mClientFreezingScreen || !mOpeningApps.isEmpty()) { + || mClientFreezingScreen || (dc != null && !dc.mOpeningApps.isEmpty())) { if (DEBUG_ORIENTATION) Slog.d(TAG_WM, "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig + ", mAppsFreezingScreen=" + mAppsFreezingScreen + ", mWindowsFreezingScreen=" + mWindowsFreezingScreen + ", mClientFreezingScreen=" + mClientFreezingScreen - + ", mOpeningApps.size()=" + mOpeningApps.size()); + + ", mOpeningApps.size()=" + (dc != null ? dc.mOpeningApps.size() : 0)); return; } @@ -5583,7 +5438,7 @@ public class WindowManagerService extends IWindowManager.Stub mScreenFrozenLock.release(); - if (updateRotation) { + if (updateRotation && displayContent != null && updateRotation) { if (DEBUG_ORIENTATION) Slog.d(TAG_WM, "Performing post-rotate rotation"); configChanged |= displayContent.updateRotationUnchecked(); } @@ -5909,7 +5764,8 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { final AppWindowToken appWindow = mRoot.getAppWindowToken(token); if (appWindow != null) { - mUnknownAppVisibilityController.notifyAppResumedFinished(appWindow); + appWindow.getDisplayContent().mUnknownAppVisibilityController + .notifyAppResumedFinished(appWindow); } } } @@ -5955,15 +5811,6 @@ public class WindowManagerService extends IWindowManager.Stub private void dumpTokensLocked(PrintWriter pw, boolean dumpAll) { pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)"); mRoot.dumpTokens(pw, dumpAll); - if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty()) { - pw.println(); - if (mOpeningApps.size() > 0) { - pw.print(" mOpeningApps="); pw.println(mOpeningApps); - } - if (mClosingApps.size() > 0) { - pw.print(" mClosingApps="); pw.println(mClosingApps); - } - } } private void dumpSessionsLocked(PrintWriter pw, boolean dumpAll) { @@ -6000,7 +5847,6 @@ public class WindowManagerService extends IWindowManager.Stub final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked(); proto.write(ROTATION, defaultDisplayContent.getRotation()); proto.write(LAST_ORIENTATION, defaultDisplayContent.getLastOrientation()); - mAppTransition.writeToProto(proto, APP_TRANSITION); } void traceStateLocked(String where) { @@ -6133,7 +5979,6 @@ public class WindowManagerService extends IWindowManager.Stub pw.println(); mInputManagerCallback.dump(pw, " "); - mUnknownAppVisibilityController.dump(pw, " "); mTaskSnapshotController.dump(pw, " "); if (dumpAll) { @@ -6172,9 +6017,6 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" window="); pw.print(mWindowAnimationScaleSetting); pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting); pw.print(" animator="); pw.println(mAnimatorDurationScaleSetting); - pw.print(" mSkipAppTransitionAnimation=");pw.println(mSkipAppTransitionAnimation); - pw.println(" mLayoutToAnim:"); - mAppTransition.dump(pw, " "); if (mRecentsAnimationController != null) { pw.print(" mRecentsAnimationController="); pw.println(mRecentsAnimationController); mRecentsAnimationController.dump(pw, " "); @@ -7024,10 +6866,12 @@ public class WindowManagerService extends IWindowManager.Stub } } + // TODO(multi-display): currently only used by PWM to notify keyguard transitions as well + // forwarding it to SystemUI for synchronizing status and navigation bar animations. @Override public void registerAppTransitionListener(AppTransitionListener listener) { synchronized (mWindowMap) { - mAppTransition.registerListenerLocked(listener); + getDefaultDisplayContentLocked().mAppTransition.registerListenerLocked(listener); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 2f89d5ced6cc..9dc77219e8db 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1364,7 +1364,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override boolean hasContentToDisplay() { if (!mAppFreezing && isDrawnLw() && (mViewVisibility == View.VISIBLE - || (isAnimating() && !mService.mAppTransition.isTransitionSet()))) { + || (isAnimating() && !getDisplayContent().mAppTransition.isTransitionSet()))) { return true; } @@ -1473,7 +1473,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * of a transition that has not yet been started. */ boolean isReadyForDisplay() { - if (mToken.waitingToShow && mService.mAppTransition.isTransitionSet()) { + if (mToken.waitingToShow && getDisplayContent().mAppTransition.isTransitionSet()) { return false; } final boolean parentAndClientVisible = !isParentWindowHidden() diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 2beb7887698e..838d2a1abb81 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1364,7 +1364,8 @@ class WindowStateAnimator { break; } if (attr >= 0) { - a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr, TRANSIT_NONE); + a = mWin.getDisplayContent().mAppTransition.loadAnimationAttr( + mWin.mAttrs, attr, TRANSIT_NONE); } } if (DEBUG_ANIM) Slog.v(TAG, diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index e13a70a399e2..e82ffe8b271e 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -16,62 +16,18 @@ package com.android.server.wm; -import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; -import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; -import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; -import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; -import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; -import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; -import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER; -import static android.view.WindowManager.TRANSIT_NONE; -import static android.view.WindowManager.TRANSIT_TASK_CLOSE; -import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE; -import static android.view.WindowManager.TRANSIT_TASK_OPEN; -import static android.view.WindowManager.TRANSIT_TASK_TO_BACK; -import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; -import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE; -import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN; -import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE; -import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE; -import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN; -import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN; -import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; -import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; -import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT; -import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN; -import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN; -import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; -import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING; import static com.android.server.wm.WindowManagerService.H.REPORT_WINDOWS_CHANGE; import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD; -import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES; -import android.app.WindowConfiguration; import android.os.Debug; import android.os.Trace; -import android.util.ArraySet; import android.util.Slog; import android.util.SparseIntArray; -import android.view.Display; -import android.view.RemoteAnimationAdapter; -import android.view.RemoteAnimationDefinition; -import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; -import android.view.WindowManager.TransitionType; -import android.view.animation.Animation; - -import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; -import java.util.function.Predicate; /** * Positions windows and their surfaces. @@ -233,550 +189,6 @@ class WindowSurfacePlacer { return mInLayout; } - /** - * @return bitmap indicating if another pass through layout must be made. - */ - int handleAppTransitionReadyLocked() { - int appsCount = mService.mOpeningApps.size(); - if (!transitionGoodToGo(appsCount, mTempTransitionReasons)) { - return 0; - } - Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady"); - - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); - int transit = mService.mAppTransition.getAppTransition(); - if (mService.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) { - transit = WindowManager.TRANSIT_UNSET; - } - mService.mSkipAppTransitionAnimation = false; - mService.mNoAnimationNotifyOnTransitionFinished.clear(); - - mService.mAppTransition.removeAppTransitionTimeoutCallbacks(); - - final DisplayContent displayContent = mService.getDefaultDisplayContentLocked(); - - mService.mRoot.mWallpaperMayChange = false; - - int i; - for (i = 0; i < appsCount; i++) { - final AppWindowToken wtoken = mService.mOpeningApps.valueAt(i); - // Clearing the mAnimatingExit flag before entering animation. It's set to true if app - // window is removed, or window relayout to invisible. This also affects window - // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the - // transition selection depends on wallpaper target visibility. - wtoken.clearAnimatingFlags(); - } - - // Adjust wallpaper before we pull the lower/upper target, since pending changes - // (like the clearAnimatingFlags() above) might affect wallpaper target result. - // Or, the opening app window should be a wallpaper target. - mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(displayContent, - mService.mOpeningApps); - - // Determine if closing and opening app token sets are wallpaper targets, in which case - // special animations are needed. - final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null; - final boolean openingAppHasWallpaper = canBeWallpaperTarget(mService.mOpeningApps) - && hasWallpaperTarget; - final boolean closingAppHasWallpaper = canBeWallpaperTarget(mService.mClosingApps) - && hasWallpaperTarget; - - transit = maybeUpdateTransitToTranslucentAnim(transit); - transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper, - closingAppHasWallpaper); - - // Find the layout params of the top-most application window in the tokens, which is - // what will control the animation theme. If all closing windows are obscured, then there is - // no need to do an animation. This is the case, for example, when this transition is being - // done behind a dream window. - final ArraySet<Integer> activityTypes = collectActivityTypes(mService.mOpeningApps, - mService.mClosingApps); - final boolean allowAnimations = mService.mPolicy.allowAppAnimationsLw(); - final AppWindowToken animLpToken = allowAnimations - ? findAnimLayoutParamsToken(transit, activityTypes) - : null; - final AppWindowToken topOpeningApp = allowAnimations - ? getTopApp(mService.mOpeningApps, false /* ignoreHidden */) - : null; - final AppWindowToken topClosingApp = allowAnimations - ? getTopApp(mService.mClosingApps, false /* ignoreHidden */) - : null; - final LayoutParams animLp = getAnimLp(animLpToken); - overrideWithRemoteAnimationIfSet(animLpToken, transit, activityTypes); - - final boolean voiceInteraction = containsVoiceInteraction(mService.mOpeningApps) - || containsVoiceInteraction(mService.mOpeningApps); - - final int layoutRedo; - mService.mSurfaceAnimationRunner.deferStartingAnimations(); - try { - processApplicationsAnimatingInPlace(transit); - - handleClosingApps(transit, animLp, voiceInteraction); - handleOpeningApps(transit, animLp, voiceInteraction); - - mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp); - - final int flags = mService.mAppTransition.getTransitFlags(); - layoutRedo = mService.mAppTransition.goodToGo(transit, topOpeningApp, topClosingApp, - mService.mOpeningApps, mService.mClosingApps); - handleNonAppWindowsInTransition(transit, flags); - mService.mAppTransition.postAnimationCallback(); - mService.mAppTransition.clear(); - } finally { - mService.mSurfaceAnimationRunner.continueStartingAnimations(); - } - - mService.mTaskSnapshotController.onTransitionStarting(); - - mService.mOpeningApps.clear(); - mService.mClosingApps.clear(); - mService.mUnknownAppVisibilityController.clear(); - - // This has changed the visibility of windows, so perform - // a new layout to get them all up-to-date. - displayContent.setLayoutNeeded(); - - // TODO(multidisplay): IMEs are only supported on the default display. - final DisplayContent dc = mService.getDefaultDisplayContentLocked(); - dc.computeImeTarget(true /* updateImeTarget */); - mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, - true /*updateInputWindows*/); - mService.mFocusMayChange = false; - - mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, - mTempTransitionReasons.clone()).sendToTarget(); - - Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); - - return layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG; - } - - private static LayoutParams getAnimLp(AppWindowToken wtoken) { - final WindowState mainWindow = wtoken != null ? wtoken.findMainWindow() : null; - return mainWindow != null ? mainWindow.mAttrs : null; - } - - /** - * Overrides the pending transition with the remote animation defined for the transition in the - * set of defined remote animations in the app window token. - */ - private void overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit, - ArraySet<Integer> activityTypes) { - if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) { - // The crash transition has higher priority than any involved remote animations. - return; - } - if (animLpToken == null) { - return; - } - final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition(); - if (definition != null) { - final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes); - if (adapter != null) { - mService.mAppTransition.overridePendingAppTransitionRemote(adapter); - } - } - } - - /** - * @return The window token that determines the animation theme. - */ - private AppWindowToken findAnimLayoutParamsToken(@TransitionType int transit, - ArraySet<Integer> activityTypes) { - AppWindowToken result; - - // Remote animations always win, but fullscreen tokens override non-fullscreen tokens. - result = lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps, - w -> w.getRemoteAnimationDefinition() != null - && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes)); - if (result != null) { - return result; - } - result = lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps, - w -> w.fillsParent() && w.findMainWindow() != null); - if (result != null) { - return result; - } - return lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps, - w -> w.findMainWindow() != null); - } - - /** - * @return The set of {@link WindowConfiguration.ActivityType}s contained in the set of apps in - * {@code array1} and {@code array2}. - */ - private ArraySet<Integer> collectActivityTypes(ArraySet<AppWindowToken> array1, - ArraySet<AppWindowToken> array2) { - final ArraySet<Integer> result = new ArraySet<>(); - for (int i = array1.size() - 1; i >= 0; i--) { - result.add(array1.valueAt(i).getActivityType()); - } - for (int i = array2.size() - 1; i >= 0; i--) { - result.add(array2.valueAt(i).getActivityType()); - } - return result; - } - - private AppWindowToken lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1, - ArraySet<AppWindowToken> array2, Predicate<AppWindowToken> filter) { - final int array1count = array1.size(); - final int count = array1count + array2.size(); - int bestPrefixOrderIndex = Integer.MIN_VALUE; - AppWindowToken bestToken = null; - for (int i = 0; i < count; i++) { - final AppWindowToken wtoken = i < array1count - ? array1.valueAt(i) - : array2.valueAt(i - array1count); - final int prefixOrderIndex = wtoken.getPrefixOrderIndex(); - if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) { - bestPrefixOrderIndex = prefixOrderIndex; - bestToken = wtoken; - } - } - return bestToken; - } - - private boolean containsVoiceInteraction(ArraySet<AppWindowToken> apps) { - for (int i = apps.size() - 1; i >= 0; i--) { - if (apps.valueAt(i).mVoiceInteraction) { - return true; - } - } - return false; - } - - private void handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction) { - final int appsCount = mService.mOpeningApps.size(); - for (int i = 0; i < appsCount; i++) { - AppWindowToken wtoken = mService.mOpeningApps.valueAt(i); - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); - - if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)) { - // This token isn't going to be animating. Add it to the list of tokens to - // be notified of app transition complete since the notification will not be - // sent be the app window animator. - mService.mNoAnimationNotifyOnTransitionFinished.add(wtoken.token); - } - wtoken.updateReportedVisibilityLocked(); - wtoken.waitingToShow = false; - if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, - ">>> OPEN TRANSACTION handleAppTransitionReadyLocked()"); - mService.openSurfaceTransaction(); - try { - wtoken.showAllWindowsLocked(); - } finally { - mService.closeSurfaceTransaction("handleAppTransitionReadyLocked"); - if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, - "<<< CLOSE TRANSACTION handleAppTransitionReadyLocked()"); - } - - if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) { - wtoken.attachThumbnailAnimation(); - } else if (mService.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) { - wtoken.attachCrossProfileAppsThumbnailAnimation(); - } - } - } - - private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction) { - final int appsCount; - appsCount = mService.mClosingApps.size(); - for (int i = 0; i < appsCount; i++) { - AppWindowToken wtoken = mService.mClosingApps.valueAt(i); - - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); - // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not - // animating? - wtoken.setVisibility(animLp, false, transit, false, voiceInteraction); - wtoken.updateReportedVisibilityLocked(); - // Force the allDrawn flag, because we want to start - // this guy's animations regardless of whether it's - // gotten drawn. - wtoken.allDrawn = true; - wtoken.deferClearAllDrawn = false; - // Ensure that apps that are mid-starting are also scheduled to have their - // starting windows removed after the animation is complete - if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit - && wtoken.getController() != null) { - wtoken.getController().removeStartingWindow(); - } - - if (mService.mAppTransition.isNextAppTransitionThumbnailDown()) { - wtoken.attachThumbnailAnimation(); - } - } - } - - private void handleNonAppWindowsInTransition(int transit, int flags) { - if (transit == TRANSIT_KEYGUARD_GOING_AWAY) { - if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0 - && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0) { - Animation anim = mService.mPolicy.createKeyguardWallpaperExit( - (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0); - if (anim != null) { - mService.getDefaultDisplayContentLocked().mWallpaperController - .startWallpaperAnimation(anim); - } - } - } - if (transit == TRANSIT_KEYGUARD_GOING_AWAY - || transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) { - mService.getDefaultDisplayContentLocked().startKeyguardExitOnNonAppWindows( - transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER, - (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0); - } - } - - private boolean transitionGoodToGo(int appsCount, SparseIntArray outReasons) { - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "Checking " + appsCount + " opening apps (frozen=" - + mService.mDisplayFrozen + " timeout=" - + mService.mAppTransition.isTimeout() + ")..."); - final ScreenRotationAnimation screenRotationAnimation = - mService.mAnimator.getScreenRotationAnimationLocked( - Display.DEFAULT_DISPLAY); - - outReasons.clear(); - if (!mService.mAppTransition.isTimeout()) { - // Imagine the case where we are changing orientation due to an app transition, but a previous - // orientation change is still in progress. We won't process the orientation change - // for our transition because we need to wait for the rotation animation to finish. - // If we start the app transition at this point, we will interrupt it halfway with a new rotation - // animation after the old one finally finishes. It's better to defer the - // app transition. - if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() && - mService.getDefaultDisplayContentLocked().rotationNeedsUpdate()) { - if (DEBUG_APP_TRANSITIONS) { - Slog.v(TAG, "Delaying app transition for screen rotation animation to finish"); - } - return false; - } - for (int i = 0; i < appsCount; i++) { - AppWindowToken wtoken = mService.mOpeningApps.valueAt(i); - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "Check opening app=" + wtoken + ": allDrawn=" - + wtoken.allDrawn + " startingDisplayed=" - + wtoken.startingDisplayed + " startingMoved=" - + wtoken.startingMoved + " isRelaunching()=" - + wtoken.isRelaunching() + " startingWindow=" - + wtoken.startingWindow); - - - final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching(); - if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) { - return false; - } - final int windowingMode = wtoken.getWindowingMode(); - if (allDrawn) { - outReasons.put(windowingMode, APP_TRANSITION_WINDOWS_DRAWN); - } else { - outReasons.put(windowingMode, - wtoken.startingData instanceof SplashScreenStartingData - ? APP_TRANSITION_SPLASH_SCREEN - : APP_TRANSITION_SNAPSHOT); - } - } - - // We also need to wait for the specs to be fetched, if needed. - if (mService.mAppTransition.isFetchingAppTransitionsSpecs()) { - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "isFetchingAppTransitionSpecs=true"); - return false; - } - - if (!mService.mUnknownAppVisibilityController.allResolved()) { - if (DEBUG_APP_TRANSITIONS) { - Slog.v(TAG, "unknownApps is not empty: " - + mService.mUnknownAppVisibilityController.getDebugMessage()); - } - return false; - } - - // If the wallpaper is visible, we need to check it's ready too. - boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() || - mWallpaperControllerLocked.wallpaperTransitionReady(); - if (wallpaperReady) { - return true; - } - return false; - } - return true; - } - - private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper, - boolean closingAppHasWallpaper) { - // Given no app transition pass it through instead of a wallpaper transition. - // Never convert the crashing transition. - // Never update the transition for the wallpaper if we are just docking from recents - if (transit == TRANSIT_NONE || transit == TRANSIT_CRASHING_ACTIVITY_CLOSE - || transit == TRANSIT_DOCK_TASK_FROM_RECENTS) { - return transit; - } - - final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); - final boolean showWallpaper = wallpaperTarget != null - && (wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0; - // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set, - // don't consider upgrading to wallpaper transition. - final WindowState oldWallpaper = - (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper) - ? null - : wallpaperTarget; - final ArraySet<AppWindowToken> openingApps = mService.mOpeningApps; - final ArraySet<AppWindowToken> closingApps = mService.mClosingApps; - final AppWindowToken topOpeningApp = getTopApp(mService.mOpeningApps, - false /* ignoreHidden */); - final AppWindowToken topClosingApp = getTopApp(mService.mClosingApps, - true /* ignoreHidden */); - - boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps); - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "New wallpaper target=" + wallpaperTarget - + ", oldWallpaper=" + oldWallpaper - + ", openingApps=" + openingApps - + ", closingApps=" + closingApps); - - if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) { - transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "New transit: " + AppTransition.appTransitionToString(transit)); - } - // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic - // relies on the fact that we always execute a Keyguard transition after preparing one. - else if (!isKeyguardGoingAwayTransit(transit)) { - if (closingAppHasWallpaper && openingAppHasWallpaper) { - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!"); - switch (transit) { - case TRANSIT_ACTIVITY_OPEN: - case TRANSIT_TASK_OPEN: - case TRANSIT_TASK_TO_FRONT: - transit = TRANSIT_WALLPAPER_INTRA_OPEN; - break; - case TRANSIT_ACTIVITY_CLOSE: - case TRANSIT_TASK_CLOSE: - case TRANSIT_TASK_TO_BACK: - transit = TRANSIT_WALLPAPER_INTRA_CLOSE; - break; - } - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "New transit: " + AppTransition.appTransitionToString(transit)); - } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty() - && !openingApps.contains(oldWallpaper.mAppToken) - && closingApps.contains(oldWallpaper.mAppToken) - && topClosingApp == oldWallpaper.mAppToken) { - // We are transitioning from an activity with a wallpaper to one without. - transit = TRANSIT_WALLPAPER_CLOSE; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: " - + AppTransition.appTransitionToString(transit)); - } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() - && openingApps.contains(wallpaperTarget.mAppToken) - && topOpeningApp == wallpaperTarget.mAppToken - && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) { - // We are transitioning from an activity without - // a wallpaper to now showing the wallpaper - transit = TRANSIT_WALLPAPER_OPEN; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: " - + AppTransition.appTransitionToString(transit)); - } - } - return transit; - } - - /** - * There are cases where we open/close a new task/activity, but in reality only a translucent - * activity on top of existing activities is opening/closing. For that one, we have a different - * animation because non of the task/activity animations actually work well with translucent - * apps. - * - * @param transit The current transition type. - * @return The current transition type or - * {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE}/ - * {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the - * situation. - */ - @VisibleForTesting - int maybeUpdateTransitToTranslucentAnim(int transit) { - final boolean taskOrActivity = AppTransition.isTaskTransit(transit) - || AppTransition.isActivityTransit(transit); - boolean allOpeningVisible = true; - boolean allTranslucentOpeningApps = !mService.mOpeningApps.isEmpty(); - for (int i = mService.mOpeningApps.size() - 1; i >= 0; i--) { - final AppWindowToken token = mService.mOpeningApps.valueAt(i); - if (!token.isVisible()) { - allOpeningVisible = false; - if (token.fillsParent()) { - allTranslucentOpeningApps = false; - } - } - } - boolean allTranslucentClosingApps = !mService.mClosingApps.isEmpty(); - for (int i = mService.mClosingApps.size() - 1; i >= 0; i--) { - if (mService.mClosingApps.valueAt(i).fillsParent()) { - allTranslucentClosingApps = false; - break; - } - } - - if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) { - return TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE; - } - if (taskOrActivity && allTranslucentOpeningApps && mService.mClosingApps.isEmpty()) { - return TRANSIT_TRANSLUCENT_ACTIVITY_OPEN; - } - return transit; - } - - private boolean canBeWallpaperTarget(ArraySet<AppWindowToken> apps) { - for (int i = apps.size() - 1; i >= 0; i--) { - if (apps.valueAt(i).windowsCanBeWallpaperTarget()) { - return true; - } - } - return false; - } - - /** - * Finds the top app in a list of apps, using its {@link AppWindowToken#getPrefixOrderIndex} to - * compare z-order. - * - * @param apps The list of apps to search. - * @param ignoreHidden If set to true, ignores apps that are {@link AppWindowToken#isHidden}. - * @return The top {@link AppWindowToken}. - */ - private AppWindowToken getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden) { - int topPrefixOrderIndex = Integer.MIN_VALUE; - AppWindowToken topApp = null; - for (int i = apps.size() - 1; i >= 0; i--) { - final AppWindowToken app = apps.valueAt(i); - if (ignoreHidden && app.isHidden()) { - continue; - } - final int prefixOrderIndex = app.getPrefixOrderIndex(); - if (prefixOrderIndex > topPrefixOrderIndex) { - topPrefixOrderIndex = prefixOrderIndex; - topApp = app; - } - } - return topApp; - } - - private void processApplicationsAnimatingInPlace(int transit) { - if (transit == TRANSIT_TASK_IN_PLACE) { - // TODO (b/111362605): non-default-display transition. - // Find the focused window - final WindowState win = mService.getDefaultDisplayContentLocked().findFocusedWindow(); - if (win != null) { - final AppWindowToken wtoken = win.mAppToken; - if (DEBUG_APP_TRANSITIONS) - Slog.v(TAG, "Now animating app in place " + wtoken); - wtoken.cancelAnimation(); - wtoken.applyAnimationLocked(null, transit, false, false); - wtoken.updateReportedVisibilityLocked(); - wtoken.showAllWindowsLocked(); - } - } - } - void requestTraversal() { if (!mTraversalScheduled) { mTraversalScheduled = true; diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java index 057f04740198..aa495f7d4515 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -36,14 +36,14 @@ import org.junit.runner.RunWith; @SmallTest @Presubmit @RunWith(AndroidJUnit4.class) -public class WindowSurfacePlacerTest extends WindowTestsBase { +public class AppTransitionControllerTest extends WindowTestsBase { - private WindowSurfacePlacer mWindowSurfacePlacer; + private AppTransitionController mAppTransitionController; @Before public void setUp() throws Exception { super.setUp(); - mWindowSurfacePlacer = new WindowSurfacePlacer(sWm); + mAppTransitionController = new AppTransitionController(sWm, mDisplayContent); } @Test @@ -55,10 +55,11 @@ public class WindowSurfacePlacerTest extends WindowTestsBase { WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); translucentOpening.setFillsParent(false); translucentOpening.setHidden(true); - sWm.mOpeningApps.add(behind); - sWm.mOpeningApps.add(translucentOpening); + mDisplayContent.mOpeningApps.add(behind); + mDisplayContent.mOpeningApps.add(translucentOpening); assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN, - mWindowSurfacePlacer.maybeUpdateTransitToTranslucentAnim(TRANSIT_TASK_OPEN)); + mAppTransitionController.maybeUpdateTransitToTranslucentAnim( + TRANSIT_TASK_OPEN)); } } @@ -70,9 +71,10 @@ public class WindowSurfacePlacerTest extends WindowTestsBase { final AppWindowToken translucentClosing = createAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); translucentClosing.setFillsParent(false); - sWm.mClosingApps.add(translucentClosing); + mDisplayContent.mClosingApps.add(translucentClosing); assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE, - mWindowSurfacePlacer.maybeUpdateTransitToTranslucentAnim(TRANSIT_TASK_CLOSE)); + mAppTransitionController.maybeUpdateTransitToTranslucentAnim( + TRANSIT_TASK_CLOSE)); } } } diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java index 3053c4197f82..ee6fbac3f19f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java @@ -16,22 +16,33 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.spy; import android.content.Context; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; +import android.view.Display; +import android.view.IApplicationToken; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,51 +54,142 @@ import org.junit.runner.RunWith; @SmallTest @Presubmit @RunWith(AndroidJUnit4.class) -public class AppTransitionTests { +public class AppTransitionTests extends WindowTestsBase { - @Rule - public final WindowManagerServiceRule mRule = new WindowManagerServiceRule(); - private WindowManagerService mWm; + private DisplayContent mDc; @Before public void setUp() throws Exception { + super.setUp(); final Context context = InstrumentationRegistry.getTargetContext(); - mWm = mRule.getWindowManagerService(); + mDc = sWm.getDefaultDisplayContentLocked(); + // For unit test, we don't need to test performSurfacePlacement to prevent some + // abnormal interaction with surfaceflinger native side. + sWm.mRoot = spy(sWm.mRoot); + doNothing().when(sWm.mRoot).performSurfacePlacement(anyBoolean()); } @Test public void testKeyguardOverride() throws Exception { - mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); - mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); - assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mWm.mAppTransition.getAppTransition()); + sWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); + sWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); + assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition()); } @Test public void testKeyguardKeep() throws Exception { - mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); - mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); - assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mWm.mAppTransition.getAppTransition()); + sWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); + sWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); + assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition()); } @Test public void testForceOverride() throws Exception { - mWm.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */); - mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */, - 0 /* flags */, true /* forceOverride */); - assertEquals(TRANSIT_ACTIVITY_OPEN, mWm.mAppTransition.getAppTransition()); + sWm.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */); + mDc.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN, + false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */); + assertEquals(TRANSIT_ACTIVITY_OPEN, mDc.mAppTransition.getAppTransition()); } @Test public void testCrashing() throws Exception { - mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); - mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); - assertEquals(TRANSIT_CRASHING_ACTIVITY_CLOSE, mWm.mAppTransition.getAppTransition()); + sWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); + sWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); + assertEquals(TRANSIT_CRASHING_ACTIVITY_CLOSE, mDc.mAppTransition.getAppTransition()); } @Test public void testKeepKeyguard_withCrashing() throws Exception { - mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); - mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); - assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mWm.mAppTransition.getAppTransition()); + sWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); + sWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); + assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition()); + } + + @Test + public void testAppTransitionStateForMultiDisplay() throws Exception { + // Create 2 displays & presume both display the state is ON for ready to display & animate. + final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON); + final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON); + + // Create 2 app window tokens to represent 2 activity window. + final WindowTestUtils.TestAppWindowToken token1 = createTestAppWindowToken(dc1, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final WindowTestUtils.TestAppWindowToken token2 = createTestAppWindowToken(dc2, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + + // Set TestAppWindowContainerController & assign first app token state to be good to go. + final WindowTestUtils.TestAppWindowContainerController controller1 = + createAppWindowController(dc1, token1.appToken); + final WindowTestUtils.TestAppWindowContainerController controller2 = + createAppWindowController(dc1, token2.appToken); + controller1.setContainer(token1); + token1.allDrawn = true; + token1.startingDisplayed = true; + token1.startingMoved = true; + controller2.setContainer(token2); + + // Simulate activity resume / finish flows to prepare app transition & set visibility, + // make sure transition is set as expected for each display. + dc1.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN, + false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); + assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition()); + dc2.getController().prepareAppTransition(TRANSIT_ACTIVITY_CLOSE, + false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); + assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition()); + // One activity window is visible for resuming & the other activity window is invisible + // for finishing in different display. + controller1.setVisibility(true, false); + controller2.setVisibility(false, false); + + // Make sure each display is in animating stage. + assertTrue(dc1.mOpeningApps.size() > 0); + assertTrue(dc2.mClosingApps.size() > 0); + assertTrue(dc1.isAppAnimating()); + assertTrue(dc2.isAppAnimating()); + } + + @Test + public void testCleanAppTransitionWhenTaskStackReparent() throws Exception { + // Create 2 displays & presume both display the state is ON for ready to display & animate. + final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON); + final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON); + + final TaskStack stack1 = createTaskStackOnDisplay(dc1); + final Task task1 = createTaskInStack(stack1, 0 /* userId */); + final WindowTestUtils.TestAppWindowToken token1 = + WindowTestUtils.createTestAppWindowToken(dc1); + task1.addChild(token1, 0); + + // Simulate same app is during opening / closing transition set stage. + dc1.mClosingApps.add(token1); + assertTrue(dc1.mClosingApps.size() > 0); + + dc1.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN, + false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); + assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition()); + assertTrue(dc1.mAppTransition.isTransitionSet()); + + dc1.mOpeningApps.add(token1); + assertTrue(dc1.mOpeningApps.size() > 0); + + // Move stack to another display. + stack1.getController().reparent(dc2.getDisplayId(), new Rect(), true); + + // Verify if token are cleared from both pending transition list in former display. + assertFalse(dc1.mOpeningApps.contains(token1)); + assertFalse(dc1.mOpeningApps.contains(token1)); + } + + private WindowTestUtils.TestAppWindowContainerController createAppWindowController( + DisplayContent dc, IApplicationToken token) { + return createAppWindowController( + new WindowTestUtils.TestTaskWindowContainerController( + createStackControllerOnDisplay(dc)), token); + } + + private WindowTestUtils.TestAppWindowContainerController createAppWindowController( + WindowTestUtils.TestTaskWindowContainerController taskController, + IApplicationToken token) { + return new WindowTestUtils.TestAppWindowContainerController(taskController, token); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java index e6e08bb93c8e..d65055cf0e07 100644 --- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.view.Display.DEFAULT_DISPLAY; + import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; @@ -90,7 +92,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { private AppTransitionListener mListener; MockAppTransition(Context context) { - super(context, sWm); + super(context, sWm, mDisplayContent); } @Override diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java index 7d19baa9f871..ae92984ebf65 100644 --- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -85,7 +85,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { @Test public void testRun() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); - sWm.mOpeningApps.add(win.mAppToken); + mDisplayContent.mOpeningApps.add(win.mAppToken); try { final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, new Point(50, 100), new Rect(50, 100, 150, 150)); @@ -113,7 +113,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { finishedCaptor.getValue().onAnimationFinished(); verify(mFinishedCallback).onAnimationFinished(eq(adapter)); } finally { - sWm.mOpeningApps.clear(); + mDisplayContent.mOpeningApps.clear(); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java index 53a1185b4655..cb5c1cd67092 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java @@ -74,7 +74,7 @@ public class TaskStackTests extends WindowTestsBase { appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); assertEquals(SCREEN_ORIENTATION_PORTRAIT, stack.getOrientation()); - sWm.mClosingApps.add(appWindowToken2); + mDisplayContent.mClosingApps.add(appWindowToken2); assertEquals(SCREEN_ORIENTATION_LANDSCAPE, stack.getOrientation()); } diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java index 3ac97027a893..54456fbd6f3b 100644 --- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java @@ -40,50 +40,50 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { @Before public void setUp() throws Exception { super.setUp(); - sWm.mUnknownAppVisibilityController.clear(); + mDisplayContent.mUnknownAppVisibilityController.clear(); } @Test public void testFlow() throws Exception { final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent); - sWm.mUnknownAppVisibilityController.notifyLaunched(token); - sWm.mUnknownAppVisibilityController.notifyAppResumedFinished(token); - sWm.mUnknownAppVisibilityController.notifyRelayouted(token); + mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token); + mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token); + mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token); // Make sure our handler processed the message. Thread.sleep(100); - assertTrue(sWm.mUnknownAppVisibilityController.allResolved()); + assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); } @Test public void testMultiple() throws Exception { final AppWindowToken token1 = WindowTestUtils.createTestAppWindowToken(mDisplayContent); final AppWindowToken token2 = WindowTestUtils.createTestAppWindowToken(mDisplayContent); - sWm.mUnknownAppVisibilityController.notifyLaunched(token1); - sWm.mUnknownAppVisibilityController.notifyAppResumedFinished(token1); - sWm.mUnknownAppVisibilityController.notifyLaunched(token2); - sWm.mUnknownAppVisibilityController.notifyRelayouted(token1); - sWm.mUnknownAppVisibilityController.notifyAppResumedFinished(token2); - sWm.mUnknownAppVisibilityController.notifyRelayouted(token2); + mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token1); + mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token1); + mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token2); + mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token1); + mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token2); + mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token2); // Make sure our handler processed the message. Thread.sleep(100); - assertTrue(sWm.mUnknownAppVisibilityController.allResolved()); + assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); } @Test public void testClear() throws Exception { final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent); - sWm.mUnknownAppVisibilityController.notifyLaunched(token); - sWm.mUnknownAppVisibilityController.clear();; - assertTrue(sWm.mUnknownAppVisibilityController.allResolved()); + mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token); + mDisplayContent.mUnknownAppVisibilityController.clear();; + assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); } @Test public void testAppRemoved() throws Exception { final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent); - sWm.mUnknownAppVisibilityController.notifyLaunched(token); - sWm.mUnknownAppVisibilityController.appRemovedOrHidden(token); - assertTrue(sWm.mUnknownAppVisibilityController.allResolved()); + mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token); + mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(token); + assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java index 389eba52d88d..012c4be75fe5 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java @@ -33,6 +33,7 @@ import android.content.Context; import android.hardware.display.DisplayManagerInternal; import android.os.PowerManagerInternal; import android.os.PowerSaveState; +import android.view.Display; import android.view.InputChannel; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; @@ -114,7 +115,7 @@ public class WindowManagerServiceRule implements TestRule { runnable.run(); } return null; - }).when(atm).notifyKeyguardFlagsChanged(any()); + }).when(atm).notifyKeyguardFlagsChanged(any(), anyInt()); InputManagerService ims = mock(InputManagerService.class); // InputChannel is final and can't be mocked. @@ -142,11 +143,11 @@ public class WindowManagerServiceRule implements TestRule { mService.onInitReady(); + final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY); + final DisplayWindowController dcw = new DisplayWindowController(display, mService); // Display creation is driven by the ActivityManagerService via ActivityStackSupervisor. // We emulate those steps here. - mService.mRoot.createDisplayContent( - mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY), - mock(DisplayWindowController.class)); + mService.mRoot.createDisplayContent(display, dcw); } private void removeServices() { diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index d0a81b2ddfb2..dcfe55602e81 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -181,9 +181,12 @@ class WindowTestsBase { displayContent.removeImmediately(); } } + // Remove app transition & window freeze timeout callbacks to prevent unnecessary + // actions after test. + sWm.getDefaultDisplayContentLocked().mAppTransition + .removeAppTransitionTimeoutCallbacks(); + sWm.mH.removeMessages(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT); sWm.mInputMethodTarget = null; - sWm.mClosingApps.clear(); - sWm.mOpeningApps.clear(); } // Wait until everything is really cleaned up. @@ -354,6 +357,32 @@ class WindowTestsBase { } } + /** + * Creates a {@link DisplayContent} with given display state and adds it to the system. + * + * Unlike {@link #createNewDisplay()} that uses a mock {@link DisplayWindowController} to + * initialize {@link DisplayContent}, this method used real controller object when the test + * need to verify its related flows. + * + * @param displayState For initializing the state of the display. See + * {@link Display#getState()}. + */ + DisplayContent createNewDisplayWithController(int displayState) { + // Leverage main display info & initialize it with display state for given displayId. + DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.copyFrom(mDisplayInfo); + displayInfo.state = displayState; + final int displayId = sNextDisplayId++; + final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, + displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS); + final DisplayWindowController dcw = new DisplayWindowController(display, sWm); + synchronized (sWm.mWindowMap) { + // Display creation is driven by DisplayWindowController via ActivityStackSupervisor. + // We skip those steps here. + return sWm.mRoot.createDisplayContent(display, dcw); + } + } + /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */ WindowTestUtils.TestWindowState createWindowState(WindowManager.LayoutParams attrs, WindowToken token) { |