1 /* 2 * Copyright (C) 2022 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.car.carlauncher; 18 19 import static android.app.ActivityTaskManager.INVALID_TASK_ID; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 21 import static android.view.Display.DEFAULT_DISPLAY; 22 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK; 23 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT; 24 25 import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_SWITCHING; 26 import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED; 27 28 import static com.google.common.truth.Truth.assertThat; 29 import static com.google.common.truth.Truth.assertWithMessage; 30 31 import static org.mockito.ArgumentMatchers.any; 32 import static org.mockito.ArgumentMatchers.anyInt; 33 import static org.mockito.ArgumentMatchers.anyLong; 34 import static org.mockito.ArgumentMatchers.eq; 35 import static org.mockito.ArgumentMatchers.notNull; 36 import static org.mockito.Mockito.atLeastOnce; 37 import static org.mockito.Mockito.doAnswer; 38 import static org.mockito.Mockito.doNothing; 39 import static org.mockito.Mockito.doReturn; 40 import static org.mockito.Mockito.mock; 41 import static org.mockito.Mockito.spy; 42 import static org.mockito.Mockito.times; 43 import static org.mockito.Mockito.verify; 44 import static org.mockito.Mockito.verifyZeroInteractions; 45 import static org.mockito.Mockito.when; 46 47 import android.app.Activity; 48 import android.app.ActivityManager; 49 import android.app.ActivityTaskManager; 50 import android.app.TaskStackListener; 51 import android.car.Car; 52 import android.car.app.CarActivityManager; 53 import android.car.test.mocks.AbstractExtendedMockitoTestCase; 54 import android.car.user.CarUserManager; 55 import android.content.ComponentName; 56 import android.content.Intent; 57 import android.net.Uri; 58 import android.os.Binder; 59 import android.os.IBinder; 60 import android.os.Looper; 61 import android.view.SurfaceControl; 62 import android.view.SurfaceHolder; 63 import android.window.TaskAppearedInfo; 64 import android.window.WindowContainerToken; 65 import android.window.WindowContainerTransaction; 66 67 import androidx.annotation.NonNull; 68 import androidx.lifecycle.Lifecycle; 69 import androidx.test.core.app.ActivityScenario; 70 import androidx.test.ext.junit.rules.ActivityScenarioRule; 71 import androidx.test.ext.junit.runners.AndroidJUnit4; 72 73 import com.android.car.carlauncher.taskstack.TaskStackChangeListeners; 74 import com.android.dx.mockito.inline.extended.ExtendedMockito; 75 import com.android.wm.shell.ShellTaskOrganizer; 76 import com.android.wm.shell.common.HandlerExecutor; 77 import com.android.wm.shell.common.SyncTransactionQueue; 78 import com.android.wm.shell.startingsurface.StartingWindowController; 79 import com.android.wm.shell.sysui.ShellController; 80 import com.android.wm.shell.sysui.ShellInit; 81 import com.android.wm.shell.taskview.TaskView; 82 import com.android.wm.shell.transition.Transitions; 83 84 import com.google.common.collect.ImmutableList; 85 import com.google.common.collect.ImmutableSet; 86 87 import org.junit.After; 88 import org.junit.Before; 89 import org.junit.Rule; 90 import org.junit.Test; 91 import org.junit.runner.RunWith; 92 import org.mockito.ArgumentCaptor; 93 import org.mockito.Captor; 94 import org.mockito.Mock; 95 import org.mockito.invocation.InvocationOnMock; 96 97 import java.util.ArrayList; 98 import java.util.List; 99 import java.util.Set; 100 import java.util.concurrent.CountDownLatch; 101 import java.util.concurrent.TimeUnit; 102 import java.util.concurrent.atomic.AtomicReference; 103 import java.util.stream.Collectors; 104 105 //TODO(b/266742500): Test that uses setUpLaunchRootTaskView get null listener 106 @RunWith(AndroidJUnit4.class) 107 public class TaskViewManagerTest extends AbstractExtendedMockitoTestCase { 108 @Rule 109 public ActivityScenarioRule mActivityRule = new ActivityScenarioRule<>(TestActivity.class); 110 111 @Mock 112 private ShellTaskOrganizer mOrganizer; 113 @Mock 114 private SyncTransactionQueue mSyncQueue; 115 @Mock 116 private Transitions mTransitions; 117 @Mock 118 private HandlerExecutor mShellExecutor; 119 @Mock 120 private CarActivityManager mCarActivityManager; 121 @Mock 122 private Car mCar; 123 @Mock 124 private TaskStackChangeListeners mTaskStackChangeListeners; 125 @Mock 126 private CarUserManager mCarUserManager; 127 @Mock 128 private WindowContainerToken mToken; 129 130 @Mock 131 private ShellController mShellController; 132 @Mock 133 private StartingWindowController mStartingWindowController; 134 @Mock 135 private TaskViewInputInterceptor mTaskViewInputInterceptor; 136 137 @Captor 138 private ArgumentCaptor<TaskStackListener> mTaskStackListenerArgumentCaptor; 139 @Captor 140 private ArgumentCaptor<CarUserManager.UserLifecycleListener> 141 mUserLifecycleListenerArgumentCaptor; 142 143 private TestActivity mActivity; 144 private Car.CarServiceLifecycleListener mCarServiceLifecycleListener; 145 private ActivityTaskManager mSpyActivityTaskManager; 146 private CountDownLatch mIdleHandlerLatch = new CountDownLatch(1); 147 private SurfaceControl mLeash; 148 149 @Override onSessionBuilder(@onNull CustomMockitoSessionBuilder builder)150 protected void onSessionBuilder(@NonNull CustomMockitoSessionBuilder builder) { 151 builder.spyStatic(ActivityTaskManager.class); 152 builder.spyStatic(Car.class); 153 builder.spyStatic(TaskStackChangeListeners.class); 154 } 155 156 @Before setUp()157 public void setUp() { 158 ExtendedMockito.doAnswer(invocation -> { 159 mCarServiceLifecycleListener = invocation.getArgument(3); 160 return mCar; 161 }).when(() -> Car.createCar(any(), any(), anyLong(), any())); 162 when(mCar.getCarManager(eq(Car.CAR_ACTIVITY_SERVICE))).thenReturn(mCarActivityManager); 163 when(mCar.getCarManager(eq(Car.CAR_USER_SERVICE))).thenReturn(mCarUserManager); 164 165 ExtendedMockito.doReturn(mTaskStackChangeListeners).when(() -> 166 TaskStackChangeListeners.getInstance()); 167 doNothing().when(mTaskStackChangeListeners).registerTaskStackListener( 168 mTaskStackListenerArgumentCaptor.capture()); 169 170 doNothing().when(mCarUserManager).addListener(any(), any(), 171 mUserLifecycleListenerArgumentCaptor.capture()); 172 173 mLeash = new SurfaceControl.Builder(null) 174 .setName("test") 175 .build(); 176 177 doAnswer((InvocationOnMock invocationOnMock) -> { 178 SyncTransactionQueue.TransactionRunnable r = 179 invocationOnMock.getArgument(0); 180 r.runWithTransaction(new SurfaceControl.Transaction()); 181 return null; 182 }).when(mSyncQueue).runInSync(any()); 183 184 doAnswer((InvocationOnMock invocationOnMock) -> { 185 Runnable r = invocationOnMock.getArgument(0); 186 r.run(); 187 return null; 188 }).when(mShellExecutor).execute(any()); 189 doReturn(mShellExecutor).when(mOrganizer).getExecutor(); 190 191 mSpyActivityTaskManager = spy(ActivityTaskManager.getInstance()); 192 ExtendedMockito.doReturn(mSpyActivityTaskManager).when(() -> 193 ActivityTaskManager.getInstance()); 194 195 ActivityScenario<TestActivity> scenario = mActivityRule.getScenario(); 196 scenario.onActivity(activity -> mActivity = activity); 197 } 198 199 @After tearDown()200 public void tearDown() throws InterruptedException { 201 mActivity.finishCompletely(); 202 } 203 createMultiWindowTask(int taskId)204 private TaskAppearedInfo createMultiWindowTask(int taskId) { 205 ActivityManager.RunningTaskInfo taskInfo = 206 new ActivityManager.RunningTaskInfo(); 207 taskInfo.taskId = taskId; 208 taskInfo.configuration.windowConfiguration.setWindowingMode( 209 WINDOWING_MODE_MULTI_WINDOW); 210 taskInfo.parentTaskId = INVALID_TASK_ID; 211 taskInfo.token = mock(WindowContainerToken.class); 212 taskInfo.isVisible = true; 213 return new TaskAppearedInfo(taskInfo, new SurfaceControl()); 214 } 215 createMultiWindowTask(int taskId, IBinder token)216 private TaskAppearedInfo createMultiWindowTask(int taskId, IBinder token) { 217 TaskAppearedInfo taskInfo = createMultiWindowTask(taskId); 218 when(taskInfo.getTaskInfo().token.asBinder()).thenReturn(token); 219 return taskInfo; 220 } 221 222 @Test init_cleansUpExistingMultiWindowTasks()223 public void init_cleansUpExistingMultiWindowTasks() { 224 TaskAppearedInfo existingTask1 = createMultiWindowTask(/* taskId= */ 1); 225 TaskAppearedInfo existingTask2 = createMultiWindowTask(/* taskId= */ 2); 226 doReturn(ImmutableList.of(existingTask1, existingTask2)) 227 .when(mOrganizer).registerOrganizer(); 228 ExtendedMockito.doReturn(false).when(mSpyActivityTaskManager).removeTask(anyInt()); 229 230 createTaskViewManager(); 231 232 verify(mSpyActivityTaskManager).removeTask(eq(1)); 233 verify(mSpyActivityTaskManager).removeTask(eq(2)); 234 } 235 236 @Test testCreateControlledTaskView()237 public void testCreateControlledTaskView() throws Exception { 238 TaskViewManager taskViewManager = createTaskViewManager(); 239 240 Intent activityIntent = new Intent(); 241 Set<String> packagesThatCanRestart = ImmutableSet.of("com.random.package"); 242 ControlledCarTaskViewCallbacks controlledCarTaskViewCallbacks = mock( 243 ControlledCarTaskViewCallbacks.class); 244 when(controlledCarTaskViewCallbacks.getDependingPackageNames()) 245 .thenReturn(packagesThatCanRestart); 246 247 taskViewManager.createControlledCarTaskView( 248 mActivity.getMainExecutor(), 249 ControlledCarTaskViewConfig.builder() 250 .setActivityIntent(activityIntent) 251 .setAutoRestartOnCrash(false) 252 .build(), 253 controlledCarTaskViewCallbacks 254 ); 255 256 runOnMainAndWait(() -> {}); 257 verify(controlledCarTaskViewCallbacks).onTaskViewCreated(any()); 258 verifyZeroInteractions(mTaskViewInputInterceptor); 259 } 260 261 @Test testCreateControlledTaskView_initializesInterceptor_whenCapturingEvents()262 public void testCreateControlledTaskView_initializesInterceptor_whenCapturingEvents() throws 263 Exception { 264 TaskViewManager taskViewManager = createTaskViewManager(); 265 266 Intent activityIntent = new Intent(); 267 Set<String> packagesThatCanRestart = ImmutableSet.of("com.random.package"); 268 ControlledCarTaskViewCallbacks controlledCarTaskViewCallbacks = mock( 269 ControlledCarTaskViewCallbacks.class); 270 when(controlledCarTaskViewCallbacks.getDependingPackageNames()) 271 .thenReturn(packagesThatCanRestart); 272 273 taskViewManager.createControlledCarTaskView( 274 mActivity.getMainExecutor(), 275 ControlledCarTaskViewConfig.builder() 276 .setActivityIntent(activityIntent) 277 .setAutoRestartOnCrash(false) 278 .setCaptureLongPress(true) 279 .build(), 280 controlledCarTaskViewCallbacks 281 ); 282 283 runOnMainAndWait(() -> {}); 284 verify(mTaskViewInputInterceptor).init(); 285 } 286 287 @Test testCreateControlledTaskView_callsOnReadyWhenVisible()288 public void testCreateControlledTaskView_callsOnReadyWhenVisible() throws Exception { 289 TaskViewManager taskViewManager = createTaskViewManager(); 290 Intent activityIntent = new Intent("ACTION_VIEW"); 291 Set<String> packagesThatCanRestart = ImmutableSet.of("com.random.package"); 292 ControlledCarTaskViewCallbacks controlledCarTaskViewCallbacks = mock( 293 ControlledCarTaskViewCallbacks.class); 294 when(controlledCarTaskViewCallbacks.getDependingPackageNames()) 295 .thenReturn(packagesThatCanRestart); 296 taskViewManager.createControlledCarTaskView( 297 mActivity.getMainExecutor(), 298 ControlledCarTaskViewConfig.builder() 299 .setActivityIntent(activityIntent) 300 .setAutoRestartOnCrash(false) 301 .build(), 302 controlledCarTaskViewCallbacks 303 ); 304 ControlledCarTaskView taskView = spy(taskViewManager.getControlledTaskViews().get(0)); 305 doNothing().when(taskView).startActivity(); 306 307 taskView.surfaceCreated(mock(SurfaceHolder.class)); 308 309 runOnMainAndWait(() -> {}); 310 verify(controlledCarTaskViewCallbacks).onTaskViewCreated(any()); 311 verify(controlledCarTaskViewCallbacks).onTaskViewReady(); 312 } 313 314 @Test testCreateLaunchRootTaskView()315 public void testCreateLaunchRootTaskView() throws Exception { 316 LaunchRootCarTaskViewCallbacks taskViewCallbacks = 317 mock(LaunchRootCarTaskViewCallbacks.class); 318 TaskViewManager taskViewManager = createTaskViewManager(); 319 320 taskViewManager.createLaunchRootTaskView( 321 mActivity.getMainExecutor(), 322 taskViewCallbacks 323 ); 324 runOnMainAndWait(() -> {}); 325 TaskView taskView = taskViewManager.getLaunchRootCarTaskView(); 326 taskView.surfaceCreated(mock(SurfaceHolder.class)); 327 328 runOnMainAndWait(() -> {}); 329 verify(taskViewCallbacks).onTaskViewCreated(any()); 330 verify(mOrganizer).createRootTask(eq(DEFAULT_DISPLAY), 331 eq(WINDOWING_MODE_MULTI_WINDOW), 332 any(ShellTaskOrganizer.TaskListener.class), 333 /* removeWithTaskOrganizer= */ eq(true)); 334 } 335 336 @Test testCreateLaunchRootTaskView_callsOnReadyWhenVisible()337 public void testCreateLaunchRootTaskView_callsOnReadyWhenVisible() throws Exception { 338 TaskAppearedInfo fakeLaunchRootTaskInfo = createMultiWindowTask(1); 339 LaunchRootCarTaskViewCallbacks taskViewCallbacks = 340 mock(LaunchRootCarTaskViewCallbacks.class); 341 TaskViewManager taskViewManager = createTaskViewManager(); 342 doAnswer(invocation -> { 343 ShellTaskOrganizer.TaskListener listener = invocation.getArgument(2); 344 listener.onTaskAppeared(fakeLaunchRootTaskInfo.getTaskInfo(), mLeash); 345 return null; 346 }).when(mOrganizer).createRootTask(eq(DEFAULT_DISPLAY), 347 eq(WINDOWING_MODE_MULTI_WINDOW), 348 any(ShellTaskOrganizer.TaskListener.class), 349 /* removeWithTaskOrganizer= */ eq(true)); 350 351 taskViewManager.createLaunchRootTaskView( 352 mActivity.getMainExecutor(), 353 taskViewCallbacks 354 ); 355 runOnMainAndWait(() -> {}); 356 TaskView taskView = taskViewManager.getLaunchRootCarTaskView(); 357 taskView.surfaceCreated(mock(SurfaceHolder.class)); 358 359 runOnMainAndWait(() -> {}); 360 verify(taskViewCallbacks).onTaskViewCreated(any()); 361 verify(taskViewCallbacks).onTaskViewReady(); 362 ArgumentCaptor<WindowContainerTransaction> wctCaptor = ArgumentCaptor.forClass( 363 WindowContainerTransaction.class); 364 verify(mSyncQueue, atLeastOnce()).queue(wctCaptor.capture()); 365 List<WindowContainerTransaction> wcts = wctCaptor.getAllValues(); 366 assertWithMessage("There must be a WindowContainerTransaction to set the" 367 + " root task as the launch root.") 368 .that(wcts.stream() 369 .flatMap(wct -> wct.getHierarchyOps().stream()) 370 .map(WindowContainerTransaction.HierarchyOp::getType) 371 .anyMatch(type -> type == HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT)) 372 .isTrue(); 373 } 374 375 @Test testLaunchRootTaskView_onBackPressed_removesTopTask()376 public void testLaunchRootTaskView_onBackPressed_removesTopTask() throws Exception { 377 IBinder task1Token = new Binder(); 378 IBinder task2Token = new Binder(); 379 IBinder task3Token = new Binder(); 380 ActivityManager.RunningTaskInfo task1 = createMultiWindowTask(1, task1Token).getTaskInfo(); 381 ActivityManager.RunningTaskInfo task2 = createMultiWindowTask(2, task2Token).getTaskInfo(); 382 ActivityManager.RunningTaskInfo task3 = createMultiWindowTask(3, task3Token).getTaskInfo(); 383 TaskViewManager taskViewManager = createTaskViewManager(); 384 runOnMainAndWait(() -> {}); 385 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 386 runOnMainAndWait(() -> {}); 387 // Set up a LaunchRootTaskView 388 AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>(); 389 ActivityManager.RunningTaskInfo launchRootTask = 390 setUpLaunchRootTaskView(taskViewManager, rootTaskListener, /* rootTaskId = */ 100); 391 runOnMainAndWait(() -> {}); 392 // Trigger a taskAppeared on the launch root task to mimic the task appearance. 393 rootTaskListener.get().onTaskAppeared(task1, mLeash); 394 rootTaskListener.get().onTaskAppeared(task2, mLeash); 395 rootTaskListener.get().onTaskAppeared(task3, mLeash); 396 rootTaskListener.get().onTaskInfoChanged(task1); 397 // The resultant stack top to bottom is task1, task3, task2 398 runOnMainAndWait(() -> {}); 399 400 // Act 401 // Press back button 3 times, trigger corresponding task vanishing as well. In real 402 // scenario, removeTask() will trigger onTaskVanished. 403 rootTaskListener.get().onBackPressedOnTaskRoot(launchRootTask); 404 rootTaskListener.get().onTaskVanished(task1); 405 rootTaskListener.get().onBackPressedOnTaskRoot(launchRootTask); 406 rootTaskListener.get().onTaskVanished(task3); 407 rootTaskListener.get().onBackPressedOnTaskRoot(launchRootTask); 408 409 // Assert 410 ArgumentCaptor<WindowContainerTransaction> wctCaptor = ArgumentCaptor.forClass( 411 WindowContainerTransaction.class); 412 verify(mSyncQueue, atLeastOnce()).queue(wctCaptor.capture()); 413 List<WindowContainerTransaction> wcts = wctCaptor.getAllValues(); 414 List<WindowContainerTransaction.HierarchyOp> removeTaskOps = 415 wcts.stream().flatMap(wct -> wct.getHierarchyOps().stream()) 416 .filter(op -> op.getType() == HIERARCHY_OP_TYPE_REMOVE_TASK) 417 .collect(Collectors.toList()); 418 assertWithMessage("There must be a WindowContainerTransaction to remove" 419 + " 2 of the 3 tasks.") 420 .that(removeTaskOps.size()) 421 .isEqualTo(2); 422 assertThat(removeTaskOps.get(0).getContainer()).isEqualTo(task1Token); 423 assertThat(removeTaskOps.get(1).getContainer()).isEqualTo(task3Token); 424 assertThat(taskViewManager.getRootTaskCount()).isEqualTo(1); 425 assertThat(taskViewManager.getTopTaskInLaunchRootTask().taskId).isEqualTo(2); 426 } 427 428 @Test testCreateSemiControlledTaskView()429 public void testCreateSemiControlledTaskView() throws Exception { 430 SemiControlledCarTaskViewCallbacks taskViewCallbacks = 431 mock(SemiControlledCarTaskViewCallbacks.class); 432 TaskViewManager taskViewManager = createTaskViewManager(); 433 434 taskViewManager.createSemiControlledTaskView( 435 mActivity.getMainExecutor(), 436 List.of(), 437 taskViewCallbacks 438 ); 439 runOnMainAndWait(() -> {}); 440 TaskView taskView = taskViewManager.getSemiControlledTaskViews().get(0); 441 taskView.surfaceCreated(mock(SurfaceHolder.class)); 442 443 runOnMainAndWait(() -> {}); 444 verify(taskViewCallbacks).onTaskViewCreated(any()); 445 verify(mOrganizer).createRootTask(eq(DEFAULT_DISPLAY), 446 eq(WINDOWING_MODE_MULTI_WINDOW), 447 any(ShellTaskOrganizer.TaskListener.class), 448 /* removeWithTaskOrganizer= */ eq(true)); 449 } 450 451 @Test testSemiControlledTaskView_callsOnReady()452 public void testSemiControlledTaskView_callsOnReady() throws Exception { 453 List<ComponentName> persistentActivities = 454 List.of(ComponentName.unflattenFromString("com.example/.MainActivity")); 455 SemiControlledCarTaskViewCallbacks mockCallbacks = mock( 456 SemiControlledCarTaskViewCallbacks.class); 457 TaskViewManager taskViewManager = createTaskViewManager(); 458 runOnMainAndWait(() -> {}); 459 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 460 // Set up a SemiControlledCarTaskView 461 AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>(); 462 463 // Act 464 setUpSemiControlledTaskView(taskViewManager, 465 rootTaskListener, /* rootTaskId = */ 1, persistentActivities, mockCallbacks); 466 runOnMainAndWait(() -> {}); 467 468 // Assert 469 verify(mockCallbacks).onTaskViewReady(); 470 verify(mCarActivityManager).setPersistentActivitiesOnRootTask(eq(persistentActivities), 471 any()); 472 } 473 474 @Test testTaskAppeared_semiControlledTaskView_topTaskUpdated()475 public void testTaskAppeared_semiControlledTaskView_topTaskUpdated() throws Exception { 476 SemiControlledCarTaskViewCallbacks mockCallbacks = mock( 477 SemiControlledCarTaskViewCallbacks.class); 478 TaskViewManager taskViewManager = createTaskViewManager(); 479 runOnMainAndWait(() -> {}); 480 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 481 // Set up a SemiControlledCarTaskView 482 AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>(); 483 SemiControlledCarTaskView taskView = setUpSemiControlledTaskView(taskViewManager, 484 rootTaskListener, /* rootTaskId = */ 1, List.of(), mockCallbacks); 485 ActivityManager.RunningTaskInfo childTaskInfo = 486 createMultiWindowTask(2).getTaskInfo(); 487 488 // Act 489 rootTaskListener.get().onTaskAppeared(childTaskInfo, mLeash); 490 runOnMainAndWait(() -> {}); 491 492 // Assert 493 assertThat(taskView.getTopTaskInTheRootTask()).isEqualTo(childTaskInfo); 494 } 495 496 @Test testAddAllowListedActivities()497 public void testAddAllowListedActivities() throws Exception { 498 ComponentName componentName1 = ComponentName.unflattenFromString("com.example/.Activity1"); 499 ComponentName componentName2 = ComponentName.unflattenFromString("com.example/.Activity2"); 500 List<ComponentName> persistentActivities = 501 List.of(componentName1); 502 SemiControlledCarTaskViewCallbacks mockCallbacks = mock( 503 SemiControlledCarTaskViewCallbacks.class); 504 TaskViewManager taskViewManager = createTaskViewManager(); 505 runOnMainAndWait(() -> {}); 506 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 507 // Set up a SemiControlledCarTaskView 508 AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>(); 509 SemiControlledCarTaskView taskView = setUpSemiControlledTaskView(taskViewManager, 510 rootTaskListener, /* rootTaskId = */ 1, persistentActivities, mockCallbacks); 511 runOnMainAndWait(() -> {}); 512 513 // Action 514 List<ComponentName> activities = new ArrayList<>(persistentActivities); 515 activities.add(componentName2); 516 taskViewManager.addAllowListedActivities(taskView, activities); 517 518 // Assert 519 verify(mCarActivityManager).setPersistentActivitiesOnRootTask(eq(List.of(componentName2)), 520 any()); 521 } 522 523 @Test testRemoveAllowListedActivities()524 public void testRemoveAllowListedActivities() throws Exception { 525 List<ComponentName> activities = 526 List.of(ComponentName.unflattenFromString("com.example/.MainActivity")); 527 SemiControlledCarTaskViewCallbacks mockCallbacks = mock( 528 SemiControlledCarTaskViewCallbacks.class); 529 TaskViewManager taskViewManager = createTaskViewManager(); 530 runOnMainAndWait(() -> {}); 531 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 532 // Set up a SemiControlledCarTaskView 533 AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>(); 534 SemiControlledCarTaskView taskView = setUpSemiControlledTaskView(taskViewManager, 535 rootTaskListener, /* rootTaskId = */ 1, activities, mockCallbacks); 536 verify(mCarActivityManager).setPersistentActivitiesOnRootTask(eq(activities), any()); 537 538 // Action 539 taskViewManager.removeAllowListedActivities(taskView, activities); 540 541 // Assert 542 verify(mCarActivityManager).setPersistentActivitiesOnRootTask(eq(activities), eq(null)); 543 assertThat(taskView.getPersistentActivities().size()).isEqualTo(0); 544 } 545 546 @Test testSetAllowListedActivities()547 public void testSetAllowListedActivities() throws Exception { 548 ComponentName componentName1 = ComponentName.unflattenFromString("com.example/.Activity1"); 549 ComponentName componentName2 = ComponentName.unflattenFromString("com.example/.Activity2"); 550 ComponentName componentName3 = ComponentName.unflattenFromString("com.example/.Activity3"); 551 List<ComponentName> activities1 = 552 List.of(componentName1, componentName2); 553 List<ComponentName> activities2 = 554 List.of(componentName2, componentName3); 555 556 SemiControlledCarTaskViewCallbacks mockCallbacks = mock( 557 SemiControlledCarTaskViewCallbacks.class); 558 TaskViewManager taskViewManager = createTaskViewManager(); 559 runOnMainAndWait(() -> { 560 }); 561 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 562 // Set up a SemiControlledCarTaskView 563 AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>(); 564 SemiControlledCarTaskView taskView = setUpSemiControlledTaskView(taskViewManager, 565 rootTaskListener, /* rootTaskId = */ 1, activities1, mockCallbacks); 566 runOnMainAndWait(() -> { 567 }); 568 verify(mCarActivityManager).setPersistentActivitiesOnRootTask(eq(activities1), notNull()); 569 assertThat(taskView.getPersistentActivities()).isEqualTo(activities1); 570 571 // Action 572 taskViewManager.setAllowListedActivities(taskView, activities2); 573 runOnMainAndWait(() -> { 574 }); 575 576 // Assert 577 verify(mCarActivityManager).setPersistentActivitiesOnRootTask(eq(activities1), eq(null)); 578 verify(mCarActivityManager, atLeastOnce()).setPersistentActivitiesOnRootTask( 579 eq(activities2), notNull()); 580 assertThat(taskView.getPersistentActivities()).isEqualTo(activities2); 581 } 582 583 @Test testTaskInfoChanged_semiControlledTaskView_topTaskUpdated()584 public void testTaskInfoChanged_semiControlledTaskView_topTaskUpdated() throws Exception { 585 TaskViewManager taskViewManager = createTaskViewManager(); 586 runOnMainAndWait(() -> {}); 587 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 588 // Set up a SemiControlledCarTaskView 589 AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>(); 590 SemiControlledCarTaskView taskView = setUpSemiControlledTaskView(taskViewManager, 591 rootTaskListener, /* rootTaskId = */ 1, List.of(), mock( 592 SemiControlledCarTaskViewCallbacks.class)); 593 ActivityManager.RunningTaskInfo childTaskInfo = 594 createMultiWindowTask(1).getTaskInfo(); 595 rootTaskListener.get().onTaskAppeared(childTaskInfo, mLeash); 596 runOnMainAndWait(() -> {}); 597 ActivityManager.RunningTaskInfo childTaskInfo2 = 598 createMultiWindowTask(2).getTaskInfo(); 599 rootTaskListener.get().onTaskAppeared(childTaskInfo2, mLeash); 600 runOnMainAndWait(() -> {}); 601 602 // Act 603 rootTaskListener.get().onTaskInfoChanged(childTaskInfo); 604 runOnMainAndWait(() -> {}); 605 606 // Assert 607 assertThat(taskView.getTopTaskInTheRootTask()).isEqualTo(childTaskInfo); 608 } 609 610 @Test testTaskVanished_semiControlledTaskView_topTaskUpdated()611 public void testTaskVanished_semiControlledTaskView_topTaskUpdated() throws Exception { 612 TaskViewManager taskViewManager = createTaskViewManager(); 613 runOnMainAndWait(() -> {}); 614 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 615 // Set up a SemiControlledCarTaskView 616 AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>(); 617 SemiControlledCarTaskView taskView = setUpSemiControlledTaskView(taskViewManager, 618 rootTaskListener, /* rootTaskId = */ 1, List.of(), 619 mock(SemiControlledCarTaskViewCallbacks.class)); 620 ActivityManager.RunningTaskInfo childTaskInfo = 621 createMultiWindowTask(1).getTaskInfo(); 622 rootTaskListener.get().onTaskAppeared(childTaskInfo, mLeash); 623 runOnMainAndWait(() -> {}); 624 ActivityManager.RunningTaskInfo childTaskInfo2 = 625 createMultiWindowTask(2).getTaskInfo(); 626 rootTaskListener.get().onTaskAppeared(childTaskInfo2, mLeash); 627 runOnMainAndWait(() -> {}); 628 629 // Act 630 rootTaskListener.get().onTaskVanished(childTaskInfo2); 631 runOnMainAndWait(() -> {}); 632 633 // Assert 634 assertThat(taskView.getTopTaskInTheRootTask()).isEqualTo(childTaskInfo); 635 } 636 637 setUpLaunchRootTaskView(TaskViewManager taskViewManager, AtomicReference<ShellTaskOrganizer.TaskListener> listener, int rootTaskId)638 private ActivityManager.RunningTaskInfo setUpLaunchRootTaskView(TaskViewManager taskViewManager, 639 AtomicReference<ShellTaskOrganizer.TaskListener> listener, 640 int rootTaskId) throws Exception { 641 ActivityManager.RunningTaskInfo launchRootTaskInfo = 642 createMultiWindowTask(rootTaskId).getTaskInfo(); 643 doAnswer(invocation -> { 644 listener.set(invocation.getArgument(2)); 645 listener.get().onTaskAppeared(launchRootTaskInfo, mLeash); 646 return null; 647 }).when(mOrganizer).createRootTask(eq(DEFAULT_DISPLAY), 648 eq(WINDOWING_MODE_MULTI_WINDOW), 649 any(ShellTaskOrganizer.TaskListener.class), 650 /* removeWithTaskOrganizer= */ eq(true)); 651 taskViewManager.createLaunchRootTaskView( 652 mActivity.getMainExecutor(), 653 mock(LaunchRootCarTaskViewCallbacks.class) 654 ); 655 runOnMainAndWait(() -> {}); 656 LaunchRootCarTaskView launchRootCarTaskView = taskViewManager.getLaunchRootCarTaskView(); 657 launchRootCarTaskView.surfaceCreated(mock(SurfaceHolder.class)); 658 runOnMainAndWait(() -> {}); 659 return launchRootTaskInfo; 660 } 661 setUpSemiControlledTaskView( TaskViewManager taskViewManager, AtomicReference<ShellTaskOrganizer.TaskListener> listener, int rootTaskId, List<ComponentName> persistentActivities, SemiControlledCarTaskViewCallbacks callbacks)662 private SemiControlledCarTaskView setUpSemiControlledTaskView( 663 TaskViewManager taskViewManager, 664 AtomicReference<ShellTaskOrganizer.TaskListener> listener, int rootTaskId, 665 List<ComponentName> persistentActivities, 666 SemiControlledCarTaskViewCallbacks callbacks) 667 throws Exception { 668 IBinder taskToken = new Binder(); 669 ActivityManager.RunningTaskInfo rootTaskInfo = 670 createMultiWindowTask(rootTaskId, taskToken).getTaskInfo(); 671 doAnswer(invocation -> { 672 listener.set(invocation.getArgument(2)); 673 listener.get().onTaskAppeared(rootTaskInfo, mLeash); 674 return null; 675 }).when(mOrganizer).createRootTask(eq(DEFAULT_DISPLAY), 676 eq(WINDOWING_MODE_MULTI_WINDOW), 677 any(ShellTaskOrganizer.TaskListener.class), 678 /* removeWithTaskOrganizer= */ eq(true)); 679 taskViewManager.createSemiControlledTaskView( 680 mActivity.getMainExecutor(), 681 persistentActivities, 682 callbacks 683 ); 684 runOnMainAndWait(() -> {}); 685 SemiControlledCarTaskView semiControlledCarTaskView = 686 taskViewManager.getSemiControlledTaskViews().get(0); 687 semiControlledCarTaskView.surfaceCreated(mock(SurfaceHolder.class)); 688 runOnMainAndWait(() -> {}); 689 return semiControlledCarTaskView; 690 } 691 692 @Test testInit_registersTaskMonitor()693 public void testInit_registersTaskMonitor() throws Exception { 694 createTaskViewManager(); 695 runOnMainAndWait(() -> {}); 696 697 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 698 699 verify(mCarActivityManager).registerTaskMonitor(); 700 } 701 702 @Test testTaskAppeared_launchRootTaskView_updatesCarActivityManager()703 public void testTaskAppeared_launchRootTaskView_updatesCarActivityManager() throws Exception { 704 TaskViewManager taskViewManager = createTaskViewManager(); 705 runOnMainAndWait(() -> {}); 706 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 707 // Set up a LaunchRootTaskView 708 AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>(); 709 setUpLaunchRootTaskView(taskViewManager, rootTaskListener, /* rootTaskId = */ 1); 710 ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo(); 711 712 // Act 713 rootTaskListener.get().onTaskAppeared(taskInfo, mLeash); 714 runOnMainAndWait(() -> {}); 715 716 // Assert 717 verify(mCarActivityManager).onTaskAppeared(taskInfo); 718 } 719 720 @Test testTaskInfoChanged_launchRootTaskView_updatesCarActivityManager()721 public void testTaskInfoChanged_launchRootTaskView_updatesCarActivityManager() 722 throws Exception { 723 TaskViewManager taskViewManager = createTaskViewManager(); 724 runOnMainAndWait(() -> {}); 725 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 726 // Set up a LaunchRootTaskView 727 AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>(); 728 setUpLaunchRootTaskView(taskViewManager, rootTaskListener, /* rootTaskId = */ 1); 729 ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo(); 730 731 // Act 732 rootTaskListener.get().onTaskInfoChanged(taskInfo); 733 runOnMainAndWait(() -> {}); 734 735 // Assert 736 verify(mCarActivityManager).onTaskInfoChanged(taskInfo); 737 } 738 739 @Test testTaskVanished_launchRootTaskView_updatesCarActivityManager()740 public void testTaskVanished_launchRootTaskView_updatesCarActivityManager() throws Exception { 741 TaskViewManager taskViewManager = createTaskViewManager(); 742 runOnMainAndWait(() -> {}); 743 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 744 // Set up a LaunchRootTaskView 745 AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>(); 746 setUpLaunchRootTaskView(taskViewManager, rootTaskListener, /* rootTaskId = */ 1); 747 ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo(); 748 749 // Act 750 rootTaskListener.get().onTaskVanished(taskInfo); 751 runOnMainAndWait(() -> {}); 752 753 // Assert 754 verify(mCarActivityManager).onTaskVanished(taskInfo); 755 } 756 757 @Test testHostActivityDestroyed_releasesAllTaskViews()758 public void testHostActivityDestroyed_releasesAllTaskViews() throws Exception { 759 testReleaseAllTaskViews(() -> { 760 ActivityScenario<TestActivity> scenario = mActivityRule.getScenario(); 761 scenario.moveToState(Lifecycle.State.DESTROYED); 762 }); 763 } 764 setUpControlledTaskView(TaskViewManager taskViewManager, Intent activityIntent, Set<String> packagesThatCanRestart)765 private void setUpControlledTaskView(TaskViewManager taskViewManager, Intent activityIntent, 766 Set<String> packagesThatCanRestart) throws Exception { 767 ControlledCarTaskViewCallbacks controlledCarTaskViewCallbacks = mock( 768 ControlledCarTaskViewCallbacks.class); 769 when(controlledCarTaskViewCallbacks.getDependingPackageNames()) 770 .thenReturn(packagesThatCanRestart); 771 taskViewManager.createControlledCarTaskView( 772 mActivity.getMainExecutor(), 773 ControlledCarTaskViewConfig.builder() 774 .setActivityIntent(activityIntent) 775 .setAutoRestartOnCrash(false) 776 .build(), 777 controlledCarTaskViewCallbacks 778 ); 779 780 int lastIndex = Math.min(0, taskViewManager.getControlledTaskViews().size() - 1); 781 ControlledCarTaskView taskView = spy(taskViewManager.getControlledTaskViews() 782 .get(lastIndex)); 783 doNothing().when(taskView).startActivity(); 784 785 taskView.surfaceCreated(mock(SurfaceHolder.class)); 786 runOnMainAndWait(() -> {}); 787 } 788 789 @Test testRestartControlledTask_whenHostActivityFocussed()790 public void testRestartControlledTask_whenHostActivityFocussed() throws Exception { 791 TaskViewManager taskViewManager = createTaskViewManager(); 792 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 793 794 // Send onTaskVanished to mimic the task removal behavior. 795 setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"), 796 ImmutableSet.of("com.random.package2")); 797 ControlledCarTaskView controlledCarTaskView = 798 taskViewManager.getControlledTaskViews().get(0); 799 ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo(); 800 controlledCarTaskView.dispatchTaskAppeared(taskInfo, mLeash); 801 controlledCarTaskView.dispatchTaskVanished(taskInfo); 802 assertThat(controlledCarTaskView.getTaskId()).isEqualTo(INVALID_TASK_ID); 803 804 // Stub the taskview with a spy to assert on startActivity. 805 ControlledCarTaskView spiedTaskView = spy(controlledCarTaskView); 806 doNothing().when(spiedTaskView).startActivity(); 807 taskViewManager.getControlledTaskViews().set(0, spiedTaskView); 808 809 // Act 810 mTaskStackListenerArgumentCaptor.getValue().onTaskFocusChanged(mActivity.getTaskId(), 811 /* focused = */ true); 812 813 // Assert 814 verify(spiedTaskView).startActivity(); 815 } 816 817 @Test testControlledTaskNotRestarted_ifAlreadyRunning_whenHostActivityFocussed()818 public void testControlledTaskNotRestarted_ifAlreadyRunning_whenHostActivityFocussed() 819 throws Exception { 820 TaskViewManager taskViewManager = createTaskViewManager(); 821 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 822 823 setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"), 824 ImmutableSet.of("com.random.package2")); 825 ControlledCarTaskView controlledCarTaskView = 826 taskViewManager.getControlledTaskViews().get(0); 827 ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo(); 828 controlledCarTaskView.dispatchTaskAppeared(taskInfo, mLeash); 829 830 // Stub the taskview with a spy to assert on startActivity. 831 ControlledCarTaskView spiedTaskView = spy(controlledCarTaskView); 832 doNothing().when(spiedTaskView).startActivity(); 833 taskViewManager.getControlledTaskViews().set(0, spiedTaskView); 834 835 // Act 836 mTaskStackListenerArgumentCaptor.getValue().onTaskFocusChanged(mActivity.getTaskId(), 837 /* focused = */ true); 838 839 // Assert 840 verify(spiedTaskView, times(0)).startActivity(); 841 } 842 843 @Test testRestartControlledTask_whenHostActivityRestarted()844 public void testRestartControlledTask_whenHostActivityRestarted() throws Exception { 845 TaskViewManager taskViewManager = createTaskViewManager(); 846 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 847 848 // Send onTaskVanished to mimic the task removal behavior. 849 setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"), 850 ImmutableSet.of("com.random.package2")); 851 ControlledCarTaskView controlledCarTaskView = 852 taskViewManager.getControlledTaskViews().get(0); 853 ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo(); 854 controlledCarTaskView.dispatchTaskAppeared(taskInfo, mLeash); 855 controlledCarTaskView.dispatchTaskVanished(taskInfo); 856 assertThat(controlledCarTaskView.getTaskId()).isEqualTo(INVALID_TASK_ID); 857 858 // Stub the taskview with a spy to assert on startActivity. 859 ControlledCarTaskView spiedTaskView = spy(controlledCarTaskView); 860 doNothing().when(spiedTaskView).startActivity(); 861 taskViewManager.getControlledTaskViews().set(0, spiedTaskView); 862 863 // Act 864 mTaskStackListenerArgumentCaptor.getValue().onActivityRestartAttempt( 865 createMultiWindowTask(mActivity.getTaskId()).getTaskInfo(), 866 /* homeTaskVisible = */ true, false, 867 /* focused = */ true); 868 869 // Assert 870 verify(spiedTaskView).startActivity(); 871 } 872 873 @Test testRestartControlledTask_whenPackageThatCanRestartChanged()874 public void testRestartControlledTask_whenPackageThatCanRestartChanged() throws Exception { 875 TaskViewManager taskViewManager = createTaskViewManager(); 876 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 877 878 // Send onTaskVanished to mimic the task removal behavior. 879 setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"), 880 ImmutableSet.of("com.relevant.package")); 881 ControlledCarTaskView controlledCarTaskView = 882 taskViewManager.getControlledTaskViews().get(0); 883 ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo(); 884 controlledCarTaskView.dispatchTaskAppeared(taskInfo, mLeash); 885 controlledCarTaskView.dispatchTaskVanished(taskInfo); 886 assertThat(controlledCarTaskView.getTaskId()).isEqualTo(INVALID_TASK_ID); 887 888 // Stub the taskview with a spy to assert on startActivity. 889 ControlledCarTaskView spiedTaskView = spy(controlledCarTaskView); 890 doNothing().when(spiedTaskView).startActivity(); 891 taskViewManager.getControlledTaskViews().set(0, spiedTaskView); 892 893 // Act 894 taskViewManager.getPackageBroadcastReceiver().onReceive(mActivity, 895 new Intent().setData(Uri.parse("package:com.relevant.package"))); 896 897 // Assert 898 verify(spiedTaskView).startActivity(); 899 } 900 901 @Test testControlledTaskNotRestarted_whenARandomPackageChanged()902 public void testControlledTaskNotRestarted_whenARandomPackageChanged() throws Exception { 903 TaskViewManager taskViewManager = createTaskViewManager(); 904 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 905 906 // Send onTaskVanished to mimic the task removal behavior. 907 setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"), 908 ImmutableSet.of("com.relevant.package")); 909 ControlledCarTaskView controlledCarTaskView = 910 taskViewManager.getControlledTaskViews().get(0); 911 ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo(); 912 controlledCarTaskView.dispatchTaskAppeared(taskInfo, mLeash); 913 controlledCarTaskView.dispatchTaskVanished(taskInfo); 914 assertThat(controlledCarTaskView.getTaskId()).isEqualTo(INVALID_TASK_ID); 915 916 // Stub the taskview with a spy to assert on startActivity. 917 ControlledCarTaskView spiedTaskView = spy(controlledCarTaskView); 918 doNothing().when(spiedTaskView).startActivity(); 919 taskViewManager.getControlledTaskViews().set(0, spiedTaskView); 920 921 // Act 922 taskViewManager.getPackageBroadcastReceiver().onReceive(mActivity, 923 new Intent().setData(Uri.parse("package:com.random.package"))); 924 925 // Assert 926 verify(spiedTaskView, times(0)).startActivity(); 927 } 928 929 // User switch related tests. 930 931 @Test testRestartControlledTask_onUserUnlocked()932 public void testRestartControlledTask_onUserUnlocked() throws Exception { 933 TaskViewManager taskViewManager = createTaskViewManager(); 934 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 935 936 // Send onTaskVanished to mimic the task removal behavior. 937 setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"), 938 ImmutableSet.of("com.random.package2")); 939 ControlledCarTaskView controlledCarTaskView = 940 taskViewManager.getControlledTaskViews().get(0); 941 ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo(); 942 controlledCarTaskView.dispatchTaskAppeared(taskInfo, mLeash); 943 controlledCarTaskView.dispatchTaskVanished(taskInfo); 944 assertThat(controlledCarTaskView.getTaskId()).isEqualTo(INVALID_TASK_ID); 945 946 // Stub the taskview with a spy to assert on startActivity. 947 ControlledCarTaskView spiedTaskView = spy(controlledCarTaskView); 948 doNothing().when(spiedTaskView).startActivity(); 949 taskViewManager.getControlledTaskViews().set(0, spiedTaskView); 950 951 // Act 952 mUserLifecycleListenerArgumentCaptor.getValue().onEvent( 953 new CarUserManager.UserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED, 954 mActivity.getUserId())); 955 956 // Assert 957 verify(spiedTaskView).startActivity(); 958 } 959 960 @Test testUserSwitch_releasesAllTaskViews()961 public void testUserSwitch_releasesAllTaskViews() throws Exception { 962 testReleaseAllTaskViews(/* actionBlock= */ () -> 963 mUserLifecycleListenerArgumentCaptor.getValue().onEvent( 964 new CarUserManager.UserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_SWITCHING, 965 /* from= */ mActivity.getUserId(), /* to= */ 20)) 966 ); 967 } 968 testReleaseAllTaskViews(Runnable actionBlock)969 private void testReleaseAllTaskViews(Runnable actionBlock) throws Exception { 970 TaskViewManager taskViewManager = createTaskViewManager(); 971 runOnMainAndWait(() -> {}); 972 mCarServiceLifecycleListener.onLifecycleChanged(mCar, true); 973 // Create a few TaskViews 974 AtomicReference<ShellTaskOrganizer.TaskListener> listener = new AtomicReference<>(); 975 setUpLaunchRootTaskView(taskViewManager, listener, /* rootTaskId = */ 1); 976 LaunchRootCarTaskView launchRootCarTaskView = taskViewManager.getLaunchRootCarTaskView(); 977 978 setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"), 979 ImmutableSet.of("com.random.package")); 980 ControlledCarTaskView controlledCarTaskView = taskViewManager.getControlledTaskViews() 981 .get(0); 982 setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"), 983 ImmutableSet.of("com.random.package2")); 984 ControlledCarTaskView controlledCarTaskView2 = taskViewManager.getControlledTaskViews() 985 .get(1); 986 987 SemiControlledCarTaskViewCallbacks taskViewCallbacks = 988 mock(SemiControlledCarTaskViewCallbacks.class); 989 taskViewManager.createSemiControlledTaskView( 990 mActivity.getMainExecutor(), 991 List.of(), 992 taskViewCallbacks 993 ); 994 runOnMainAndWait(() -> {}); 995 taskViewManager.createSemiControlledTaskView( 996 mActivity.getMainExecutor(), 997 List.of(), 998 taskViewCallbacks 999 ); 1000 runOnMainAndWait(() -> {}); 1001 // Trigger surfaceCreated on SemiControlledTaskView. 1002 SemiControlledCarTaskView semiControlledCarTaskView = 1003 taskViewManager.getSemiControlledTaskViews().get(0); 1004 semiControlledCarTaskView.surfaceCreated(mock(SurfaceHolder.class)); 1005 runOnMainAndWait(() -> {}); 1006 SemiControlledCarTaskView semiControlledCarTaskView2 = 1007 taskViewManager.getSemiControlledTaskViews().get(1); 1008 semiControlledCarTaskView2.surfaceCreated(mock(SurfaceHolder.class)); 1009 runOnMainAndWait(() -> {}); 1010 1011 // Act 1012 actionBlock.run(); 1013 1014 // Assert 1015 assertThat(launchRootCarTaskView.isInitialized()).isFalse(); 1016 assertThat(controlledCarTaskView.isInitialized()).isFalse(); 1017 assertThat(controlledCarTaskView2.isInitialized()).isFalse(); 1018 assertThat(semiControlledCarTaskView.isInitialized()).isFalse(); 1019 assertThat(semiControlledCarTaskView2.isInitialized()).isFalse(); 1020 1021 assertThat(taskViewManager.getSemiControlledTaskViews()).isEmpty(); 1022 assertThat(taskViewManager.getLaunchRootCarTaskView()).isNull(); 1023 assertThat(taskViewManager.getControlledTaskViews()).isEmpty(); 1024 1025 verify(mOrganizer).unregisterOrganizer(); 1026 verify(mTaskViewInputInterceptor).release(); 1027 } 1028 createTaskViewManager()1029 private TaskViewManager createTaskViewManager() { 1030 // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not. 1031 // http://b/25897652. 1032 Looper looper = Looper.myLooper(); 1033 if (looper == null) { 1034 Looper.prepare(); 1035 } 1036 1037 TaskViewManager taskViewManager = new TaskViewManager(mActivity, mShellExecutor, 1038 mOrganizer, mSyncQueue, mTransitions, new ShellInit(mShellExecutor), 1039 mShellController, mStartingWindowController); 1040 taskViewManager.setTaskViewInputInterceptor(mTaskViewInputInterceptor); 1041 return taskViewManager; 1042 } 1043 runOnMainAndWait(Runnable r)1044 private void runOnMainAndWait(Runnable r) throws Exception { 1045 mActivity.getMainExecutor().execute(() -> { 1046 r.run(); 1047 mIdleHandlerLatch.countDown(); 1048 mIdleHandlerLatch = new CountDownLatch(1); 1049 }); 1050 mIdleHandlerLatch.await(5, TimeUnit.SECONDS); 1051 } 1052 1053 public static class TestActivity extends Activity { 1054 private static final int FINISH_TIMEOUT_MS = 1000; 1055 private final CountDownLatch mDestroyed = new CountDownLatch(1); 1056 1057 @Override onDestroy()1058 protected void onDestroy() { 1059 super.onDestroy(); 1060 mDestroyed.countDown(); 1061 } 1062 finishCompletely()1063 void finishCompletely() throws InterruptedException { 1064 finish(); 1065 mDestroyed.await(FINISH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1066 } 1067 } 1068 } 1069