• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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