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