blob: c3279bf057377ed9f4fd00805e727b161617f8f4 [file] [log] [blame]
/*
* Copyright (C) 2016 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_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
import static android.view.WindowManager.TRANSIT_OPEN;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Test class for {@link AppTransition}.
*
* Build/Install/Run:
* atest WmTests:AppTransitionTests
*/
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
public class AppTransitionTests extends WindowTestsBase {
private DisplayContent mDc;
@Before
public void setUp() throws Exception {
doNothing().when(mWm.mRoot).performSurfacePlacement();
mDc = mWm.getDefaultDisplayContentLocked();
}
@Test
public void testKeyguardOverride() {
final DisplayContent dc = createNewDisplay(Display.STATE_ON);
final ActivityRecord activity = createActivityRecord(dc);
mDc.prepareAppTransition(TRANSIT_OPEN);
mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY);
mDc.mOpeningApps.add(activity);
assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
AppTransitionController.getTransitCompatType(mDc.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
null /* wallpaperTarget */, null /* oldWallpaper */,
false /*skipAppTransitionAnimation*/));
}
@Test
public void testKeyguardKeep() {
final DisplayContent dc = createNewDisplay(Display.STATE_ON);
final ActivityRecord activity = createActivityRecord(dc);
mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY);
mDc.prepareAppTransition(TRANSIT_OPEN);
mDc.mOpeningApps.add(activity);
assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
AppTransitionController.getTransitCompatType(mDc.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
null /* wallpaperTarget */, null /* oldWallpaper */,
false /*skipAppTransitionAnimation*/));
}
@Test
public void testCrashing() {
final DisplayContent dc = createNewDisplay(Display.STATE_ON);
final ActivityRecord activity = createActivityRecord(dc);
mDc.prepareAppTransition(TRANSIT_OPEN);
mDc.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
mDc.mClosingApps.add(activity);
assertEquals(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
AppTransitionController.getTransitCompatType(mDc.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
null /* wallpaperTarget */, null /* oldWallpaper */,
false /*skipAppTransitionAnimation*/));
}
@Test
public void testKeepKeyguard_withCrashing() {
final DisplayContent dc = createNewDisplay(Display.STATE_ON);
final ActivityRecord activity = createActivityRecord(dc);
mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY);
mDc.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
mDc.mClosingApps.add(activity);
assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
AppTransitionController.getTransitCompatType(mDc.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
null /* wallpaperTarget */, null /* oldWallpaper */,
false /*skipAppTransitionAnimation*/));
}
@Test
public void testSkipTransitionAnimation() {
final DisplayContent dc = createNewDisplay(Display.STATE_ON);
final ActivityRecord activity = createActivityRecord(dc);
mDc.prepareAppTransition(TRANSIT_OPEN);
mDc.prepareAppTransition(TRANSIT_CLOSE);
mDc.mClosingApps.add(activity);
assertEquals(TRANSIT_OLD_UNSET,
AppTransitionController.getTransitCompatType(mDc.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
null /* wallpaperTarget */, null /* oldWallpaper */,
true /*skipAppTransitionAnimation*/));
}
@Test
public void testAppTransitionStateForMultiDisplay() {
// Create 2 displays & presume both display the state is ON for ready to display & animate.
final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
final DisplayContent dc2 = createNewDisplay(Display.STATE_ON);
// Create 2 app window tokens to represent 2 activity window.
final ActivityRecord activity1 = createActivityRecord(dc1);
final ActivityRecord activity2 = createActivityRecord(dc2);
activity1.allDrawn = true;
activity1.startingDisplayed = true;
activity1.startingMoved = true;
// Simulate activity resume / finish flows to prepare app transition & set visibility,
// make sure transition is set as expected for each display.
dc1.prepareAppTransition(TRANSIT_OPEN);
dc2.prepareAppTransition(TRANSIT_CLOSE);
// One activity window is visible for resuming & the other activity window is invisible
// for finishing in different display.
activity1.setVisibility(true, false);
activity2.setVisibility(false, false);
// Make sure each display is in animating stage.
assertTrue(dc1.mOpeningApps.size() > 0);
assertTrue(dc2.mClosingApps.size() > 0);
assertTrue(dc1.isAppTransitioning());
assertTrue(dc2.isAppTransitioning());
}
@Test
public void testCleanAppTransitionWhenRootTaskReparent() {
// Create 2 displays & presume both display the state is ON for ready to display & animate.
final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
final DisplayContent dc2 = createNewDisplay(Display.STATE_ON);
final Task rootTask1 = createTask(dc1);
final Task task1 = createTaskInRootTask(rootTask1, 0 /* userId */);
final ActivityRecord activity1 = createNonAttachedActivityRecord(dc1);
task1.addChild(activity1, 0);
// Simulate same app is during opening / closing transition set stage.
dc1.mClosingApps.add(activity1);
assertTrue(dc1.mClosingApps.size() > 0);
dc1.prepareAppTransition(TRANSIT_OPEN);
assertTrue(dc1.mAppTransition.containsTransitRequest(TRANSIT_OPEN));
assertTrue(dc1.mAppTransition.isTransitionSet());
dc1.mOpeningApps.add(activity1);
assertTrue(dc1.mOpeningApps.size() > 0);
// Move root task to another display.
rootTask1.reparent(dc2.getDefaultTaskDisplayArea(), true);
// Verify if token are cleared from both pending transition list in former display.
assertFalse(dc1.mOpeningApps.contains(activity1));
assertFalse(dc1.mOpeningApps.contains(activity1));
}
@Test
public void testLoadAnimationSafely() {
DisplayContent dc = createNewDisplay(Display.STATE_ON);
assertNull(dc.mAppTransition.loadAnimationSafely(
getInstrumentation().getTargetContext(), -1));
}
@Test
public void testCancelRemoteAnimationWhenFreeze() {
final DisplayContent dc = createNewDisplay(Display.STATE_ON);
doReturn(false).when(dc).onDescendantOrientationChanged(any());
final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
dc, "exiting app");
final ActivityRecord exitingActivity= exitingAppWindow.mActivityRecord;
// Wait until everything in animation handler get executed to prevent the exiting window
// from being removed during WindowSurfacePlacer Traversal.
waitUntilHandlersIdle();
// Set a remote animator.
final TestRemoteAnimationRunner runner = new TestRemoteAnimationRunner();
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
runner, 100, 50, true /* changeNeedsSnapshot */);
// RemoteAnimationController will tracking RemoteAnimationAdapter's caller with calling pid.
adapter.setCallingPidUid(123, 456);
// Simulate activity finish flows to prepare app transition & set visibility,
// make sure transition is set as expected.
dc.prepareAppTransition(TRANSIT_CLOSE);
assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_CLOSE));
dc.mAppTransition.overridePendingAppTransitionRemote(adapter);
exitingActivity.setVisibility(false, false);
assertTrue(dc.mClosingApps.size() > 0);
// Make sure window is in animating stage before freeze, and cancel after freeze.
assertTrue(dc.isAppTransitioning());
assertFalse(runner.mCancelled);
dc.mAppTransition.freeze();
assertFalse(dc.isAppTransitioning());
assertTrue(runner.mCancelled);
}
@Test
public void testGetAnimationStyleResId() {
// Verify getAnimationStyleResId will return as LayoutParams.windowAnimations when without
// specifying window type.
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
attrs.windowAnimations = 0x12345678;
assertEquals(attrs.windowAnimations, mDc.mAppTransition.getAnimationStyleResId(attrs));
// Verify getAnimationStyleResId will return system resource Id when the window type is
// starting window.
attrs.type = TYPE_APPLICATION_STARTING;
assertEquals(mDc.mAppTransition.getDefaultWindowAnimationStyleResId(),
mDc.mAppTransition.getAnimationStyleResId(attrs));
}
private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
boolean mCancelled = false;
@Override
public void onAnimationStart(@WindowManager.TransitionOldType int transit,
RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
}
@Override
public void onAnimationCancelled() {
mCancelled = true;
}
@Override
public IBinder asBinder() {
return null;
}
}
}