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.view.Display.DEFAULT_DISPLAY; 23 24 import static com.google.common.truth.Truth.assertThat; 25 import static com.google.common.truth.Truth.assertWithMessage; 26 27 import static org.junit.Assert.assertThrows; 28 import static org.junit.Assert.assertTrue; 29 import static org.junit.Assume.assumeTrue; 30 31 import android.app.Activity; 32 import android.content.ComponentName; 33 import android.content.Intent; 34 import android.graphics.Rect; 35 import android.os.Binder; 36 import android.os.Bundle; 37 import android.os.IBinder; 38 import android.platform.test.annotations.Presubmit; 39 import android.server.wm.WindowManagerState.Task; 40 import android.server.wm.WindowManagerState.TaskFragment; 41 import android.view.SurfaceControl; 42 import android.window.TaskFragmentCreationParams; 43 import android.window.TaskFragmentInfo; 44 import android.window.TaskFragmentOrganizer; 45 import android.window.WindowContainerToken; 46 import android.window.WindowContainerTransaction; 47 48 import com.android.compatibility.common.util.ApiTest; 49 50 import org.junit.Test; 51 52 /** 53 * Tests that verify the behavior of {@link TaskFragmentOrganizer}. 54 * 55 * Build/Install/Run: 56 * atest CtsWindowManagerDeviceTestCases:TaskFragmentOrganizerTest 57 */ 58 @Presubmit 59 public class TaskFragmentOrganizerTest extends TaskFragmentOrganizerTestBase { 60 private final ComponentName mLaunchingActivity = new ComponentName(mContext, 61 WindowMetricsActivityTests.MetricsActivity.class); 62 63 /** 64 * Verifies the behavior of 65 * {@link WindowContainerTransaction#createTaskFragment(TaskFragmentCreationParams)} to create 66 * TaskFragment. 67 */ 68 @Test testCreateTaskFragment()69 public void testCreateTaskFragment() { 70 assumeTrue("MultiWindow is not supported.", supportsMultiWindow()); 71 mWmState.computeState(mOwnerActivityName); 72 Task parentTask = mWmState.getRootTask(mOwnerActivity.getTaskId()); 73 final int originalTaskFragCount = parentTask.getTaskFragments().size(); 74 75 final IBinder taskFragToken = new Binder(); 76 final Rect bounds = new Rect(0, 0, 1000, 1000); 77 final int windowingMode = WINDOWING_MODE_MULTI_WINDOW; 78 final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder( 79 mTaskFragmentOrganizer.getOrganizerToken(), taskFragToken, mOwnerToken) 80 .setInitialBounds(bounds) 81 .setWindowingMode(windowingMode) 82 .build(); 83 final WindowContainerTransaction wct = new WindowContainerTransaction() 84 .createTaskFragment(params); 85 mTaskFragmentOrganizer.applyTransaction(wct); 86 87 mTaskFragmentOrganizer.waitForTaskFragmentCreated(); 88 89 final TaskFragmentInfo info = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken); 90 91 assertEmptyTaskFragment(info, taskFragToken); 92 assertThat(info.getConfiguration().windowConfiguration.getBounds()).isEqualTo(bounds); 93 assertThat(info.getWindowingMode()).isEqualTo(windowingMode); 94 95 mWmState.computeState(mOwnerActivityName); 96 parentTask = mWmState.getRootTask(mOwnerActivity.getTaskId()); 97 final int curTaskFragCount = parentTask.getTaskFragments().size(); 98 99 assertWithMessage("There must be a TaskFragment created under Task#" 100 + mOwnerTaskId).that(curTaskFragCount - originalTaskFragCount) 101 .isEqualTo(1); 102 } 103 104 /** 105 * Verifies the behavior of 106 * {@link WindowContainerTransaction#reparentActivityToTaskFragment(IBinder, IBinder)} to 107 * reparent {@link Activity} to TaskFragment. 108 */ 109 @Test testReparentActivity()110 public void testReparentActivity() { 111 mWmState.computeState(mOwnerActivityName); 112 113 final TaskFragmentCreationParams params = generateTaskFragCreationParams(); 114 final IBinder taskFragToken = params.getFragmentToken(); 115 final WindowContainerTransaction wct = new WindowContainerTransaction() 116 .createTaskFragment(params) 117 .reparentActivityToTaskFragment(taskFragToken, mOwnerToken); 118 mTaskFragmentOrganizer.applyTransaction(wct); 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); 151 152 mTaskFragmentOrganizer.waitForTaskFragmentCreated(); 153 154 TaskFragmentInfo info = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken); 155 assertNotEmptyTaskFragment(info, taskFragToken); 156 157 mWmState.waitForActivityState(mLaunchingActivity, STATE_RESUMED); 158 159 Task parentTask = mWmState.getRootTask(mOwnerActivity.getTaskId()); 160 TaskFragment taskFragment = mWmState.getTaskFragmentByActivity(mLaunchingActivity); 161 162 // Assert window hierarchy must be as follows 163 // - owner Activity's Task (parentTask) 164 // - taskFragment 165 // - LAUNCHING_ACTIVITY 166 // - owner Activity 167 assertWindowHierarchy(parentTask, taskFragment, mWmState.getActivity(mLaunchingActivity)); 168 assertWindowHierarchy(parentTask, mWmState.getActivity(mOwnerActivityName)); 169 assertWithMessage("The owner Activity's Task must be reused as" 170 + " the launching Activity's Task.").that(parentTask) 171 .isEqualTo(mWmState.getTaskByActivity(mLaunchingActivity)); 172 } 173 174 /** 175 * Verifies the behavior of 176 * {@link WindowContainerTransaction#deleteTaskFragment(WindowContainerToken)} to remove 177 * the organized TaskFragment. 178 */ 179 @Test 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.getRootTask(mOwnerTaskId).getTaskFragments() 187 .size(); 188 189 WindowContainerTransaction wct = new WindowContainerTransaction() 190 .deleteTaskFragment(taskFragmentInfo.getToken()); 191 mTaskFragmentOrganizer.applyTransaction(wct); 192 193 assertTrue(mWmState.waitForWithAmState( 194 state -> state.getRootTask(mOwnerTaskId).getTaskFragments().isEmpty(), 195 "Wait for TaskFragment removal")); 196 // Remove an empty TaskFragment may not trigger SurfacePlacement because there is no 197 // activity resume/pause. 198 // Launch an activity to trigger a callback on SurfacePlacement to the organizer. 199 startActivityInWindowingModeFullScreen(WindowMetricsActivityTests.MetricsActivity.class); 200 201 mTaskFragmentOrganizer.waitForTaskFragmentRemoved(); 202 203 assertEmptyTaskFragment(mTaskFragmentOrganizer.getRemovedTaskFragmentInfo(taskFragToken), 204 taskFragToken); 205 206 mWmState.computeState(mOwnerActivityName); 207 final int currTaskFragCount = mWmState.getRootTask(mOwnerTaskId).getTaskFragments().size(); 208 assertWithMessage("TaskFragment with token " + taskFragToken + " must be" 209 + " removed.").that(originalTaskFragCount - currTaskFragCount).isEqualTo(1); 210 } 211 212 /** 213 * Verifies the behavior of {@link WindowContainerTransaction#finishActivity(IBinder)} to finish 214 * an Activity. 215 */ 216 @Test 217 @ApiTest(apis = { 218 "android.window.TaskFragmentOrganizer#applyTransaction", 219 "android.window.WindowContainerTransaction#finishActivity"}) testFinishActivity()220 public void testFinishActivity() { 221 assumeExtensionVersionAtLeast2(); 222 final Activity activity = startNewActivity( 223 WindowMetricsActivityTests.MetricsActivity.class); 224 // Make sure mLaunchingActivity is mapping to the correct component that is started. 225 mWmState.waitAndAssertActivityState(mLaunchingActivity, STATE_RESUMED); 226 227 final WindowContainerTransaction wct = new WindowContainerTransaction() 228 .finishActivity(getActivityToken(activity)); 229 mTaskFragmentOrganizer.applyTransaction(wct); 230 231 mWmState.waitAndAssertActivityRemoved(mLaunchingActivity); 232 } 233 234 /** 235 * Verifies the visibility of an activity behind a TaskFragment that has the same 236 * bounds of the host Task. 237 */ 238 @Test testActivityVisibilityBehindTaskFragment()239 public void testActivityVisibilityBehindTaskFragment() { 240 // Start an activity and reparent it to a TaskFragment. 241 final Activity embeddedActivity = 242 startActivity(WindowMetricsActivityTests.MetricsActivity.class); 243 final IBinder embeddedActivityToken = getActivityToken(embeddedActivity); 244 final TaskFragmentCreationParams params = generateTaskFragCreationParams(); 245 final IBinder taskFragToken = params.getFragmentToken(); 246 final WindowContainerTransaction wct = new WindowContainerTransaction() 247 .createTaskFragment(params) 248 .reparentActivityToTaskFragment(taskFragToken, embeddedActivityToken); 249 mTaskFragmentOrganizer.applyTransaction(wct); 250 mTaskFragmentOrganizer.waitForTaskFragmentCreated(); 251 // The activity below must be occluded and stopped. 252 waitAndAssertActivityState(mOwnerActivityName, STATE_STOPPED, 253 "Activity must be stopped"); 254 255 // Finishing the top activity and remain the TaskFragment on top. The next top activity 256 // must be resumed. 257 embeddedActivity.finish(); 258 waitAndAssertResumedActivity(mOwnerActivityName, "Activity must be resumed"); 259 } 260 261 /** 262 * Verifies that config changes with {@link WindowContainerTransaction.Change#getChangeMask()} 263 * are disallowed for embedded TaskFragments. 264 */ 265 @Test testTaskFragmentConfigChange_disallowChangeMaskChanges()266 public void testTaskFragmentConfigChange_disallowChangeMaskChanges() { 267 final TaskFragmentInfo taskFragmentInfo = createTaskFragment(mLaunchingActivity); 268 final WindowContainerToken token = taskFragmentInfo.getToken(); 269 270 final WindowContainerTransaction wct0 = new WindowContainerTransaction() 271 .scheduleFinishEnterPip(token, new Rect(0, 0, 100, 100)); 272 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct0)); 273 274 final WindowContainerTransaction wct1 = new WindowContainerTransaction() 275 .setBoundsChangeTransaction(token, new SurfaceControl.Transaction()); 276 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct1)); 277 278 final WindowContainerTransaction wct3 = new WindowContainerTransaction() 279 .setFocusable(token, false /* focusable */); 280 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct3)); 281 282 final WindowContainerTransaction wct4 = new WindowContainerTransaction() 283 .setHidden(token, false /* hidden */); 284 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct4)); 285 } 286 } 287