diff options
10 files changed, 206 insertions, 17 deletions
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index cc6c999ed255..db6ad3df17b0 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -434,6 +434,11 @@ interface IActivityTaskManager { void registerRemoteAnimationForNextActivityStart(in String packageName, in RemoteAnimationAdapter adapter); + /** + * Registers remote animations for a display. + */ + void registerRemoteAnimationsForDisplay(int displayId, in RemoteAnimationDefinition definition); + /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */ void alwaysShowUnsupportedCompileSdkWarning(in ComponentName activity); diff --git a/core/java/android/view/RemoteAnimationAdapter.java b/core/java/android/view/RemoteAnimationAdapter.java index 3c9ce788b706..6f5a85d210af 100644 --- a/core/java/android/view/RemoteAnimationAdapter.java +++ b/core/java/android/view/RemoteAnimationAdapter.java @@ -18,7 +18,6 @@ package android.view; import android.annotation.UnsupportedAppUsage; import android.app.ActivityOptions; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -52,6 +51,7 @@ public class RemoteAnimationAdapter implements Parcelable { private final IRemoteAnimationRunner mRunner; private final long mDuration; private final long mStatusBarTransitionDelay; + private final boolean mChangeNeedsSnapshot; /** @see #getCallingPid */ private int mCallingPid; @@ -59,21 +59,31 @@ public class RemoteAnimationAdapter implements Parcelable { /** * @param runner The interface that gets notified when we actually need to start the animation. * @param duration The duration of the animation. + * @param changeNeedsSnapshot For change transitions, whether this should create a snapshot by + * screenshotting the task. * @param statusBarTransitionDelay The desired delay for all visual animations in the * status bar caused by this app animation in millis. */ @UnsupportedAppUsage public RemoteAnimationAdapter(IRemoteAnimationRunner runner, long duration, - long statusBarTransitionDelay) { + long statusBarTransitionDelay, boolean changeNeedsSnapshot) { mRunner = runner; mDuration = duration; + mChangeNeedsSnapshot = changeNeedsSnapshot; mStatusBarTransitionDelay = statusBarTransitionDelay; } + @UnsupportedAppUsage + public RemoteAnimationAdapter(IRemoteAnimationRunner runner, long duration, + long statusBarTransitionDelay) { + this(runner, duration, statusBarTransitionDelay, false /* changeNeedsSnapshot */); + } + public RemoteAnimationAdapter(Parcel in) { mRunner = IRemoteAnimationRunner.Stub.asInterface(in.readStrongBinder()); mDuration = in.readLong(); mStatusBarTransitionDelay = in.readLong(); + mChangeNeedsSnapshot = in.readBoolean(); } public IRemoteAnimationRunner getRunner() { @@ -88,6 +98,10 @@ public class RemoteAnimationAdapter implements Parcelable { return mStatusBarTransitionDelay; } + public boolean getChangeNeedsSnapshot() { + return mChangeNeedsSnapshot; + } + /** * To be called by system_server to keep track which pid is running this animation. */ @@ -112,6 +126,7 @@ public class RemoteAnimationAdapter implements Parcelable { dest.writeStrongInterface(mRunner); dest.writeLong(mDuration); dest.writeLong(mStatusBarTransitionDelay); + dest.writeBoolean(mChangeNeedsSnapshot); } public static final Creator<RemoteAnimationAdapter> CREATOR diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index e5a66cb4238f..5fabde45db55 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -4399,6 +4399,27 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @Override + public void registerRemoteAnimationsForDisplay(int displayId, + RemoteAnimationDefinition definition) { + mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, + "registerRemoteAnimations"); + definition.setCallingPid(Binder.getCallingPid()); + synchronized (mGlobalLock) { + final ActivityDisplay display = mRootActivityContainer.getActivityDisplay(displayId); + if (display == null) { + Slog.e(TAG, "Couldn't find display with id: " + displayId); + return; + } + final long origId = Binder.clearCallingIdentity(); + try { + display.mDisplayContent.registerRemoteAnimations(definition); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */ @Override public void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) { diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 49308b8f92b4..8f0a7c08fca8 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -76,6 +76,7 @@ public class AppTransitionController { private final WindowManagerService mService; private final DisplayContent mDisplayContent; private final WallpaperController mWallpaperControllerLocked; + private RemoteAnimationDefinition mRemoteAnimationDefinition = null; private final SparseIntArray mTempTransitionReasons = new SparseIntArray(); @@ -85,6 +86,10 @@ public class AppTransitionController { mWallpaperControllerLocked = mDisplayContent.mWallpaperController; } + void registerRemoteAnimations(RemoteAnimationDefinition definition) { + mRemoteAnimationDefinition = definition; + } + /** * Handle application transition for given display. */ @@ -216,6 +221,21 @@ public class AppTransitionController { return mainWindow != null ? mainWindow.mAttrs : null; } + RemoteAnimationAdapter getRemoteAnimationOverride(AppWindowToken animLpToken, int transit, + ArraySet<Integer> activityTypes) { + final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition(); + if (definition != null) { + final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes); + if (adapter != null) { + return adapter; + } + } + if (mRemoteAnimationDefinition == null) { + return null; + } + return mRemoteAnimationDefinition.getAdapter(transit, activityTypes); + } + /** * Overrides the pending transition with the remote animation defined for the transition in the * set of defined remote animations in the app window token. @@ -229,11 +249,8 @@ public class AppTransitionController { if (animLpToken == null) { return; } - final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition(); - if (definition == null) { - return; - } - final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes); + final RemoteAnimationAdapter adapter = + getRemoteAnimationOverride(animLpToken, transit, activityTypes); if (adapter != null) { animLpToken.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote( adapter); diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 65b36a092228..d915e1014411 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -105,6 +105,7 @@ import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; import android.view.IApplicationToken; import android.view.InputApplicationHandle; +import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; @@ -1621,6 +1622,17 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree t.reparent(getSurfaceControl(), mTransitChangeLeash); onAnimationLeashCreated(t, mTransitChangeLeash); + // Skip creating snapshot if this transition is controlled by a remote animator which + // doesn't need it. + ArraySet<Integer> activityTypes = new ArraySet<>(); + activityTypes.add(getActivityType()); + RemoteAnimationAdapter adapter = + mDisplayContent.mAppTransitionController.getRemoteAnimationOverride( + this, TRANSIT_TASK_CHANGE_WINDOWING_MODE, activityTypes); + if (adapter != null && !adapter.getChangeNeedsSnapshot()) { + return; + } + if (mThumbnail == null && getTask() != null) { final TaskSnapshotController snapshotCtrl = mWmService.mTaskSnapshotController; final ArraySet<Task> tasks = new ArraySet<>(); @@ -1639,6 +1651,11 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return mTransitChangeLeash != null || isChangeTransition(mTransit); } + @VisibleForTesting + AppWindowThumbnail getThumbnail() { + return mThumbnail; + } + @Override void checkAppWindowsReadyToShow() { if (allDrawn == mLastAllDrawn) { @@ -2349,7 +2366,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree AnimationAdapter thumbnailAdapter = null; getAnimationBounds(mTmpPoint, mTmpRect); - boolean isChanging = isChangeTransition(transit) && mThumbnail != null; + boolean isChanging = isChangeTransition(transit) && enter; // Delaying animation start isn't compatible with remote animations at all. if (getDisplayContent().mAppTransition.getRemoteAnimationController() != null @@ -2368,11 +2385,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree getDisplayContent().getDisplayInfo(), duration, true /* isAppAnimation */, false /* isThumbnail */), mWmService.mSurfaceAnimationRunner); - thumbnailAdapter = new LocalAnimationAdapter( - new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect, - getDisplayContent().getDisplayInfo(), duration, - true /* isAppAnimation */, true /* isThumbnail */), - mWmService.mSurfaceAnimationRunner); + if (mThumbnail != null) { + thumbnailAdapter = new LocalAnimationAdapter( + new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect, + getDisplayContent().getDisplayInfo(), duration, + true /* isAppAnimation */, true /* isThumbnail */), + mWmService.mSurfaceAnimationRunner); + } mTransit = transit; mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags(); } else { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 8f976e74670d..4ddecfb18c38 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -162,6 +162,7 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InsetsState.InternalInsetType; import android.view.MagnificationSpec; +import android.view.RemoteAnimationDefinition; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; @@ -1091,6 +1092,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mLastWindowForcedOrientation; } + void registerRemoteAnimations(RemoteAnimationDefinition definition) { + mAppTransitionController.registerRemoteAnimations(definition); + } + /** * Temporarily pauses rotation changes until resumed. * diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index f760b39c5332..5f95691d5307 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -322,8 +322,10 @@ class RemoteAnimationController implements DeathRecipient { mStartBounds = new Rect(startBounds); mTmpRect.set(startBounds); mTmpRect.offsetTo(0, 0); - mThumbnailAdapter = - new RemoteAnimationAdapterWrapper(this, new Point(0, 0), mTmpRect); + if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) { + mThumbnailAdapter = + new RemoteAnimationAdapterWrapper(this, new Point(0, 0), mTmpRect); + } } else { mStartBounds = null; } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java new file mode 100644 index 000000000000..19ace3c0336c --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019 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.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import android.os.IBinder; +import android.view.IRemoteAnimationFinishedCallback; +import android.view.IRemoteAnimationRunner; +import android.view.RemoteAnimationAdapter; +import android.view.RemoteAnimationDefinition; +import android.view.RemoteAnimationTarget; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for change transitions + * + * Build/Install/Run: + * atest WmTests:AppChangeTransitionTests + */ +@FlakyTest(detail = "Promote when shown to be stable.") +@SmallTest +public class AppChangeTransitionTests extends WindowTestsBase { + + private TaskStack mStack; + private Task mTask; + private WindowTestUtils.TestAppWindowToken mToken; + + @Before + public void setUp() throws Exception { + mStack = createTaskStackOnDisplay(mDisplayContent); + mTask = createTaskInStack(mStack, 0 /* userId */); + mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent); + mToken.mSkipOnParentSet = false; + + mTask.addChild(mToken, 0); + } + + class TestRemoteAnimationRunner implements IRemoteAnimationRunner { + @Override + public void onAnimationStart(RemoteAnimationTarget[] apps, + IRemoteAnimationFinishedCallback finishedCallback) { + for (RemoteAnimationTarget target : apps) { + assertNotNull(target.startBounds); + } + try { + finishedCallback.onAnimationFinished(); + } catch (Exception e) { + throw new RuntimeException("Something went wrong"); + } + } + + @Override + public void onAnimationCancelled() { + } + + @Override + public IBinder asBinder() { + return null; + } + } + + @Test + public void testModeChangeRemoteAnimatorNoSnapshot() { + RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); + RemoteAnimationAdapter adapter = + new RemoteAnimationAdapter(new TestRemoteAnimationRunner(), 10, 1, false); + definition.addRemoteAnimation(TRANSIT_TASK_CHANGE_WINDOWING_MODE, adapter); + mDisplayContent.registerRemoteAnimations(definition); + + mTask.setWindowingMode(WINDOWING_MODE_FREEFORM); + assertEquals(1, mDisplayContent.mChangingApps.size()); + assertNull(mToken.getThumbnail()); + + waitUntilHandlersIdle(); + mToken.removeImmediately(); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index 9478be90b5c3..b867799c16cb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -77,7 +77,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { MockitoAnnotations.initMocks(this); when(mMockRunner.asBinder()).thenReturn(new Binder()); - mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50); + mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50, true /* changeNeedsSnapshot */); mAdapter.setCallingPid(123); mWm.mH.runWithScissors(() -> mHandler = new TestHandler(null, mClock), 0); mController = new RemoteAnimationController(mWm, mAdapter, mHandler); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java index 44e998b7e62a..2263cf3a02e0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java @@ -152,6 +152,7 @@ public class WindowTestUtils { public static class TestAppWindowToken extends AppWindowToken { boolean mOnTop = false; private Transaction mPendingTransactionOverride; + boolean mSkipOnParentSet = true; private TestAppWindowToken(DisplayContent dc) { super(dc.mWmService, new IApplicationToken.Stub() { @@ -200,7 +201,9 @@ public class WindowTestUtils { @Override void onParentSet() { - // Do nothing. + if (!mSkipOnParentSet) { + super.onParentSet(); + } } @Override |