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