• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 android.car.app;
18 
19 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
20 
21 import static com.android.car.internal.util.VersionUtils.assertPlatformVersionAtLeastU;
22 
23 import android.annotation.IntDef;
24 import android.annotation.MainThread;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.RequiresApi;
28 import android.annotation.RequiresPermission;
29 import android.annotation.SystemApi;
30 import android.annotation.UiContext;
31 import android.app.Activity;
32 import android.app.ActivityManager;
33 import android.car.Car;
34 import android.car.CarManagerBase;
35 import android.car.annotation.AddedInOrBefore;
36 import android.car.annotation.ApiRequirements;
37 import android.car.user.CarUserManager;
38 import android.car.view.MirroredSurfaceView;
39 import android.content.ActivityNotFoundException;
40 import android.content.ComponentName;
41 import android.content.Context;
42 import android.graphics.Rect;
43 import android.os.Binder;
44 import android.os.Build;
45 import android.os.Bundle;
46 import android.os.IBinder;
47 import android.os.RemoteException;
48 import android.os.ServiceSpecificException;
49 import android.util.Log;
50 import android.util.Pair;
51 import android.view.Display;
52 import android.view.SurfaceControl;
53 
54 import com.android.internal.annotations.VisibleForTesting;
55 import com.android.internal.util.Preconditions;
56 
57 import java.lang.annotation.ElementType;
58 import java.lang.annotation.Retention;
59 import java.lang.annotation.RetentionPolicy;
60 import java.lang.annotation.Target;
61 import java.util.Collections;
62 import java.util.List;
63 import java.util.concurrent.Executor;
64 
65 /**
66  * API to manage {@link android.app.Activity} in Car.
67  *
68  * @hide
69  */
70 @SystemApi
71 public final class CarActivityManager extends CarManagerBase {
72     private static final String TAG = CarActivityManager.class.getSimpleName();
73 
74     /** Indicates that the operation was successful. */
75     @AddedInOrBefore(majorVersion = 33)
76     public static final int RESULT_SUCCESS = 0;
77     /** Indicates that the operation was failed with the unknown reason. */
78     @AddedInOrBefore(majorVersion = 33)
79     public static final int RESULT_FAILURE = -1;
80     /**
81      * Indicates that the operation was failed because the requester isn't the current user or
82      * the system user
83      */
84     @AddedInOrBefore(majorVersion = 33)
85     public static final int RESULT_INVALID_USER = -2;
86 
87     /** @hide */
88     @Retention(RetentionPolicy.SOURCE)
89     @IntDef(prefix = "RESULT_", value = {
90             RESULT_SUCCESS,
91             RESULT_FAILURE,
92             RESULT_INVALID_USER,
93     })
94     @Target({ElementType.TYPE_USE})
95     public @interface ResultTypeEnum {}
96 
97     /**
98      * Internal error code for throwing {@link ActivityNotFoundException} from service.
99      * @hide
100      */
101     @AddedInOrBefore(majorVersion = 33)
102     public static final int ERROR_CODE_ACTIVITY_NOT_FOUND = -101;
103 
104     private final ICarActivityService mService;
105     private IBinder mTaskMonitorToken;
106     private CarTaskViewControllerSupervisor mCarTaskViewControllerSupervisor;
107 
108     /**
109      * @hide
110      */
CarActivityManager(@onNull Car car, @NonNull IBinder service)111     public CarActivityManager(@NonNull Car car, @NonNull IBinder service) {
112         this(car, ICarActivityService.Stub.asInterface(service));
113     }
114 
115     /**
116      * @hide
117      */
118     @VisibleForTesting
CarActivityManager(@onNull Car car, @NonNull ICarActivityService service)119     public CarActivityManager(@NonNull Car car, @NonNull ICarActivityService service) {
120         super(car);
121         mService = service;
122     }
123 
124     /**
125      * Designates the given {@code activity} to be launched in {@code TaskDisplayArea} of
126      * {@code featureId} in the display of {@code displayId}.
127      * <p>Note: this will not affect the existing {@link Activity}.
128      * Note: You can map assign {@code Activity} to one {@code TaskDisplayArea} only. If
129      * you assign it to the multiple {@code TaskDisplayArea}s, then the last one wins.
130      * Note: The requester should be the current user or the system user, if not, the operation will
131      * be failed with {@code RESULT_INVALID_USER}.
132      *
133      * @param activity {@link Activity} to designate
134      * @param displayId {@code Display} where {@code TaskDisplayArea} is located in
135      * @param featureId {@code TaskDisplayArea} where {@link Activity} is launched in, if it is
136      *         {@code DisplayAreaOrganizer.FEATURE_UNDEFINED}, then it'll remove the existing one.
137      * @return {@code ResultTypeEnum}. {@code RESULT_SUCCESS} if the operation is successful,
138      *         otherwise, {@code RESULT_XXX} depending on the type of the error.
139      * @throws {@link IllegalArgumentException} if {@code displayId} or {@code featureId} is
140      *         invalid. {@link ActivityNotFoundException} if {@code activity} is not found
141      *         when it tries to remove.
142      */
143     @RequiresPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH)
144     @ResultTypeEnum
145     @AddedInOrBefore(majorVersion = 33)
setPersistentActivity( @onNull ComponentName activity, int displayId, int featureId)146     public int setPersistentActivity(
147             @NonNull ComponentName activity, int displayId, int featureId) {
148         try {
149             return mService.setPersistentActivity(activity, displayId, featureId);
150         } catch (IllegalArgumentException | IllegalStateException | SecurityException e) {
151             throw e;
152         } catch (ServiceSpecificException e) {
153             return handleServiceSpecificFromCarService(e);
154         } catch (RemoteException | RuntimeException e) {
155             return handleExceptionFromCarService(e, RESULT_FAILURE);
156         }
157     }
158 
159     /**
160      * Designates the given {@code activities} to be launched in the root task associated with the
161      * given {@code rootTaskToken}.
162      * <p>Note: If an activity is already persisted on a root task, it will be overridden by the
163      * {@code rootTaskToken} supplied in the latest call.
164      * <p>Note: If {@code rootTaskToken} is null, the designation will be removed and the given
165      * activities will follow default behavior.
166      *
167      * @param activities list of {@link ComponentName} of activities to be designated on the
168      *                   root task.
169      * @param rootTaskToken the binder token of the root task.
170      */
171     @RequiresPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH)
172     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
173             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
setPersistentActivitiesOnRootTask(@onNull List<ComponentName> activities, @Nullable IBinder rootTaskToken)174     public void setPersistentActivitiesOnRootTask(@NonNull List<ComponentName> activities,
175             @Nullable IBinder rootTaskToken) {
176         assertPlatformVersionAtLeastU();
177         try {
178             mService.setPersistentActivitiesOnRootTask(activities, rootTaskToken);
179         } catch (IllegalArgumentException | IllegalStateException | SecurityException e) {
180             throw e;
181         }  catch (RemoteException | RuntimeException e) {
182             handleExceptionFromCarService(e, RESULT_FAILURE);
183         }
184     }
185 
186     /** @hide */
187     @Override
188     @AddedInOrBefore(majorVersion = 33)
onCarDisconnected()189     protected void onCarDisconnected() {
190         mTaskMonitorToken = null;
191     }
192 
handleServiceSpecificFromCarService(ServiceSpecificException e)193     private int handleServiceSpecificFromCarService(ServiceSpecificException e)
194             throws ActivityNotFoundException {
195         if (e.errorCode == ERROR_CODE_ACTIVITY_NOT_FOUND) {
196             throw new ActivityNotFoundException(e.getMessage());
197         }
198         // don't know what this is
199         throw new IllegalStateException(e);
200     }
201 
202     /**
203      * Registers the caller as TaskMonitor, which can provide Task lifecycle events to CarService.
204      * The caller should provide a binder token, which is used to check if the given TaskMonitor is
205      * live and the reported events are from the legitimate TaskMonitor.
206      * @hide
207      */
208     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
209     @AddedInOrBefore(majorVersion = 33)
registerTaskMonitor()210     public boolean registerTaskMonitor() {
211         Preconditions.checkState(
212                 mTaskMonitorToken == null, "Can't register the multiple TaskMonitors");
213         IBinder token = new Binder();
214         try {
215             mService.registerTaskMonitor(token);
216             mTaskMonitorToken = token;
217             return true;
218         } catch (RemoteException e) {
219             handleRemoteExceptionFromCarService(e);
220         }
221         return false;
222     }
223 
224     /**
225      * Reports that a Task is created.
226      * @deprecated Use {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)}
227      * @hide
228      */
229     @Deprecated
230     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
231     @AddedInOrBefore(majorVersion = 33)
onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo)232     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
233         onTaskAppearedInternal(taskInfo, null);
234     }
235 
236     /**
237      * Reports that a Task is created.
238      * @hide
239      */
240     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
241     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
242             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, @Nullable SurfaceControl leash)243     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
244                 @Nullable SurfaceControl leash) {
245         onTaskAppearedInternal(taskInfo, leash);
246     }
247 
onTaskAppearedInternal( ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash)248     private void onTaskAppearedInternal(
249             ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
250         if (!hasValidToken()) return;
251         try {
252             mService.onTaskAppeared(mTaskMonitorToken, taskInfo, leash);
253         } catch (RemoteException e) {
254             handleRemoteExceptionFromCarService(e);
255         }
256     }
257 
258     /**
259      * Reports that a Task is vanished.
260      * @hide
261      */
262     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
263     @AddedInOrBefore(majorVersion = 33)
onTaskVanished(ActivityManager.RunningTaskInfo taskInfo)264     public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
265         if (!hasValidToken()) return;
266         try {
267             mService.onTaskVanished(mTaskMonitorToken, taskInfo);
268         } catch (RemoteException e) {
269             handleRemoteExceptionFromCarService(e);
270         }
271     }
272 
273     /**
274      * Reports that some Task's states are changed.
275      * @hide
276      */
277     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
278     @AddedInOrBefore(majorVersion = 33)
onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo)279     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
280         if (!hasValidToken()) return;
281         try {
282             mService.onTaskInfoChanged(mTaskMonitorToken, taskInfo);
283         } catch (RemoteException e) {
284             handleRemoteExceptionFromCarService(e);
285         }
286     }
287 
288     /**
289      * Unregisters the caller from TaskMonitor.
290      * @hide
291      */
292     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
293     @AddedInOrBefore(majorVersion = 33)
unregisterTaskMonitor()294     public void unregisterTaskMonitor() {
295         if (!hasValidToken()) return;
296         try {
297             mService.unregisterTaskMonitor(mTaskMonitorToken);
298             mTaskMonitorToken = null;
299         } catch (RemoteException e) {
300             handleRemoteExceptionFromCarService(e);
301         }
302     }
303 
304     /**
305      * Returns all the visible tasks in the all displays. The order is not guaranteed.
306      */
307     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
308     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_1,
309              minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
310     @NonNull
getVisibleTasks()311     public List<ActivityManager.RunningTaskInfo> getVisibleTasks() {
312         try {
313             return mService.getVisibleTasks(Display.INVALID_DISPLAY);
314         } catch (RemoteException e) {
315             handleRemoteExceptionFromCarService(e);
316         }
317         return Collections.emptyList();
318     }
319 
320     /**
321      * Returns all the visible tasks in the given display. The order is not guaranteed.
322      *
323      * @param displayId the id of {@link Display} to retrieve the tasks,
324      *         {@link Display.INVALID_DISPLAY} to retrieve the tasks in the all displays.
325      */
326     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
327     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
328             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
329     @NonNull
getVisibleTasks(int displayId)330     public List<ActivityManager.RunningTaskInfo> getVisibleTasks(int displayId) {
331         try {
332             return mService.getVisibleTasks(displayId);
333         } catch (RemoteException e) {
334             handleRemoteExceptionFromCarService(e);
335         }
336         return Collections.emptyList();
337     }
338 
339     /**
340      * Starts user picker UI (=user selection UI) to the given display.
341      *
342      * <p>User picker UI will run as {@link android.os.UserHandle#SYSTEM} user.
343      */
344     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
345     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
346             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
startUserPickerOnDisplay(int displayId)347     public void startUserPickerOnDisplay(int displayId) {
348         assertPlatformVersionAtLeastU();
349         try {
350             mService.startUserPickerOnDisplay(displayId);
351         } catch (RemoteException e) {
352             handleRemoteExceptionFromCarService(e);
353         }
354     }
355 
356     /**
357      * Creates the mirroring token of the given Task.
358      *
359      * @param taskId The Task to mirror.
360      * @return A token to access the Task Surface. The token is used to identify the target
361      *     Task's Surface for {@link MirroredSurfaceView}.
362      */
363     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
364     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
365             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
366     @Nullable
createTaskMirroringToken(int taskId)367     public IBinder createTaskMirroringToken(int taskId) {
368         assertPlatformVersionAtLeastU();
369         try {
370             return mService.createTaskMirroringToken(taskId);
371         } catch (RemoteException e) {
372             return handleRemoteExceptionFromCarService(e, /* returnValue= */ null);
373         }
374     }
375 
376     /**
377      * Creates the mirroring token of the given Display.
378      *
379      * @param displayId The Display to mirror.
380      * @return A token to access the Display Surface. The token is used to identify the target
381      *     Display's Surface for {@link MirroredSurfaceView}.
382      */
383     @RequiresPermission(Car.PERMISSION_MIRROR_DISPLAY)
384     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
385             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
386     @Nullable
createDisplayMirroringToken(int displayId)387     public IBinder createDisplayMirroringToken(int displayId) {
388         assertPlatformVersionAtLeastU();
389         try {
390             return mService.createDisplayMirroringToken(displayId);
391         } catch (RemoteException e) {
392             return handleRemoteExceptionFromCarService(e, /* returnValue= */ null);
393         }
394     }
395 
396     /**
397      * Gets a mirrored {@link SurfaceControl} of the Task identified by the given Token.
398      *
399      * @param token  The token to access the Surface.
400      * @return A Pair of {@link SurfaceControl} and the bounds of the mirrored Task,
401      *     or {code null} if it can't find the target Surface to mirror.
402      *
403      * @hide Used by {@link MirroredSurfaceView} only.
404      */
405     @RequiresPermission(Car.PERMISSION_ACCESS_MIRRORRED_SURFACE)
406     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
407             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
408     @Nullable
getMirroredSurface(@onNull IBinder token)409     public Pair<SurfaceControl, Rect> getMirroredSurface(@NonNull IBinder token) {
410         assertPlatformVersionAtLeastU();
411         try {
412             Rect outBounds = new Rect();
413             // SurfaceControl constructor is hidden api, so we can get it by the return value.
414             SurfaceControl sc = mService.getMirroredSurface(token, outBounds);
415             if (sc == null) {
416                 return null;
417             }
418             return Pair.create(sc, outBounds);
419         } catch (RemoteException e) {
420             return handleRemoteExceptionFromCarService(e, /* returnValue= */ null);
421         }
422     }
423 
424     /**
425      * Registers a system ui proxy which will be used by the client apps to interact with the
426      * system-ui for things like creating task views, getting notified about immersive mode
427      * request, etc.
428      *
429      * <p>This is meant to be called only by the SystemUI.
430      *
431      * @param carSystemUIProxy the implementation of the {@link CarSystemUIProxy}.
432      * @throws UnsupportedOperationException when called more than once for the same SystemUi
433      *         process.
434      * @hide
435      */
436     @SystemApi
437     @RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
438     @ApiRequirements(
439             minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
440             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
registerCarSystemUIProxy(@onNull CarSystemUIProxy carSystemUIProxy)441     public void registerCarSystemUIProxy(@NonNull CarSystemUIProxy carSystemUIProxy) {
442         assertPlatformVersionAtLeastU();
443         try {
444             mService.registerCarSystemUIProxy(new CarSystemUIProxyAidlWrapper(carSystemUIProxy));
445         } catch (RemoteException e) {
446             handleRemoteExceptionFromCarService(e);
447         }
448     }
449 
450     /**
451      * Returns true if the {@link CarSystemUIProxy} is registered, false otherwise.
452      *
453      * @hide
454      */
455     @SystemApi
456     @ApiRequirements(
457             minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
458             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
isCarSystemUIProxyRegistered()459     public boolean isCarSystemUIProxyRegistered() {
460         assertPlatformVersionAtLeastU();
461         try {
462             return mService.isCarSystemUIProxyRegistered();
463         } catch (RemoteException e) {
464             handleRemoteExceptionFromCarService(e);
465             return false;
466         }
467     }
468 
469     /**
470      * Gets the {@link CarTaskViewController} using the {@code carTaskViewControllerCallback}.
471      *
472      * This method is expected to be called from the {@link Activity#onCreate(Bundle)}. It will
473      * take care of freeing up the held resources when activity is destroyed. If an activity is
474      * recreated, it should be called again in the next {@link Activity#onCreate(Bundle)}.
475      *
476      * @param carTaskViewControllerCallback the callback which the client can use to monitor the
477      *                                      lifecycle of the {@link CarTaskViewController}.
478      * @param hostActivity the activity that will host the taskviews.
479      * @hide
480      */
481     @SystemApi
482     @RequiresPermission(allOf = {Car.PERMISSION_MANAGE_CAR_SYSTEM_UI, INTERACT_ACROSS_USERS})
483     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
484     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
485             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
486     @MainThread
getCarTaskViewController( @onNull Activity hostActivity, @NonNull Executor callbackExecutor, @NonNull CarTaskViewControllerCallback carTaskViewControllerCallback)487     public void getCarTaskViewController(
488             @NonNull Activity hostActivity,
489             @NonNull Executor callbackExecutor,
490             @NonNull CarTaskViewControllerCallback carTaskViewControllerCallback) {
491         getCarTaskViewController(
492                 hostActivity,
493                 CarTaskViewControllerHostLifecycleFactory.forActivity(hostActivity),
494                 callbackExecutor,
495                 carTaskViewControllerCallback);
496     }
497 
498     /**
499      * Gets the {@link CarTaskViewController} using the {@code carTaskViewControllerCallback}.
500      *
501      * This method is expected to be called when the container (host) is created. It will
502      * take care of freeing up the held resources when container is destroyed. If the container is
503      * recreated, this method should be called again after it gets created again.
504      *
505      * @param carTaskViewControllerCallback the callback which the client can use to monitor the
506      *                                      lifecycle of the {@link CarTaskViewController}.
507      * @param hostContext the visual hostContext which the container (host) is associated with.
508      * @param callbackExecutor the executor which the {@code carTaskViewControllerCallback} will be
509      *                         executed on.
510      * @param carTaskViewControllerHostLifecycle the lifecycle of the container (host).
511      * @hide
512      */
513     // TODO(b/293297847): Expose this as system API
514     @RequiresPermission(allOf = {Car.PERMISSION_MANAGE_CAR_SYSTEM_UI, INTERACT_ACROSS_USERS})
515     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
516     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
517             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
518     @MainThread
getCarTaskViewController( @iContext @onNull Context hostContext, @NonNull CarTaskViewControllerHostLifecycle carTaskViewControllerHostLifecycle, @NonNull Executor callbackExecutor, @NonNull CarTaskViewControllerCallback carTaskViewControllerCallback)519     public void getCarTaskViewController(
520             @UiContext @NonNull Context hostContext,
521             @NonNull CarTaskViewControllerHostLifecycle carTaskViewControllerHostLifecycle,
522             @NonNull Executor callbackExecutor,
523             @NonNull CarTaskViewControllerCallback carTaskViewControllerCallback) {
524         assertPlatformVersionAtLeastU();
525         try {
526             if (mCarTaskViewControllerSupervisor == null) {
527                 // Same supervisor is used for multiple activities.
528                 mCarTaskViewControllerSupervisor = new CarTaskViewControllerSupervisor(mService,
529                         getContext().getMainExecutor(), mCar.getCarManager(CarUserManager.class));
530             }
531             mCarTaskViewControllerSupervisor.createCarTaskViewController(
532                     hostContext,
533                     carTaskViewControllerHostLifecycle,
534                     callbackExecutor,
535                     carTaskViewControllerCallback);
536         } catch (RemoteException e) {
537             handleRemoteExceptionFromCarService(e);
538         }
539     }
540 
541     /**
542      * Moves the given {@code RootTask} with its child {@code Activties} to the specified
543      * {@code Display}.
544      * @param taskId the id of the target {@code RootTask} to move
545      * @param displayId the displayId to move the {@code RootTask} to
546      * @throws IllegalArgumentException if the given {@code taskId} or {@code displayId} is invalid
547      * @throws IllegalArgumentException if the given {@code RootTask} is already in the given
548      *     {@code Display}
549      * Note: the operation can be failed if the given {@code Display} doesn't allow for the type of
550      * the given {@code RootTask} to be launched.
551      */
552     @RequiresPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH)
553     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
554             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
moveRootTaskToDisplay(int taskId, int displayId)555     public void moveRootTaskToDisplay(int taskId, int displayId) {
556         assertPlatformVersionAtLeastU();
557         try {
558             mService.moveRootTaskToDisplay(taskId, displayId);
559         } catch (RemoteException e) {
560             handleRemoteExceptionFromCarService(e);
561         }
562     }
563 
hasValidToken()564     private boolean hasValidToken() {
565         boolean valid = mTaskMonitorToken != null;
566         if (!valid) {
567             Log.w(TAG, "Has invalid token, skip the operation: "
568                     + new Throwable().getStackTrace()[1].getMethodName());
569         }
570         return valid;
571     }
572 }
573