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