1 /* 2 * Copyright (C) 2020 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.ActivityManager.START_CANCELED; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 24 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 25 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 26 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 27 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; 28 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; 29 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 30 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 31 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 32 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 33 import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; 34 import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; 35 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 36 37 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 38 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 39 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 40 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; 41 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; 43 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 44 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 45 import static com.android.server.wm.ActivityRecord.State.RESUMED; 46 import static com.android.server.wm.WindowContainer.POSITION_TOP; 47 import static com.android.server.wm.WindowContainer.SYNC_STATE_READY; 48 49 import static com.google.common.truth.Truth.assertThat; 50 51 import static org.junit.Assert.assertEquals; 52 import static org.junit.Assert.assertFalse; 53 import static org.junit.Assert.assertNotNull; 54 import static org.junit.Assert.assertTrue; 55 import static org.mockito.ArgumentMatchers.any; 56 import static org.mockito.ArgumentMatchers.anyBoolean; 57 import static org.mockito.ArgumentMatchers.anyInt; 58 import static org.mockito.ArgumentMatchers.eq; 59 import static org.mockito.Mockito.atLeastOnce; 60 import static org.mockito.Mockito.clearInvocations; 61 62 import android.app.ActivityManager; 63 import android.app.ActivityManager.RunningTaskInfo; 64 import android.app.ActivityOptions; 65 import android.app.ActivityTaskManager.RootTaskInfo; 66 import android.app.IRequestFinishCallback; 67 import android.app.PictureInPictureParams; 68 import android.content.pm.ActivityInfo; 69 import android.content.pm.ParceledListSlice; 70 import android.content.res.Configuration; 71 import android.graphics.Rect; 72 import android.os.Binder; 73 import android.os.IBinder; 74 import android.os.RemoteException; 75 import android.platform.test.annotations.Presubmit; 76 import android.util.ArrayMap; 77 import android.util.Rational; 78 import android.view.Display; 79 import android.view.SurfaceControl; 80 import android.window.ITaskOrganizer; 81 import android.window.IWindowContainerTransactionCallback; 82 import android.window.StartingWindowInfo; 83 import android.window.StartingWindowRemovalInfo; 84 import android.window.TaskAppearedInfo; 85 import android.window.WindowContainerToken; 86 import android.window.WindowContainerTransaction; 87 88 import androidx.test.filters.SmallTest; 89 90 import com.android.server.wm.TaskOrganizerController.PendingTaskEvent; 91 92 import org.junit.Before; 93 import org.junit.Test; 94 import org.junit.runner.RunWith; 95 import org.mockito.ArgumentCaptor; 96 97 import java.util.ArrayList; 98 import java.util.HashSet; 99 import java.util.List; 100 101 /** 102 * Test class for {@link ITaskOrganizer} and {@link android.window.ITaskOrganizerController}. 103 * 104 * Build/Install/Run: 105 * atest WmTests:WindowOrganizerTests 106 */ 107 @SmallTest 108 @Presubmit 109 @RunWith(WindowTestRunner.class) 110 public class WindowOrganizerTests extends WindowTestsBase { 111 createMockOrganizer()112 private ITaskOrganizer createMockOrganizer() { 113 final ITaskOrganizer organizer = mock(ITaskOrganizer.class); 114 when(organizer.asBinder()).thenReturn(new Binder()); 115 return organizer; 116 } 117 registerMockOrganizer(ArrayList<TaskAppearedInfo> existingTasks)118 private ITaskOrganizer registerMockOrganizer(ArrayList<TaskAppearedInfo> existingTasks) { 119 final ITaskOrganizer organizer = createMockOrganizer(); 120 ParceledListSlice<TaskAppearedInfo> tasks = 121 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer); 122 if (existingTasks != null) { 123 existingTasks.addAll(tasks.getList()); 124 } 125 return organizer; 126 } 127 registerMockOrganizer()128 private ITaskOrganizer registerMockOrganizer() { 129 return registerMockOrganizer(null); 130 } 131 createTask(Task rootTask, boolean fakeDraw)132 Task createTask(Task rootTask, boolean fakeDraw) { 133 final Task task = createTaskInRootTask(rootTask, 0); 134 135 if (fakeDraw) { 136 task.setHasBeenVisible(true); 137 } 138 return task; 139 } 140 createTask(Task rootTask)141 Task createTask(Task rootTask) { 142 // Fake draw notifications for most of our tests. 143 return createTask(rootTask, true); 144 } 145 createRootTask()146 Task createRootTask() { 147 return createTask(mDisplayContent); 148 } 149 150 @Before setUp()151 public void setUp() { 152 // We defer callbacks since we need to adjust task surface visibility, but for these tests, 153 // just run the callbacks synchronously 154 mWm.mAtmService.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer((r) -> r.run()); 155 } 156 157 @Test testAppearVanish()158 public void testAppearVanish() throws RemoteException { 159 final ITaskOrganizer organizer = registerMockOrganizer(); 160 final Task rootTask = createRootTask(); 161 final Task task = createTask(rootTask); 162 // Ensure events dispatch to organizer. 163 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 164 165 verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 166 167 rootTask.removeImmediately(); 168 // Ensure events dispatch to organizer. 169 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 170 verify(organizer).onTaskVanished(any()); 171 } 172 173 @Test testAppearWaitsForVisibility()174 public void testAppearWaitsForVisibility() throws RemoteException { 175 final ITaskOrganizer organizer = registerMockOrganizer(); 176 final Task rootTask = createRootTask(); 177 final Task task = createTask(rootTask, false); 178 // Ensure events dispatch to organizer. 179 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 180 181 verify(organizer, never()) 182 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 183 rootTask.setHasBeenVisible(true); 184 // Ensure events dispatch to organizer. 185 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 186 assertTrue(rootTask.getHasBeenVisible()); 187 188 verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 189 190 rootTask.removeImmediately(); 191 // Ensure events dispatch to organizer. 192 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 193 verify(organizer).onTaskVanished(any()); 194 } 195 196 @Test testNoVanishedIfNoAppear()197 public void testNoVanishedIfNoAppear() throws RemoteException { 198 final ITaskOrganizer organizer = registerMockOrganizer(); 199 final Task rootTask = createRootTask(); 200 final Task task = createTask(rootTask, false /* hasBeenVisible */); 201 202 // In this test we skip making the Task visible, and verify 203 // that even though a TaskOrganizer is set remove doesn't emit 204 // a vanish callback, because we never emitted appear. 205 rootTask.setTaskOrganizer(organizer); 206 verify(organizer, never()) 207 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 208 rootTask.removeImmediately(); 209 verify(organizer, never()).onTaskVanished(any()); 210 } 211 212 @Test testTaskNoDraw()213 public void testTaskNoDraw() throws RemoteException { 214 final ITaskOrganizer organizer = registerMockOrganizer(); 215 final Task rootTask = createRootTask(); 216 final Task task = createTask(rootTask, false /* fakeDraw */); 217 // Ensure events dispatch to organizer. 218 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 219 220 verify(organizer, never()) 221 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 222 assertTrue(rootTask.isOrganized()); 223 224 mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); 225 // Ensure events dispatch to organizer. 226 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 227 assertTaskVanished(organizer, false /* expectVanished */, rootTask); 228 assertFalse(rootTask.isOrganized()); 229 } 230 231 @Test testClearOrganizer()232 public void testClearOrganizer() throws RemoteException { 233 final ITaskOrganizer organizer = registerMockOrganizer(); 234 final Task rootTask = createRootTask(); 235 final Task task = createTask(rootTask); 236 // Ensure events dispatch to organizer. 237 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 238 239 verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 240 assertTrue(rootTask.isOrganized()); 241 242 rootTask.setTaskOrganizer(null); 243 // Ensure events dispatch to organizer. 244 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 245 246 verify(organizer).onTaskVanished(any()); 247 assertFalse(rootTask.isOrganized()); 248 } 249 250 @Test testUnregisterOrganizer()251 public void testUnregisterOrganizer() throws RemoteException { 252 final ITaskOrganizer organizer = registerMockOrganizer(); 253 final Task rootTask = createRootTask(); 254 final Task task = createTask(rootTask); 255 // Ensure events dispatch to organizer. 256 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 257 258 verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 259 assertTrue(rootTask.isOrganized()); 260 261 mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); 262 // Ensure events dispatch to organizer. 263 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 264 265 assertTaskVanished(organizer, true /* expectVanished */, rootTask); 266 assertFalse(rootTask.isOrganized()); 267 } 268 269 @Test testUnregisterOrganizerReturnsRegistrationToPrevious()270 public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException { 271 final Task rootTask = createRootTask(); 272 final Task task = createTask(rootTask); 273 final Task rootTask2 = createRootTask(); 274 final Task task2 = createTask(rootTask2); 275 final Task rootTask3 = createRootTask(); 276 final Task task3 = createTask(rootTask3); 277 final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); 278 final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); 279 // Ensure events dispatch to organizer. 280 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 281 282 // verify that tasks are returned and taskAppeared is not called 283 assertContainsTasks(existingTasks, rootTask, rootTask2, rootTask3); 284 verify(organizer, times(0)).onTaskAppeared(any(RunningTaskInfo.class), 285 any(SurfaceControl.class)); 286 verify(organizer, times(0)).onTaskVanished(any()); 287 assertTrue(rootTask.isOrganized()); 288 289 // Now we replace the registration and verify the new organizer receives existing tasks 290 final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>(); 291 final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2); 292 // Ensure events dispatch to organizer. 293 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 294 assertContainsTasks(existingTasks2, rootTask, rootTask2, rootTask3); 295 verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class), 296 any(SurfaceControl.class)); 297 verify(organizer2, times(0)).onTaskVanished(any()); 298 // Removed tasks from the original organizer 299 assertTaskVanished(organizer, true /* expectVanished */, rootTask, rootTask2, rootTask3); 300 assertTrue(rootTask2.isOrganized()); 301 302 // Now we unregister the second one, the first one should automatically be reregistered 303 // so we verify that it's now seeing changes. 304 mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2); 305 // Ensure events dispatch to organizer. 306 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 307 verify(organizer, times(3)) 308 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 309 assertTaskVanished(organizer2, true /* expectVanished */, rootTask, rootTask2, rootTask3); 310 } 311 312 @Test testRegisterTaskOrganizerWithExistingTasks()313 public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException { 314 final Task rootTask = createRootTask(); 315 final Task task = createTask(rootTask); 316 final Task rootTask2 = createRootTask(); 317 final Task task2 = createTask(rootTask2); 318 ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); 319 final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); 320 assertContainsTasks(existingTasks, rootTask, rootTask2); 321 322 // Verify we don't get onTaskAppeared if we are returned the tasks 323 verify(organizer, never()) 324 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 325 } 326 327 @Test testTaskTransaction()328 public void testTaskTransaction() { 329 removeGlobalMinSizeRestriction(); 330 final Task rootTask = new TaskBuilder(mSupervisor) 331 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 332 final Task task = rootTask.getTopMostTask(); 333 testTransaction(task); 334 } 335 336 @Test testRootTaskTransaction()337 public void testRootTaskTransaction() { 338 removeGlobalMinSizeRestriction(); 339 final Task rootTask = new TaskBuilder(mSupervisor) 340 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 341 RootTaskInfo info = 342 mWm.mAtmService.getRootTaskInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD); 343 assertEquals(rootTask.mRemoteToken.toWindowContainerToken(), info.token); 344 testTransaction(rootTask); 345 } 346 347 @Test testDisplayAreaTransaction()348 public void testDisplayAreaTransaction() { 349 removeGlobalMinSizeRestriction(); 350 final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea(); 351 testTransaction(displayArea); 352 } 353 testTransaction(WindowContainer wc)354 private void testTransaction(WindowContainer wc) { 355 WindowContainerTransaction t = new WindowContainerTransaction(); 356 Rect newBounds = new Rect(10, 10, 100, 100); 357 t.setBounds(wc.mRemoteToken.toWindowContainerToken(), new Rect(10, 10, 100, 100)); 358 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 359 assertEquals(newBounds, wc.getBounds()); 360 } 361 362 @Test testSetWindowingMode()363 public void testSetWindowingMode() { 364 final Task rootTask = new TaskBuilder(mSupervisor) 365 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 366 testSetWindowingMode(rootTask); 367 368 final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea(); 369 displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM); 370 testSetWindowingMode(displayArea); 371 } 372 testSetWindowingMode(WindowContainer wc)373 private void testSetWindowingMode(WindowContainer wc) { 374 final WindowContainerTransaction t = new WindowContainerTransaction(); 375 t.setWindowingMode(wc.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN); 376 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 377 assertEquals(WINDOWING_MODE_FULLSCREEN, wc.getWindowingMode()); 378 } 379 380 @Test testSetActivityWindowingMode()381 public void testSetActivityWindowingMode() { 382 final ActivityRecord record = makePipableActivity(); 383 final Task rootTask = record.getRootTask(); 384 final WindowContainerTransaction t = new WindowContainerTransaction(); 385 386 t.setWindowingMode(rootTask.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_PINNED); 387 t.setActivityWindowingMode( 388 rootTask.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN); 389 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 390 391 assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode()); 392 assertEquals(WINDOWING_MODE_PINNED, rootTask.getWindowingMode()); 393 } 394 395 @Test testContainerFocusableChanges()396 public void testContainerFocusableChanges() { 397 removeGlobalMinSizeRestriction(); 398 final Task rootTask = new TaskBuilder(mSupervisor) 399 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 400 final Task task = rootTask.getTopMostTask(); 401 WindowContainerTransaction t = new WindowContainerTransaction(); 402 assertTrue(task.isFocusable()); 403 t.setFocusable(rootTask.mRemoteToken.toWindowContainerToken(), false); 404 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 405 assertFalse(task.isFocusable()); 406 t.setFocusable(rootTask.mRemoteToken.toWindowContainerToken(), true); 407 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 408 assertTrue(task.isFocusable()); 409 } 410 411 @Test testContainerHiddenChanges()412 public void testContainerHiddenChanges() { 413 removeGlobalMinSizeRestriction(); 414 final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) 415 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 416 WindowContainerTransaction t = new WindowContainerTransaction(); 417 assertTrue(rootTask.shouldBeVisible(null)); 418 t.setHidden(rootTask.mRemoteToken.toWindowContainerToken(), true); 419 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 420 assertFalse(rootTask.shouldBeVisible(null)); 421 t.setHidden(rootTask.mRemoteToken.toWindowContainerToken(), false); 422 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 423 assertTrue(rootTask.shouldBeVisible(null)); 424 } 425 426 @Test testSetIgnoreOrientationRequest_taskDisplayArea()427 public void testSetIgnoreOrientationRequest_taskDisplayArea() { 428 removeGlobalMinSizeRestriction(); 429 final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); 430 final Task rootTask = taskDisplayArea.createRootTask( 431 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 432 final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build(); 433 taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 434 mDisplayContent.setFocusedApp(activity); 435 activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 436 437 // TDA returns UNSET when ignoreOrientationRequest == true 438 // DC is UNSPECIFIED when child returns UNSET 439 assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET); 440 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); 441 442 WindowContainerTransaction t = new WindowContainerTransaction(); 443 t.setIgnoreOrientationRequest( 444 taskDisplayArea.mRemoteToken.toWindowContainerToken(), 445 false /* ignoreOrientationRequest */); 446 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 447 448 // TDA returns app request orientation when ignoreOrientationRequest == false 449 // DC uses the same as TDA returns when it is not UNSET. 450 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 451 assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 452 453 t.setIgnoreOrientationRequest( 454 taskDisplayArea.mRemoteToken.toWindowContainerToken(), 455 true /* ignoreOrientationRequest */); 456 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 457 458 // TDA returns UNSET when ignoreOrientationRequest == true 459 // DC is UNSPECIFIED when child returns UNSET 460 assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET); 461 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); 462 } 463 464 @Test testSetIgnoreOrientationRequest_displayContent()465 public void testSetIgnoreOrientationRequest_displayContent() { 466 removeGlobalMinSizeRestriction(); 467 final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); 468 final Task rootTask = taskDisplayArea.createRootTask( 469 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 470 final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build(); 471 mDisplayContent.setFocusedApp(activity); 472 activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 473 474 // DC uses the orientation request from app 475 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 476 477 WindowContainerTransaction t = new WindowContainerTransaction(); 478 t.setIgnoreOrientationRequest( 479 mDisplayContent.mRemoteToken.toWindowContainerToken(), 480 true /* ignoreOrientationRequest */); 481 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 482 483 // DC returns UNSPECIFIED when ignoreOrientationRequest == true 484 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); 485 486 t.setIgnoreOrientationRequest( 487 mDisplayContent.mRemoteToken.toWindowContainerToken(), 488 false /* ignoreOrientationRequest */); 489 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 490 491 // DC uses the orientation request from app after mIgnoreOrientationRequest is set to false 492 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 493 } 494 495 @Test testOverrideConfigSize()496 public void testOverrideConfigSize() { 497 removeGlobalMinSizeRestriction(); 498 final Task rootTask = new TaskBuilder(mSupervisor) 499 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 500 final Task task = rootTask.getTopMostTask(); 501 WindowContainerTransaction t = new WindowContainerTransaction(); 502 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 503 final int origScreenWDp = task.getConfiguration().screenHeightDp; 504 final int origScreenHDp = task.getConfiguration().screenHeightDp; 505 t = new WindowContainerTransaction(); 506 // verify that setting config overrides on parent restricts children. 507 t.setScreenSizeDp(rootTask.mRemoteToken 508 .toWindowContainerToken(), origScreenWDp, origScreenHDp / 2); 509 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 510 assertEquals(origScreenHDp / 2, task.getConfiguration().screenHeightDp); 511 t = new WindowContainerTransaction(); 512 t.setScreenSizeDp(rootTask.mRemoteToken.toWindowContainerToken(), SCREEN_WIDTH_DP_UNDEFINED, 513 SCREEN_HEIGHT_DP_UNDEFINED); 514 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 515 assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp); 516 } 517 518 @Test testCreateDeleteRootTasks()519 public void testCreateDeleteRootTasks() { 520 DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY); 521 522 Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 523 dc, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null); 524 RunningTaskInfo info1 = task1.getTaskInfo(); 525 assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, 526 info1.configuration.windowConfiguration.getWindowingMode()); 527 assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType); 528 529 Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 530 dc, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); 531 RunningTaskInfo info2 = task2.getTaskInfo(); 532 assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, 533 info2.configuration.windowConfiguration.getWindowingMode()); 534 assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType); 535 536 List<Task> infos = getTasksCreatedByOrganizer(dc); 537 assertEquals(2, infos.size()); 538 539 assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token)); 540 infos = getTasksCreatedByOrganizer(dc); 541 assertEquals(1, infos.size()); 542 assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, infos.get(0).getWindowingMode()); 543 } 544 545 @Test testSetAdjacentLaunchRoot()546 public void testSetAdjacentLaunchRoot() { 547 DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY); 548 549 final Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 550 dc, WINDOWING_MODE_MULTI_WINDOW, null); 551 final RunningTaskInfo info1 = task1.getTaskInfo(); 552 final Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 553 dc, WINDOWING_MODE_MULTI_WINDOW, null); 554 final RunningTaskInfo info2 = task2.getTaskInfo(); 555 556 WindowContainerTransaction wct = new WindowContainerTransaction(); 557 wct.setAdjacentRoots(info1.token, info2.token, false /* moveTogether */); 558 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 559 assertEquals(task1.getAdjacentTaskFragment(), task2); 560 assertEquals(task2.getAdjacentTaskFragment(), task1); 561 562 wct = new WindowContainerTransaction(); 563 wct.setLaunchAdjacentFlagRoot(info1.token); 564 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 565 assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, task1); 566 567 task1.setAdjacentTaskFragment(null, false /* moveTogether */); 568 task2.setAdjacentTaskFragment(null, false /* moveTogether */); 569 wct = new WindowContainerTransaction(); 570 wct.clearLaunchAdjacentFlagRoot(info1.token); 571 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 572 assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, null); 573 } 574 575 @Test testTileAddRemoveChild()576 public void testTileAddRemoveChild() { 577 final StubOrganizer listener = new StubOrganizer(); 578 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); 579 Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask( 580 mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); 581 RunningTaskInfo info1 = task.getTaskInfo(); 582 583 final Task rootTask = createTask( 584 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); 585 assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode()); 586 WindowContainerTransaction wct = new WindowContainerTransaction(); 587 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */); 588 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 589 assertEquals(info1.configuration.windowConfiguration.getWindowingMode(), 590 rootTask.getWindowingMode()); 591 592 // Info should reflect new membership 593 List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent); 594 info1 = infos.get(0).getTaskInfo(); 595 assertEquals(ACTIVITY_TYPE_STANDARD, info1.topActivityType); 596 597 // Children inherit configuration 598 Rect newSize = new Rect(10, 10, 300, 300); 599 Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask(); 600 Configuration c = new Configuration(task1.getRequestedOverrideConfiguration()); 601 c.windowConfiguration.setBounds(newSize); 602 doNothing().when(rootTask).adjustForMinimalTaskDimensions(any(), any(), any()); 603 task1.onRequestedOverrideConfigurationChanged(c); 604 assertEquals(newSize, rootTask.getBounds()); 605 606 wct = new WindowContainerTransaction(); 607 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); 608 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 609 assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode()); 610 infos = getTasksCreatedByOrganizer(mDisplayContent); 611 info1 = infos.get(0).getTaskInfo(); 612 assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType); 613 } 614 615 @UseTestDisplay 616 @Test testTaskInfoCallback()617 public void testTaskInfoCallback() { 618 final ArrayList<RunningTaskInfo> lastReportedTiles = new ArrayList<>(); 619 final boolean[] called = {false}; 620 final StubOrganizer listener = new StubOrganizer() { 621 @Override 622 public void onTaskInfoChanged(RunningTaskInfo info) { 623 lastReportedTiles.add(info); 624 called[0] = true; 625 } 626 }; 627 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); 628 Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask( 629 mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); 630 RunningTaskInfo info1 = task.getTaskInfo(); 631 // Ensure events dispatch to organizer. 632 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 633 lastReportedTiles.clear(); 634 called[0] = false; 635 636 final Task rootTask = createTask( 637 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); 638 Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask(); 639 WindowContainerTransaction wct = new WindowContainerTransaction(); 640 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */); 641 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 642 assertTrue(called[0]); 643 assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType); 644 645 lastReportedTiles.clear(); 646 called[0] = false; 647 final Task rootTask2 = createTask( 648 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME); 649 wct = new WindowContainerTransaction(); 650 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), 651 info1.token, true /* onTop */); 652 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 653 assertTrue(called[0]); 654 assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType); 655 656 lastReportedTiles.clear(); 657 called[0] = false; 658 task1.positionChildAt(POSITION_TOP, rootTask, false /* includingParents */); 659 assertTrue(called[0]); 660 assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType); 661 662 lastReportedTiles.clear(); 663 called[0] = false; 664 wct = new WindowContainerTransaction(); 665 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), 666 null, true /* onTop */); 667 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), 668 null, true /* onTop */); 669 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 670 assertTrue(called[0]); 671 assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType); 672 } 673 674 @UseTestDisplay 675 @Test testHierarchyTransaction()676 public void testHierarchyTransaction() { 677 final ArrayMap<IBinder, RunningTaskInfo> lastReportedTiles = new ArrayMap<>(); 678 final StubOrganizer listener = new StubOrganizer() { 679 @Override 680 public void onTaskInfoChanged(RunningTaskInfo info) { 681 lastReportedTiles.put(info.token.asBinder(), info); 682 } 683 }; 684 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); 685 686 Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 687 mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null); 688 RunningTaskInfo info1 = task1.getTaskInfo(); 689 Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 690 mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); 691 RunningTaskInfo info2 = task2.getTaskInfo(); 692 // Ensure events dispatch to organizer. 693 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 694 695 final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks( 696 mDisplayContent.mDisplayId, null /* activityTypes */).size(); 697 698 final Task rootTask = createTask( 699 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); 700 final Task rootTask2 = createTask( 701 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME); 702 703 // Check getRootTasks works 704 List<RunningTaskInfo> roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks( 705 mDisplayContent.mDisplayId, null /* activityTypes */); 706 assertEquals(initialRootTaskCount + 2, roots.size()); 707 708 lastReportedTiles.clear(); 709 WindowContainerTransaction wct = new WindowContainerTransaction(); 710 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), 711 info1.token, true /* onTop */); 712 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), 713 info2.token, true /* onTop */); 714 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 715 assertFalse(lastReportedTiles.isEmpty()); 716 assertEquals(ACTIVITY_TYPE_STANDARD, 717 lastReportedTiles.get(info1.token.asBinder()).topActivityType); 718 assertEquals(ACTIVITY_TYPE_HOME, 719 lastReportedTiles.get(info2.token.asBinder()).topActivityType); 720 721 lastReportedTiles.clear(); 722 wct = new WindowContainerTransaction(); 723 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), 724 info1.token, false /* onTop */); 725 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 726 assertFalse(lastReportedTiles.isEmpty()); 727 // Standard should still be on top of tile 1, so no change there 728 assertFalse(lastReportedTiles.containsKey(info1.token.asBinder())); 729 // But tile 2 has no children, so should become undefined 730 assertEquals(ACTIVITY_TYPE_UNDEFINED, 731 lastReportedTiles.get(info2.token.asBinder()).topActivityType); 732 733 // Check the getChildren call 734 List<RunningTaskInfo> children = 735 mWm.mAtmService.mTaskOrganizerController.getChildTasks(info1.token, 736 null /* activityTypes */); 737 assertEquals(2, children.size()); 738 children = mWm.mAtmService.mTaskOrganizerController.getChildTasks(info2.token, 739 null /* activityTypes */); 740 assertEquals(0, children.size()); 741 742 // Check that getRootTasks doesn't include children of tiles 743 roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks(mDisplayContent.mDisplayId, 744 null /* activityTypes */); 745 assertEquals(initialRootTaskCount, roots.size()); 746 747 lastReportedTiles.clear(); 748 wct = new WindowContainerTransaction(); 749 wct.reorder(rootTask2.mRemoteToken.toWindowContainerToken(), true /* onTop */); 750 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 751 // Home should now be on top. No change occurs in second tile, so not reported 752 assertEquals(1, lastReportedTiles.size()); 753 assertEquals(ACTIVITY_TYPE_HOME, 754 lastReportedTiles.get(info1.token.asBinder()).topActivityType); 755 756 // This just needs to not crash (ie. it should be possible to reparent to display twice) 757 wct = new WindowContainerTransaction(); 758 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); 759 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 760 wct = new WindowContainerTransaction(); 761 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); 762 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 763 } 764 getTasksCreatedByOrganizer(DisplayContent dc)765 private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) { 766 final ArrayList<Task> out = new ArrayList<>(); 767 dc.forAllRootTasks(task -> { 768 if (task.mCreatedByOrganizer) { 769 out.add(task); 770 } 771 }); 772 return out; 773 } 774 775 @Test testBLASTCallbackWithActivityChildren()776 public void testBLASTCallbackWithActivityChildren() { 777 final Task rootTaskController1 = createRootTask(); 778 final Task task = createTask(rootTaskController1); 779 final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window"); 780 781 w.mActivityRecord.mVisibleRequested = true; 782 w.mActivityRecord.setVisible(true); 783 784 BLASTSyncEngine bse = new BLASTSyncEngine(mWm); 785 786 BLASTSyncEngine.TransactionReadyListener transactionListener = 787 mock(BLASTSyncEngine.TransactionReadyListener.class); 788 789 int id = bse.startSyncSet(transactionListener); 790 bse.addToSyncSet(id, task); 791 bse.setReady(id); 792 bse.onSurfacePlacement(); 793 794 // Even though w is invisible (and thus activity isn't waiting on it), activity will 795 // continue to wait until it has at-least 1 visible window. 796 // Since we have a child window we still shouldn't be done. 797 verify(transactionListener, never()).onTransactionReady(anyInt(), any()); 798 799 makeWindowVisible(w); 800 bse.onSurfacePlacement(); 801 w.immediatelyNotifyBlastSync(); 802 bse.onSurfacePlacement(); 803 804 verify(transactionListener).onTransactionReady(anyInt(), any()); 805 } 806 807 static class StubOrganizer extends ITaskOrganizer.Stub { 808 RunningTaskInfo mInfo; 809 810 @Override addStartingWindow(StartingWindowInfo info, IBinder appToken)811 public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { } 812 @Override removeStartingWindow(StartingWindowRemovalInfo removalInfo)813 public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { } 814 @Override copySplashScreenView(int taskId)815 public void copySplashScreenView(int taskId) { } 816 @Override onTaskAppeared(RunningTaskInfo info, SurfaceControl leash)817 public void onTaskAppeared(RunningTaskInfo info, SurfaceControl leash) { 818 mInfo = info; 819 } 820 @Override onTaskVanished(RunningTaskInfo info)821 public void onTaskVanished(RunningTaskInfo info) { 822 } 823 @Override onTaskInfoChanged(RunningTaskInfo info)824 public void onTaskInfoChanged(RunningTaskInfo info) { 825 } 826 @Override onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)827 public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { 828 } 829 @Override onImeDrawnOnTask(int taskId)830 public void onImeDrawnOnTask(int taskId) throws RemoteException { 831 } 832 @Override onAppSplashScreenViewRemoved(int taskId)833 public void onAppSplashScreenViewRemoved(int taskId) { 834 } 835 }; 836 makePipableActivity()837 private ActivityRecord makePipableActivity() { 838 final ActivityRecord record = createActivityRecordWithParentTask(mDisplayContent, 839 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); 840 record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; 841 spyOn(record); 842 doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean()); 843 844 record.getTask().setHasBeenVisible(true); 845 return record; 846 } 847 848 @Test testEnterPipParams()849 public void testEnterPipParams() { 850 final StubOrganizer o = new StubOrganizer(); 851 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o); 852 final ActivityRecord record = makePipableActivity(); 853 854 final PictureInPictureParams p = new PictureInPictureParams.Builder() 855 .setAspectRatio(new Rational(1, 2)).build(); 856 assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode( 857 record.token, p)); 858 waitUntilHandlersIdle(); 859 assertNotNull(o.mInfo); 860 assertNotNull(o.mInfo.pictureInPictureParams); 861 } 862 863 @Test testChangePipParams()864 public void testChangePipParams() { 865 class ChangeSavingOrganizer extends StubOrganizer { 866 RunningTaskInfo mChangedInfo; 867 @Override 868 public void onTaskInfoChanged(RunningTaskInfo info) { 869 mChangedInfo = info; 870 } 871 } 872 ChangeSavingOrganizer o = new ChangeSavingOrganizer(); 873 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o); 874 875 final ActivityRecord record = makePipableActivity(); 876 final PictureInPictureParams p = new PictureInPictureParams.Builder() 877 .setAspectRatio(new Rational(1, 2)).build(); 878 assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode( 879 record.token, p)); 880 waitUntilHandlersIdle(); 881 assertNotNull(o.mInfo); 882 assertNotNull(o.mInfo.pictureInPictureParams); 883 884 final PictureInPictureParams p2 = new PictureInPictureParams.Builder() 885 .setAspectRatio(new Rational(3, 4)).build(); 886 mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2); 887 waitUntilHandlersIdle(); 888 // Ensure events dispatch to organizer. 889 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 890 assertNotNull(o.mChangedInfo); 891 assertNotNull(o.mChangedInfo.pictureInPictureParams); 892 final Rational ratio = o.mChangedInfo.pictureInPictureParams.getAspectRatioRational(); 893 assertEquals(3, ratio.getNumerator()); 894 assertEquals(4, ratio.getDenominator()); 895 } 896 897 @Test testChangeTaskDescription()898 public void testChangeTaskDescription() { 899 class ChangeSavingOrganizer extends StubOrganizer { 900 RunningTaskInfo mChangedInfo; 901 @Override 902 public void onTaskInfoChanged(RunningTaskInfo info) { 903 mChangedInfo = info; 904 } 905 } 906 ChangeSavingOrganizer o = new ChangeSavingOrganizer(); 907 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o); 908 909 final Task rootTask = createRootTask(); 910 final Task task = createTask(rootTask); 911 final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); 912 913 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 914 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); 915 waitUntilHandlersIdle(); 916 assertEquals("TestDescription", o.mChangedInfo.taskDescription.getLabel()); 917 } 918 919 @Test testPreventDuplicateAppear()920 public void testPreventDuplicateAppear() throws RemoteException { 921 final ITaskOrganizer organizer = registerMockOrganizer(); 922 final Task rootTask = createRootTask(); 923 final Task task = createTask(rootTask, false /* fakeDraw */); 924 925 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 926 rootTask.setTaskOrganizer(organizer); 927 // setHasBeenVisible was already called once by the set-up code. 928 rootTask.setHasBeenVisible(true); 929 // Ensure events dispatch to organizer. 930 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 931 verify(organizer, times(1)) 932 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 933 934 rootTask.setTaskOrganizer(null); 935 // Ensure events dispatch to organizer. 936 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 937 verify(organizer, times(1)).onTaskVanished(any()); 938 rootTask.setTaskOrganizer(organizer); 939 // Ensure events dispatch to organizer. 940 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 941 verify(organizer, times(2)) 942 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 943 944 rootTask.removeImmediately(); 945 // Ensure events dispatch to organizer. 946 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 947 verify(organizer, times(2)).onTaskVanished(any()); 948 } 949 950 @Test testInterceptBackPressedOnTaskRoot()951 public void testInterceptBackPressedOnTaskRoot() throws RemoteException { 952 final ITaskOrganizer organizer = registerMockOrganizer(); 953 final Task rootTask = createRootTask(); 954 final Task task = createTask(rootTask); 955 final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task); 956 final Task rootTask2 = createRootTask(); 957 final Task task2 = createTask(rootTask2); 958 final ActivityRecord activity2 = createActivityRecord(rootTask.mDisplayContent, task2); 959 960 assertTrue(rootTask.isOrganized()); 961 assertTrue(rootTask2.isOrganized()); 962 963 // Verify a back pressed does not call the organizer 964 mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token, 965 new IRequestFinishCallback.Default()); 966 // Ensure events dispatch to organizer. 967 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 968 verify(organizer, never()).onBackPressedOnTaskRoot(any()); 969 970 // Enable intercepting back 971 mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( 972 rootTask.mRemoteToken.toWindowContainerToken(), true); 973 974 // Verify now that the back press does call the organizer 975 mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token, 976 new IRequestFinishCallback.Default()); 977 // Ensure events dispatch to organizer. 978 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 979 verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); 980 981 // Disable intercepting back 982 mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( 983 rootTask.mRemoteToken.toWindowContainerToken(), false); 984 985 // Verify now that the back press no longer calls the organizer 986 mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token, 987 new IRequestFinishCallback.Default()); 988 // Ensure events dispatch to organizer. 989 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 990 verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); 991 } 992 993 @Test testBLASTCallbackWithWindows()994 public void testBLASTCallbackWithWindows() throws Exception { 995 final Task rootTaskController = createRootTask(); 996 final Task task = createTask(rootTaskController); 997 final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1"); 998 final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2"); 999 makeWindowVisible(w1); 1000 makeWindowVisible(w2); 1001 1002 IWindowContainerTransactionCallback mockCallback = 1003 mock(IWindowContainerTransactionCallback.class); 1004 int id = mWm.mAtmService.mWindowOrganizerController.startSyncWithOrganizer(mockCallback); 1005 1006 mWm.mAtmService.mWindowOrganizerController.addToSyncSet(id, task); 1007 mWm.mAtmService.mWindowOrganizerController.setSyncReady(id); 1008 1009 // Since we have a window we have to wait for it to draw to finish sync. 1010 verify(mockCallback, never()).onTransactionReady(anyInt(), any()); 1011 assertTrue(w1.useBLASTSync()); 1012 assertTrue(w2.useBLASTSync()); 1013 1014 // Make second (bottom) ready. If we started with the top, since activities fillsParent 1015 // by default, the sync would be considered finished. 1016 w2.immediatelyNotifyBlastSync(); 1017 mWm.mSyncEngine.onSurfacePlacement(); 1018 verify(mockCallback, never()).onTransactionReady(anyInt(), any()); 1019 1020 assertEquals(SYNC_STATE_READY, w2.mSyncState); 1021 // Even though one Window finished drawing, both windows should still be using blast sync 1022 assertTrue(w1.useBLASTSync()); 1023 assertTrue(w2.useBLASTSync()); 1024 1025 w1.immediatelyNotifyBlastSync(); 1026 mWm.mSyncEngine.onSurfacePlacement(); 1027 verify(mockCallback).onTransactionReady(anyInt(), any()); 1028 assertFalse(w1.useBLASTSync()); 1029 assertFalse(w2.useBLASTSync()); 1030 } 1031 1032 @Test testDisplayAreaHiddenTransaction()1033 public void testDisplayAreaHiddenTransaction() { 1034 removeGlobalMinSizeRestriction(); 1035 1036 WindowContainerTransaction trx = new WindowContainerTransaction(); 1037 1038 TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); 1039 1040 trx.setHidden(taskDisplayArea.mRemoteToken.toWindowContainerToken(), true); 1041 mWm.mAtmService.mWindowOrganizerController.applyTransaction(trx); 1042 1043 taskDisplayArea.forAllTasks(daTask -> { 1044 assertTrue(daTask.isForceHidden()); 1045 }); 1046 1047 trx.setHidden(taskDisplayArea.mRemoteToken.toWindowContainerToken(), false); 1048 mWm.mAtmService.mWindowOrganizerController.applyTransaction(trx); 1049 1050 taskDisplayArea.forAllTasks(daTask -> { 1051 assertFalse(daTask.isForceHidden()); 1052 }); 1053 } 1054 1055 @Test testReparentToOrganizedTask()1056 public void testReparentToOrganizedTask() { 1057 final ITaskOrganizer organizer = registerMockOrganizer(); 1058 Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask( 1059 mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null); 1060 final Task task1 = createRootTask(); 1061 final Task task2 = createTask(rootTask, false /* fakeDraw */); 1062 WindowContainerTransaction wct = new WindowContainerTransaction(); 1063 wct.reparent(task1.mRemoteToken.toWindowContainerToken(), 1064 rootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */); 1065 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1066 assertTrue(task1.isOrganized()); 1067 assertTrue(task2.isOrganized()); 1068 } 1069 1070 @Test testAppearDeferThenInfoChange()1071 public void testAppearDeferThenInfoChange() { 1072 final ITaskOrganizer organizer = registerMockOrganizer(); 1073 final Task rootTask = createRootTask(); 1074 1075 // Assume layout defer 1076 mWm.mWindowPlacerLocked.deferLayout(); 1077 1078 final Task task = createTask(rootTask); 1079 final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); 1080 1081 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1082 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); 1083 waitUntilHandlersIdle(); 1084 1085 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask); 1086 assertEquals(1, pendingEvents.size()); 1087 assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType); 1088 assertEquals("TestDescription", 1089 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); 1090 } 1091 1092 @Test testAppearDeferThenVanish()1093 public void testAppearDeferThenVanish() { 1094 final ITaskOrganizer organizer = registerMockOrganizer(); 1095 final Task rootTask = createRootTask(); 1096 1097 // Assume layout defer 1098 mWm.mWindowPlacerLocked.deferLayout(); 1099 1100 final Task task = createTask(rootTask); 1101 1102 rootTask.removeImmediately(); 1103 waitUntilHandlersIdle(); 1104 1105 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask); 1106 assertEquals(0, pendingEvents.size()); 1107 } 1108 1109 @Test testInfoChangeDeferMultiple()1110 public void testInfoChangeDeferMultiple() { 1111 final ITaskOrganizer organizer = registerMockOrganizer(); 1112 final Task rootTask = createRootTask(); 1113 final Task task = createTask(rootTask); 1114 final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); 1115 1116 // Assume layout defer 1117 mWm.mWindowPlacerLocked.deferLayout(); 1118 1119 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1120 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); 1121 waitUntilHandlersIdle(); 1122 1123 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask); 1124 assertEquals(1, pendingEvents.size()); 1125 assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); 1126 assertEquals("TestDescription", 1127 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); 1128 1129 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2")); 1130 waitUntilHandlersIdle(); 1131 1132 pendingEvents = getTaskPendingEvent(rootTask); 1133 assertEquals(1, pendingEvents.size()); 1134 assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); 1135 assertEquals("TestDescription2", 1136 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); 1137 } 1138 1139 @Test testInfoChangDeferThenVanish()1140 public void testInfoChangDeferThenVanish() { 1141 final ITaskOrganizer organizer = registerMockOrganizer(); 1142 final Task rootTask = createRootTask(); 1143 final Task task = createTask(rootTask); 1144 final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); 1145 1146 // Assume layout defer 1147 mWm.mWindowPlacerLocked.deferLayout(); 1148 1149 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1150 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); 1151 1152 rootTask.removeImmediately(); 1153 waitUntilHandlersIdle(); 1154 1155 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask); 1156 assertEquals(1, pendingEvents.size()); 1157 assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); 1158 assertEquals("TestDescription", 1159 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); 1160 } 1161 1162 @Test testVanishDeferThenInfoChange()1163 public void testVanishDeferThenInfoChange() { 1164 final ITaskOrganizer organizer = registerMockOrganizer(); 1165 final Task rootTask = createRootTask(); 1166 final Task task = createTask(rootTask); 1167 final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); 1168 1169 // Assume layout defer 1170 mWm.mWindowPlacerLocked.deferLayout(); 1171 1172 rootTask.removeImmediately(); 1173 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1174 waitUntilHandlersIdle(); 1175 1176 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask); 1177 assertEquals(1, pendingEvents.size()); 1178 assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); 1179 } 1180 1181 @Test testVanishDeferThenBackOnRoot()1182 public void testVanishDeferThenBackOnRoot() { 1183 final ITaskOrganizer organizer = registerMockOrganizer(); 1184 final Task rootTask = createRootTask(); 1185 final Task task = createTask(rootTask); 1186 final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); 1187 1188 // Assume layout defer 1189 mWm.mWindowPlacerLocked.deferLayout(); 1190 1191 rootTask.removeImmediately(); 1192 mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token, 1193 new IRequestFinishCallback.Default()); 1194 waitUntilHandlersIdle(); 1195 1196 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask); 1197 assertEquals(1, pendingEvents.size()); 1198 assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); 1199 } 1200 getTaskPendingEvent(Task task)1201 private ArrayList<PendingTaskEvent> getTaskPendingEvent(Task task) { 1202 ArrayList<PendingTaskEvent> total = 1203 mWm.mAtmService.mTaskOrganizerController.getPendingEventList(); 1204 ArrayList<PendingTaskEvent> result = new ArrayList(); 1205 1206 for (int i = 0; i < total.size(); i++) { 1207 PendingTaskEvent entry = total.get(i); 1208 if (entry.mTask.mTaskId == task.mTaskId) { 1209 result.add(entry); 1210 } 1211 } 1212 1213 return result; 1214 } 1215 1216 @Test testReparentNonResizableTaskToSplitScreen()1217 public void testReparentNonResizableTaskToSplitScreen() { 1218 final ActivityRecord activity = new ActivityBuilder(mAtm) 1219 .setCreateTask(true) 1220 .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE) 1221 .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) 1222 .build(); 1223 final Task rootTask = activity.getRootTask(); 1224 rootTask.setResizeMode(activity.info.resizeMode); 1225 final Task splitPrimaryRootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask( 1226 mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null); 1227 final WindowContainerTransaction wct = new WindowContainerTransaction(); 1228 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), 1229 splitPrimaryRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */); 1230 1231 // Can't reparent non-resizable to split screen 1232 mAtm.mSupportsNonResizableMultiWindow = -1; 1233 mAtm.mWindowOrganizerController.applyTransaction(wct); 1234 1235 assertEquals(rootTask, activity.getRootTask()); 1236 1237 // Allow reparent non-resizable to split screen 1238 mAtm.mSupportsNonResizableMultiWindow = 1; 1239 mAtm.mWindowOrganizerController.applyTransaction(wct); 1240 1241 assertEquals(splitPrimaryRootTask, activity.getRootTask()); 1242 } 1243 1244 @Test testSizeCompatModeChangedOnFirstOrganizedTask()1245 public void testSizeCompatModeChangedOnFirstOrganizedTask() throws RemoteException { 1246 final ITaskOrganizer organizer = registerMockOrganizer(); 1247 final Task rootTask = createRootTask(); 1248 final Task task = createTask(rootTask); 1249 final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task); 1250 final ArgumentCaptor<RunningTaskInfo> infoCaptor = 1251 ArgumentCaptor.forClass(RunningTaskInfo.class); 1252 1253 assertTrue(rootTask.isOrganized()); 1254 1255 spyOn(activity); 1256 doReturn(true).when(activity).inSizeCompatMode(); 1257 doReturn(true).when(activity).isState(RESUMED); 1258 1259 // Ensure task info show top activity in size compat. 1260 rootTask.onSizeCompatActivityChanged(); 1261 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1262 verify(organizer).onTaskInfoChanged(infoCaptor.capture()); 1263 RunningTaskInfo info = infoCaptor.getValue(); 1264 assertEquals(rootTask.mTaskId, info.taskId); 1265 assertTrue(info.topActivityInSizeCompat); 1266 1267 // Ensure task info show top activity that is not in foreground as not in size compat. 1268 clearInvocations(organizer); 1269 doReturn(false).when(activity).isState(RESUMED); 1270 rootTask.onSizeCompatActivityChanged(); 1271 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1272 verify(organizer).onTaskInfoChanged(infoCaptor.capture()); 1273 info = infoCaptor.getValue(); 1274 assertEquals(rootTask.mTaskId, info.taskId); 1275 assertFalse(info.topActivityInSizeCompat); 1276 1277 // Ensure task info show non size compat top activity as not in size compat. 1278 clearInvocations(organizer); 1279 doReturn(true).when(activity).isState(RESUMED); 1280 doReturn(false).when(activity).inSizeCompatMode(); 1281 rootTask.onSizeCompatActivityChanged(); 1282 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1283 verify(organizer).onTaskInfoChanged(infoCaptor.capture()); 1284 info = infoCaptor.getValue(); 1285 assertEquals(rootTask.mTaskId, info.taskId); 1286 assertFalse(info.topActivityInSizeCompat); 1287 } 1288 1289 @Test testStartTasksInTransaction()1290 public void testStartTasksInTransaction() { 1291 WindowContainerTransaction wct = new WindowContainerTransaction(); 1292 ActivityOptions testOptions = ActivityOptions.makeBasic(); 1293 testOptions.setTransientLaunch(); 1294 wct.startTask(1, null /* options */); 1295 wct.startTask(2, testOptions.toBundle()); 1296 spyOn(mWm.mAtmService.mTaskSupervisor); 1297 doReturn(START_CANCELED).when(mWm.mAtmService.mTaskSupervisor).startActivityFromRecents( 1298 anyInt(), anyInt(), anyInt(), any()); 1299 clearInvocations(mWm.mAtmService); 1300 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1301 1302 verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents( 1303 anyInt(), anyInt(), eq(1), any()); 1304 1305 final ArgumentCaptor<SafeActivityOptions> optionsCaptor = 1306 ArgumentCaptor.forClass(SafeActivityOptions.class); 1307 verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents( 1308 anyInt(), anyInt(), eq(2), optionsCaptor.capture()); 1309 assertTrue(optionsCaptor.getValue().getOriginalOptions().getTransientLaunch()); 1310 } 1311 1312 @Test testResumeTopsWhenLeavingPinned()1313 public void testResumeTopsWhenLeavingPinned() { 1314 final ActivityRecord record = makePipableActivity(); 1315 final Task rootTask = record.getRootTask(); 1316 1317 clearInvocations(mWm.mAtmService.mRootWindowContainer); 1318 final WindowContainerTransaction t = new WindowContainerTransaction(); 1319 WindowContainerToken wct = rootTask.mRemoteToken.toWindowContainerToken(); 1320 t.setWindowingMode(wct, WINDOWING_MODE_PINNED); 1321 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 1322 verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities(); 1323 1324 clearInvocations(mWm.mAtmService.mRootWindowContainer); 1325 t.setWindowingMode(wct, WINDOWING_MODE_FULLSCREEN); 1326 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 1327 verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities(); 1328 } 1329 1330 /** 1331 * Verifies that task vanished is called for a specific task. 1332 */ assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks)1333 private void assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks) 1334 throws RemoteException { 1335 ArgumentCaptor<RunningTaskInfo> arg = ArgumentCaptor.forClass(RunningTaskInfo.class); 1336 verify(organizer, atLeastOnce()).onTaskVanished(arg.capture()); 1337 List<RunningTaskInfo> taskInfos = arg.getAllValues(); 1338 1339 HashSet<Integer> vanishedTaskIds = new HashSet<>(); 1340 for (int i = 0; i < taskInfos.size(); i++) { 1341 vanishedTaskIds.add(taskInfos.get(i).taskId); 1342 } 1343 HashSet<Integer> taskIds = new HashSet<>(); 1344 for (int i = 0; i < tasks.length; i++) { 1345 taskIds.add(tasks[i].mTaskId); 1346 } 1347 1348 assertTrue(expectVanished 1349 ? vanishedTaskIds.containsAll(taskIds) 1350 : !vanishedTaskIds.removeAll(taskIds)); 1351 } 1352 assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks)1353 private void assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks) { 1354 HashSet<Integer> taskIds = new HashSet<>(); 1355 for (int i = 0; i < taskInfos.size(); i++) { 1356 taskIds.add(taskInfos.get(i).getTaskInfo().taskId); 1357 } 1358 for (int i = 0; i < expectedTasks.length; i++) { 1359 assertTrue(taskIds.contains(expectedTasks[i].mTaskId)); 1360 } 1361 } 1362 } 1363