1 /*
2  * Copyright (C) 2021 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 android.server.wm.taskfragment;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
20 import static android.server.wm.WindowManagerState.STATE_RESUMED;
21 import static android.server.wm.WindowManagerState.STATE_STOPPED;
22 import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE;
23 import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CLOSE;
24 import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_OPEN;
25 
26 import static com.google.common.truth.Truth.assertThat;
27 import static com.google.common.truth.Truth.assertWithMessage;
28 
29 import android.app.Activity;
30 import android.content.ComponentName;
31 import android.content.Intent;
32 import android.graphics.Rect;
33 import android.os.Binder;
34 import android.os.Bundle;
35 import android.os.IBinder;
36 import android.platform.test.annotations.Presubmit;
37 import android.server.wm.MetricsActivity;
38 import android.server.wm.WindowManagerState.Task;
39 import android.server.wm.WindowManagerState.TaskFragment;
40 import android.server.wm.WindowManagerTestBase;
41 import android.window.TaskFragmentCreationParams;
42 import android.window.TaskFragmentInfo;
43 import android.window.TaskFragmentOrganizer;
44 import android.window.WindowContainerTransaction;
45 
46 import com.android.compatibility.common.util.ApiTest;
47 
48 import org.junit.Test;
49 
50 /**
51  * Tests that verify the behavior of {@link TaskFragmentOrganizer}.
52  *
53  * Build/Install/Run:
54  *     atest CtsWindowManagerDeviceTaskFragment:TaskFragmentOrganizerTest
55  */
56 @Presubmit
57 @android.server.wm.annotation.Group2
58 public class TaskFragmentOrganizerTest extends TaskFragmentOrganizerTestBase {
59     private final ComponentName mLaunchingActivity =
60             new ComponentName(mContext, MetricsActivity.class);
61 
62     /**
63      * Verifies the behavior of
64      * {@link WindowContainerTransaction#createTaskFragment(TaskFragmentCreationParams)} to create
65      * TaskFragment.
66      */
67     @Test
testCreateTaskFragment()68     public void testCreateTaskFragment() {
69         mWmState.computeState(mOwnerActivityName);
70         Task parentTask = mWmState.getRootTask(mOwnerActivity.getTaskId());
71         final int originalTaskFragCount = parentTask.getTaskFragments().size();
72 
73         final IBinder taskFragToken = new Binder();
74         final Rect bounds = new Rect(0, 0, 1000, 1000);
75         final int windowingMode = WINDOWING_MODE_MULTI_WINDOW;
76         final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
77                 mTaskFragmentOrganizer.getOrganizerToken(), taskFragToken, mOwnerToken)
78                 .setInitialRelativeBounds(bounds)
79                 .setWindowingMode(windowingMode)
80                 .build();
81         final WindowContainerTransaction wct = new WindowContainerTransaction()
82                 .createTaskFragment(params);
83         mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_OPEN,
84                 false /* shouldApplyIndependently */);
85 
86         mTaskFragmentOrganizer.waitForTaskFragmentCreated();
87 
88         final TaskFragmentInfo info = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken);
89 
90         assertEmptyTaskFragment(info, taskFragToken);
91         assertThat(info.getConfiguration().windowConfiguration.getBounds()).isEqualTo(bounds);
92         assertThat(info.getWindowingMode()).isEqualTo(windowingMode);
93 
94         mWmState.computeState(mOwnerActivityName);
95         parentTask = mWmState.getRootTask(mOwnerActivity.getTaskId());
96         final int curTaskFragCount = parentTask.getTaskFragments().size();
97 
98         assertWithMessage("There must be a TaskFragment created under Task#"
99                 + mOwnerTaskId).that(curTaskFragCount - originalTaskFragCount)
100                 .isEqualTo(1);
101     }
102 
103     /**
104      * Verifies the behavior of
105      * {@link WindowContainerTransaction#reparentActivityToTaskFragment(IBinder, IBinder)} to
106      * reparent {@link Activity} to TaskFragment.
107      */
108     @Test
testReparentActivity()109     public void testReparentActivity() {
110         mWmState.computeState(mOwnerActivityName);
111 
112         final TaskFragmentCreationParams params = generateTaskFragCreationParams();
113         final IBinder taskFragToken = params.getFragmentToken();
114         final WindowContainerTransaction wct = new WindowContainerTransaction()
115                 .createTaskFragment(params)
116                 .reparentActivityToTaskFragment(taskFragToken, mOwnerToken);
117         mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_CHANGE,
118                 false /* shouldApplyIndependently */);
119 
120         mTaskFragmentOrganizer.waitForTaskFragmentCreated();
121 
122         assertNotEmptyTaskFragment(mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken),
123                 taskFragToken, mOwnerToken);
124 
125         mWmState.waitForActivityState(mOwnerActivityName, STATE_RESUMED);
126 
127         final Task parentTask = mWmState.getTaskByActivity(mOwnerActivityName);
128         final TaskFragment taskFragment = mWmState.getTaskFragmentByActivity(mOwnerActivityName);
129 
130         // Assert window hierarchy must be as follows
131         // - owner Activity's Task (parentTask)
132         //   - taskFragment
133         //     - owner Activity
134         assertWindowHierarchy(parentTask, taskFragment, mWmState.getActivity(mOwnerActivityName));
135     }
136 
137     /**
138      * Verifies the behavior of
139      * {@link WindowContainerTransaction#startActivityInTaskFragment(IBinder, IBinder, Intent,
140      * Bundle)} to start Activity in TaskFragment without creating new Task.
141      */
142     @Test
testStartActivityInTaskFragment_reuseTask()143     public void testStartActivityInTaskFragment_reuseTask() {
144         final TaskFragmentCreationParams params = generateTaskFragCreationParams();
145         final IBinder taskFragToken = params.getFragmentToken();
146         final WindowContainerTransaction wct = new WindowContainerTransaction()
147                 .createTaskFragment(params)
148                 .startActivityInTaskFragment(taskFragToken, mOwnerToken,
149                         new Intent().setComponent(mLaunchingActivity), null /* activityOptions */);
150         mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_OPEN,
151                 false /* shouldApplyIndependently */);
152 
153         mTaskFragmentOrganizer.waitForTaskFragmentCreated();
154 
155         TaskFragmentInfo info = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken);
156         assertNotEmptyTaskFragment(info, taskFragToken);
157 
158         mWmState.waitForActivityState(mLaunchingActivity, STATE_RESUMED);
159 
160         Task parentTask = mWmState.getTaskByActivity(mOwnerActivityName);
161         TaskFragment taskFragment = mWmState.getTaskFragmentByActivity(mLaunchingActivity);
162 
163         // Assert window hierarchy must be as follows
164         // - owner Activity's Task (parentTask)
165         //   - taskFragment
166         //     - LAUNCHING_ACTIVITY
167         //   - owner Activity
168         assertWindowHierarchy(parentTask, taskFragment, mWmState.getActivity(mLaunchingActivity));
169         assertWindowHierarchy(parentTask, mWmState.getActivity(mOwnerActivityName));
170         assertWithMessage("The owner Activity's Task must be reused as"
171                 + " the launching Activity's Task.").that(parentTask)
172                 .isEqualTo(mWmState.getTaskByActivity(mLaunchingActivity));
173     }
174 
175     /**
176      * Verifies the behavior of {@link WindowContainerTransaction#deleteTaskFragment} to remove the
177      * organized TaskFragment.
178      */
179     @Test
180     @ApiTest(apis = {
181             "android.window.WindowContainerTransaction#deleteTaskFragment"})
testDeleteTaskFragment()182     public void testDeleteTaskFragment() {
183         final TaskFragmentInfo taskFragmentInfo = createTaskFragment(null);
184         final IBinder taskFragToken = taskFragmentInfo.getFragmentToken();
185         assertEmptyTaskFragment(taskFragmentInfo, taskFragmentInfo.getFragmentToken());
186 
187         mWmState.computeState(mOwnerActivityName);
188         final int originalTaskFragCount = mWmState.getTaskByActivity(mOwnerActivityName)
189                 .getTaskFragments().size();
190 
191         WindowContainerTransaction wct = new WindowContainerTransaction()
192                 .deleteTaskFragment(taskFragToken);
193         mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_CLOSE,
194                 false /* shouldApplyIndependently */);
195 
196         mTaskFragmentOrganizer.waitForTaskFragmentRemoved();
197 
198         assertEmptyTaskFragment(mTaskFragmentOrganizer.getRemovedTaskFragmentInfo(taskFragToken),
199                 taskFragToken);
200 
201         mWmState.computeState(mOwnerActivityName);
202         final int currTaskFragCount = mWmState.getTaskByActivity(mOwnerActivityName)
203                 .getTaskFragments().size();
204         assertWithMessage("TaskFragment with token " + taskFragToken + " must be"
205                 + " removed.").that(originalTaskFragCount - currTaskFragCount).isEqualTo(1);
206     }
207 
208     /**
209      * Verifies the behavior of {@link WindowContainerTransaction#deleteTaskFragment} to remove the
210      * organized TaskFragment with activity embedded.
211      */
212     @Test
213     @ApiTest(apis = {
214             "android.window.WindowContainerTransaction#deleteTaskFragment",
215             "android.window.TaskFragmentOrganizer#onTransactionReady"})
testDeleteTaskFragmentWithActivity()216     public void testDeleteTaskFragmentWithActivity() {
217         final TaskFragmentInfo taskFragmentInfo = createTaskFragment(mLaunchingActivity);
218         final IBinder taskFragToken = taskFragmentInfo.getFragmentToken();
219         assertNotEmptyTaskFragment(taskFragmentInfo, taskFragmentInfo.getFragmentToken());
220 
221         mWmState.computeState(mOwnerActivityName);
222         final int originalTaskFragCount = mWmState.getTaskByActivity(mOwnerActivityName)
223                 .getTaskFragments().size();
224 
225         WindowContainerTransaction wct = new WindowContainerTransaction()
226                 .deleteTaskFragment(taskFragToken);
227         mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_CLOSE,
228                 false /* shouldApplyIndependently */);
229 
230         mTaskFragmentOrganizer.waitForTaskFragmentRemoved();
231 
232         assertEmptyTaskFragment(mTaskFragmentOrganizer.getRemovedTaskFragmentInfo(taskFragToken),
233                 taskFragToken);
234 
235         mWmState.computeState(mOwnerActivityName);
236         final int currTaskFragCount = mWmState.getTaskByActivity(mOwnerActivityName)
237                 .getTaskFragments().size();
238         assertWithMessage("TaskFragment with token " + taskFragToken + " must be"
239                 + " removed.").that(originalTaskFragCount - currTaskFragCount).isEqualTo(1);
240     }
241 
242     /**
243      * Verifies the behavior of {@link WindowContainerTransaction#finishActivity(IBinder)} to finish
244      * an Activity.
245      */
246     @Test
247     @ApiTest(apis = {
248             "android.window.TaskFragmentOrganizer#applyTransaction",
249             "android.window.WindowContainerTransaction#finishActivity"})
testFinishActivity()250     public void testFinishActivity() {
251         final Activity activity = startNewActivity(MetricsActivity.class);
252         // Make sure mLaunchingActivity is mapping to the correct component that is started.
253         mWmState.waitAndAssertActivityState(mLaunchingActivity, STATE_RESUMED);
254 
255         final WindowContainerTransaction wct = new WindowContainerTransaction()
256                 .finishActivity(getActivityToken(activity));
257         mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_CLOSE,
258                 false /* shouldApplyIndependently */);
259 
260         mWmState.waitAndAssertActivityRemoved(mLaunchingActivity);
261     }
262 
263     /**
264      * Verifies the visibility of an activity behind a TaskFragment that has the same
265      * bounds of the host Task.
266      */
267     @Test
testActivityVisibilityBehindTaskFragment()268     public void testActivityVisibilityBehindTaskFragment() {
269         // Start an activity and reparent it to a TaskFragment.
270         final Activity embeddedActivity =
271                 WindowManagerTestBase.startActivity(MetricsActivity.class);
272         final IBinder embeddedActivityToken = getActivityToken(embeddedActivity);
273         final TaskFragmentCreationParams params = generateTaskFragCreationParams();
274         final IBinder taskFragToken = params.getFragmentToken();
275         final WindowContainerTransaction wct = new WindowContainerTransaction()
276                 .createTaskFragment(params)
277                 .reparentActivityToTaskFragment(taskFragToken, embeddedActivityToken);
278         mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_CHANGE,
279                 false /* shouldApplyIndependently */);
280         mTaskFragmentOrganizer.waitForTaskFragmentCreated();
281         // The activity below must be occluded and stopped.
282         waitAndAssertActivityState(mOwnerActivityName, STATE_STOPPED,
283                 "Activity must be stopped");
284 
285         // Finishing the top activity and remain the TaskFragment on top. The next top activity
286         // must be resumed.
287         embeddedActivity.finish();
288         waitAndAssertResumedActivity(mOwnerActivityName, "Activity must be resumed");
289     }
290 }
291