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