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 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; 35 36 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 37 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 38 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 39 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; 40 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 41 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; 42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 43 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 44 import static com.android.server.wm.ActivityRecord.State.RESUMED; 45 import static com.android.server.wm.WindowContainer.POSITION_TOP; 46 import static com.android.server.wm.WindowContainer.SYNC_STATE_READY; 47 import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION; 48 import static com.android.server.wm.testing.Assert.assertThrows; 49 50 import static com.google.common.truth.Truth.assertThat; 51 52 import static org.junit.Assert.assertArrayEquals; 53 import static org.junit.Assert.assertEquals; 54 import static org.junit.Assert.assertFalse; 55 import static org.junit.Assert.assertNotNull; 56 import static org.junit.Assert.assertTrue; 57 import static org.mockito.ArgumentMatchers.any; 58 import static org.mockito.ArgumentMatchers.anyBoolean; 59 import static org.mockito.ArgumentMatchers.anyInt; 60 import static org.mockito.ArgumentMatchers.eq; 61 import static org.mockito.Mockito.atLeastOnce; 62 import static org.mockito.Mockito.clearInvocations; 63 64 import android.annotation.NonNull; 65 import android.app.ActivityManager; 66 import android.app.ActivityManager.RunningTaskInfo; 67 import android.app.ActivityOptions; 68 import android.app.ActivityTaskManager.RootTaskInfo; 69 import android.app.IRequestFinishCallback; 70 import android.app.PictureInPictureParams; 71 import android.content.pm.ActivityInfo; 72 import android.content.pm.ParceledListSlice; 73 import android.content.res.Configuration; 74 import android.graphics.Rect; 75 import android.os.Binder; 76 import android.os.IBinder; 77 import android.os.RemoteException; 78 import android.platform.test.annotations.Presubmit; 79 import android.util.ArrayMap; 80 import android.util.Rational; 81 import android.view.Display; 82 import android.view.SurfaceControl; 83 import android.view.WindowInsets; 84 import android.window.ITaskFragmentOrganizer; 85 import android.window.ITaskOrganizer; 86 import android.window.IWindowContainerTransactionCallback; 87 import android.window.StartingWindowInfo; 88 import android.window.StartingWindowRemovalInfo; 89 import android.window.TaskAppearedInfo; 90 import android.window.TaskFragmentOrganizer; 91 import android.window.WindowContainerToken; 92 import android.window.WindowContainerTransaction; 93 94 import androidx.test.filters.SmallTest; 95 96 import com.android.server.wm.TaskOrganizerController.PendingTaskEvent; 97 import com.android.window.flags.Flags; 98 99 import org.junit.Test; 100 import org.junit.runner.RunWith; 101 import org.mockito.ArgumentCaptor; 102 103 import java.util.ArrayList; 104 import java.util.HashSet; 105 import java.util.List; 106 import java.util.function.BiConsumer; 107 108 /** 109 * Test class for {@link ITaskOrganizer} and {@link android.window.ITaskOrganizerController}. 110 * 111 * Build/Install/Run: 112 * atest WmTests:WindowOrganizerTests 113 */ 114 @SmallTest 115 @Presubmit 116 @RunWith(WindowTestRunner.class) 117 public class WindowOrganizerTests extends WindowTestsBase { 118 createMockOrganizer()119 private ITaskOrganizer createMockOrganizer() { 120 final ITaskOrganizer organizer = mock(ITaskOrganizer.class); 121 when(organizer.asBinder()).thenReturn(new Binder()); 122 return organizer; 123 } 124 registerMockOrganizer(ArrayList<TaskAppearedInfo> existingTasks)125 private ITaskOrganizer registerMockOrganizer(ArrayList<TaskAppearedInfo> existingTasks) { 126 final ITaskOrganizer organizer = createMockOrganizer(); 127 ParceledListSlice<TaskAppearedInfo> tasks = 128 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer); 129 if (existingTasks != null) { 130 existingTasks.addAll(tasks.getList()); 131 } 132 return organizer; 133 } 134 registerMockOrganizer()135 private ITaskOrganizer registerMockOrganizer() { 136 return registerMockOrganizer(null); 137 } 138 createTask(Task rootTask, boolean fakeDraw)139 Task createTask(Task rootTask, boolean fakeDraw) { 140 final Task task = createTaskInRootTask(rootTask, 0); 141 142 if (fakeDraw) { 143 task.setHasBeenVisible(true); 144 } 145 return task; 146 } 147 createTask(Task rootTask)148 Task createTask(Task rootTask) { 149 // Fake draw notifications for most of our tests. 150 return createTask(rootTask, true); 151 } 152 createRootTask()153 Task createRootTask() { 154 return createTask(mDisplayContent); 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 testTaskFragmentHiddenFocusableTranslucentChanges()582 public void testTaskFragmentHiddenFocusableTranslucentChanges() { 583 mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG); 584 585 removeGlobalMinSizeRestriction(); 586 final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) 587 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build(); 588 589 final WindowContainerTransaction t = new WindowContainerTransaction(); 590 final TaskFragmentOrganizer organizer = 591 createTaskFragmentOrganizer(t, true /* isSystemOrganizer */); 592 593 final IBinder token = new Binder(); 594 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 595 .setParentTask(rootTask) 596 .setFragmentToken(token) 597 .setOrganizer(organizer) 598 .createActivityCount(1) 599 .build(); 600 601 // Should be visible and focusable initially. 602 assertTrue(rootTask.shouldBeVisible(null)); 603 assertTrue(taskFragment.shouldBeVisible(null)); 604 assertTrue(taskFragment.isFocusable()); 605 assertTrue(taskFragment.isTopActivityFocusable()); 606 assertFalse(taskFragment.isForceTranslucent()); 607 608 // Apply transaction to the TaskFragment hidden and not focusable. 609 t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), true); 610 t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), false); 611 t.setForceTranslucent(taskFragment.mRemoteToken.toWindowContainerToken(), true); 612 mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked( 613 t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE, 614 false /* shouldApplyIndependently */, null /* remoteTransition */); 615 616 // Should be not visible and not focusable after the transaction. 617 assertFalse(taskFragment.shouldBeVisible(null)); 618 assertFalse(taskFragment.isFocusable()); 619 assertFalse(taskFragment.isTopActivityFocusable()); 620 assertTrue(taskFragment.isForceTranslucent()); 621 622 // Apply transaction to the TaskFragment not hidden and focusable. 623 t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), false); 624 t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), true); 625 t.setForceTranslucent(taskFragment.mRemoteToken.toWindowContainerToken(), false); 626 mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked( 627 t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE, 628 false /* shouldApplyIndependently */, null /* remoteTransition */); 629 630 // Should be visible and focusable after the transaction. 631 assertTrue(taskFragment.shouldBeVisible(null)); 632 assertTrue(taskFragment.isFocusable()); 633 assertTrue(taskFragment.isTopActivityFocusable()); 634 assertFalse(taskFragment.isForceTranslucent()); 635 } 636 637 @Test testTaskFragmentChangeHidden_throwsWhenNotSystemOrganizer()638 public void testTaskFragmentChangeHidden_throwsWhenNotSystemOrganizer() { 639 // Non-system organizers are not allow to update the hidden state. 640 testTaskFragmentChangesWithoutSystemOrganizerThrowException( 641 (t, windowContainerToken) -> t.setHidden(windowContainerToken, true)); 642 } 643 644 @Test testTaskFragmentChangeFocusable_throwsWhenNotSystemOrganizer()645 public void testTaskFragmentChangeFocusable_throwsWhenNotSystemOrganizer() { 646 // Non-system organizers are not allow to update the focusable state. 647 testTaskFragmentChangesWithoutSystemOrganizerThrowException( 648 (t, windowContainerToken) -> t.setFocusable(windowContainerToken, false)); 649 } 650 651 @Test testTaskFragmentChangeTranslucent_throwsWhenNotSystemOrganizer()652 public void testTaskFragmentChangeTranslucent_throwsWhenNotSystemOrganizer() { 653 // Non-system organizers are not allow to update the translucent state. 654 testTaskFragmentChangesWithoutSystemOrganizerThrowException( 655 (t, windowContainerToken) -> t.setForceTranslucent(windowContainerToken, true)); 656 } 657 testTaskFragmentChangesWithoutSystemOrganizerThrowException( BiConsumer<WindowContainerTransaction, WindowContainerToken> addOp)658 private void testTaskFragmentChangesWithoutSystemOrganizerThrowException( 659 BiConsumer<WindowContainerTransaction, WindowContainerToken> addOp) { 660 mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG); 661 662 removeGlobalMinSizeRestriction(); 663 final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) 664 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build(); 665 666 final WindowContainerTransaction t = new WindowContainerTransaction(); 667 final TaskFragmentOrganizer organizer = 668 createTaskFragmentOrganizer(t, false /* isSystemOrganizer */); 669 670 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 671 .setParentTask(rootTask) 672 .setFragmentToken(new Binder()) 673 .setOrganizer(organizer) 674 .createActivityCount(1) 675 .build(); 676 677 addOp.accept(t, taskFragment.mRemoteToken.toWindowContainerToken()); 678 679 assertThrows(SecurityException.class, () -> 680 mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked( 681 t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE, 682 false /* shouldApplyIndependently */, null /* remoteTransition */) 683 ); 684 } 685 686 @Test testContainerTranslucentChanges()687 public void testContainerTranslucentChanges() { 688 removeGlobalMinSizeRestriction(); 689 final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) 690 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build(); 691 final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build(); 692 WindowContainerTransaction t = new WindowContainerTransaction(); 693 assertFalse(rootTask.isTranslucent(activity)); 694 t.setForceTranslucent(rootTask.mRemoteToken.toWindowContainerToken(), true); 695 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 696 assertTrue(rootTask.isTranslucent(activity)); 697 t.setForceTranslucent(rootTask.mRemoteToken.toWindowContainerToken(), false); 698 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 699 assertFalse(rootTask.isTranslucent(activity)); 700 } 701 702 @Test testSetIgnoreOrientationRequest_taskDisplayArea()703 public void testSetIgnoreOrientationRequest_taskDisplayArea() { 704 removeGlobalMinSizeRestriction(); 705 final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); 706 final Task rootTask = taskDisplayArea.createRootTask( 707 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 708 final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build(); 709 taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 710 mDisplayContent.setFocusedApp(activity); 711 activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 712 713 // TDA returns UNSET when ignoreOrientationRequest == true 714 // DC is UNSPECIFIED when child returns UNSET 715 assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET); 716 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); 717 718 WindowContainerTransaction t = new WindowContainerTransaction(); 719 t.setIgnoreOrientationRequest( 720 taskDisplayArea.mRemoteToken.toWindowContainerToken(), 721 false /* ignoreOrientationRequest */); 722 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 723 724 // TDA returns app request orientation when ignoreOrientationRequest == false 725 // DC uses the same as TDA returns when it is not UNSET. 726 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 727 assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 728 729 t.setIgnoreOrientationRequest( 730 taskDisplayArea.mRemoteToken.toWindowContainerToken(), 731 true /* ignoreOrientationRequest */); 732 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 733 734 // TDA returns UNSET when ignoreOrientationRequest == true 735 // DC is UNSPECIFIED when child returns UNSET 736 assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET); 737 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); 738 } 739 740 @Test testSetIgnoreOrientationRequest_displayContent()741 public void testSetIgnoreOrientationRequest_displayContent() { 742 removeGlobalMinSizeRestriction(); 743 final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); 744 final Task rootTask = taskDisplayArea.createRootTask( 745 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 746 final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build(); 747 mDisplayContent.setFocusedApp(activity); 748 activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 749 750 // DC uses the orientation request from app 751 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 752 753 WindowContainerTransaction t = new WindowContainerTransaction(); 754 t.setIgnoreOrientationRequest( 755 mDisplayContent.mRemoteToken.toWindowContainerToken(), 756 true /* ignoreOrientationRequest */); 757 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 758 759 // DC returns UNSPECIFIED when ignoreOrientationRequest == true 760 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); 761 762 t.setIgnoreOrientationRequest( 763 mDisplayContent.mRemoteToken.toWindowContainerToken(), 764 false /* ignoreOrientationRequest */); 765 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 766 767 // DC uses the orientation request from app after mIgnoreOrientationRequest is set to false 768 assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 769 } 770 771 @Test testOverrideConfigSize()772 public void testOverrideConfigSize() { 773 removeGlobalMinSizeRestriction(); 774 final Task rootTask = new TaskBuilder(mSupervisor) 775 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 776 final Task task = rootTask.getTopMostTask(); 777 WindowContainerTransaction t = new WindowContainerTransaction(); 778 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 779 final int origScreenWDp = task.getConfiguration().screenHeightDp; 780 final int origScreenHDp = task.getConfiguration().screenHeightDp; 781 t = new WindowContainerTransaction(); 782 // verify that setting config overrides on parent restricts children. 783 t.setScreenSizeDp(rootTask.mRemoteToken 784 .toWindowContainerToken(), origScreenWDp, origScreenHDp / 2); 785 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 786 assertEquals(origScreenHDp / 2, task.getConfiguration().screenHeightDp); 787 t = new WindowContainerTransaction(); 788 t.setScreenSizeDp(rootTask.mRemoteToken.toWindowContainerToken(), SCREEN_WIDTH_DP_UNDEFINED, 789 SCREEN_HEIGHT_DP_UNDEFINED); 790 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 791 assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp); 792 } 793 794 @Test testCreateDeleteRootTasks()795 public void testCreateDeleteRootTasks() { 796 DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY); 797 798 Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 799 dc, WINDOWING_MODE_FULLSCREEN, null); 800 RunningTaskInfo info1 = task1.getTaskInfo(); 801 assertEquals(WINDOWING_MODE_FULLSCREEN, 802 info1.configuration.windowConfiguration.getWindowingMode()); 803 assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType); 804 805 Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 806 dc, WINDOWING_MODE_MULTI_WINDOW, null); 807 RunningTaskInfo info2 = task2.getTaskInfo(); 808 assertEquals(WINDOWING_MODE_MULTI_WINDOW, 809 info2.configuration.windowConfiguration.getWindowingMode()); 810 assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType); 811 812 List<Task> infos = getTasksCreatedByOrganizer(dc); 813 assertEquals(2, infos.size()); 814 815 assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token)); 816 infos = getTasksCreatedByOrganizer(dc); 817 assertEquals(1, infos.size()); 818 assertEquals(WINDOWING_MODE_MULTI_WINDOW, infos.get(0).getWindowingMode()); 819 } 820 821 @Test testSetAdjacentLaunchRoot()822 public void testSetAdjacentLaunchRoot() { 823 DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY); 824 825 final Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 826 dc, WINDOWING_MODE_MULTI_WINDOW, null); 827 final RunningTaskInfo info1 = task1.getTaskInfo(); 828 final Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 829 dc, WINDOWING_MODE_MULTI_WINDOW, null); 830 final RunningTaskInfo info2 = task2.getTaskInfo(); 831 832 WindowContainerTransaction wct = new WindowContainerTransaction(); 833 wct.setAdjacentRoots(info1.token, info2.token); 834 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 835 assertEquals(task1.getAdjacentTaskFragment(), task2); 836 assertEquals(task2.getAdjacentTaskFragment(), task1); 837 838 wct = new WindowContainerTransaction(); 839 wct.setLaunchAdjacentFlagRoot(info1.token); 840 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 841 assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, task1); 842 843 wct = new WindowContainerTransaction(); 844 wct.clearAdjacentRoots(info1.token); 845 wct.clearLaunchAdjacentFlagRoot(info1.token); 846 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 847 assertEquals(task1.getAdjacentTaskFragment(), null); 848 assertEquals(task2.getAdjacentTaskFragment(), null); 849 assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, null); 850 } 851 852 @Test testTileAddRemoveChild()853 public void testTileAddRemoveChild() { 854 final StubOrganizer listener = new StubOrganizer(); 855 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); 856 Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask( 857 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null); 858 RunningTaskInfo info1 = task.getTaskInfo(); 859 860 final Task rootTask = createTask( 861 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); 862 assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode()); 863 WindowContainerTransaction wct = new WindowContainerTransaction(); 864 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */); 865 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 866 assertEquals(info1.configuration.windowConfiguration.getWindowingMode(), 867 rootTask.getWindowingMode()); 868 869 // Info should reflect new membership 870 List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent); 871 info1 = infos.get(0).getTaskInfo(); 872 assertEquals(ACTIVITY_TYPE_STANDARD, info1.topActivityType); 873 874 // Children inherit configuration 875 Rect newSize = new Rect(10, 10, 300, 300); 876 Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask(); 877 Configuration c = new Configuration(task1.getRequestedOverrideConfiguration()); 878 c.windowConfiguration.setBounds(newSize); 879 doNothing().when(rootTask).adjustForMinimalTaskDimensions(any(), any(), any()); 880 task1.onRequestedOverrideConfigurationChanged(c); 881 assertEquals(newSize, rootTask.getBounds()); 882 883 wct = new WindowContainerTransaction(); 884 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); 885 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 886 assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode()); 887 infos = getTasksCreatedByOrganizer(mDisplayContent); 888 info1 = infos.get(0).getTaskInfo(); 889 assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType); 890 } 891 892 @Test testAddInsetsSource()893 public void testAddInsetsSource() { 894 final Task rootTask = createTask(mDisplayContent); 895 896 final Task navigationBarInsetsReceiverTask = createTaskInRootTask(rootTask, 0); 897 navigationBarInsetsReceiverTask.getConfiguration().windowConfiguration.setBounds(new Rect( 898 0, 200, 1080, 700)); 899 900 final WindowContainerTransaction wct = new WindowContainerTransaction(); 901 wct.addInsetsSource( 902 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(), 903 new Binder(), 904 0 /* index */, 905 WindowInsets.Type.systemOverlays(), 906 new Rect(0, 0, 1080, 200), 907 null /* boundingRects */); 908 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 909 910 assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSources 911 .valueAt(0).getType()).isEqualTo( 912 WindowInsets.Type.systemOverlays()); 913 } 914 915 @Test testAddInsetsSource_withBoundingRects()916 public void testAddInsetsSource_withBoundingRects() { 917 final Task rootTask = createTask(mDisplayContent); 918 919 final Task navigationBarInsetsReceiverTask = createTaskInRootTask(rootTask, 0); 920 navigationBarInsetsReceiverTask.getConfiguration().windowConfiguration.setBounds(new Rect( 921 0, 200, 1080, 700)); 922 923 final Rect[] boundingRects = new Rect[]{ 924 new Rect(0, 0, 10, 10), new Rect(100, 100, 200, 100) 925 }; 926 final WindowContainerTransaction wct = new WindowContainerTransaction(); 927 wct.addInsetsSource( 928 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(), 929 new Binder(), 930 0 /* index */, 931 WindowInsets.Type.systemOverlays(), 932 new Rect(0, 0, 1080, 200), 933 boundingRects); 934 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 935 936 assertArrayEquals(boundingRects, navigationBarInsetsReceiverTask.mLocalInsetsSources 937 .valueAt(0).getBoundingRects()); 938 } 939 940 @Test testRemoveInsetsSource()941 public void testRemoveInsetsSource() { 942 final Task rootTask = createTask(mDisplayContent); 943 944 final Task navigationBarInsetsReceiverTask = createTaskInRootTask(rootTask, 0); 945 navigationBarInsetsReceiverTask.getConfiguration().windowConfiguration.setBounds(new Rect( 946 0, 200, 1080, 700)); 947 final Binder owner = new Binder(); 948 final WindowContainerTransaction wct = new WindowContainerTransaction(); 949 wct.addInsetsSource( 950 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(), 951 owner, 952 0 /* index */, 953 WindowInsets.Type.systemOverlays(), 954 new Rect(0, 0, 1080, 200), 955 null /* boundingRects */); 956 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 957 958 final WindowContainerTransaction wct2 = new WindowContainerTransaction(); 959 wct2.removeInsetsSource( 960 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(), 961 owner, 962 0 /* index */, 963 WindowInsets.Type.systemOverlays()); 964 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct2); 965 966 assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSources.size()).isEqualTo(0); 967 } 968 969 @Test testTaskInfoCallback()970 public void testTaskInfoCallback() { 971 final ArrayList<RunningTaskInfo> lastReportedTiles = new ArrayList<>(); 972 final boolean[] called = {false}; 973 final StubOrganizer listener = new StubOrganizer() { 974 @Override 975 public void onTaskInfoChanged(RunningTaskInfo info) { 976 lastReportedTiles.add(info); 977 called[0] = true; 978 } 979 }; 980 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); 981 Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask( 982 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null); 983 RunningTaskInfo info1 = task.getTaskInfo(); 984 // Ensure events dispatch to organizer. 985 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 986 lastReportedTiles.clear(); 987 called[0] = false; 988 989 final Task rootTask = createTask( 990 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); 991 Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask(); 992 WindowContainerTransaction wct = new WindowContainerTransaction(); 993 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */); 994 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 995 assertTrue(called[0]); 996 assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType); 997 998 lastReportedTiles.clear(); 999 called[0] = false; 1000 final Task rootTask2 = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask(); 1001 wct = new WindowContainerTransaction(); 1002 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), 1003 info1.token, true /* onTop */); 1004 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1005 assertTrue(called[0]); 1006 assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType); 1007 1008 lastReportedTiles.clear(); 1009 called[0] = false; 1010 task1.positionChildAt(POSITION_TOP, rootTask, false /* includingParents */); 1011 mAtm.mTaskOrganizerController.dispatchPendingEvents(); 1012 assertTrue(called[0]); 1013 assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType); 1014 1015 lastReportedTiles.clear(); 1016 called[0] = false; 1017 wct = new WindowContainerTransaction(); 1018 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), 1019 null, true /* onTop */); 1020 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), 1021 null, true /* onTop */); 1022 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1023 assertTrue(called[0]); 1024 assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType); 1025 } 1026 1027 @Test testHierarchyTransaction()1028 public void testHierarchyTransaction() { 1029 final ArrayMap<IBinder, RunningTaskInfo> lastReportedTiles = new ArrayMap<>(); 1030 final StubOrganizer listener = new StubOrganizer() { 1031 @Override 1032 public void onTaskInfoChanged(RunningTaskInfo info) { 1033 lastReportedTiles.put(info.token.asBinder(), info); 1034 } 1035 }; 1036 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); 1037 1038 Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 1039 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null); 1040 RunningTaskInfo info1 = task1.getTaskInfo(); 1041 Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( 1042 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null); 1043 RunningTaskInfo info2 = task2.getTaskInfo(); 1044 // Ensure events dispatch to organizer. 1045 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1046 1047 // 2 + 1 (home) = 3 1048 final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks( 1049 mDisplayContent.mDisplayId, null /* activityTypes */).size(); 1050 final Task rootTask = createTask( 1051 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); 1052 1053 // Check getRootTasks works 1054 List<RunningTaskInfo> roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks( 1055 mDisplayContent.mDisplayId, null /* activityTypes */); 1056 assertEquals(initialRootTaskCount + 1, roots.size()); 1057 1058 lastReportedTiles.clear(); 1059 WindowContainerTransaction wct = new WindowContainerTransaction(); 1060 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), 1061 info1.token, true /* onTop */); 1062 final Task rootTask2 = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask(); 1063 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), 1064 info2.token, true /* onTop */); 1065 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1066 assertFalse(lastReportedTiles.isEmpty()); 1067 assertEquals(ACTIVITY_TYPE_STANDARD, 1068 lastReportedTiles.get(info1.token.asBinder()).topActivityType); 1069 assertEquals(ACTIVITY_TYPE_HOME, 1070 lastReportedTiles.get(info2.token.asBinder()).topActivityType); 1071 1072 lastReportedTiles.clear(); 1073 wct = new WindowContainerTransaction(); 1074 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), 1075 info1.token, false /* onTop */); 1076 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1077 assertFalse(lastReportedTiles.isEmpty()); 1078 // Standard should still be on top of tile 1, so no change there 1079 assertFalse(lastReportedTiles.containsKey(info1.token.asBinder())); 1080 // But tile 2 has no children, so should become undefined 1081 assertEquals(ACTIVITY_TYPE_UNDEFINED, 1082 lastReportedTiles.get(info2.token.asBinder()).topActivityType); 1083 1084 // Check the getChildren call 1085 List<RunningTaskInfo> children = 1086 mWm.mAtmService.mTaskOrganizerController.getChildTasks(info1.token, 1087 null /* activityTypes */); 1088 assertEquals(2, children.size()); 1089 children = mWm.mAtmService.mTaskOrganizerController.getChildTasks(info2.token, 1090 null /* activityTypes */); 1091 assertEquals(0, children.size()); 1092 1093 // Check that getRootTasks doesn't include children of tiles 1094 roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks(mDisplayContent.mDisplayId, 1095 null /* activityTypes */); 1096 // Home (rootTask2) was moved into task1, so only remain 2 roots: task1 and task2. 1097 assertEquals(initialRootTaskCount - 1, roots.size()); 1098 1099 lastReportedTiles.clear(); 1100 wct = new WindowContainerTransaction(); 1101 wct.reorder(rootTask2.mRemoteToken.toWindowContainerToken(), true /* onTop */); 1102 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1103 // Home should now be on top. No change occurs in second tile, so not reported 1104 assertEquals(1, lastReportedTiles.size()); 1105 assertEquals(ACTIVITY_TYPE_HOME, 1106 lastReportedTiles.get(info1.token.asBinder()).topActivityType); 1107 1108 // This just needs to not crash (ie. it should be possible to reparent to display twice) 1109 wct = new WindowContainerTransaction(); 1110 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); 1111 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1112 wct = new WindowContainerTransaction(); 1113 wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); 1114 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1115 } 1116 getTasksCreatedByOrganizer(DisplayContent dc)1117 private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) { 1118 final ArrayList<Task> out = new ArrayList<>(); 1119 dc.forAllRootTasks(task -> { 1120 if (task.mCreatedByOrganizer) { 1121 out.add(task); 1122 } 1123 }); 1124 return out; 1125 } 1126 1127 @Test testBLASTCallbackWithActivityChildren()1128 public void testBLASTCallbackWithActivityChildren() { 1129 final Task rootTaskController1 = createRootTask(); 1130 final Task task = createTask(rootTaskController1); 1131 final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window"); 1132 1133 w.mActivityRecord.setVisibleRequested(true); 1134 w.mActivityRecord.setVisible(true); 1135 1136 BLASTSyncEngine bse = new BLASTSyncEngine(mWm); 1137 1138 BLASTSyncEngine.TransactionReadyListener transactionListener = 1139 mock(BLASTSyncEngine.TransactionReadyListener.class); 1140 1141 final int id = bse.startSyncSet(transactionListener, BLAST_TIMEOUT_DURATION, "Test", 1142 false /* parallel */); 1143 bse.addToSyncSet(id, task); 1144 bse.setReady(id); 1145 bse.onSurfacePlacement(); 1146 1147 // Even though w is invisible (and thus activity isn't waiting on it), activity will 1148 // continue to wait until it has at-least 1 visible window. 1149 // Since we have a child window we still shouldn't be done. 1150 verify(transactionListener, never()).onTransactionReady(anyInt(), any()); 1151 1152 makeWindowVisible(w); 1153 bse.onSurfacePlacement(); 1154 w.immediatelyNotifyBlastSync(); 1155 bse.onSurfacePlacement(); 1156 1157 verify(transactionListener).onTransactionReady(anyInt(), any()); 1158 } 1159 1160 static class StubOrganizer extends ITaskOrganizer.Stub { 1161 RunningTaskInfo mInfo; 1162 1163 @Override addStartingWindow(StartingWindowInfo info)1164 public void addStartingWindow(StartingWindowInfo info) { } 1165 @Override removeStartingWindow(StartingWindowRemovalInfo removalInfo)1166 public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { } 1167 @Override copySplashScreenView(int taskId)1168 public void copySplashScreenView(int taskId) { } 1169 @Override onTaskAppeared(RunningTaskInfo info, SurfaceControl leash)1170 public void onTaskAppeared(RunningTaskInfo info, SurfaceControl leash) { 1171 mInfo = info; 1172 } 1173 @Override onTaskVanished(RunningTaskInfo info)1174 public void onTaskVanished(RunningTaskInfo info) { 1175 } 1176 @Override onTaskInfoChanged(RunningTaskInfo info)1177 public void onTaskInfoChanged(RunningTaskInfo info) { 1178 } 1179 @Override onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)1180 public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { 1181 } 1182 @Override onImeDrawnOnTask(int taskId)1183 public void onImeDrawnOnTask(int taskId) throws RemoteException { 1184 } 1185 @Override onAppSplashScreenViewRemoved(int taskId)1186 public void onAppSplashScreenViewRemoved(int taskId) { 1187 } 1188 }; 1189 makePipableActivity()1190 private ActivityRecord makePipableActivity() { 1191 final ActivityRecord record = createActivityRecordWithParentTask(mDisplayContent, 1192 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); 1193 record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; 1194 record.setPictureInPictureParams(new PictureInPictureParams.Builder() 1195 .setAutoEnterEnabled(true).build()); 1196 spyOn(record); 1197 doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean()); 1198 1199 record.getTask().setHasBeenVisible(true); 1200 return record; 1201 } 1202 1203 @Test testEnterPipParams()1204 public void testEnterPipParams() { 1205 final StubOrganizer o = new StubOrganizer(); 1206 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o); 1207 final ActivityRecord record = makePipableActivity(); 1208 1209 final PictureInPictureParams p = new PictureInPictureParams.Builder() 1210 .setAspectRatio(new Rational(1, 2)).build(); 1211 assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode( 1212 record.token, p)); 1213 waitUntilHandlersIdle(); 1214 assertNotNull(o.mInfo); 1215 assertNotNull(o.mInfo.pictureInPictureParams); 1216 } 1217 1218 @Test testChangePipParams()1219 public void testChangePipParams() { 1220 class ChangeSavingOrganizer extends StubOrganizer { 1221 RunningTaskInfo mChangedInfo; 1222 @Override 1223 public void onTaskInfoChanged(RunningTaskInfo info) { 1224 mChangedInfo = info; 1225 } 1226 } 1227 ChangeSavingOrganizer o = new ChangeSavingOrganizer(); 1228 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o); 1229 1230 final ActivityRecord record = makePipableActivity(); 1231 final PictureInPictureParams p = new PictureInPictureParams.Builder() 1232 .setAspectRatio(new Rational(1, 2)).build(); 1233 assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode( 1234 record.token, p)); 1235 waitUntilHandlersIdle(); 1236 assertNotNull(o.mInfo); 1237 assertNotNull(o.mInfo.pictureInPictureParams); 1238 1239 // Bypass the quota check, which causes NPE in current test setup. 1240 if (mWm.mAtmService.mActivityClientController.mSetPipAspectRatioQuotaTracker != null) { 1241 mWm.mAtmService.mActivityClientController.mSetPipAspectRatioQuotaTracker 1242 .setEnabled(false); 1243 } 1244 1245 final PictureInPictureParams p2 = new PictureInPictureParams.Builder() 1246 .setAspectRatio(new Rational(3, 4)).build(); 1247 mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2); 1248 waitUntilHandlersIdle(); 1249 // Ensure events dispatch to organizer. 1250 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1251 assertNotNull(o.mChangedInfo); 1252 assertNotNull(o.mChangedInfo.pictureInPictureParams); 1253 final Rational ratio = o.mChangedInfo.pictureInPictureParams.getAspectRatio(); 1254 assertEquals(3, ratio.getNumerator()); 1255 assertEquals(4, ratio.getDenominator()); 1256 } 1257 1258 @Test testChangeTaskDescription()1259 public void testChangeTaskDescription() { 1260 class ChangeSavingOrganizer extends StubOrganizer { 1261 RunningTaskInfo mChangedInfo; 1262 @Override 1263 public void onTaskInfoChanged(RunningTaskInfo info) { 1264 mChangedInfo = info; 1265 } 1266 } 1267 ChangeSavingOrganizer o = new ChangeSavingOrganizer(); 1268 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o); 1269 1270 final Task rootTask = createRootTask(); 1271 final Task task = createTask(rootTask); 1272 final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task); 1273 1274 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1275 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); 1276 mAtm.mTaskOrganizerController.dispatchPendingEvents(); 1277 assertEquals("TestDescription", o.mChangedInfo.taskDescription.getLabel()); 1278 } 1279 1280 @Test testPreventDuplicateAppear()1281 public void testPreventDuplicateAppear() throws RemoteException { 1282 final ITaskOrganizer organizer = registerMockOrganizer(); 1283 final Task rootTask = createRootTask(); 1284 final Task task = createTask(rootTask, false /* fakeDraw */); 1285 1286 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1287 rootTask.setTaskOrganizer(organizer); 1288 // setHasBeenVisible was already called once by the set-up code. 1289 rootTask.setHasBeenVisible(true); 1290 // Ensure events dispatch to organizer. 1291 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1292 verify(organizer, times(1)) 1293 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 1294 1295 rootTask.setTaskOrganizer(null); 1296 // Ensure events dispatch to organizer. 1297 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1298 verify(organizer, times(1)).onTaskVanished(any()); 1299 rootTask.setTaskOrganizer(organizer); 1300 // Ensure events dispatch to organizer. 1301 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1302 verify(organizer, times(2)) 1303 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); 1304 1305 rootTask.removeImmediately(); 1306 // Ensure events dispatch to organizer. 1307 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1308 verify(organizer, times(2)).onTaskVanished(any()); 1309 } 1310 1311 @Test testInterceptBackPressedOnTaskRoot()1312 public void testInterceptBackPressedOnTaskRoot() throws RemoteException { 1313 final ITaskOrganizer organizer = registerMockOrganizer(); 1314 final Task rootTask = createRootTask(); 1315 final Task task = createTask(rootTask); 1316 final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task); 1317 final Task rootTask2 = createRootTask(); 1318 final Task task2 = createTask(rootTask2); 1319 final ActivityRecord activity2 = createActivityRecord(rootTask.mDisplayContent, task2); 1320 1321 assertTrue(rootTask.isOrganized()); 1322 assertTrue(rootTask2.isOrganized()); 1323 1324 // Verify a back pressed does not call the organizer 1325 mWm.mAtmService.mActivityClientController.onBackPressed(activity.token, 1326 new IRequestFinishCallback.Default()); 1327 // Ensure events dispatch to organizer. 1328 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1329 verify(organizer, never()).onBackPressedOnTaskRoot(any()); 1330 1331 // Enable intercepting back 1332 mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( 1333 rootTask.mRemoteToken.toWindowContainerToken(), true); 1334 1335 // Verify now that the back press does call the organizer 1336 mWm.mAtmService.mActivityClientController.onBackPressed(activity.token, 1337 new IRequestFinishCallback.Default()); 1338 // Ensure events dispatch to organizer. 1339 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1340 verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); 1341 1342 // Disable intercepting back 1343 mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( 1344 rootTask.mRemoteToken.toWindowContainerToken(), false); 1345 1346 // Verify now that the back press no longer calls the organizer 1347 mWm.mAtmService.mActivityClientController.onBackPressed(activity.token, 1348 new IRequestFinishCallback.Default()); 1349 // Ensure events dispatch to organizer. 1350 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1351 verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); 1352 } 1353 1354 @Test testBLASTCallbackWithWindows()1355 public void testBLASTCallbackWithWindows() throws Exception { 1356 final Task rootTaskController = createRootTask(); 1357 final Task task = createTask(rootTaskController); 1358 final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1"); 1359 final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2"); 1360 makeWindowVisible(w1); 1361 makeWindowVisible(w2); 1362 1363 IWindowContainerTransactionCallback mockCallback = 1364 mock(IWindowContainerTransactionCallback.class); 1365 int id = mWm.mAtmService.mWindowOrganizerController.startSyncWithOrganizer(mockCallback); 1366 1367 mWm.mAtmService.mWindowOrganizerController.addToSyncSet(id, task); 1368 mWm.mAtmService.mWindowOrganizerController.setSyncReady(id); 1369 1370 // Since we have a window we have to wait for it to draw to finish sync. 1371 verify(mockCallback, never()).onTransactionReady(anyInt(), any()); 1372 assertTrue(w1.syncNextBuffer()); 1373 assertTrue(w2.syncNextBuffer()); 1374 1375 // Make second (bottom) ready. If we started with the top, since activities fillsParent 1376 // by default, the sync would be considered finished. 1377 w2.immediatelyNotifyBlastSync(); 1378 mWm.mSyncEngine.onSurfacePlacement(); 1379 verify(mockCallback, never()).onTransactionReady(anyInt(), any()); 1380 1381 assertEquals(SYNC_STATE_READY, w2.mSyncState); 1382 // Even though one Window finished drawing, both windows should still be using blast sync 1383 assertTrue(w1.syncNextBuffer()); 1384 assertTrue(w2.syncNextBuffer()); 1385 1386 // A drawn window in non-explicit sync can complete the sync state automatically. 1387 w1.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN; 1388 w1.mPrepareSyncSeqId = 0; 1389 makeLastConfigReportedToClient(w1, true /* visible */); 1390 mWm.mSyncEngine.onSurfacePlacement(); 1391 verify(mockCallback).onTransactionReady(anyInt(), any()); 1392 assertFalse(w1.syncNextBuffer()); 1393 assertFalse(w2.syncNextBuffer()); 1394 } 1395 1396 @Test testDisplayAreaHiddenTransaction()1397 public void testDisplayAreaHiddenTransaction() { 1398 removeGlobalMinSizeRestriction(); 1399 1400 WindowContainerTransaction trx = new WindowContainerTransaction(); 1401 1402 TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); 1403 1404 trx.setHidden(taskDisplayArea.mRemoteToken.toWindowContainerToken(), true); 1405 mWm.mAtmService.mWindowOrganizerController.applyTransaction(trx); 1406 1407 taskDisplayArea.forAllTasks(daTask -> { 1408 assertTrue(daTask.isForceHidden()); 1409 }); 1410 1411 trx.setHidden(taskDisplayArea.mRemoteToken.toWindowContainerToken(), false); 1412 mWm.mAtmService.mWindowOrganizerController.applyTransaction(trx); 1413 1414 taskDisplayArea.forAllTasks(daTask -> { 1415 assertFalse(daTask.isForceHidden()); 1416 }); 1417 } 1418 1419 @Test testReparentToOrganizedTask()1420 public void testReparentToOrganizedTask() { 1421 final ITaskOrganizer organizer = registerMockOrganizer(); 1422 Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask( 1423 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null); 1424 final Task task1 = createRootTask(); 1425 final Task task2 = createTask(rootTask, false /* fakeDraw */); 1426 WindowContainerTransaction wct = new WindowContainerTransaction(); 1427 wct.reparent(task1.mRemoteToken.toWindowContainerToken(), 1428 rootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */); 1429 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1430 assertTrue(task1.isOrganized()); 1431 assertTrue(task2.isOrganized()); 1432 } 1433 1434 @Test testAppearDeferThenInfoChange()1435 public void testAppearDeferThenInfoChange() { 1436 final ITaskOrganizer organizer = registerMockOrganizer(); 1437 final Task rootTask = createRootTask(); 1438 // Flush EVENT_APPEARED. 1439 mAtm.mTaskOrganizerController.dispatchPendingEvents(); 1440 1441 // Assume layout defer 1442 mWm.mWindowPlacerLocked.deferLayout(); 1443 1444 final Task task = createTask(rootTask); 1445 final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); 1446 1447 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1448 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); 1449 waitUntilHandlersIdle(); 1450 1451 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask); 1452 assertEquals(1, pendingEvents.size()); 1453 assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType); 1454 assertEquals("TestDescription", 1455 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); 1456 } 1457 1458 @Test testReorderWithParents()1459 public void testReorderWithParents() { 1460 /* 1461 root 1462 ____|______ 1463 | | 1464 firstTda secondTda 1465 | | 1466 firstRootTask secondRootTask 1467 1468 */ 1469 final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); 1470 final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( 1471 mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", 1472 FEATURE_VENDOR_FIRST); 1473 final Task firstRootTask = firstTaskDisplayArea.createRootTask( 1474 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 1475 final Task secondRootTask = secondTaskDisplayArea.createRootTask( 1476 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 1477 final ActivityRecord firstActivity = new ActivityBuilder(mAtm) 1478 .setTask(firstRootTask).build(); 1479 final ActivityRecord secondActivity = new ActivityBuilder(mAtm) 1480 .setTask(secondRootTask).build(); 1481 // This assertion is just a defense to ensure that firstRootTask is not the top most 1482 // by default 1483 assertThat(mDisplayContent.getTopRootTask()).isEqualTo(secondRootTask); 1484 WindowContainerTransaction wct = new WindowContainerTransaction(); 1485 1486 // Reorder to top 1487 wct.reorder(firstRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */, 1488 true /* includingParents */); 1489 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1490 1491 // firstRootTask can only be on the top if its TDA was also reordered to the Top which 1492 // in-turn ensures that the reorder happened including the parents. 1493 assertThat(mDisplayContent.getTopRootTask()).isEqualTo(firstRootTask); 1494 1495 // Reorder to bottom 1496 wct.reorder(firstRootTask.mRemoteToken.toWindowContainerToken(), false /* onTop */, 1497 true /* includingParents */); 1498 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1499 1500 // firstRootTask can only be on the bottom if its TDA was also reordered to the bottom 1501 // which in-turn ensures that the reorder happened including the parents. 1502 assertThat(mDisplayContent.getBottomMostTask()).isEqualTo(firstRootTask); 1503 } 1504 1505 @Test testAppearDeferThenVanish()1506 public void testAppearDeferThenVanish() { 1507 final ITaskOrganizer organizer = registerMockOrganizer(); 1508 final Task rootTask = createRootTask(); 1509 // Flush EVENT_APPEARED. 1510 mAtm.mTaskOrganizerController.dispatchPendingEvents(); 1511 1512 // Assume layout defer 1513 mWm.mWindowPlacerLocked.deferLayout(); 1514 1515 final Task task = createTask(rootTask); 1516 1517 rootTask.removeImmediately(); 1518 waitUntilHandlersIdle(); 1519 1520 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask); 1521 assertEquals(0, pendingEvents.size()); 1522 } 1523 1524 @Test testInfoChangeDeferMultiple()1525 public void testInfoChangeDeferMultiple() { 1526 final ITaskOrganizer organizer = registerMockOrganizer(); 1527 final Task rootTask = createRootTask(); 1528 final Task task = createTask(rootTask); 1529 final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task); 1530 1531 // Assume layout defer 1532 mWm.mWindowPlacerLocked.deferLayout(); 1533 1534 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1535 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); 1536 waitUntilHandlersIdle(); 1537 1538 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask); 1539 assertEquals(1, pendingEvents.size()); 1540 assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); 1541 assertEquals("TestDescription", 1542 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); 1543 1544 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2")); 1545 waitUntilHandlersIdle(); 1546 1547 pendingEvents = getTaskPendingEvent(organizer, rootTask); 1548 assertEquals(1, pendingEvents.size()); 1549 assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); 1550 assertEquals("TestDescription2", 1551 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); 1552 } 1553 1554 @Test testInfoChangDeferThenVanish()1555 public void testInfoChangDeferThenVanish() { 1556 final ITaskOrganizer organizer = registerMockOrganizer(); 1557 final Task rootTask = createRootTask(); 1558 final Task task = createTask(rootTask); 1559 final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task); 1560 1561 // Assume layout defer 1562 mWm.mWindowPlacerLocked.deferLayout(); 1563 1564 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1565 record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); 1566 1567 rootTask.removeImmediately(); 1568 waitUntilHandlersIdle(); 1569 1570 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask); 1571 assertEquals(1, pendingEvents.size()); 1572 assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); 1573 assertEquals("TestDescription", 1574 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); 1575 } 1576 1577 @Test testVanishDeferThenInfoChange()1578 public void testVanishDeferThenInfoChange() { 1579 final ITaskOrganizer organizer = registerMockOrganizer(); 1580 final Task rootTask = createRootTask(); 1581 final Task task = createTask(rootTask); 1582 createActivityRecordAndDispatchPendingEvents(task); 1583 1584 // Assume layout defer 1585 mWm.mWindowPlacerLocked.deferLayout(); 1586 1587 rootTask.removeImmediately(); 1588 rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1589 waitUntilHandlersIdle(); 1590 1591 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask); 1592 assertEquals(1, pendingEvents.size()); 1593 assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); 1594 } 1595 1596 @Test testVanishDeferThenBackOnRoot()1597 public void testVanishDeferThenBackOnRoot() { 1598 final ITaskOrganizer organizer = registerMockOrganizer(); 1599 final Task rootTask = createRootTask(); 1600 final Task task = createTask(rootTask); 1601 final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task); 1602 1603 // Assume layout defer 1604 mWm.mWindowPlacerLocked.deferLayout(); 1605 1606 rootTask.removeImmediately(); 1607 mWm.mAtmService.mActivityClientController.onBackPressed(record.token, 1608 new IRequestFinishCallback.Default()); 1609 waitUntilHandlersIdle(); 1610 1611 ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask); 1612 assertEquals(1, pendingEvents.size()); 1613 assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); 1614 } 1615 getTaskPendingEvent(ITaskOrganizer organizer, Task task)1616 private ArrayList<PendingTaskEvent> getTaskPendingEvent(ITaskOrganizer organizer, Task task) { 1617 ArrayList<PendingTaskEvent> total = 1618 mWm.mAtmService.mTaskOrganizerController 1619 .getTaskOrganizerPendingEvents(organizer.asBinder()) 1620 .getPendingEventList(); 1621 ArrayList<PendingTaskEvent> result = new ArrayList(); 1622 1623 for (int i = 0; i < total.size(); i++) { 1624 PendingTaskEvent entry = total.get(i); 1625 if (entry.mTask.mTaskId == task.mTaskId) { 1626 result.add(entry); 1627 } 1628 } 1629 1630 return result; 1631 } 1632 1633 @Test testReparentNonResizableTaskToSplitScreen()1634 public void testReparentNonResizableTaskToSplitScreen() { 1635 final ActivityRecord activity = new ActivityBuilder(mAtm) 1636 .setCreateTask(true) 1637 .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE) 1638 .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) 1639 .build(); 1640 final Task rootTask = activity.getRootTask(); 1641 rootTask.setResizeMode(activity.info.resizeMode); 1642 final Task splitPrimaryRootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask( 1643 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null); 1644 final WindowContainerTransaction wct = new WindowContainerTransaction(); 1645 wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), 1646 splitPrimaryRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */); 1647 1648 // Can't reparent non-resizable to split screen 1649 mAtm.mSupportsNonResizableMultiWindow = -1; 1650 mAtm.mWindowOrganizerController.applyTransaction(wct); 1651 1652 assertEquals(rootTask, activity.getRootTask()); 1653 1654 // Allow reparent non-resizable to split screen 1655 mAtm.mSupportsNonResizableMultiWindow = 1; 1656 mAtm.mWindowOrganizerController.applyTransaction(wct); 1657 1658 assertEquals(splitPrimaryRootTask, activity.getRootTask()); 1659 } 1660 1661 @Test testSizeCompatModeChangedOnFirstOrganizedTask()1662 public void testSizeCompatModeChangedOnFirstOrganizedTask() throws RemoteException { 1663 final ITaskOrganizer organizer = registerMockOrganizer(); 1664 final Task rootTask = createRootTask(); 1665 final Task task = createTask(rootTask); 1666 final ActivityRecord activity = createActivityRecordAndDispatchPendingEvents(task); 1667 final ArgumentCaptor<RunningTaskInfo> infoCaptor = 1668 ArgumentCaptor.forClass(RunningTaskInfo.class); 1669 1670 assertTrue(rootTask.isOrganized()); 1671 1672 doReturn(true).when(activity).inSizeCompatMode(); 1673 doReturn(true).when(activity).isState(RESUMED); 1674 1675 // Ensure task info show top activity in size compat. 1676 rootTask.onSizeCompatActivityChanged(); 1677 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1678 verify(organizer).onTaskInfoChanged(infoCaptor.capture()); 1679 RunningTaskInfo info = infoCaptor.getValue(); 1680 assertEquals(rootTask.mTaskId, info.taskId); 1681 assertTrue(info.appCompatTaskInfo.topActivityInSizeCompat); 1682 1683 // Ensure task info show top activity that is not visible as not in size compat. 1684 clearInvocations(organizer); 1685 doReturn(false).when(activity).isVisible(); 1686 rootTask.onSizeCompatActivityChanged(); 1687 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1688 verify(organizer).onTaskInfoChanged(infoCaptor.capture()); 1689 info = infoCaptor.getValue(); 1690 assertEquals(rootTask.mTaskId, info.taskId); 1691 assertFalse(info.appCompatTaskInfo.topActivityInSizeCompat); 1692 1693 // Ensure task info show non size compat top activity as not in size compat. 1694 clearInvocations(organizer); 1695 doReturn(true).when(activity).isVisible(); 1696 doReturn(false).when(activity).inSizeCompatMode(); 1697 rootTask.onSizeCompatActivityChanged(); 1698 mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); 1699 verify(organizer).onTaskInfoChanged(infoCaptor.capture()); 1700 info = infoCaptor.getValue(); 1701 assertEquals(rootTask.mTaskId, info.taskId); 1702 assertFalse(info.appCompatTaskInfo.topActivityInSizeCompat); 1703 } 1704 1705 @Test testStartTasksInTransaction()1706 public void testStartTasksInTransaction() { 1707 WindowContainerTransaction wct = new WindowContainerTransaction(); 1708 ActivityOptions testOptions = ActivityOptions.makeBasic(); 1709 testOptions.setTransientLaunch(); 1710 wct.startTask(1, null /* options */); 1711 wct.startTask(2, testOptions.toBundle()); 1712 spyOn(mWm.mAtmService.mTaskSupervisor); 1713 doReturn(START_CANCELED).when(mWm.mAtmService.mTaskSupervisor).startActivityFromRecents( 1714 anyInt(), anyInt(), anyInt(), any()); 1715 clearInvocations(mWm.mAtmService); 1716 mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); 1717 1718 verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents( 1719 anyInt(), anyInt(), eq(1), any()); 1720 1721 final ArgumentCaptor<SafeActivityOptions> optionsCaptor = 1722 ArgumentCaptor.forClass(SafeActivityOptions.class); 1723 verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents( 1724 anyInt(), anyInt(), eq(2), optionsCaptor.capture()); 1725 assertTrue(optionsCaptor.getValue().getOriginalOptions().getTransientLaunch()); 1726 } 1727 1728 @Test testResumeTopsWhenLeavingPinned()1729 public void testResumeTopsWhenLeavingPinned() { 1730 final ActivityRecord record = makePipableActivity(); 1731 final Task rootTask = record.getRootTask(); 1732 1733 clearInvocations(mWm.mAtmService.mRootWindowContainer); 1734 final WindowContainerTransaction t = new WindowContainerTransaction(); 1735 WindowContainerToken wct = rootTask.mRemoteToken.toWindowContainerToken(); 1736 t.setWindowingMode(wct, WINDOWING_MODE_PINNED); 1737 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 1738 verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities(); 1739 1740 clearInvocations(mWm.mAtmService.mRootWindowContainer); 1741 // The token for the PIP root task may have changed when the task entered PIP mode, so do 1742 // not reuse the one from above. 1743 final WindowContainerToken newToken = 1744 record.getRootTask().mRemoteToken.toWindowContainerToken(); 1745 t.setWindowingMode(newToken, WINDOWING_MODE_FULLSCREEN); 1746 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 1747 verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities(); 1748 } 1749 1750 @Test testSetAlwaysOnTop()1751 public void testSetAlwaysOnTop() { 1752 final Task rootTask = new TaskBuilder(mSupervisor) 1753 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 1754 testSetAlwaysOnTop(rootTask); 1755 1756 final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea(); 1757 displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM); 1758 testSetAlwaysOnTop(displayArea); 1759 } 1760 testSetAlwaysOnTop(WindowContainer wc)1761 private void testSetAlwaysOnTop(WindowContainer wc) { 1762 final WindowContainerTransaction t = new WindowContainerTransaction(); 1763 t.setAlwaysOnTop(wc.mRemoteToken.toWindowContainerToken(), true); 1764 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 1765 assertTrue(wc.isAlwaysOnTop()); 1766 1767 t.setAlwaysOnTop(wc.mRemoteToken.toWindowContainerToken(), false); 1768 mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); 1769 assertFalse(wc.isAlwaysOnTop()); 1770 } 1771 createActivityRecordAndDispatchPendingEvents(Task task)1772 private ActivityRecord createActivityRecordAndDispatchPendingEvents(Task task) { 1773 final ActivityRecord record = createActivityRecord(task); 1774 // Flush EVENT_APPEARED. 1775 mAtm.mTaskOrganizerController.dispatchPendingEvents(); 1776 return record; 1777 } 1778 1779 /** 1780 * Verifies that task vanished is called for a specific task. 1781 */ assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks)1782 private void assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks) 1783 throws RemoteException { 1784 ArgumentCaptor<RunningTaskInfo> arg = ArgumentCaptor.forClass(RunningTaskInfo.class); 1785 verify(organizer, atLeastOnce()).onTaskVanished(arg.capture()); 1786 List<RunningTaskInfo> taskInfos = arg.getAllValues(); 1787 1788 HashSet<Integer> vanishedTaskIds = new HashSet<>(); 1789 for (int i = 0; i < taskInfos.size(); i++) { 1790 vanishedTaskIds.add(taskInfos.get(i).taskId); 1791 } 1792 HashSet<Integer> taskIds = new HashSet<>(); 1793 for (int i = 0; i < tasks.length; i++) { 1794 taskIds.add(tasks[i].mTaskId); 1795 } 1796 1797 assertTrue(expectVanished 1798 ? vanishedTaskIds.containsAll(taskIds) 1799 : !vanishedTaskIds.removeAll(taskIds)); 1800 } 1801 assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks)1802 private void assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks) { 1803 HashSet<Integer> taskIds = new HashSet<>(); 1804 for (int i = 0; i < taskInfos.size(); i++) { 1805 taskIds.add(taskInfos.get(i).getTaskInfo().taskId); 1806 } 1807 for (int i = 0; i < expectedTasks.length; i++) { 1808 assertTrue(taskIds.contains(expectedTasks[i].mTaskId)); 1809 } 1810 } 1811 1812 @NonNull createTaskFragmentOrganizer( @onNull WindowContainerTransaction t, boolean isSystemOrganizer)1813 private TaskFragmentOrganizer createTaskFragmentOrganizer( 1814 @NonNull WindowContainerTransaction t, boolean isSystemOrganizer) { 1815 final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); 1816 final ITaskFragmentOrganizer organizerInterface = 1817 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()); 1818 registerTaskFragmentOrganizer( 1819 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()), 1820 isSystemOrganizer); 1821 t.setTaskFragmentOrganizer(organizerInterface); 1822 1823 return organizer; 1824 } 1825 } 1826