/* * 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.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.fail; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.IInterface; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import androidx.test.filters.SmallTest; import com.android.server.testutils.OffsettableClock; import com.android.server.testutils.TestHandler; import com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * Build/Install/Run: * atest WmTests:RemoteAnimationControllerTest */ @SmallTest @Presubmit @RunWith(WindowTestRunner.class) public class RemoteAnimationControllerTest extends WindowTestsBase { @Mock SurfaceControl mMockLeash; @Mock SurfaceControl mMockThumbnailLeash; @Mock Transaction mMockTransaction; @Mock OnAnimationFinishedCallback mFinishedCallback; @Mock OnAnimationFinishedCallback mThumbnailFinishedCallback; @Mock IRemoteAnimationRunner mMockRunner; private RemoteAnimationAdapter mAdapter; private RemoteAnimationController mController; private final OffsettableClock mClock = new OffsettableClock.Stopped(); private TestHandler mHandler; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mMockRunner.asBinder()).thenReturn(new Binder()); mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50, true /* changeNeedsSnapshot */); mAdapter.setCallingPidUid(123, 456); runWithScissors(mWm.mH, () -> mHandler = new TestHandler(null, mClock), 0); mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter, mHandler, false /*isActivityEmbedding*/); } private WindowState createAppOverlayWindow() { final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION_OVERLAY, "testOverlayWindow"); win.mActivityRecord = null; win.mHasSurface = true; return win; } @Test public void testForwardsShowBackdrop() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mDisplayContent.mOpeningApps.add(win.mActivityRecord); final WindowState overlayWin = createAppOverlayWindow(); try { final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, true /* showBackdrop */).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN), appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, appsCaptor.getValue().length); final RemoteAnimationTarget app = appsCaptor.getValue()[0]; assertTrue(app.showBackdrop); } finally { mDisplayContent.mOpeningApps.clear(); } } @Test public void testRun() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mDisplayContent.mOpeningApps.add(win.mActivityRecord); final WindowState overlayWin = createAppOverlayWindow(); try { final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN), appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, appsCaptor.getValue().length); final RemoteAnimationTarget app = appsCaptor.getValue()[0]; assertEquals(new Point(50, 100), app.position); assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds); assertEquals(win.mActivityRecord.getPrefixOrderIndex(), app.prefixOrderIndex); assertEquals(win.mActivityRecord.getTask().mTaskId, app.taskId); assertEquals(mMockLeash, app.leash); assertEquals(false, app.isTranslucent); verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y); verify(mMockTransaction).setWindowCrop(mMockLeash, 100, 50); finishedCaptor.getValue().onAnimationFinished(); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), eq(adapter)); assertEquals(0, nonAppsCaptor.getValue().length); } finally { mDisplayContent.mOpeningApps.clear(); } } @Test public void testCancel() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); adapter.onAnimationCancelled(mMockLeash); verify(mMockRunner).onAnimationCancelled(); } @Test public void testTimeout() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); mClock.fastForward(10500); mHandler.timeAdvance(); verify(mMockRunner).onAnimationCancelled(); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), eq(adapter)); } @Test public void testTimeout_scaled() throws Exception { try { mWm.setAnimationScale(2, 5.0f); final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); mClock.fastForward(10500); mHandler.timeAdvance(); verify(mMockRunner, never()).onAnimationCancelled(); mClock.fastForward(52500); mHandler.timeAdvance(); verify(mMockRunner).onAnimationCancelled(); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), eq(adapter)); } finally { mWm.setAnimationScale(2, 1.0f); } } @Test public void testZeroAnimations() throws Exception { mController.goodToGo(TRANSIT_OLD_NONE); verify(mMockRunner, never()).onAnimationStart(anyInt(), any(), any(), any(), any()); verify(mMockRunner).onAnimationCancelled(); } @Test public void testNotReallyStarted() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mController.createRemoteAnimationRecord(win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false); mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); verify(mMockRunner, never()).onAnimationStart(anyInt(), any(), any(), any(), any()); verify(mMockRunner).onAnimationCancelled(); } @Test public void testOneNotStarted() throws Exception { final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1"); final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2"); mController.createRemoteAnimationRecord(win1.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false); final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win2.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN), appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, appsCaptor.getValue().length); assertEquals(mMockLeash, appsCaptor.getValue()[0].leash); } @Test public void testRemovedBeforeStarted() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); win.mActivityRecord.removeImmediately(); mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); verify(mMockRunner, never()).onAnimationStart(anyInt(), any(), any(), any(), any()); verify(mMockRunner).onAnimationCancelled(); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), eq(adapter)); } @Test public void testOpeningTaskWithTopFinishingActivity() { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "win"); final Task task = win.getTask(); final ActivityRecord topFinishing = new ActivityBuilder(mAtm).setTask(task).build(); // Now the task contains: // - Activity[1] (top, finishing, no window) // - Activity[0] (has window) topFinishing.finishing = true; spyOn(mDisplayContent.mAppTransition); doReturn(mController).when(mDisplayContent.mAppTransition).getRemoteAnimationController(); task.applyAnimationUnchecked(null /* lp */, true /* enter */, TRANSIT_OLD_TASK_OPEN, false /* isVoiceInteraction */, null /* sources */); mController.goodToGo(TRANSIT_OLD_TASK_OPEN); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); try { verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN), appsCaptor.capture(), any(), any(), any()); } catch (RemoteException ignored) { } assertEquals(1, appsCaptor.getValue().length); assertEquals(RemoteAnimationTarget.MODE_OPENING, appsCaptor.getValue()[0].mode); } @Test public void testChangeToSmallerSize() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mDisplayContent.mChangingContainers.add(win.mActivityRecord); try { final RemoteAnimationRecord record = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), new Rect(0, 0, 200, 200), false); assertNotNull(record.mThumbnailAdapter); ((AnimationAdapter) record.mAdapter) .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mFinishedCallback); ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback); mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE), appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, appsCaptor.getValue().length); final RemoteAnimationTarget app = appsCaptor.getValue()[0]; assertEquals(RemoteAnimationTarget.MODE_CHANGING, app.mode); assertEquals(new Point(50, 100), app.position); assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds); assertEquals(new Rect(0, 0, 200, 200), app.startBounds); assertEquals(mMockLeash, app.leash); assertEquals(mMockThumbnailLeash, app.startLeash); assertEquals(false, app.isTranslucent); verify(mMockTransaction).setPosition( mMockLeash, app.startBounds.left, app.startBounds.top); verify(mMockTransaction).setWindowCrop( mMockLeash, app.startBounds.width(), app.startBounds.height()); verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0); verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, app.startBounds.width(), app.startBounds.height()); finishedCaptor.getValue().onAnimationFinished(); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mAdapter)); verify(mThumbnailFinishedCallback).onAnimationFinished( eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter)); } finally { mDisplayContent.mChangingContainers.clear(); } } @Test public void testChangeTolargerSize() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mDisplayContent.mChangingContainers.add(win.mActivityRecord); try { final RemoteAnimationRecord record = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(0, 0), null, new Rect(0, 0, 200, 200), new Rect(50, 100, 150, 150), false); assertNotNull(record.mThumbnailAdapter); ((AnimationAdapter) record.mAdapter) .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mFinishedCallback); ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback); mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE), appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, appsCaptor.getValue().length); final RemoteAnimationTarget app = appsCaptor.getValue()[0]; assertEquals(RemoteAnimationTarget.MODE_CHANGING, app.mode); assertEquals(new Point(0, 0), app.position); assertEquals(new Rect(0, 0, 200, 200), app.sourceContainerBounds); assertEquals(new Rect(50, 100, 150, 150), app.startBounds); assertEquals(mMockLeash, app.leash); assertEquals(mMockThumbnailLeash, app.startLeash); assertEquals(false, app.isTranslucent); verify(mMockTransaction).setPosition( mMockLeash, app.startBounds.left, app.startBounds.top); verify(mMockTransaction).setWindowCrop( mMockLeash, app.startBounds.width(), app.startBounds.height()); verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0); verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, app.startBounds.width(), app.startBounds.height()); finishedCaptor.getValue().onAnimationFinished(); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mAdapter)); verify(mThumbnailFinishedCallback).onAnimationFinished( eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter)); } finally { mDisplayContent.mChangingContainers.clear(); } } @Test public void testChangeToDifferentPosition() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mDisplayContent.mChangingContainers.add(win.mActivityRecord); try { final RemoteAnimationRecord record = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(100, 100), null, new Rect(150, 150, 400, 400), new Rect(50, 100, 150, 150), false); assertNotNull(record.mThumbnailAdapter); ((AnimationAdapter) record.mAdapter) .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mFinishedCallback); ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback); mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE), appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, appsCaptor.getValue().length); final RemoteAnimationTarget app = appsCaptor.getValue()[0]; assertEquals(RemoteAnimationTarget.MODE_CHANGING, app.mode); assertEquals(new Point(100, 100), app.position); assertEquals(new Rect(150, 150, 400, 400), app.sourceContainerBounds); assertEquals(new Rect(50, 100, 150, 150), app.startBounds); assertEquals(mMockLeash, app.leash); assertEquals(mMockThumbnailLeash, app.startLeash); assertEquals(false, app.isTranslucent); verify(mMockTransaction).setPosition( mMockLeash, app.position.x + app.startBounds.left - app.screenSpaceBounds.left, app.position.y + app.startBounds.top - app.screenSpaceBounds.top); verify(mMockTransaction).setWindowCrop( mMockLeash, app.startBounds.width(), app.startBounds.height()); verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0); verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, app.startBounds.width(), app.startBounds.height()); finishedCaptor.getValue().onAnimationFinished(); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mAdapter)); verify(mThumbnailFinishedCallback).onAnimationFinished( eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter)); } finally { mDisplayContent.mChangingContainers.clear(); } } @Test public void testWallpaperIncluded_expectTarget() throws Exception { final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */); spyOn(mDisplayContent.mWallpaperController); doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible(); final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mDisplayContent.mOpeningApps.add(win.mActivityRecord); try { final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN), appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, wallpapersCaptor.getValue().length); } finally { mDisplayContent.mOpeningApps.clear(); } } @Test public void testWallpaperAnimatorCanceled_expectAnimationKeepsRunning() throws Exception { final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */); spyOn(mDisplayContent.mWallpaperController); doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible(); final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mDisplayContent.mOpeningApps.add(win.mActivityRecord); try { final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor nonAPpsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN), appsCaptor.capture(), wallpapersCaptor.capture(), nonAPpsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, wallpapersCaptor.getValue().length); // Cancel the wallpaper window animator and ensure the runner is not canceled wallpaperWindowToken.cancelAnimation(); verify(mMockRunner, never()).onAnimationCancelled(); } finally { mDisplayContent.mOpeningApps.clear(); } } @Test public void testNonAppIncluded_keygaurdGoingAway() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mDisplayContent.mOpeningApps.add(win.mActivityRecord); // Add overlay window hidden by the keyguard. final WindowState overlayWin = createAppOverlayWindow(); overlayWin.hide(false /* doAnimation */, false /* requestAnim */); try { final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_KEYGUARD_GOING_AWAY), appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, appsCaptor.getValue().length); final RemoteAnimationTarget app = appsCaptor.getValue()[0]; assertEquals(new Point(50, 100), app.position); assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds); assertEquals(win.mActivityRecord.getPrefixOrderIndex(), app.prefixOrderIndex); assertEquals(win.mActivityRecord.getTask().mTaskId, app.taskId); assertEquals(mMockLeash, app.leash); assertEquals(false, app.isTranslucent); verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y); verify(mMockTransaction).setWindowCrop(mMockLeash, 100, 50); finishedCaptor.getValue().onAnimationFinished(); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), eq(adapter)); assertEquals(1, nonAppsCaptor.getValue().length); } finally { mDisplayContent.mOpeningApps.clear(); } } @Test public void testNonAppIncluded_keygaurdGoingAwayToWallpaper() throws Exception { final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */); spyOn(mDisplayContent.mWallpaperController); doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible(); final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mDisplayContent.mOpeningApps.add(win.mActivityRecord); // Add overlay window hidden by the keyguard. final WindowState overlayWin = createAppOverlayWindow(); overlayWin.hide(false /* doAnimation */, false /* requestAnim */); try { final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER), appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, wallpapersCaptor.getValue().length); assertEquals(1, nonAppsCaptor.getValue().length); } finally { mDisplayContent.mOpeningApps.clear(); } } @Test public void testNonAppTarget_sendNavBar() throws Exception { final int transit = TRANSIT_OLD_TASK_OPEN; final AnimationAdapter adapter = setupForNonAppTargetNavBar(transit, true); final ArgumentCaptor nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); verify(mMockRunner).onAnimationStart(eq(transit), any(), any(), nonAppsCaptor.capture(), finishedCaptor.capture()); boolean containNavTarget = false; for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { containNavTarget = true; break; } } assertTrue(containNavTarget); assertEquals(1, mController.mPendingNonAppAnimations.size()); final NonAppWindowAnimationAdapter nonAppAdapter = mController.mPendingNonAppAnimations.get(0); spyOn(nonAppAdapter.getLeashFinishedCallback()); finishedCaptor.getValue().onAnimationFinished(); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), eq(adapter)); verify(nonAppAdapter.getLeashFinishedCallback()) .onAnimationFinished(nonAppAdapter.getLastAnimationType(), nonAppAdapter); } @Test public void testNonAppTarget_notSendNavBar_notAttachToApp() throws Exception { final int transit = TRANSIT_OLD_TASK_OPEN; setupForNonAppTargetNavBar(transit, false); final ArgumentCaptor nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); verify(mMockRunner).onAnimationStart(eq(transit), any(), any(), nonAppsCaptor.capture(), any()); for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { fail("Non-app animation target must not contain navbar"); } } } @Test public void testNonAppTarget_notSendNavBar_controlledByFadeRotation() throws Exception { final AsyncRotationController mockController = mock(AsyncRotationController.class); doReturn(mockController).when(mDisplayContent).getAsyncRotationController(); final int transit = TRANSIT_OLD_TASK_OPEN; setupForNonAppTargetNavBar(transit, true); final ArgumentCaptor nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); verify(mMockRunner).onAnimationStart(eq(transit), any(), any(), nonAppsCaptor.capture(), any()); for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { fail("Non-app animation target must not contain navbar"); } } } @Test public void testNonAppTarget_notSendNavBar_controlledByRecents() throws Exception { final RecentsAnimationController mockController = mock(RecentsAnimationController.class); doReturn(mockController).when(mWm).getRecentsAnimationController(); final int transit = TRANSIT_OLD_TASK_OPEN; setupForNonAppTargetNavBar(transit, true); final ArgumentCaptor nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); verify(mMockRunner).onAnimationStart(eq(transit), any(), any(), nonAppsCaptor.capture(), any()); for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { fail("Non-app animation target must not contain navbar"); } } } @SetupWindows(addWindows = W_INPUT_METHOD) @Test public void testLaunchRemoteAnimationWithoutImeBehind() { final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1"); final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2"); // Simulating win1 has shown IME and being IME layering/input target mDisplayContent.setImeLayeringTarget(win1); mDisplayContent.setImeInputTarget(win1); mImeWindow.mWinAnimator.mSurfaceController = mock(WindowSurfaceController.class); mImeWindow.mWinAnimator.hide(mDisplayContent.getPendingTransaction(), "test"); spyOn(mDisplayContent); doReturn(true).when(mImeWindow.mWinAnimator.mSurfaceController).hasSurface(); doReturn(true).when(mImeWindow.mWinAnimator.mSurfaceController) .prepareToShowInTransaction(any(), anyFloat()); makeWindowVisibleAndDrawn(mImeWindow); assertTrue(mImeWindow.isOnScreen()); assertFalse(mImeWindow.isParentWindowHidden()); try { // Simulating now win1 is being covered by the lockscreen which has no surface, // and then launching an activity win2 with the remote animation win1.mHasSurface = false; win1.mActivityRecord.setVisibility(false); mDisplayContent.mOpeningApps.add(win2.mActivityRecord); final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win2.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mDisplayContent.applySurfaceChangesTransaction(); mController.goodToGo(TRANSIT_OLD_TASK_OPEN); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN), any(), any(), any(), any()); // Verify the IME window won't apply surface change transaction with forAllImeWindows verify(mDisplayContent, never()).forAllImeWindows(any(), eq(true)); } catch (Exception e) { // no-op } finally { mDisplayContent.mOpeningApps.clear(); } } private AnimationAdapter setupForNonAppTargetNavBar(int transit, boolean shouldAttachNavBar) { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mDisplayContent.mOpeningApps.add(win.mActivityRecord); final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar"); mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs); final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); spyOn(policy); doReturn(shouldAttachNavBar).when(policy).shouldAttachNavBarToAppDuringTransition(); final AnimationAdapter adapter = mController.createRemoteAnimationRecord( win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(transit); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); return adapter; } private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { verify(binder, atLeast(0)).asBinder(); verifyNoMoreInteractions(binder); } }