1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
23 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
24 
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertFalse;
27 import static org.junit.Assert.assertNotNull;
28 import static org.junit.Assert.assertNull;
29 import static org.junit.Assert.assertTrue;
30 
31 import android.os.IBinder;
32 import android.platform.test.annotations.Presubmit;
33 import android.view.Display;
34 import android.view.IRemoteAnimationFinishedCallback;
35 import android.view.IRemoteAnimationRunner;
36 import android.view.RemoteAnimationAdapter;
37 import android.view.RemoteAnimationDefinition;
38 import android.view.RemoteAnimationTarget;
39 import android.view.WindowManager;
40 
41 import androidx.test.filters.SmallTest;
42 
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 
46 /**
47  * Tests for change transitions
48  *
49  * Build/Install/Run:
50  *  atest WmTests:AppChangeTransitionTests
51  */
52 @SmallTest
53 @Presubmit
54 @RunWith(WindowTestRunner.class)
55 public class AppChangeTransitionTests extends WindowTestsBase {
56 
57     private Task mTask;
58     private ActivityRecord mActivity;
59 
setUpOnDisplay(DisplayContent dc)60     public void setUpOnDisplay(DisplayContent dc) {
61         mActivity = createActivityRecord(dc, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
62         mTask = mActivity.getTask();
63 
64         // Set a remote animator with snapshot disabled. Snapshots don't work in wmtests.
65         RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
66         RemoteAnimationAdapter adapter =
67                 new RemoteAnimationAdapter(new TestRemoteAnimationRunner(), 10, 1, false);
68         definition.addRemoteAnimation(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE, adapter);
69         dc.registerRemoteAnimations(definition);
70     }
71 
72     class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
73         @Override
onAnimationStart(@indowManager.TransitionOldType int transit, RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback)74         public void onAnimationStart(@WindowManager.TransitionOldType int transit,
75                 RemoteAnimationTarget[] apps,
76                 RemoteAnimationTarget[] wallpapers,
77                 RemoteAnimationTarget[] nonApps,
78                 IRemoteAnimationFinishedCallback finishedCallback) {
79             for (RemoteAnimationTarget target : apps) {
80                 assertNotNull(target.startBounds);
81             }
82             try {
83                 finishedCallback.onAnimationFinished();
84             } catch (Exception e) {
85                 throw new RuntimeException("Something went wrong");
86             }
87         }
88 
89         @Override
onAnimationCancelled()90         public void onAnimationCancelled() {
91         }
92 
93         @Override
asBinder()94         public IBinder asBinder() {
95             return null;
96         }
97     }
98 
99     @Test
testModeChangeRemoteAnimatorNoSnapshot()100     public void testModeChangeRemoteAnimatorNoSnapshot() {
101         // setup currently defaults to no snapshot.
102         setUpOnDisplay(mDisplayContent);
103 
104         mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
105         assertEquals(1, mDisplayContent.mChangingContainers.size());
106 
107         // Verify we are in a change transition, but without a snapshot.
108         // Though, the test will actually have crashed by now if a snapshot is attempted.
109         assertNull(mTask.mSurfaceFreezer.mSnapshot);
110         assertTrue(mTask.isInChangeTransition());
111 
112         waitUntilHandlersIdle();
113         mActivity.removeImmediately();
114     }
115 
116     @Test
testCancelPendingChangeOnRemove()117     public void testCancelPendingChangeOnRemove() {
118         // setup currently defaults to no snapshot.
119         setUpOnDisplay(mDisplayContent);
120 
121         mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
122         assertEquals(1, mDisplayContent.mChangingContainers.size());
123         assertTrue(mTask.isInChangeTransition());
124 
125         // Removing the app-token from the display should clean-up the
126         // the change leash.
127         mDisplayContent.removeAppToken(mActivity.token);
128         assertEquals(0, mDisplayContent.mChangingContainers.size());
129         assertFalse(mTask.isInChangeTransition());
130 
131         waitUntilHandlersIdle();
132         mActivity.removeImmediately();
133     }
134 
135     @Test
testNoChangeOnOldDisplayWhenMoveDisplay()136     public void testNoChangeOnOldDisplayWhenMoveDisplay() {
137         mDisplayContent.getDefaultTaskDisplayArea().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
138         final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
139         dc1.getDefaultTaskDisplayArea().setWindowingMode(WINDOWING_MODE_FREEFORM);
140         setUpOnDisplay(dc1);
141 
142         assertEquals(WINDOWING_MODE_FREEFORM, mTask.getWindowingMode());
143 
144         // Reparenting to a display with different windowing mode may trigger
145         // a change transition internally, but it should be cleaned-up once
146         // the display change is complete.
147         mTask.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true);
148 
149         assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode());
150 
151         // Make sure the change transition is not the old display
152         assertFalse(dc1.mChangingContainers.contains(mTask));
153 
154         waitUntilHandlersIdle();
155         mActivity.removeImmediately();
156     }
157 
158     @Test
testCancelPendingChangeOnHide()159     public void testCancelPendingChangeOnHide() {
160         // setup currently defaults to no snapshot.
161         setUpOnDisplay(mDisplayContent);
162 
163         mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
164         assertEquals(1, mDisplayContent.mChangingContainers.size());
165         assertTrue(mTask.isInChangeTransition());
166 
167         // Changing visibility should cancel the change transition and become closing
168         mActivity.setVisibility(false);
169         assertEquals(0, mDisplayContent.mChangingContainers.size());
170         assertFalse(mTask.isInChangeTransition());
171 
172         waitUntilHandlersIdle();
173         mActivity.removeImmediately();
174     }
175 }
176