• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.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.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING;
22 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED;
23 
24 import static com.android.car.carlauncher.CarLauncher.TAG;
25 import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
26 
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.app.Activity;
30 import android.app.ActivityManager;
31 import android.app.ActivityTaskManager;
32 import android.app.Application.ActivityLifecycleCallbacks;
33 import android.app.TaskInfo;
34 import android.app.TaskStackListener;
35 import android.car.Car;
36 import android.car.app.CarActivityManager;
37 import android.car.user.CarUserManager;
38 import android.car.user.UserLifecycleEventFilter;
39 import android.content.BroadcastReceiver;
40 import android.content.ComponentName;
41 import android.content.Context;
42 import android.content.Intent;
43 import android.content.IntentFilter;
44 import android.os.Bundle;
45 import android.os.Handler;
46 import android.os.UserManager;
47 import android.util.Log;
48 import android.util.Slog;
49 import android.view.WindowManagerGlobal;
50 import android.window.TaskAppearedInfo;
51 import android.window.WindowContainerToken;
52 import android.window.WindowContainerTransaction;
53 
54 import com.android.car.carlauncher.taskstack.TaskStackChangeListeners;
55 import com.android.internal.annotations.VisibleForTesting;
56 import com.android.launcher3.icons.IconProvider;
57 import com.android.wm.shell.ShellTaskOrganizer;
58 import com.android.wm.shell.common.DisplayController;
59 import com.android.wm.shell.common.HandlerExecutor;
60 import com.android.wm.shell.common.SyncTransactionQueue;
61 import com.android.wm.shell.common.TransactionPool;
62 import com.android.wm.shell.common.annotations.ShellMainThread;
63 import com.android.wm.shell.fullscreen.FullscreenTaskListener;
64 import com.android.wm.shell.startingsurface.StartingWindowController;
65 import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
66 import com.android.wm.shell.sysui.ShellCommandHandler;
67 import com.android.wm.shell.sysui.ShellController;
68 import com.android.wm.shell.sysui.ShellInit;
69 import com.android.wm.shell.taskview.TaskViewTransitions;
70 import com.android.wm.shell.transition.Transitions;
71 
72 import java.util.ArrayList;
73 import java.util.List;
74 import java.util.concurrent.Executor;
75 import java.util.concurrent.atomic.AtomicReference;
76 
77 
78 /**
79  * A manager for creating {@link ControlledCarTaskView}, {@link LaunchRootCarTaskView} &
80  * {@link SemiControlledCarTaskView}.
81  */
82 public final class TaskViewManager {
83     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
84     private static final String SCHEME_PACKAGE = "package";
85 
86     private final AtomicReference<CarActivityManager> mCarActivityManagerRef =
87             new AtomicReference<>();
88     @ShellMainThread
89     private final HandlerExecutor mShellExecutor;
90     private final SyncTransactionQueue mSyncQueue;
91     private final Transitions mTransitions;
92     private final TaskViewTransitions mTaskViewTransitions;
93     private final ShellTaskOrganizer mTaskOrganizer;
94     private final int mHostTaskId;
95 
96     // All TaskView are bound to the Host Activity if it exists.
97     @ShellMainThread
98     private final List<ControlledCarTaskView> mControlledTaskViews = new ArrayList<>();
99     @ShellMainThread
100     private final List<SemiControlledCarTaskView> mSemiControlledTaskViews = new ArrayList<>();
101     @ShellMainThread
102     private LaunchRootCarTaskView mLaunchRootCarTaskView = null;
103 
104     private TaskViewInputInterceptor mTaskViewInputInterceptor;
105     private CarUserManager mCarUserManager;
106     private Activity mContext;
107     private Car mCar;
108     private boolean mReleased = false;
109 
110     private final TaskStackListener mTaskStackListener = new TaskStackListener() {
111         @Override
112         public void onTaskFocusChanged(int taskId, boolean focused) {
113             boolean hostFocused = taskId == mHostTaskId && focused;
114             if (DBG) {
115                 Log.d(TAG, "onTaskFocusChanged: taskId=" + taskId
116                         + ", hostFocused=" + hostFocused);
117             }
118             if (!hostFocused) {
119                 return;
120             }
121 
122             for (int i = mControlledTaskViews.size() - 1; i >= 0; --i) {
123                 ControlledCarTaskView taskView = mControlledTaskViews.get(i);
124                 if (taskView.getTaskId() == INVALID_TASK_ID) {
125                     // If the task in TaskView is crashed when host is in background,
126                     // We'd like to restart it when host becomes foreground and focused.
127                     taskView.startActivity();
128                 }
129             }
130         }
131 
132         @Override
133         public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
134                 boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
135             if (DBG) {
136                 Log.d(TAG, "onActivityRestartAttempt: taskId=" + task.taskId
137                         + ", homeTaskVisible=" + homeTaskVisible + ", wasVisible=" + wasVisible);
138             }
139             if (mHostTaskId != task.taskId) {
140                 return;
141             }
142             showEmbeddedTasks();
143         }
144     };
145 
146     private final CarUserManager.UserLifecycleListener mUserLifecycleListener = event -> {
147         if (DBG) {
148             Log.d(TAG, "UserLifecycleListener.onEvent: For User "
149                     + mContext.getUserId()
150                     + ", received an event " + event);
151         }
152 
153         // When user-unlocked, if task isn't launched yet, then try to start it.
154         if (event.getEventType() == USER_LIFECYCLE_EVENT_TYPE_UNLOCKED
155                 && mContext.getUserId() == event.getUserId()) {
156             for (int i = mControlledTaskViews.size() - 1; i >= 0; --i) {
157                 ControlledCarTaskView taskView = mControlledTaskViews.get(i);
158                 if (taskView.getTaskId() == INVALID_TASK_ID) {
159                     taskView.startActivity();
160                 }
161             }
162         }
163 
164         // When user-switching, onDestroy in the previous user's Host app isn't called.
165         // So try to release the resource explicitly.
166         if (event.getEventType() == USER_LIFECYCLE_EVENT_TYPE_SWITCHING
167                 && mContext.getUserId() == event.getPreviousUserId()) {
168             release();
169         }
170     };
171 
172     private final BroadcastReceiver mPackageBroadcastReceiver = new BroadcastReceiver() {
173         @Override
174         public void onReceive(Context context, Intent intent) {
175             if (DBG) Log.d(TAG, "onReceive: intent=" + intent);
176 
177             if (!isHostVisible()) {
178                 return;
179             }
180 
181             String packageName = intent.getData().getSchemeSpecificPart();
182             for (int i = mControlledTaskViews.size() - 1; i >= 0; --i) {
183                 ControlledCarTaskView taskView = mControlledTaskViews.get(i);
184                 if (taskView.getTaskId() == INVALID_TASK_ID
185                         && taskView.getDependingPackageNames().contains(packageName)) {
186                     taskView.startActivity();
187                 }
188             }
189         }
190     };
191 
TaskViewManager(Activity context, Handler mainHandler)192     public TaskViewManager(Activity context, Handler mainHandler) {
193         this(context, mainHandler, new HandlerExecutor(mainHandler));
194     }
195 
TaskViewManager(Activity context, Handler mainHandler, HandlerExecutor handlerExecutor)196     private TaskViewManager(Activity context, Handler mainHandler,
197             HandlerExecutor handlerExecutor) {
198         this(context, mainHandler, handlerExecutor, new ShellTaskOrganizer(handlerExecutor),
199                 new TransactionPool(), new ShellCommandHandler(), new ShellInit(handlerExecutor));
200     }
201 
TaskViewManager(Activity context, Handler mainHandler, HandlerExecutor handlerExecutor, ShellTaskOrganizer taskOrganizer, TransactionPool transactionPool, ShellCommandHandler shellCommandHandler, ShellInit shellinit)202     private TaskViewManager(Activity context, Handler mainHandler, HandlerExecutor handlerExecutor,
203             ShellTaskOrganizer taskOrganizer, TransactionPool transactionPool,
204             ShellCommandHandler shellCommandHandler, ShellInit shellinit) {
205         this(context, mainHandler, handlerExecutor, taskOrganizer,
206                 transactionPool,
207                 shellinit,
208                 new ShellController(context, shellinit, shellCommandHandler, handlerExecutor),
209                 new DisplayController(context,
210                         WindowManagerGlobal.getWindowManagerService(), shellinit, handlerExecutor)
211         );
212     }
213 
TaskViewManager(Activity context, Handler mainHandler, HandlerExecutor handlerExecutor, ShellTaskOrganizer taskOrganizer, TransactionPool transactionPool, ShellInit shellinit, ShellController shellController, DisplayController dc)214     private TaskViewManager(Activity context, Handler mainHandler, HandlerExecutor handlerExecutor,
215             ShellTaskOrganizer taskOrganizer, TransactionPool transactionPool, ShellInit shellinit,
216             ShellController shellController, DisplayController dc) {
217         this(context, handlerExecutor, taskOrganizer,
218                 new SyncTransactionQueue(transactionPool, handlerExecutor),
219                 new Transitions(context, shellinit, shellController, taskOrganizer,
220                         transactionPool, dc, handlerExecutor, mainHandler, handlerExecutor),
221                 shellinit,
222                 shellController,
223                 new StartingWindowController(context, shellinit,
224                         shellController,
225                         taskOrganizer,
226                         handlerExecutor,
227                         new PhoneStartingWindowTypeAlgorithm(),
228                         new IconProvider(context),
229                         transactionPool));
230     }
231 
232     @VisibleForTesting
TaskViewManager(Activity context, HandlerExecutor handlerExecutor, ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Transitions transitions, ShellInit shellInit, ShellController shellController, StartingWindowController startingWindowController)233     TaskViewManager(Activity context, HandlerExecutor handlerExecutor,
234             ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
235             Transitions transitions, ShellInit shellInit, ShellController shellController,
236             StartingWindowController startingWindowController) {
237         if (DBG) Slog.d(TAG, "TaskViewManager(), u=" + context.getUserId());
238         mContext = context;
239         mShellExecutor = handlerExecutor;
240         mTaskOrganizer = shellTaskOrganizer;
241         mHostTaskId = mContext.getTaskId();
242         mSyncQueue = syncQueue;
243         mTransitions = transitions;
244         mTaskViewTransitions = new TaskViewTransitions(mTransitions);
245         mTaskViewInputInterceptor = new TaskViewInputInterceptor(context, this);
246 
247         initCar();
248         shellInit.init();
249         initTaskOrganizer(mCarActivityManagerRef);
250         mContext.registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
251     }
252 
initCar()253     private void initCar() {
254         mCar = Car.createCar(/* context= */ mContext, /* handler= */ null,
255                 Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
256                 (car, ready) -> {
257                     if (!ready) {
258                         Log.w(TAG, "CarService looks crashed");
259                         mCarActivityManagerRef.set(null);
260                         return;
261                     }
262                     setCarUserManager((CarUserManager) car.getCarManager(Car.CAR_USER_SERVICE));
263                     UserLifecycleEventFilter filter = new UserLifecycleEventFilter.Builder()
264                             .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED)
265                             .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING).build();
266                     mCarUserManager.addListener(mContext.getMainExecutor(), filter,
267                             mUserLifecycleListener);
268                     CarActivityManager carAM = (CarActivityManager) car.getCarManager(
269                             Car.CAR_ACTIVITY_SERVICE);
270                     mCarActivityManagerRef.set(carAM);
271 
272                     carAM.registerTaskMonitor();
273                 });
274 
275         TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
276 
277         IntentFilter packageIntentFilter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
278         packageIntentFilter.addDataScheme(SCHEME_PACKAGE);
279         mContext.registerReceiver(mPackageBroadcastReceiver, packageIntentFilter);
280     }
281 
282     // TODO(b/239958124A): Remove this method when unit tests for TaskViewManager have been added.
283     /**
284      * This method only exists for the container activity to set mock car user manager in tests.
285      */
setCarUserManager(CarUserManager carUserManager)286     void setCarUserManager(CarUserManager carUserManager) {
287         mCarUserManager = carUserManager;
288     }
289 
initTransitions(ShellInit shellInit, TransactionPool txPool, ShellController shellController, Handler mainHandler)290     private Transitions initTransitions(ShellInit shellInit, TransactionPool txPool,
291             ShellController shellController, Handler mainHandler) {
292         DisplayController dc = new DisplayController(mContext,
293                 WindowManagerGlobal.getWindowManagerService(), shellInit, mShellExecutor);
294         return new Transitions(mContext, shellInit, shellController, mTaskOrganizer,
295                 txPool, dc, mShellExecutor, mainHandler, mShellExecutor);
296     }
297 
initTaskOrganizer(AtomicReference<CarActivityManager> carActivityManagerRef)298     private void initTaskOrganizer(AtomicReference<CarActivityManager> carActivityManagerRef) {
299         FullscreenTaskListener fullscreenTaskListener = new CarFullscreenTaskMonitorListener(
300                 carActivityManagerRef, mSyncQueue);
301         mTaskOrganizer.addListenerForType(fullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN);
302         List<TaskAppearedInfo> taskAppearedInfos = mTaskOrganizer.registerOrganizer();
303         cleanUpExistingTaskViewTasks(taskAppearedInfos);
304     }
305 
306     /**
307      * Creates a {@link ControlledCarTaskView}.
308      *
309      * @param callbackExecutor the executor which the {@link ControlledCarTaskViewCallbacks} will
310      *                         be executed on.
311      * @param controlledCarTaskViewConfig the configuration for the underlying
312      * {@link ControlledCarTaskView}.
313      * @param taskViewCallbacks the callbacks for the underlying TaskView.
314      */
createControlledCarTaskView( Executor callbackExecutor, ControlledCarTaskViewConfig controlledCarTaskViewConfig, ControlledCarTaskViewCallbacks taskViewCallbacks)315     public void createControlledCarTaskView(
316             Executor callbackExecutor,
317             ControlledCarTaskViewConfig controlledCarTaskViewConfig,
318             ControlledCarTaskViewCallbacks taskViewCallbacks) {
319         mShellExecutor.execute(() -> {
320             ControlledCarTaskView taskView = new ControlledCarTaskView(mContext, mTaskOrganizer,
321                     mTaskViewTransitions, mSyncQueue, callbackExecutor, controlledCarTaskViewConfig,
322                     taskViewCallbacks, mContext.getSystemService(UserManager.class), this);
323             mControlledTaskViews.add(taskView);
324 
325             if (controlledCarTaskViewConfig.mCaptureGestures
326                     || controlledCarTaskViewConfig.mCaptureLongPress) {
327                 mTaskViewInputInterceptor.init();
328             }
329         });
330 
331     }
332 
333     /**
334      * Creates a {@link LaunchRootCarTaskView}.
335      *
336      * @param callbackExecutor the executor which the {@link LaunchRootCarTaskViewCallbacks} will be
337      *                         executed on.
338      * @param taskViewCallbacks the callbacks for the underlying TaskView.
339      */
createLaunchRootTaskView(Executor callbackExecutor, LaunchRootCarTaskViewCallbacks taskViewCallbacks)340     public void createLaunchRootTaskView(Executor callbackExecutor,
341             LaunchRootCarTaskViewCallbacks taskViewCallbacks) {
342         mShellExecutor.execute(() -> {
343             if (mLaunchRootCarTaskView != null) {
344                 throw new IllegalStateException("Cannot create more than one launch root task");
345             }
346             mLaunchRootCarTaskView = new LaunchRootCarTaskView(mContext, mTaskOrganizer,
347                     mTaskViewTransitions, mSyncQueue,
348                     callbackExecutor, taskViewCallbacks, mCarActivityManagerRef);
349         });
350     }
351 
352     /**
353      * updates the window visibility associated with {@link WindowContainerToken}
354      *
355      * @param token {@link WindowContainerToken} of the window that needs to be hidden
356      * @param visibility {true} if window needs to be displayed {false} otherwise
357      */
updateTaskVisibility(WindowContainerToken token, boolean visibility)358     public void updateTaskVisibility(WindowContainerToken token, boolean visibility) {
359         WindowContainerTransaction wct = new WindowContainerTransaction();
360         wct.setHidden(token, !visibility);
361         mSyncQueue.queue(wct);
362     }
363 
364     /**
365      * Creates a {@link SemiControlledCarTaskView}.
366      *
367      * @param callbackExecutor the executor which the {@link SemiControlledCarTaskViewCallbacks}
368      *                         will be executed on.
369      * @param allowListedActivities the list of activities that will always be started in this
370      *                              taskview.
371      * @param taskViewCallbacks the callbacks for the underlying TaskView.
372      */
createSemiControlledTaskView(Executor callbackExecutor, List<ComponentName> allowListedActivities, SemiControlledCarTaskViewCallbacks taskViewCallbacks)373     public void createSemiControlledTaskView(Executor callbackExecutor,
374             List<ComponentName> allowListedActivities,
375             SemiControlledCarTaskViewCallbacks taskViewCallbacks) {
376         mShellExecutor.execute(() -> {
377             SemiControlledCarTaskView taskView = new SemiControlledCarTaskView(mContext,
378                     mTaskOrganizer, mTaskViewTransitions, mSyncQueue,
379                     callbackExecutor, allowListedActivities, taskViewCallbacks,
380                     mCarActivityManagerRef);
381             mSemiControlledTaskViews.add(taskView);
382         });
383     }
384 
385     /**
386      * Releases {@link TaskViewManager} and unregisters the underlying {@link ShellTaskOrganizer}.
387      * It also removes all TaskViews which are created by this {@link TaskViewManager}.
388      */
release()389     void release() {
390         mShellExecutor.execute(() -> {
391             if (DBG) Slog.d(TAG, "TaskViewManager.release, u=" + mContext.getUser());
392 
393             if (mCarUserManager != null) {
394                 mCarUserManager.removeListener(mUserLifecycleListener);
395             }
396             TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
397             mContext.unregisterReceiver(mPackageBroadcastReceiver);
398 
399             CarActivityManager carAM = mCarActivityManagerRef.get();
400             if (carAM != null) {
401                 carAM.unregisterTaskMonitor();
402                 mCarActivityManagerRef.set(null);
403             }
404 
405             for (int i = mControlledTaskViews.size() - 1; i >= 0; --i) {
406                 mControlledTaskViews.get(i).release();
407             }
408             mControlledTaskViews.clear();
409 
410             for (int i = mSemiControlledTaskViews.size() - 1; i >= 0; --i) {
411                 mSemiControlledTaskViews.get(i).release();
412             }
413             mSemiControlledTaskViews.clear();
414 
415             if (mLaunchRootCarTaskView != null) {
416                 mLaunchRootCarTaskView.release();
417                 mLaunchRootCarTaskView = null;
418             }
419 
420             mContext.unregisterActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
421             mTaskOrganizer.unregisterOrganizer();
422             mTaskViewInputInterceptor.release();
423 
424             if (mCar != null) {
425                 mCar.disconnect();
426             }
427             mReleased = true;
428         });
429     }
430 
431     /**
432      * Shows all the embedded tasks. If the tasks are
433      */
showEmbeddedTasks()434     public void showEmbeddedTasks() {
435         WindowContainerTransaction wct = new WindowContainerTransaction();
436         for (int i = mControlledTaskViews.size() - 1; i >= 0; --i) {
437             // showEmbeddedTasks() will restart the crashed tasks too.
438             mControlledTaskViews.get(i).showEmbeddedTask(wct);
439         }
440         if (mLaunchRootCarTaskView != null) {
441             mLaunchRootCarTaskView.showEmbeddedTask(wct);
442         }
443         for (int i = mSemiControlledTaskViews.size() - 1; i >= 0; --i) {
444             mSemiControlledTaskViews.get(i).showEmbeddedTask(wct);
445         }
446         mSyncQueue.queue(wct);
447     }
448 
449     /**
450      * @return {@code true} if the host activity is in resumed or started state, {@code false}
451      * otherwise.
452      */
isHostVisible()453     boolean isHostVisible() {
454         // This code relies on Activity#isVisibleForAutofill() instead of maintaining a custom
455         // activity state.
456         return mContext.isVisibleForAutofill();
457     }
458 
459     private final ActivityLifecycleCallbacks mActivityLifecycleCallbacks =
460             new ActivityLifecycleCallbacks() {
461                 @Override
462                 public void onActivityCreated(@NonNull Activity activity,
463                         @Nullable Bundle savedInstanceState) {}
464 
465                 @Override
466                 public void onActivityStarted(@NonNull Activity activity) {}
467 
468                 @Override
469                 public void onActivityResumed(@NonNull Activity activity) {}
470 
471                 @Override
472                 public void onActivityPaused(@NonNull Activity activity) {}
473 
474                 @Override
475                 public void onActivityStopped(@NonNull Activity activity) {}
476 
477                 @Override
478                 public void onActivitySaveInstanceState(@NonNull Activity activity,
479                         @NonNull Bundle outState) {}
480 
481                 @Override
482                 public void onActivityDestroyed(@NonNull Activity activity) {
483                     release();
484                 }
485             };
486 
cleanUpExistingTaskViewTasks(List<TaskAppearedInfo> taskAppearedInfos)487     private static void cleanUpExistingTaskViewTasks(List<TaskAppearedInfo> taskAppearedInfos) {
488         ActivityTaskManager atm = ActivityTaskManager.getInstance();
489         for (TaskAppearedInfo taskAppearedInfo : taskAppearedInfos) {
490             TaskInfo taskInfo = taskAppearedInfo.getTaskInfo();
491             // Only TaskView tasks have WINDOWING_MODE_MULTI_WINDOW.
492             if (taskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
493                 if (DBG) Slog.d(TAG, "Found the dangling task, removing: " + taskInfo.taskId);
494                 atm.removeTask(taskInfo.taskId);
495             }
496         }
497     }
498 
499     @VisibleForTesting
getControlledTaskViews()500     List<ControlledCarTaskView> getControlledTaskViews() {
501         return mControlledTaskViews;
502     }
503 
504     @VisibleForTesting
getLaunchRootCarTaskView()505     LaunchRootCarTaskView getLaunchRootCarTaskView() {
506         return mLaunchRootCarTaskView;
507     }
508 
509     @VisibleForTesting
getSemiControlledTaskViews()510     List<SemiControlledCarTaskView> getSemiControlledTaskViews() {
511         return mSemiControlledTaskViews;
512     }
513 
514     @VisibleForTesting
getPackageBroadcastReceiver()515     BroadcastReceiver getPackageBroadcastReceiver() {
516         return mPackageBroadcastReceiver;
517     }
518 
519     @VisibleForTesting
520     /** Only meant for testing, should not be used by real code. */
setTaskViewInputInterceptor(TaskViewInputInterceptor taskViewInputInterceptor)521     void setTaskViewInputInterceptor(TaskViewInputInterceptor taskViewInputInterceptor) {
522         mTaskViewInputInterceptor = taskViewInputInterceptor;
523     }
524 
getRootTaskCount()525     public int getRootTaskCount() {
526         return mLaunchRootCarTaskView != null ? mLaunchRootCarTaskView.getRootTaskCount() : 0;
527     }
528 
529     /**
530      * Returns the {@link android.app.ActivityManager.RunningTaskInfo} of the top task inside the
531      * launch root car task view.
532      */
getTopTaskInLaunchRootTask()533     public ActivityManager.RunningTaskInfo getTopTaskInLaunchRootTask() {
534         return mLaunchRootCarTaskView != null
535                 ? mLaunchRootCarTaskView.getTopTaskInLaunchRootTask() : null;
536     }
537 
isReleased()538     boolean isReleased() {
539         return mReleased;
540     }
541 
542     /**
543      * Adds {@code activities} to allowed list of {@code carTaskView} if this car task view is a
544      * known {@link SemiControlledCarTaskView}.
545      */
addAllowListedActivities(@onNull CarTaskView carTaskView, List<ComponentName> activities)546     public void addAllowListedActivities(@NonNull CarTaskView carTaskView,
547             List<ComponentName> activities) {
548         if (activities.size() == 0) {
549             if (DBG) {
550                 Log.d(TAG, "No activity to add to allowlist");
551             }
552             return;
553         }
554         for (SemiControlledCarTaskView semiControlledCarTaskView: mSemiControlledTaskViews) {
555             if (semiControlledCarTaskView.equals(carTaskView)) {
556                 semiControlledCarTaskView.addAllowListedActivities(activities);
557                 return;
558             }
559         }
560     }
561 
562     /**
563      * Removes {@code activities} from allowed list of {@code carTaskView} if this CarTaskView is a
564      * known SemiControlledCarTaskView.
565      */
removeAllowListedActivities(@onNull CarTaskView carTaskView, List<ComponentName> activities)566     public void removeAllowListedActivities(@NonNull CarTaskView carTaskView,
567             List<ComponentName> activities) {
568         if (activities.size() == 0) {
569             if (DBG) {
570                 Log.d(TAG, "No activity to remove from allowlist");
571             }
572             return;
573         }
574         for (SemiControlledCarTaskView semiControlledCarTaskView: mSemiControlledTaskViews) {
575             if (semiControlledCarTaskView.equals(carTaskView)) {
576                 semiControlledCarTaskView.removeAllowListedActivities(activities);
577                 return;
578             }
579         }
580     }
581 
582     /**
583      * Sets {@code activities} to be the allowed list of {@code carTaskView} if this CarTaskView
584      * is a known SemiControlledCarTaskView.
585      */
setAllowListedActivities(CarTaskView carTaskView, List<ComponentName> activities)586     public void setAllowListedActivities(CarTaskView carTaskView, List<ComponentName> activities) {
587         if (activities.size() == 0) {
588             if (DBG) {
589                 Log.d(TAG, "No activity to remove from allowlist");
590             }
591             return;
592         }
593         for (SemiControlledCarTaskView semiControlledCarTaskView: mSemiControlledTaskViews) {
594             if (semiControlledCarTaskView.equals(carTaskView)) {
595                 semiControlledCarTaskView.setAllowListedActivities(activities);
596                 return;
597             }
598         }
599     }
600 }
601