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_UNDEFINED; 28 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 29 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 30 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 31 import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; 32 import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; 33 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 34 35 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 36 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 37 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 38 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; 39 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 40 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; 41 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 43 import static com.android.server.wm.ActivityRecord.State.RESUMED; 44 import static com.android.server.wm.WindowContainer.POSITION_TOP; 45 import static com.android.server.wm.WindowContainer.SYNC_STATE_READY; 46 import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION; 47 48 import static com.google.common.truth.Truth.assertThat; 49 50 import static org.junit.Assert.assertEquals; 51 import static org.junit.Assert.assertFalse; 52 import static org.junit.Assert.assertNotNull; 53 import static org.junit.Assert.assertTrue; 54 import static org.mockito.ArgumentMatchers.any; 55 import static org.mockito.ArgumentMatchers.anyBoolean; 56 import static org.mockito.ArgumentMatchers.anyInt; 57 import static org.mockito.ArgumentMatchers.eq; 58 import static org.mockito.Mockito.atLeastOnce; 59 import static org.mockito.Mockito.clearInvocations; 60 61 import android.app.ActivityManager; 62 import android.app.ActivityManager.RunningTaskInfo; 63 import android.app.ActivityOptions; 64 import android.app.ActivityTaskManager.RootTaskInfo; 65 import android.app.IRequestFinishCallback; 66 import android.app.PictureInPictureParams; 67 import android.content.pm.ActivityInfo; 68 import android.content.pm.ParceledListSlice; 69 import android.content.res.Configuration; 70 import android.graphics.Rect; 71 import android.os.Binder; 72 import android.os.IBinder; 73 import android.os.RemoteException; 74 import android.platform.test.annotations.Presubmit; 75 import android.util.ArrayMap; 76 import android.util.Rational; 77 import android.view.Display; 78 import android.view.SurfaceControl; 79 import android.view.WindowInsets; 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 verify(organizer, times(0)).onTaskVanished(any()); 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 testRemoveWithOrganizerRemovesTask()251 public void testRemoveWithOrganizerRemovesTask() throws RemoteException { 252 final ITaskOrganizer organizer = registerMockOrganizer(); 253 final Task rootTask = createRootTask(); 254 final Task task = createTask(rootTask); 255 rootTask.mRemoveWithTaskOrganizer = true; 256 257 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 258 verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 259 assertTrue(rootTask.isOrganized()); 260 261 spyOn(mWm.mAtmService); 262 rootTask.setTaskOrganizer(null); 263 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 264 265 verify(mWm.mAtmService).removeTask(eq(rootTask.mTaskId)); 266 } 267 268 @Test testNoRemoveWithOrganizerNoRemoveTask()269 public void testNoRemoveWithOrganizerNoRemoveTask() throws RemoteException { 270 final ITaskOrganizer organizer = registerMockOrganizer(); 271 final Task rootTask = createRootTask(); 272 final Task task = createTask(rootTask); 273 rootTask.mRemoveWithTaskOrganizer = false; 274 275 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 276 verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 277 assertTrue(rootTask.isOrganized()); 278 279 spyOn(mWm.mAtmService); 280 rootTask.setTaskOrganizer(null); 281 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 282 283 verify(mWm.mAtmService, never()).removeTask(eq(rootTask.mTaskId)); 284 } 285 286 @Test testUnregisterOrganizer()287 public void testUnregisterOrganizer() throws RemoteException { 288 final ITaskOrganizer organizer = registerMockOrganizer(); 289 final Task rootTask = createRootTask(); 290 final Task task = createTask(rootTask); 291 // Ensure events dispatch to organizer. 292 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 293 294 verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 295 assertTrue(rootTask.isOrganized()); 296 297 mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); 298 // Ensure events dispatch to organizer. 299 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 300 301 verify(organizer, times(0)).onTaskVanished(any()); 302 assertFalse(rootTask.isOrganized()); 303 } 304 305 @Test testUnregisterOrganizerReturnsRegistrationToPrevious()306 public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException { 307 final Task rootTask = createRootTask(); 308 final Task task = createTask(rootTask); 309 final Task rootTask2 = createRootTask(); 310 final Task task2 = createTask(rootTask2); 311 final Task rootTask3 = createRootTask(); 312 final Task task3 = createTask(rootTask3); 313 final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); 314 final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); 315 // Ensure events dispatch to organizer. 316 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 317 318 // verify that tasks are returned and taskAppeared is not called 319 assertContainsTasks(existingTasks, rootTask, rootTask2, rootTask3); 320 verify(organizer, times(0)).onTaskAppeared(any(RunningTaskInfo.class), 321 any(SurfaceControl.class)); 322 verify(organizer, times(0)).onTaskVanished(any()); 323 assertTrue(rootTask.isOrganized()); 324 325 // Now we replace the registration and verify the new organizer receives existing tasks 326 final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>(); 327 final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2); 328 // Ensure events dispatch to organizer. 329 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 330 assertContainsTasks(existingTasks2, rootTask, rootTask2, rootTask3); 331 verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class), 332 any(SurfaceControl.class)); 333 verify(organizer2, times(0)).onTaskVanished(any()); 334 // Removed tasks from the original organizer 335 assertTaskVanished(organizer, true /* expectVanished */, rootTask, rootTask2, rootTask3); 336 assertTrue(rootTask2.isOrganized()); 337 338 // Now we unregister the second one, the first one should automatically be reregistered 339 // so we verify that it's now seeing changes. 340 mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2); 341 // Ensure events dispatch to organizer. 342 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 343 verify(organizer, times(3)) 344 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 345 verify(organizer2, times(0)).onTaskVanished(any()); 346 } 347 348 @Test testUnregisterOrganizer_removesTasksCreatedByIt()349 public void testUnregisterOrganizer_removesTasksCreatedByIt() throws RemoteException { 350 final Task rootTask = createRootTask(); 351 final Task task = createTask(rootTask); 352 final Task rootTask2 = createRootTask(); 353 rootTask2.mCreatedByOrganizer = true; 354 final Task task2 = createTask(rootTask2); 355 final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); 356 final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); 357 // Ensure events dispatch to organizer. 358 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 359 360 // verify that tasks are returned and taskAppeared is called only for rootTask2 since it 361 // is the one created by this organizer. 362 assertContainsTasks(existingTasks, rootTask); 363 verify(organizer, times(1)).onTaskAppeared(any(RunningTaskInfo.class), 364 any(SurfaceControl.class)); 365 verify(organizer, times(0)).onTaskVanished(any()); 366 assertTrue(rootTask.isOrganized()); 367 368 // Now we replace the registration and verify the new organizer receives existing tasks 369 final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>(); 370 final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2); 371 // Ensure events dispatch to organizer. 372 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 373 assertContainsTasks(existingTasks2, rootTask); 374 verify(organizer2, never()).onTaskAppeared(any(RunningTaskInfo.class), 375 any(SurfaceControl.class)); 376 verify(organizer2, times(0)).onTaskVanished(any()); 377 // The non-CreatedByOrganizer task is removed from the original organizer. 378 assertTaskVanished(organizer, true /* expectVanished */, rootTask); 379 assertEquals(organizer2, rootTask.mTaskOrganizer); 380 // The CreatedByOrganizer task should be still organized by the original organizer. 381 assertEquals(organizer, rootTask2.mTaskOrganizer); 382 383 clearInvocations(organizer); 384 // Now we unregister the second one, the first one should automatically be reregistered 385 // so we verify that it's now seeing changes. 386 mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2); 387 // Ensure events dispatch to organizer. 388 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 389 390 verify(organizer, times(2)) 391 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 392 393 // Unregister the first one. The CreatedByOrganizer task created by it must be removed. 394 mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); 395 assertFalse(rootTask2.isAttached()); 396 assertFalse(task2.isAttached()); 397 // Normal task should keep. 398 assertTrue(task.isAttached()); 399 verify(organizer2, times(0)).onTaskVanished(any()); 400 } 401 402 @Test testOrganizerDeathReturnsRegistrationToPrevious()403 public void testOrganizerDeathReturnsRegistrationToPrevious() throws RemoteException { 404 final Task rootTask = createRootTask(); 405 final Task task = createTask(rootTask); 406 final Task rootTask2 = createRootTask(); 407 final Task task2 = createTask(rootTask2); 408 final Task rootTask3 = createRootTask(); 409 final Task task3 = createTask(rootTask3); 410 final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); 411 final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); 412 // Ensure events dispatch to organizer. 413 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 414 415 // verify that tasks are returned and taskAppeared is not called 416 assertContainsTasks(existingTasks, rootTask, rootTask2, rootTask3); 417 verify(organizer, times(0)).onTaskAppeared(any(RunningTaskInfo.class), 418 any(SurfaceControl.class)); 419 verify(organizer, times(0)).onTaskVanished(any()); 420 assertTrue(rootTask.isOrganized()); 421 422 // Now we replace the registration and verify the new organizer receives existing tasks 423 final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>(); 424 final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2); 425 // Ensure events dispatch to organizer. 426 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 427 assertContainsTasks(existingTasks2, rootTask, rootTask2, rootTask3); 428 verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class), 429 any(SurfaceControl.class)); 430 verify(organizer2, times(0)).onTaskVanished(any()); 431 // Removed tasks from the original organizer 432 assertTaskVanished(organizer, true /* expectVanished */, rootTask, rootTask2, rootTask3); 433 assertTrue(rootTask2.isOrganized()); 434 435 // Trigger binderDied for second one, the first one should automatically be reregistered 436 // so we verify that it's now seeing changes. 437 mWm.mAtmService.mTaskOrganizerController.getTaskOrganizerState(organizer2.asBinder()) 438 .getDeathRecipient().binderDied(); 439 440 // Ensure events dispatch to organizer. 441 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 442 verify(organizer, times(3)) 443 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 444 verify(organizer2, times(0)).onTaskVanished(any()); 445 } 446 447 @Test testRegisterTaskOrganizerWithExistingTasks()448 public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException { 449 final Task rootTask = createRootTask(); 450 final Task task = createTask(rootTask); 451 final Task rootTask2 = createRootTask(); 452 final Task task2 = createTask(rootTask2); 453 ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); 454 final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); 455 assertContainsTasks(existingTasks, rootTask, rootTask2); 456 457 // Verify we don't get onTaskAppeared if we are returned the tasks 458 verify(organizer, never()) 459 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 460 } 461 462 @Test testRegisterTaskOrganizerWithExistingTasks_noSurfaceControl()463 public void testRegisterTaskOrganizerWithExistingTasks_noSurfaceControl() 464 throws RemoteException { 465 final Task rootTask = createRootTask(); 466 final Task task = createTask(rootTask); 467 final Task rootTask2 = createRootTask(); 468 final Task task2 = createTask(rootTask2); 469 rootTask2.setSurfaceControl(null); 470 ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); 471 final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); 472 assertContainsTasks(existingTasks, rootTask); 473 474 // Verify we don't get onTaskAppeared if we are returned the tasks 475 verify(organizer, never()) 476 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 477 } 478 479 @Test testTaskTransaction()480 public void testTaskTransaction() { 481 removeGlobalMinSizeRestriction(); 482 final Task rootTask = new TaskBuilder(mSupervisor) 483 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 484 final Task task = rootTask.getTopMostTask(); 485 testTransaction(task); 486 } 487 488 @Test testRootTaskTransaction()489 public void testRootTaskTransaction() { 490 removeGlobalMinSizeRestriction(); 491 final Task rootTask = new TaskBuilder(mSupervisor) 492 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 493 RootTaskInfo info = 494 mWm.mAtmService.getRootTaskInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD); 495 assertEquals(rootTask.mRemoteToken.toWindowContainerToken(), info.token); 496 testTransaction(rootTask); 497 } 498 499 @Test testDisplayAreaTransaction()500 public void testDisplayAreaTransaction() { 501 removeGlobalMinSizeRestriction(); 502 final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea(); 503 testTransaction(displayArea); 504 } 505 testTransaction(WindowContainer wc)506 private void testTransaction(WindowContainer wc) { 507 WindowContainerTransaction t = new WindowContainerTransaction(); 508 Rect newBounds = new Rect(10, 10, 100, 100); 509 t.setBounds(wc.mRemoteToken.toWindowContainerToken(), new Rect(10, 10, 100, 100)); 510 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 511 assertEquals(newBounds, wc.getBounds()); 512 } 513 514 @Test testSetWindowingMode()515 public void testSetWindowingMode() { 516 final Task rootTask = new TaskBuilder(mSupervisor) 517 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 518 testSetWindowingMode(rootTask); 519 520 final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea(); 521 displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM); 522 testSetWindowingMode(displayArea); 523 } 524 testSetWindowingMode(WindowContainer wc)525 private void testSetWindowingMode(WindowContainer wc) { 526 final WindowContainerTransaction t = new WindowContainerTransaction(); 527 t.setWindowingMode(wc.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN); 528 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 529 assertEquals(WINDOWING_MODE_FULLSCREEN, wc.getWindowingMode()); 530 } 531 532 @Test testSetActivityWindowingMode()533 public void testSetActivityWindowingMode() { 534 final ActivityRecord record = makePipableActivity(); 535 final Task rootTask = record.getRootTask(); 536 final WindowContainerTransaction t = new WindowContainerTransaction(); 537 538 t.setWindowingMode(rootTask.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_PINNED); 539 t.setActivityWindowingMode( 540 rootTask.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN); 541 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 542 543 assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode()); 544 // Get the root task from the PIP activity record again, since the PIP root task may have 545 // changed when the activity entered PIP mode. 546 final Task pipRootTask = record.getRootTask(); 547 assertEquals(WINDOWING_MODE_PINNED, pipRootTask.getWindowingMode()); 548 } 549 550 @Test testContainerFocusableChanges()551 public void testContainerFocusableChanges() { 552 removeGlobalMinSizeRestriction(); 553 final Task rootTask = new TaskBuilder(mSupervisor) 554 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 555 final Task task = rootTask.getTopMostTask(); 556 WindowContainerTransaction t = new WindowContainerTransaction(); 557 assertTrue(task.isFocusable()); 558 t.setFocusable(rootTask.mRemoteToken.toWindowContainerToken(), false); 559 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 560 assertFalse(task.isFocusable()); 561 t.setFocusable(rootTask.mRemoteToken.toWindowContainerToken(), true); 562 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 563 assertTrue(task.isFocusable()); 564 } 565 566 @Test testContainerHiddenChanges()567 public void testContainerHiddenChanges() { 568 removeGlobalMinSizeRestriction(); 569 final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) 570 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 571 WindowContainerTransaction t = new WindowContainerTransaction(); 572 assertTrue(rootTask.shouldBeVisible(null)); 573 t.setHidden(rootTask.mRemoteToken.toWindowContainerToken(), true); 574 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 575 assertFalse(rootTask.shouldBeVisible(null)); 576 t.setHidden(rootTask.mRemoteToken.toWindowContainerToken(), false); 577 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 578 assertTrue(rootTask.shouldBeVisible(null)); 579 } 580 581 @Test testContainerTranslucentChanges()582 public void testContainerTranslucentChanges() { 583 removeGlobalMinSizeRestriction(); 584 final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) 585 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build(); 586 final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build(); 587 WindowContainerTransaction t = new WindowContainerTransaction(); 588 assertFalse(rootTask.isTranslucent(activity)); 589 t.setForceTranslucent(rootTask.mRemoteToken.toWindowContainerToken(), true); 590 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 591 assertTrue(rootTask.isTranslucent(activity)); 592 t.setForceTranslucent(rootTask.mRemoteToken.toWindowContainerToken(), false); 593 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 594 assertFalse(rootTask.isTranslucent(activity)); 595 } 596 597 @Test testSetIgnoreOrientationRequest_taskDisplayArea()598 public void testSetIgnoreOrientationRequest_taskDisplayArea() { 599 removeGlobalMinSizeRestriction(); 600 final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); 601 final Task rootTask = taskDisplayArea.createRootTask( 602 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 603 final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build(); 604 taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 605 mDisplayContent.setFocusedApp(activity); 606 activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 607 608 // TDA returns UNSET when ignoreOrientationRequest == true 609 // DC is UNSPECIFIED when child returns UNSET 610 assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET); 611 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); 612 613 WindowContainerTransaction t = new WindowContainerTransaction(); 614 t.setIgnoreOrientationRequest( 615 taskDisplayArea.mRemoteToken.toWindowContainerToken(), 616 false /* ignoreOrientationRequest */); 617 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 618 619 // TDA returns app request orientation when ignoreOrientationRequest == false 620 // DC uses the same as TDA returns when it is not UNSET. 621 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 622 assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 623 624 t.setIgnoreOrientationRequest( 625 taskDisplayArea.mRemoteToken.toWindowContainerToken(), 626 true /* ignoreOrientationRequest */); 627 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 628 629 // TDA returns UNSET when ignoreOrientationRequest == true 630 // DC is UNSPECIFIED when child returns UNSET 631 assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET); 632 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); 633 } 634 635 @Test testSetIgnoreOrientationRequest_displayContent()636 public void testSetIgnoreOrientationRequest_displayContent() { 637 removeGlobalMinSizeRestriction(); 638 final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); 639 final Task rootTask = taskDisplayArea.createRootTask( 640 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 641 final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build(); 642 mDisplayContent.setFocusedApp(activity); 643 activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 644 645 // DC uses the orientation request from app 646 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 647 648 WindowContainerTransaction t = new WindowContainerTransaction(); 649 t.setIgnoreOrientationRequest( 650 mDisplayContent.mRemoteToken.toWindowContainerToken(), 651 true /* ignoreOrientationRequest */); 652 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 653 654 // DC returns UNSPECIFIED when ignoreOrientationRequest == true 655 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); 656 657 t.setIgnoreOrientationRequest( 658 mDisplayContent.mRemoteToken.toWindowContainerToken(), 659 false /* ignoreOrientationRequest */); 660 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 661 662 // DC uses the orientation request from app after mIgnoreOrientationRequest is set to false 663 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 664 } 665 666 @Test testOverrideConfigSize()667 public void testOverrideConfigSize() { 668 removeGlobalMinSizeRestriction(); 669 final Task rootTask = new TaskBuilder(mSupervisor) 670 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 671 final Task task = rootTask.getTopMostTask(); 672 WindowContainerTransaction t = new WindowContainerTransaction(); 673 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 674 final int origScreenWDp = task.getConfiguration().screenHeightDp; 675 final int origScreenHDp = task.getConfiguration().screenHeightDp; 676 t = new WindowContainerTransaction(); 677 // verify that setting config overrides on parent restricts children. 678 t.setScreenSizeDp(rootTask.mRemoteToken 679 .toWindowContainerToken(), origScreenWDp, origScreenHDp / 2); 680 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 681 assertEquals(origScreenHDp / 2, task.getConfiguration().screenHeightDp); 682 t = new WindowContainerTransaction(); 683 t.setScreenSizeDp(rootTask.mRemoteToken.toWindowContainerToken(), SCREEN_WIDTH_DP_UNDEFINED, 684 SCREEN_HEIGHT_DP_UNDEFINED); 685 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 686 assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp); 687 } 688 689 @Test testCreateDeleteRootTasks()690 public void testCreateDeleteRootTasks() { 691 DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY); 692 693 Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 694 dc, WINDOWING_MODE_FULLSCREEN, null); 695 RunningTaskInfo info1 = task1.getTaskInfo(); 696 assertEquals(WINDOWING_MODE_FULLSCREEN, 697 info1.configuration.windowConfiguration.getWindowingMode()); 698 assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType); 699 700 Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 701 dc, WINDOWING_MODE_MULTI_WINDOW, null); 702 RunningTaskInfo info2 = task2.getTaskInfo(); 703 assertEquals(WINDOWING_MODE_MULTI_WINDOW, 704 info2.configuration.windowConfiguration.getWindowingMode()); 705 assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType); 706 707 List<Task> infos = getTasksCreatedByOrganizer(dc); 708 assertEquals(2, infos.size()); 709 710 assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token)); 711 infos = getTasksCreatedByOrganizer(dc); 712 assertEquals(1, infos.size()); 713 assertEquals(WINDOWING_MODE_MULTI_WINDOW, infos.get(0).getWindowingMode()); 714 } 715 716 @Test testSetAdjacentLaunchRoot()717 public void testSetAdjacentLaunchRoot() { 718 DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY); 719 720 final Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 721 dc, WINDOWING_MODE_MULTI_WINDOW, null); 722 final RunningTaskInfo info1 = task1.getTaskInfo(); 723 final Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 724 dc, WINDOWING_MODE_MULTI_WINDOW, null); 725 final RunningTaskInfo info2 = task2.getTaskInfo(); 726 727 WindowContainerTransaction wct = new WindowContainerTransaction(); 728 wct.setAdjacentRoots(info1.token, info2.token); 729 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 730 assertEquals(task1.getAdjacentTaskFragment(), task2); 731 assertEquals(task2.getAdjacentTaskFragment(), task1); 732 733 wct = new WindowContainerTransaction(); 734 wct.setLaunchAdjacentFlagRoot(info1.token); 735 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 736 assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, task1); 737 738 wct = new WindowContainerTransaction(); 739 wct.clearAdjacentRoots(info1.token); 740 wct.clearLaunchAdjacentFlagRoot(info1.token); 741 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 742 assertEquals(task1.getAdjacentTaskFragment(), null); 743 assertEquals(task2.getAdjacentTaskFragment(), null); 744 assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, null); 745 } 746 747 @Test testTileAddRemoveChild()748 public void testTileAddRemoveChild() { 749 final StubOrganizer listener = new StubOrganizer(); 750 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); 751 Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask( 752 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null); 753 RunningTaskInfo info1 = task.getTaskInfo(); 754 755 final Task rootTask = createTask( 756 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); 757 assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode()); 758 WindowContainerTransaction wct = new WindowContainerTransaction(); 759 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */); 760 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 761 assertEquals(info1.configuration.windowConfiguration.getWindowingMode(), 762 rootTask.getWindowingMode()); 763 764 // Info should reflect new membership 765 List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent); 766 info1 = infos.get(0).getTaskInfo(); 767 assertEquals(ACTIVITY_TYPE_STANDARD, info1.topActivityType); 768 769 // Children inherit configuration 770 Rect newSize = new Rect(10, 10, 300, 300); 771 Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask(); 772 Configuration c = new Configuration(task1.getRequestedOverrideConfiguration()); 773 c.windowConfiguration.setBounds(newSize); 774 doNothing().when(rootTask).adjustForMinimalTaskDimensions(any(), any(), any()); 775 task1.onRequestedOverrideConfigurationChanged(c); 776 assertEquals(newSize, rootTask.getBounds()); 777 778 wct = new WindowContainerTransaction(); 779 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); 780 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 781 assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode()); 782 infos = getTasksCreatedByOrganizer(mDisplayContent); 783 info1 = infos.get(0).getTaskInfo(); 784 assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType); 785 } 786 787 @Test testAddInsetsSource()788 public void testAddInsetsSource() { 789 final Task rootTask = createTask(mDisplayContent); 790 791 final Task navigationBarInsetsReceiverTask = createTaskInRootTask(rootTask, 0); 792 navigationBarInsetsReceiverTask.getConfiguration().windowConfiguration.setBounds(new Rect( 793 0, 200, 1080, 700)); 794 795 final WindowContainerTransaction wct = new WindowContainerTransaction(); 796 wct.addInsetsSource( 797 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(), 798 new Binder(), 799 0 /* index */, 800 WindowInsets.Type.systemOverlays(), 801 new Rect(0, 0, 1080, 200)); 802 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 803 804 assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSources 805 .valueAt(0).getType()).isEqualTo( 806 WindowInsets.Type.systemOverlays()); 807 } 808 809 @Test testRemoveInsetsSource()810 public void testRemoveInsetsSource() { 811 final Task rootTask = createTask(mDisplayContent); 812 813 final Task navigationBarInsetsReceiverTask = createTaskInRootTask(rootTask, 0); 814 navigationBarInsetsReceiverTask.getConfiguration().windowConfiguration.setBounds(new Rect( 815 0, 200, 1080, 700)); 816 final Binder owner = new Binder(); 817 final WindowContainerTransaction wct = new WindowContainerTransaction(); 818 wct.addInsetsSource( 819 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(), 820 owner, 821 0 /* index */, 822 WindowInsets.Type.systemOverlays(), 823 new Rect(0, 0, 1080, 200)); 824 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 825 826 final WindowContainerTransaction wct2 = new WindowContainerTransaction(); 827 wct2.removeInsetsSource( 828 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(), 829 owner, 830 0 /* index */, 831 WindowInsets.Type.systemOverlays()); 832 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct2); 833 834 assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSources.size()).isEqualTo(0); 835 } 836 837 @Test testTaskInfoCallback()838 public void testTaskInfoCallback() { 839 final ArrayList<RunningTaskInfo> lastReportedTiles = new ArrayList<>(); 840 final boolean[] called = {false}; 841 final StubOrganizer listener = new StubOrganizer() { 842 @Override 843 public void onTaskInfoChanged(RunningTaskInfo info) { 844 lastReportedTiles.add(info); 845 called[0] = true; 846 } 847 }; 848 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); 849 Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask( 850 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null); 851 RunningTaskInfo info1 = task.getTaskInfo(); 852 // Ensure events dispatch to organizer. 853 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 854 lastReportedTiles.clear(); 855 called[0] = false; 856 857 final Task rootTask = createTask( 858 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); 859 Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask(); 860 WindowContainerTransaction wct = new WindowContainerTransaction(); 861 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */); 862 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 863 assertTrue(called[0]); 864 assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType); 865 866 lastReportedTiles.clear(); 867 called[0] = false; 868 final Task rootTask2 = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask(); 869 wct = new WindowContainerTransaction(); 870 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), 871 info1.token, true /* onTop */); 872 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 873 assertTrue(called[0]); 874 assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType); 875 876 lastReportedTiles.clear(); 877 called[0] = false; 878 task1.positionChildAt(POSITION_TOP, rootTask, false /* includingParents */); 879 mAtm.mTaskOrganizerController.dispatchPendingEvents(); 880 assertTrue(called[0]); 881 assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType); 882 883 lastReportedTiles.clear(); 884 called[0] = false; 885 wct = new WindowContainerTransaction(); 886 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), 887 null, true /* onTop */); 888 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), 889 null, true /* onTop */); 890 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 891 assertTrue(called[0]); 892 assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType); 893 } 894 895 @Test testHierarchyTransaction()896 public void testHierarchyTransaction() { 897 final ArrayMap<IBinder, RunningTaskInfo> lastReportedTiles = new ArrayMap<>(); 898 final StubOrganizer listener = new StubOrganizer() { 899 @Override 900 public void onTaskInfoChanged(RunningTaskInfo info) { 901 lastReportedTiles.put(info.token.asBinder(), info); 902 } 903 }; 904 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); 905 906 Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 907 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null); 908 RunningTaskInfo info1 = task1.getTaskInfo(); 909 Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 910 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null); 911 RunningTaskInfo info2 = task2.getTaskInfo(); 912 // Ensure events dispatch to organizer. 913 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 914 915 // 2 + 1 (home) = 3 916 final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks( 917 mDisplayContent.mDisplayId, null /* activityTypes */).size(); 918 final Task rootTask = createTask( 919 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); 920 921 // Check getRootTasks works 922 List<RunningTaskInfo> roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks( 923 mDisplayContent.mDisplayId, null /* activityTypes */); 924 assertEquals(initialRootTaskCount + 1, roots.size()); 925 926 lastReportedTiles.clear(); 927 WindowContainerTransaction wct = new WindowContainerTransaction(); 928 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), 929 info1.token, true /* onTop */); 930 final Task rootTask2 = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask(); 931 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), 932 info2.token, true /* onTop */); 933 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 934 assertFalse(lastReportedTiles.isEmpty()); 935 assertEquals(ACTIVITY_TYPE_STANDARD, 936 lastReportedTiles.get(info1.token.asBinder()).topActivityType); 937 assertEquals(ACTIVITY_TYPE_HOME, 938 lastReportedTiles.get(info2.token.asBinder()).topActivityType); 939 940 lastReportedTiles.clear(); 941 wct = new WindowContainerTransaction(); 942 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), 943 info1.token, false /* onTop */); 944 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 945 assertFalse(lastReportedTiles.isEmpty()); 946 // Standard should still be on top of tile 1, so no change there 947 assertFalse(lastReportedTiles.containsKey(info1.token.asBinder())); 948 // But tile 2 has no children, so should become undefined 949 assertEquals(ACTIVITY_TYPE_UNDEFINED, 950 lastReportedTiles.get(info2.token.asBinder()).topActivityType); 951 952 // Check the getChildren call 953 List<RunningTaskInfo> children = 954 mWm.mAtmService.mTaskOrganizerController.getChildTasks(info1.token, 955 null /* activityTypes */); 956 assertEquals(2, children.size()); 957 children = mWm.mAtmService.mTaskOrganizerController.getChildTasks(info2.token, 958 null /* activityTypes */); 959 assertEquals(0, children.size()); 960 961 // Check that getRootTasks doesn't include children of tiles 962 roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks(mDisplayContent.mDisplayId, 963 null /* activityTypes */); 964 // Home (rootTask2) was moved into task1, so only remain 2 roots: task1 and task2. 965 assertEquals(initialRootTaskCount - 1, roots.size()); 966 967 lastReportedTiles.clear(); 968 wct = new WindowContainerTransaction(); 969 wct.reorder(rootTask2.mRemoteToken.toWindowContainerToken(), true /* onTop */); 970 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 971 // Home should now be on top. No change occurs in second tile, so not reported 972 assertEquals(1, lastReportedTiles.size()); 973 assertEquals(ACTIVITY_TYPE_HOME, 974 lastReportedTiles.get(info1.token.asBinder()).topActivityType); 975 976 // This just needs to not crash (ie. it should be possible to reparent to display twice) 977 wct = new WindowContainerTransaction(); 978 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); 979 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 980 wct = new WindowContainerTransaction(); 981 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); 982 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 983 } 984 getTasksCreatedByOrganizer(DisplayContent dc)985 private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) { 986 final ArrayList<Task> out = new ArrayList<>(); 987 dc.forAllRootTasks(task -> { 988 if (task.mCreatedByOrganizer) { 989 out.add(task); 990 } 991 }); 992 return out; 993 } 994 995 @Test testBLASTCallbackWithActivityChildren()996 public void testBLASTCallbackWithActivityChildren() { 997 final Task rootTaskController1 = createRootTask(); 998 final Task task = createTask(rootTaskController1); 999 final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window"); 1000 1001 w.mActivityRecord.setVisibleRequested(true); 1002 w.mActivityRecord.setVisible(true); 1003 1004 BLASTSyncEngine bse = new BLASTSyncEngine(mWm); 1005 1006 BLASTSyncEngine.TransactionReadyListener transactionListener = 1007 mock(BLASTSyncEngine.TransactionReadyListener.class); 1008 1009 final int id = bse.startSyncSet(transactionListener, BLAST_TIMEOUT_DURATION, "Test", 1010 false /* parallel */); 1011 bse.addToSyncSet(id, task); 1012 bse.setReady(id); 1013 bse.onSurfacePlacement(); 1014 1015 // Even though w is invisible (and thus activity isn't waiting on it), activity will 1016 // continue to wait until it has at-least 1 visible window. 1017 // Since we have a child window we still shouldn't be done. 1018 verify(transactionListener, never()).onTransactionReady(anyInt(), any()); 1019 1020 makeWindowVisible(w); 1021 bse.onSurfacePlacement(); 1022 w.immediatelyNotifyBlastSync(); 1023 bse.onSurfacePlacement(); 1024 1025 verify(transactionListener).onTransactionReady(anyInt(), any()); 1026 } 1027 1028 static class StubOrganizer extends ITaskOrganizer.Stub { 1029 RunningTaskInfo mInfo; 1030 1031 @Override addStartingWindow(StartingWindowInfo info)1032 public void addStartingWindow(StartingWindowInfo info) { } 1033 @Override removeStartingWindow(StartingWindowRemovalInfo removalInfo)1034 public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { } 1035 @Override copySplashScreenView(int taskId)1036 public void copySplashScreenView(int taskId) { } 1037 @Override onTaskAppeared(RunningTaskInfo info, SurfaceControl leash)1038 public void onTaskAppeared(RunningTaskInfo info, SurfaceControl leash) { 1039 mInfo = info; 1040 } 1041 @Override onTaskVanished(RunningTaskInfo info)1042 public void onTaskVanished(RunningTaskInfo info) { 1043 } 1044 @Override onTaskInfoChanged(RunningTaskInfo info)1045 public void onTaskInfoChanged(RunningTaskInfo info) { 1046 } 1047 @Override onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)1048 public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { 1049 } 1050 @Override onImeDrawnOnTask(int taskId)1051 public void onImeDrawnOnTask(int taskId) throws RemoteException { 1052 } 1053 @Override onAppSplashScreenViewRemoved(int taskId)1054 public void onAppSplashScreenViewRemoved(int taskId) { 1055 } 1056 }; 1057 makePipableActivity()1058 private ActivityRecord makePipableActivity() { 1059 final ActivityRecord record = createActivityRecordWithParentTask(mDisplayContent, 1060 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); 1061 record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; 1062 record.setPictureInPictureParams(new PictureInPictureParams.Builder() 1063 .setAutoEnterEnabled(true).build()); 1064 spyOn(record); 1065 doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean()); 1066 1067 record.getTask().setHasBeenVisible(true); 1068 return record; 1069 } 1070 1071 @Test testEnterPipParams()1072 public void testEnterPipParams() { 1073 final StubOrganizer o = new StubOrganizer(); 1074 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o); 1075 final ActivityRecord record = makePipableActivity(); 1076 1077 final PictureInPictureParams p = new PictureInPictureParams.Builder() 1078 .setAspectRatio(new Rational(1, 2)).build(); 1079 assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode( 1080 record.token, p)); 1081 waitUntilHandlersIdle(); 1082 assertNotNull(o.mInfo); 1083 assertNotNull(o.mInfo.pictureInPictureParams); 1084 } 1085 1086 @Test testChangePipParams()1087 public void testChangePipParams() { 1088 class ChangeSavingOrganizer extends StubOrganizer { 1089 RunningTaskInfo mChangedInfo; 1090 @Override 1091 public void onTaskInfoChanged(RunningTaskInfo info) { 1092 mChangedInfo = info; 1093 } 1094 } 1095 ChangeSavingOrganizer o = new ChangeSavingOrganizer(); 1096 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o); 1097 1098 final ActivityRecord record = makePipableActivity(); 1099 final PictureInPictureParams p = new PictureInPictureParams.Builder() 1100 .setAspectRatio(new Rational(1, 2)).build(); 1101 assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode( 1102 record.token, p)); 1103 waitUntilHandlersIdle(); 1104 assertNotNull(o.mInfo); 1105 assertNotNull(o.mInfo.pictureInPictureParams); 1106 1107 final PictureInPictureParams p2 = new PictureInPictureParams.Builder() 1108 .setAspectRatio(new Rational(3, 4)).build(); 1109 mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2); 1110 waitUntilHandlersIdle(); 1111 // Ensure events dispatch to organizer. 1112 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1113 assertNotNull(o.mChangedInfo); 1114 assertNotNull(o.mChangedInfo.pictureInPictureParams); 1115 final Rational ratio = o.mChangedInfo.pictureInPictureParams.getAspectRatio(); 1116 assertEquals(3, ratio.getNumerator()); 1117 assertEquals(4, ratio.getDenominator()); 1118 } 1119 1120 @Test testChangeTaskDescription()1121 public void testChangeTaskDescription() { 1122 class ChangeSavingOrganizer extends StubOrganizer { 1123 RunningTaskInfo mChangedInfo; 1124 @Override 1125 public void onTaskInfoChanged(RunningTaskInfo info) { 1126 mChangedInfo = info; 1127 } 1128 } 1129 ChangeSavingOrganizer o = new ChangeSavingOrganizer(); 1130 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o); 1131 1132 final Task rootTask = createRootTask(); 1133 final Task task = createTask(rootTask); 1134 final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task); 1135 1136 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1137 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); 1138 mAtm.mTaskOrganizerController.dispatchPendingEvents(); 1139 assertEquals("TestDescription", o.mChangedInfo.taskDescription.getLabel()); 1140 } 1141 1142 @Test testPreventDuplicateAppear()1143 public void testPreventDuplicateAppear() throws RemoteException { 1144 final ITaskOrganizer organizer = registerMockOrganizer(); 1145 final Task rootTask = createRootTask(); 1146 final Task task = createTask(rootTask, false /* fakeDraw */); 1147 1148 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1149 rootTask.setTaskOrganizer(organizer); 1150 // setHasBeenVisible was already called once by the set-up code. 1151 rootTask.setHasBeenVisible(true); 1152 // Ensure events dispatch to organizer. 1153 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1154 verify(organizer, times(1)) 1155 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 1156 1157 rootTask.setTaskOrganizer(null); 1158 // Ensure events dispatch to organizer. 1159 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1160 verify(organizer, times(1)).onTaskVanished(any()); 1161 rootTask.setTaskOrganizer(organizer); 1162 // Ensure events dispatch to organizer. 1163 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1164 verify(organizer, times(2)) 1165 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 1166 1167 rootTask.removeImmediately(); 1168 // Ensure events dispatch to organizer. 1169 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1170 verify(organizer, times(2)).onTaskVanished(any()); 1171 } 1172 1173 @Test testInterceptBackPressedOnTaskRoot()1174 public void testInterceptBackPressedOnTaskRoot() throws RemoteException { 1175 final ITaskOrganizer organizer = registerMockOrganizer(); 1176 final Task rootTask = createRootTask(); 1177 final Task task = createTask(rootTask); 1178 final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task); 1179 final Task rootTask2 = createRootTask(); 1180 final Task task2 = createTask(rootTask2); 1181 final ActivityRecord activity2 = createActivityRecord(rootTask.mDisplayContent, task2); 1182 1183 assertTrue(rootTask.isOrganized()); 1184 assertTrue(rootTask2.isOrganized()); 1185 1186 // Verify a back pressed does not call the organizer 1187 mWm.mAtmService.mActivityClientController.onBackPressed(activity.token, 1188 new IRequestFinishCallback.Default()); 1189 // Ensure events dispatch to organizer. 1190 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1191 verify(organizer, never()).onBackPressedOnTaskRoot(any()); 1192 1193 // Enable intercepting back 1194 mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( 1195 rootTask.mRemoteToken.toWindowContainerToken(), true); 1196 1197 // Verify now that the back press does call the organizer 1198 mWm.mAtmService.mActivityClientController.onBackPressed(activity.token, 1199 new IRequestFinishCallback.Default()); 1200 // Ensure events dispatch to organizer. 1201 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1202 verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); 1203 1204 // Disable intercepting back 1205 mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( 1206 rootTask.mRemoteToken.toWindowContainerToken(), false); 1207 1208 // Verify now that the back press no longer calls the organizer 1209 mWm.mAtmService.mActivityClientController.onBackPressed(activity.token, 1210 new IRequestFinishCallback.Default()); 1211 // Ensure events dispatch to organizer. 1212 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1213 verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); 1214 } 1215 1216 @Test testBLASTCallbackWithWindows()1217 public void testBLASTCallbackWithWindows() throws Exception { 1218 final Task rootTaskController = createRootTask(); 1219 final Task task = createTask(rootTaskController); 1220 final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1"); 1221 final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2"); 1222 makeWindowVisible(w1); 1223 makeWindowVisible(w2); 1224 1225 IWindowContainerTransactionCallback mockCallback = 1226 mock(IWindowContainerTransactionCallback.class); 1227 int id = mWm.mAtmService.mWindowOrganizerController.startSyncWithOrganizer(mockCallback); 1228 1229 mWm.mAtmService.mWindowOrganizerController.addToSyncSet(id, task); 1230 mWm.mAtmService.mWindowOrganizerController.setSyncReady(id); 1231 1232 // Since we have a window we have to wait for it to draw to finish sync. 1233 verify(mockCallback, never()).onTransactionReady(anyInt(), any()); 1234 assertTrue(w1.useBLASTSync()); 1235 assertTrue(w2.useBLASTSync()); 1236 1237 // Make second (bottom) ready. If we started with the top, since activities fillsParent 1238 // by default, the sync would be considered finished. 1239 w2.immediatelyNotifyBlastSync(); 1240 mWm.mSyncEngine.onSurfacePlacement(); 1241 verify(mockCallback, never()).onTransactionReady(anyInt(), any()); 1242 1243 assertEquals(SYNC_STATE_READY, w2.mSyncState); 1244 // Even though one Window finished drawing, both windows should still be using blast sync 1245 assertTrue(w1.useBLASTSync()); 1246 assertTrue(w2.useBLASTSync()); 1247 1248 // A drawn window can complete the sync state automatically. 1249 w1.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN; 1250 makeLastConfigReportedToClient(w1, true /* visible */); 1251 mWm.mSyncEngine.onSurfacePlacement(); 1252 verify(mockCallback).onTransactionReady(anyInt(), any()); 1253 assertFalse(w1.useBLASTSync()); 1254 assertFalse(w2.useBLASTSync()); 1255 } 1256 1257 @Test testDisplayAreaHiddenTransaction()1258 public void testDisplayAreaHiddenTransaction() { 1259 removeGlobalMinSizeRestriction(); 1260 1261 WindowContainerTransaction trx = new WindowContainerTransaction(); 1262 1263 TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); 1264 1265 trx.setHidden(taskDisplayArea.mRemoteToken.toWindowContainerToken(), true); 1266 mWm.mAtmService.mWindowOrganizerController.applyTransaction(trx); 1267 1268 taskDisplayArea.forAllTasks(daTask -> { 1269 assertTrue(daTask.isForceHidden()); 1270 }); 1271 1272 trx.setHidden(taskDisplayArea.mRemoteToken.toWindowContainerToken(), false); 1273 mWm.mAtmService.mWindowOrganizerController.applyTransaction(trx); 1274 1275 taskDisplayArea.forAllTasks(daTask -> { 1276 assertFalse(daTask.isForceHidden()); 1277 }); 1278 } 1279 1280 @Test testReparentToOrganizedTask()1281 public void testReparentToOrganizedTask() { 1282 final ITaskOrganizer organizer = registerMockOrganizer(); 1283 Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask( 1284 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null); 1285 final Task task1 = createRootTask(); 1286 final Task task2 = createTask(rootTask, false /* fakeDraw */); 1287 WindowContainerTransaction wct = new WindowContainerTransaction(); 1288 wct.reparent(task1.mRemoteToken.toWindowContainerToken(), 1289 rootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */); 1290 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1291 assertTrue(task1.isOrganized()); 1292 assertTrue(task2.isOrganized()); 1293 } 1294 1295 @Test testAppearDeferThenInfoChange()1296 public void testAppearDeferThenInfoChange() { 1297 final ITaskOrganizer organizer = registerMockOrganizer(); 1298 final Task rootTask = createRootTask(); 1299 // Flush EVENT_APPEARED. 1300 mAtm.mTaskOrganizerController.dispatchPendingEvents(); 1301 1302 // Assume layout defer 1303 mWm.mWindowPlacerLocked.deferLayout(); 1304 1305 final Task task = createTask(rootTask); 1306 final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); 1307 1308 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1309 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); 1310 waitUntilHandlersIdle(); 1311 1312 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask); 1313 assertEquals(1, pendingEvents.size()); 1314 assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType); 1315 assertEquals("TestDescription", 1316 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); 1317 } 1318 1319 @Test testAppearDeferThenVanish()1320 public void testAppearDeferThenVanish() { 1321 final ITaskOrganizer organizer = registerMockOrganizer(); 1322 final Task rootTask = createRootTask(); 1323 // Flush EVENT_APPEARED. 1324 mAtm.mTaskOrganizerController.dispatchPendingEvents(); 1325 1326 // Assume layout defer 1327 mWm.mWindowPlacerLocked.deferLayout(); 1328 1329 final Task task = createTask(rootTask); 1330 1331 rootTask.removeImmediately(); 1332 waitUntilHandlersIdle(); 1333 1334 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask); 1335 assertEquals(0, pendingEvents.size()); 1336 } 1337 1338 @Test testInfoChangeDeferMultiple()1339 public void testInfoChangeDeferMultiple() { 1340 final ITaskOrganizer organizer = registerMockOrganizer(); 1341 final Task rootTask = createRootTask(); 1342 final Task task = createTask(rootTask); 1343 final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task); 1344 1345 // Assume layout defer 1346 mWm.mWindowPlacerLocked.deferLayout(); 1347 1348 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1349 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); 1350 waitUntilHandlersIdle(); 1351 1352 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask); 1353 assertEquals(1, pendingEvents.size()); 1354 assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); 1355 assertEquals("TestDescription", 1356 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); 1357 1358 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2")); 1359 waitUntilHandlersIdle(); 1360 1361 pendingEvents = getTaskPendingEvent(organizer, rootTask); 1362 assertEquals(1, pendingEvents.size()); 1363 assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); 1364 assertEquals("TestDescription2", 1365 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); 1366 } 1367 1368 @Test testInfoChangDeferThenVanish()1369 public void testInfoChangDeferThenVanish() { 1370 final ITaskOrganizer organizer = registerMockOrganizer(); 1371 final Task rootTask = createRootTask(); 1372 final Task task = createTask(rootTask); 1373 final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task); 1374 1375 // Assume layout defer 1376 mWm.mWindowPlacerLocked.deferLayout(); 1377 1378 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1379 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); 1380 1381 rootTask.removeImmediately(); 1382 waitUntilHandlersIdle(); 1383 1384 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask); 1385 assertEquals(1, pendingEvents.size()); 1386 assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); 1387 assertEquals("TestDescription", 1388 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); 1389 } 1390 1391 @Test testVanishDeferThenInfoChange()1392 public void testVanishDeferThenInfoChange() { 1393 final ITaskOrganizer organizer = registerMockOrganizer(); 1394 final Task rootTask = createRootTask(); 1395 final Task task = createTask(rootTask); 1396 createActivityRecordAndDispatchPendingEvents(task); 1397 1398 // Assume layout defer 1399 mWm.mWindowPlacerLocked.deferLayout(); 1400 1401 rootTask.removeImmediately(); 1402 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1403 waitUntilHandlersIdle(); 1404 1405 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask); 1406 assertEquals(1, pendingEvents.size()); 1407 assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); 1408 } 1409 1410 @Test testVanishDeferThenBackOnRoot()1411 public void testVanishDeferThenBackOnRoot() { 1412 final ITaskOrganizer organizer = registerMockOrganizer(); 1413 final Task rootTask = createRootTask(); 1414 final Task task = createTask(rootTask); 1415 final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task); 1416 1417 // Assume layout defer 1418 mWm.mWindowPlacerLocked.deferLayout(); 1419 1420 rootTask.removeImmediately(); 1421 mWm.mAtmService.mActivityClientController.onBackPressed(record.token, 1422 new IRequestFinishCallback.Default()); 1423 waitUntilHandlersIdle(); 1424 1425 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask); 1426 assertEquals(1, pendingEvents.size()); 1427 assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); 1428 } 1429 getTaskPendingEvent(ITaskOrganizer organizer, Task task)1430 private ArrayList<PendingTaskEvent> getTaskPendingEvent(ITaskOrganizer organizer, Task task) { 1431 ArrayList<PendingTaskEvent> total = 1432 mWm.mAtmService.mTaskOrganizerController 1433 .getTaskOrganizerPendingEvents(organizer.asBinder()) 1434 .getPendingEventList(); 1435 ArrayList<PendingTaskEvent> result = new ArrayList(); 1436 1437 for (int i = 0; i < total.size(); i++) { 1438 PendingTaskEvent entry = total.get(i); 1439 if (entry.mTask.mTaskId == task.mTaskId) { 1440 result.add(entry); 1441 } 1442 } 1443 1444 return result; 1445 } 1446 1447 @Test testReparentNonResizableTaskToSplitScreen()1448 public void testReparentNonResizableTaskToSplitScreen() { 1449 final ActivityRecord activity = new ActivityBuilder(mAtm) 1450 .setCreateTask(true) 1451 .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE) 1452 .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) 1453 .build(); 1454 final Task rootTask = activity.getRootTask(); 1455 rootTask.setResizeMode(activity.info.resizeMode); 1456 final Task splitPrimaryRootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask( 1457 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null); 1458 final WindowContainerTransaction wct = new WindowContainerTransaction(); 1459 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), 1460 splitPrimaryRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */); 1461 1462 // Can't reparent non-resizable to split screen 1463 mAtm.mSupportsNonResizableMultiWindow = -1; 1464 mAtm.mWindowOrganizerController.applyTransaction(wct); 1465 1466 assertEquals(rootTask, activity.getRootTask()); 1467 1468 // Allow reparent non-resizable to split screen 1469 mAtm.mSupportsNonResizableMultiWindow = 1; 1470 mAtm.mWindowOrganizerController.applyTransaction(wct); 1471 1472 assertEquals(splitPrimaryRootTask, activity.getRootTask()); 1473 } 1474 1475 @Test testSizeCompatModeChangedOnFirstOrganizedTask()1476 public void testSizeCompatModeChangedOnFirstOrganizedTask() throws RemoteException { 1477 final ITaskOrganizer organizer = registerMockOrganizer(); 1478 final Task rootTask = createRootTask(); 1479 final Task task = createTask(rootTask); 1480 final ActivityRecord activity = createActivityRecordAndDispatchPendingEvents(task); 1481 final ArgumentCaptor<RunningTaskInfo> infoCaptor = 1482 ArgumentCaptor.forClass(RunningTaskInfo.class); 1483 1484 assertTrue(rootTask.isOrganized()); 1485 1486 doReturn(true).when(activity).inSizeCompatMode(); 1487 doReturn(true).when(activity).isState(RESUMED); 1488 1489 // Ensure task info show top activity in size compat. 1490 rootTask.onSizeCompatActivityChanged(); 1491 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1492 verify(organizer).onTaskInfoChanged(infoCaptor.capture()); 1493 RunningTaskInfo info = infoCaptor.getValue(); 1494 assertEquals(rootTask.mTaskId, info.taskId); 1495 assertTrue(info.topActivityInSizeCompat); 1496 1497 // Ensure task info show top activity that is not visible as not in size compat. 1498 clearInvocations(organizer); 1499 doReturn(false).when(activity).isVisible(); 1500 rootTask.onSizeCompatActivityChanged(); 1501 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1502 verify(organizer).onTaskInfoChanged(infoCaptor.capture()); 1503 info = infoCaptor.getValue(); 1504 assertEquals(rootTask.mTaskId, info.taskId); 1505 assertFalse(info.topActivityInSizeCompat); 1506 1507 // Ensure task info show non size compat top activity as not in size compat. 1508 clearInvocations(organizer); 1509 doReturn(true).when(activity).isVisible(); 1510 doReturn(false).when(activity).inSizeCompatMode(); 1511 rootTask.onSizeCompatActivityChanged(); 1512 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1513 verify(organizer).onTaskInfoChanged(infoCaptor.capture()); 1514 info = infoCaptor.getValue(); 1515 assertEquals(rootTask.mTaskId, info.taskId); 1516 assertFalse(info.topActivityInSizeCompat); 1517 } 1518 1519 @Test testStartTasksInTransaction()1520 public void testStartTasksInTransaction() { 1521 WindowContainerTransaction wct = new WindowContainerTransaction(); 1522 ActivityOptions testOptions = ActivityOptions.makeBasic(); 1523 testOptions.setTransientLaunch(); 1524 wct.startTask(1, null /* options */); 1525 wct.startTask(2, testOptions.toBundle()); 1526 spyOn(mWm.mAtmService.mTaskSupervisor); 1527 doReturn(START_CANCELED).when(mWm.mAtmService.mTaskSupervisor).startActivityFromRecents( 1528 anyInt(), anyInt(), anyInt(), any()); 1529 clearInvocations(mWm.mAtmService); 1530 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1531 1532 verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents( 1533 anyInt(), anyInt(), eq(1), any()); 1534 1535 final ArgumentCaptor<SafeActivityOptions> optionsCaptor = 1536 ArgumentCaptor.forClass(SafeActivityOptions.class); 1537 verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents( 1538 anyInt(), anyInt(), eq(2), optionsCaptor.capture()); 1539 assertTrue(optionsCaptor.getValue().getOriginalOptions().getTransientLaunch()); 1540 } 1541 1542 @Test testResumeTopsWhenLeavingPinned()1543 public void testResumeTopsWhenLeavingPinned() { 1544 final ActivityRecord record = makePipableActivity(); 1545 final Task rootTask = record.getRootTask(); 1546 1547 clearInvocations(mWm.mAtmService.mRootWindowContainer); 1548 final WindowContainerTransaction t = new WindowContainerTransaction(); 1549 WindowContainerToken wct = rootTask.mRemoteToken.toWindowContainerToken(); 1550 t.setWindowingMode(wct, WINDOWING_MODE_PINNED); 1551 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 1552 verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities(); 1553 1554 clearInvocations(mWm.mAtmService.mRootWindowContainer); 1555 // The token for the PIP root task may have changed when the task entered PIP mode, so do 1556 // not reuse the one from above. 1557 final WindowContainerToken newToken = 1558 record.getRootTask().mRemoteToken.toWindowContainerToken(); 1559 t.setWindowingMode(newToken, WINDOWING_MODE_FULLSCREEN); 1560 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 1561 verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities(); 1562 } 1563 createActivityRecordAndDispatchPendingEvents(Task task)1564 private ActivityRecord createActivityRecordAndDispatchPendingEvents(Task task) { 1565 final ActivityRecord record = createActivityRecord(task); 1566 // Flush EVENT_APPEARED. 1567 mAtm.mTaskOrganizerController.dispatchPendingEvents(); 1568 return record; 1569 } 1570 1571 /** 1572 * Verifies that task vanished is called for a specific task. 1573 */ assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks)1574 private void assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks) 1575 throws RemoteException { 1576 ArgumentCaptor<RunningTaskInfo> arg = ArgumentCaptor.forClass(RunningTaskInfo.class); 1577 verify(organizer, atLeastOnce()).onTaskVanished(arg.capture()); 1578 List<RunningTaskInfo> taskInfos = arg.getAllValues(); 1579 1580 HashSet<Integer> vanishedTaskIds = new HashSet<>(); 1581 for (int i = 0; i < taskInfos.size(); i++) { 1582 vanishedTaskIds.add(taskInfos.get(i).taskId); 1583 } 1584 HashSet<Integer> taskIds = new HashSet<>(); 1585 for (int i = 0; i < tasks.length; i++) { 1586 taskIds.add(tasks[i].mTaskId); 1587 } 1588 1589 assertTrue(expectVanished 1590 ? vanishedTaskIds.containsAll(taskIds) 1591 : !vanishedTaskIds.removeAll(taskIds)); 1592 } 1593 assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks)1594 private void assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks) { 1595 HashSet<Integer> taskIds = new HashSet<>(); 1596 for (int i = 0; i < taskInfos.size(); i++) { 1597 taskIds.add(taskInfos.get(i).getTaskInfo().taskId); 1598 } 1599 for (int i = 0; i < expectedTasks.length; i++) { 1600 assertTrue(taskIds.contains(expectedTasks[i].mTaskId)); 1601 } 1602 } 1603 } 1604