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