From d39583af0e7b466d76cce1f367223a678bc20dac Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Wed, 4 Mar 2020 11:14:32 -0800 Subject: [PATCH] Start PiP dismiss from SysUI via TaskOrganizer following ag/10570572, dismiss PiP is driven by SysUI as following - SysUI issues WindowContainerTransaction and set the child windowing mode at the beginning - SysUI continues the animation - SysUI issues WindowContainerTransaction and set the final windowing mode at the end This solves also the black background issue while exiting from PiP to fullscreen mode. Known issues - app does not receive fullscreen configuration when transitioning from PiP to fullscreen mode - saving of the reentry bounds should be originated from SysUI rather than WM going forward Bug: 149947030 Bug: 151866274 Test: atest PinnedStackTests PipAnimationControllerTest Change-Id: I7ed0d8b47dcc26653ebe2f3c08acab9e8b835db4 --- .../android/app/IActivityTaskManager.aidl | 7 --- .../systemui/pip/PipTaskOrganizer.java | 62 +++++++++++++++---- .../systemui/pip/phone/PipMotionHelper.java | 6 +- .../server/wm/ActivityTaskManagerService.java | 32 +--------- .../android/server/wm/RecentTasksTest.java | 1 - 5 files changed, 53 insertions(+), 55 deletions(-) diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 03717ecd40380..53f5721781063 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -298,13 +298,6 @@ interface IActivityTaskManager { void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration, in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations); - /** - * Dismisses PiP - * @param animate True if the dismissal should be animated. - * @param animationDuration The duration of the resize animation in milliseconds or -1 if the - * default animation duration should be used. - */ - void dismissPip(boolean animate, int animationDuration); void suppressResizeConfigChanges(boolean suppress); void moveTasksToFullscreenStack(int fromStackId, boolean onTop); boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 6ce5e7cf506cc..af8b18482ea83 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -16,6 +16,9 @@ package com.android.systemui.pip; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; + import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_NONE; @@ -26,8 +29,6 @@ import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTI import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; -import android.app.ActivityTaskManager; -import android.window.ITaskOrganizerController; import android.app.PictureInPictureParams; import android.content.Context; import android.content.pm.ActivityInfo; @@ -38,9 +39,9 @@ import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.util.Size; +import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.IWindowContainer; -import android.view.SurfaceControl; import android.window.WindowContainerTransaction; import android.window.WindowOrganizer; @@ -216,6 +217,29 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { mOneShotAnimationType = animationType; } + /** + * Dismiss PiP, this is done in two phases using {@link WindowContainerTransaction} + * - setActivityWindowingMode to fullscreen at beginning of the transaction. without changing + * the windowing mode of the Task itself. This makes sure the activity render it's fullscreen + * configuration while the Task is still in PiP. + * - setWindowingMode to fullscreen at the end of transition + * @param animationDurationMs duration in millisecond for the exiting PiP transition + */ + public void dismissPip(int animationDurationMs) { + try { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setActivityWindowingMode(mToken, WINDOWING_MODE_FULLSCREEN); + WindowOrganizer.applyTransaction(wct); + } catch (RemoteException e) { + Log.e(TAG, "Failed to apply container transaction", e); + } + final Rect destinationBounds = mBoundsToRestore.remove(mToken.asBinder()); + scheduleAnimateResizePip(mLastReportedBounds, destinationBounds, + TRANSITION_DIRECTION_TO_FULLSCREEN, animationDurationMs, + null /* updateBoundsCallback */); + mInPip = false; + } + @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo info) { Objects.requireNonNull(info, "Requires RunningTaskInfo"); @@ -235,7 +259,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { mBoundsToRestore.put(mToken.asBinder(), currentBounds); if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { scheduleAnimateResizePip(currentBounds, destinationBounds, - TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null); + TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, + null /* updateBoundsCallback */); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { mUpdateHandler.post(() -> mPipAnimationController .getAnimator(mLeash, destinationBounds, 0f, 1f) @@ -249,6 +274,12 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } } + /** + * Note that dismissing PiP is now originated from SystemUI, see {@link #dismissPip(int)}. + * Meanwhile this callback is invoked whenever the task is removed. For instance: + * - as a result of removeStacksInWindowingModes from WM + * - activity itself is died + */ @Override public void onTaskVanished(ActivityManager.RunningTaskInfo info) { IWindowContainer token = info.token; @@ -259,7 +290,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } final Rect boundsToRestore = mBoundsToRestore.remove(token.asBinder()); scheduleAnimateResizePip(mLastReportedBounds, boundsToRestore, - TRANSITION_DIRECTION_TO_FULLSCREEN, mEnterExitAnimationDuration, null); + TRANSITION_DIRECTION_TO_FULLSCREEN, mEnterExitAnimationDuration, + null /* updateBoundsCallback */); mInPip = false; } @@ -274,7 +306,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { getAspectRatioOrDefault(newParams), null /* bounds */, getMinimalSize(info.topActivityInfo)); Objects.requireNonNull(destinationBounds, "Missing destination bounds"); - scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration, null); + scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration, + null /* updateBoundsCallback */); } /** @@ -434,12 +467,19 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } mLastReportedBounds.set(destinationBounds); try { - // If we are animating to fullscreen, then we need to reset the override bounds on the - // task to ensure that the task "matches" the parent's bounds - Rect taskBounds = direction == TRANSITION_DIRECTION_TO_FULLSCREEN - ? null - : destinationBounds; final WindowContainerTransaction wct = new WindowContainerTransaction(); + final Rect taskBounds; + if (direction == TRANSITION_DIRECTION_TO_FULLSCREEN) { + // If we are animating to fullscreen, then we need to reset the override bounds + // on the task to ensure that the task "matches" the parent's bounds, this applies + // also to the final windowing mode, which should be reset to undefined rather than + // fullscreen. + taskBounds = null; + wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED) + .setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); + } else { + taskBounds = destinationBounds; + } if (direction == TRANSITION_DIRECTION_TO_PIP) { wct.scheduleFinishEnterPip(mToken, taskBounds); } else { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index 449a2bca6abbb..7974281b956f7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -219,11 +219,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, cancelAnimations(); mMenuController.hideMenuWithoutResize(); mPipTaskOrganizer.getUpdateHandler().post(() -> { - try { - mActivityTaskManager.dismissPip(!skipAnimation, EXPAND_STACK_TO_FULLSCREEN_DURATION); - } catch (RemoteException e) { - Log.e(TAG, "Error expanding PiP activity", e); - } + mPipTaskOrganizer.dismissPip(skipAnimation ? 0 : EXPAND_STACK_TO_FULLSCREEN_DURATION); }); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 214a6767f3b29..14c82b3f07f1c 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -144,7 +144,6 @@ import android.app.IApplicationThread; import android.app.IAssistDataReceiver; import android.app.INotificationManager; import android.app.IRequestFinishCallback; -import android.window.ITaskOrganizerController; import android.app.ITaskStackListener; import android.app.Notification; import android.app.NotificationManager; @@ -230,9 +229,9 @@ import android.util.proto.ProtoOutputStream; import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; +import android.view.WindowManager; import android.window.IWindowOrganizerController; import android.window.WindowContainerTransaction; -import android.view.WindowManager; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -3979,35 +3978,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - /** - * Dismisses Pip - * @param animate True if the dismissal should be animated. - * @param animationDuration The duration of the resize animation in milliseconds or -1 if the - * default animation duration should be used. - */ - @Override - public void dismissPip(boolean animate, int animationDuration) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()"); - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - final ActivityStack stack = - mRootWindowContainer.getDefaultDisplay().getRootPinnedTask(); - if (stack == null) { - Slog.w(TAG, "dismissPip: pinned stack not found."); - return; - } - if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) { - throw new IllegalArgumentException("Stack: " + stack - + " doesn't support animated resize."); - } - stack.dismissPip(); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - @Override public void suppressResizeConfigChanges(boolean suppress) throws RemoteException { mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()"); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 66566bc5dff5e..0dee6f3791fff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -1115,7 +1115,6 @@ public class RecentTasksTest extends ActivityTestsBase { () -> mService.moveTaskToStack(0, INVALID_STACK_ID, true)); assertSecurityException(expectCallable, () -> mService.setTaskWindowingModeSplitScreenPrimary(0, true)); - assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0)); assertSecurityException(expectCallable, () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect())); assertSecurityException(expectCallable, () -> mService.getAllStackInfos());