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 com.android.car.am; 18 19 import static android.Manifest.permission.INTERACT_ACROSS_USERS; 20 import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS; 21 import static android.car.Car.PERMISSION_MANAGE_CAR_SYSTEM_UI; 22 import static android.car.Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY; 23 import static android.car.content.pm.CarPackageManager.BLOCKING_INTENT_EXTRA_DISPLAY_ID; 24 25 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 26 import static com.android.car.internal.util.VersionUtils.isPlatformVersionAtLeastU; 27 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.annotation.RequiresApi; 31 import android.app.ActivityManager; 32 import android.app.ActivityOptions; 33 import android.app.TaskInfo; 34 import android.car.Car; 35 import android.car.app.CarActivityManager; 36 import android.car.app.ICarActivityService; 37 import android.car.app.ICarSystemUIProxy; 38 import android.car.app.ICarSystemUIProxyCallback; 39 import android.car.builtin.app.ActivityManagerHelper; 40 import android.car.builtin.app.TaskInfoHelper; 41 import android.car.builtin.os.UserManagerHelper; 42 import android.car.builtin.util.Slogf; 43 import android.car.builtin.view.SurfaceControlHelper; 44 import android.content.ComponentName; 45 import android.content.Context; 46 import android.content.Intent; 47 import android.content.pm.PackageManager; 48 import android.graphics.Point; 49 import android.graphics.Rect; 50 import android.hardware.display.DisplayManager; 51 import android.os.Binder; 52 import android.os.Build; 53 import android.os.Handler; 54 import android.os.HandlerThread; 55 import android.os.IBinder; 56 import android.os.RemoteCallbackList; 57 import android.os.RemoteException; 58 import android.os.UserHandle; 59 import android.util.ArrayMap; 60 import android.util.ArraySet; 61 import android.util.Log; 62 import android.util.SparseArray; 63 import android.view.Display; 64 import android.view.SurfaceControl; 65 66 import com.android.car.CarLog; 67 import com.android.car.CarServiceBase; 68 import com.android.car.CarServiceHelperWrapper; 69 import com.android.car.CarServiceUtils; 70 import com.android.car.R; 71 import com.android.car.SystemActivityMonitoringService; 72 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 73 import com.android.car.internal.util.IndentingPrintWriter; 74 import com.android.internal.annotations.GuardedBy; 75 import com.android.internal.annotations.VisibleForTesting; 76 import com.android.internal.util.Preconditions; 77 78 import java.util.ArrayList; 79 import java.util.Collections; 80 import java.util.LinkedHashMap; 81 import java.util.List; 82 import java.util.Objects; 83 84 /** 85 * Service responsible for Activities in Car. 86 */ 87 public final class CarActivityService extends ICarActivityService.Stub 88 implements CarServiceBase { 89 90 private static final String TAG = CarLog.TAG_AM; 91 private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG); 92 93 private static final int MAX_RUNNING_TASKS_TO_GET = 100; 94 private static final long MIRRORING_TOKEN_TIMEOUT_MS = 10 * 60 * 1000; // 10 mins 95 96 private final Context mContext; 97 private final DisplayManager mDisplayManager; 98 private final long mMirroringTokenTimeoutMs; 99 100 private final Object mLock = new Object(); 101 102 // LinkedHashMap is used instead of SparseXXX because a predictable iteration order is needed. 103 // The tasks here need be ordered as per their stack order. The stack order is maintained 104 // using a combination of onTaskAppeared and onTaskInfoChanged callbacks. 105 @GuardedBy("mLock") 106 private final LinkedHashMap<Integer, ActivityManager.RunningTaskInfo> mTasks = 107 new LinkedHashMap<>(); 108 @GuardedBy("mLock") 109 private final SparseArray<SurfaceControl> mTaskToSurfaceMap = new SparseArray<>(); 110 111 @GuardedBy("mLock") 112 private final ArrayMap<IBinder, IBinder.DeathRecipient> mMonitorTokens = new ArrayMap<>(); 113 114 @GuardedBy("mLock") 115 private final ArraySet<MirroringToken> mMirroringTokens = new ArraySet<>(); 116 117 @GuardedBy("mLock") 118 private ICarSystemUIProxy mCarSystemUIProxy; 119 @GuardedBy("mLock") 120 private final RemoteCallbackList<ICarSystemUIProxyCallback> mCarSystemUIProxyCallbacks = 121 new RemoteCallbackList<ICarSystemUIProxyCallback>(); 122 123 private IBinder mCurrentMonitor; 124 125 public interface ActivityLaunchListener { 126 /** 127 * Notify launch of activity. 128 * 129 * @param topTask Task information for what is currently launched. 130 */ onActivityLaunch(TaskInfo topTask)131 void onActivityLaunch(TaskInfo topTask); 132 } 133 @GuardedBy("mLock") 134 private ActivityLaunchListener mActivityLaunchListener; 135 136 private final HandlerThread mMonitorHandlerThread = CarServiceUtils.getHandlerThread( 137 SystemActivityMonitoringService.class.getSimpleName()); 138 private final Handler mHandler = new Handler(mMonitorHandlerThread.getLooper()); 139 CarActivityService(Context context)140 public CarActivityService(Context context) { 141 this(context, MIRRORING_TOKEN_TIMEOUT_MS); 142 } 143 144 @VisibleForTesting CarActivityService(Context context, long mirroringTokenTimeout)145 CarActivityService(Context context, long mirroringTokenTimeout) { 146 mContext = context; 147 mDisplayManager = context.getSystemService(DisplayManager.class); 148 mMirroringTokenTimeoutMs = mirroringTokenTimeout; 149 } 150 151 @Override init()152 public void init() {} 153 154 @Override release()155 public void release() {} 156 157 @Override setPersistentActivity(ComponentName activity, int displayId, int featureId)158 public int setPersistentActivity(ComponentName activity, int displayId, int featureId) throws 159 RemoteException { 160 ensurePermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH); 161 int caller = getCaller(); 162 if (caller != UserManagerHelper.USER_SYSTEM && caller != ActivityManager.getCurrentUser()) { 163 return CarActivityManager.RESULT_INVALID_USER; 164 } 165 166 return CarServiceHelperWrapper.getInstance().setPersistentActivity(activity, displayId, 167 featureId); 168 } 169 170 @Override setPersistentActivitiesOnRootTask(@onNull List<ComponentName> activities, IBinder rootTaskToken)171 public void setPersistentActivitiesOnRootTask(@NonNull List<ComponentName> activities, 172 IBinder rootTaskToken) { 173 ensurePermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH); 174 CarServiceHelperWrapper.getInstance().setPersistentActivitiesOnRootTask(activities, 175 rootTaskToken); 176 } 177 178 @VisibleForTesting getCaller()179 int getCaller() { // Non static for mocking. 180 return UserManagerHelper.getUserId(Binder.getCallingUid()); 181 } 182 registerActivityLaunchListener(ActivityLaunchListener listener)183 public void registerActivityLaunchListener(ActivityLaunchListener listener) { 184 synchronized (mLock) { 185 mActivityLaunchListener = listener; 186 } 187 } 188 189 @Override registerTaskMonitor(IBinder token)190 public void registerTaskMonitor(IBinder token) { 191 if (DBG) Slogf.d(TAG, "registerTaskMonitor: %s", token); 192 ensureManageActivityTasksPermission(); 193 194 IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() { 195 @Override 196 public void binderDied() { 197 cleanUpMonitorToken(token); 198 } 199 }; 200 synchronized (mLock) { 201 try { 202 token.linkToDeath(deathRecipient, /* flags= */ 0); 203 } catch (RemoteException e) { 204 // 'token' owner might be dead already. 205 Slogf.e(TAG, "failed to linkToDeath: %s", token); 206 return; 207 } 208 mMonitorTokens.put(token, deathRecipient); 209 mCurrentMonitor = token; 210 // When new TaskOrganizer takes the control, it'll get the status of the whole tasks 211 // in the system again. So drops the old status. 212 mTasks.clear(); 213 } 214 } 215 ensurePermission(String permission)216 private void ensurePermission(String permission) { 217 if (mContext.checkCallingOrSelfPermission(permission) 218 != PackageManager.PERMISSION_GRANTED) { 219 throw new SecurityException("requires permission " + permission); 220 } 221 } 222 ensureManageActivityTasksPermission()223 private void ensureManageActivityTasksPermission() { 224 ensurePermission(MANAGE_ACTIVITY_TASKS); 225 } 226 cleanUpMonitorToken(IBinder token)227 private void cleanUpMonitorToken(IBinder token) { 228 synchronized (mLock) { 229 if (mCurrentMonitor == token) { 230 mCurrentMonitor = null; 231 } 232 IBinder.DeathRecipient deathRecipient = mMonitorTokens.remove(token); 233 if (deathRecipient != null) { 234 token.unlinkToDeath(deathRecipient, /* flags= */ 0); 235 } 236 } 237 } 238 239 @Override onTaskAppeared(IBinder token, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash)240 public void onTaskAppeared(IBinder token, 241 ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { 242 if (DBG) { 243 Slogf.d(TAG, "onTaskAppeared: %s, %s", token, TaskInfoHelper.toString(taskInfo)); 244 } 245 ensureManageActivityTasksPermission(); 246 synchronized (mLock) { 247 if (!isAllowedToUpdateLocked(token)) { 248 return; 249 } 250 mTasks.put(taskInfo.taskId, taskInfo); 251 if (leash != null) { 252 mTaskToSurfaceMap.put(taskInfo.taskId, leash); 253 } 254 } 255 if (TaskInfoHelper.isVisible(taskInfo)) { 256 mHandler.post(() -> notifyActivityLaunch(taskInfo)); 257 } 258 } 259 notifyActivityLaunch(TaskInfo taskInfo)260 private void notifyActivityLaunch(TaskInfo taskInfo) { 261 ActivityLaunchListener listener; 262 synchronized (mLock) { 263 listener = mActivityLaunchListener; 264 } 265 if (listener != null) { 266 listener.onActivityLaunch(taskInfo); 267 } 268 } 269 270 @GuardedBy("mLock") isAllowedToUpdateLocked(IBinder token)271 private boolean isAllowedToUpdateLocked(IBinder token) { 272 if (mCurrentMonitor != null && mCurrentMonitor == token) { 273 return true; 274 } 275 // Fallback during no current Monitor exists. 276 boolean allowed = (mCurrentMonitor == null && mMonitorTokens.containsKey(token)); 277 if (!allowed) { 278 Slogf.w(TAG, "Report with the invalid token: %s", token); 279 } 280 return allowed; 281 } 282 283 @Override onTaskVanished(IBinder token, ActivityManager.RunningTaskInfo taskInfo)284 public void onTaskVanished(IBinder token, ActivityManager.RunningTaskInfo taskInfo) { 285 if (DBG) { 286 Slogf.d(TAG, "onTaskVanished: %s, %s", token, TaskInfoHelper.toString(taskInfo)); 287 } 288 ensureManageActivityTasksPermission(); 289 synchronized (mLock) { 290 if (!isAllowedToUpdateLocked(token)) { 291 return; 292 } 293 mTasks.remove(taskInfo.taskId); 294 mTaskToSurfaceMap.remove(taskInfo.taskId); 295 } 296 } 297 298 @Override onTaskInfoChanged(IBinder token, ActivityManager.RunningTaskInfo taskInfo)299 public void onTaskInfoChanged(IBinder token, ActivityManager.RunningTaskInfo taskInfo) { 300 if (DBG) { 301 Slogf.d(TAG, "onTaskInfoChanged: %s, %s", token, TaskInfoHelper.toString(taskInfo)); 302 } 303 ensureManageActivityTasksPermission(); 304 synchronized (mLock) { 305 if (!isAllowedToUpdateLocked(token)) { 306 return; 307 } 308 // The key should be removed and added again so that it jumps to the front of the 309 // LinkedHashMap. 310 TaskInfo oldTaskInfo = mTasks.remove(taskInfo.taskId); 311 mTasks.put(taskInfo.taskId, taskInfo); 312 if ((oldTaskInfo == null || !TaskInfoHelper.isVisible(oldTaskInfo) 313 || !Objects.equals(oldTaskInfo.topActivity, taskInfo.topActivity)) 314 && TaskInfoHelper.isVisible(taskInfo)) { 315 mHandler.post(() -> notifyActivityLaunch(taskInfo)); 316 } 317 } 318 } 319 320 @Override unregisterTaskMonitor(IBinder token)321 public void unregisterTaskMonitor(IBinder token) { 322 if (DBG) Slogf.d(TAG, "unregisterTaskMonitor: %s", token); 323 ensureManageActivityTasksPermission(); 324 cleanUpMonitorToken(token); 325 } 326 327 /** 328 * Returns all the visible tasks in the given display. The order is not guaranteed. 329 */ 330 @Override getVisibleTasks(int displayId)331 public List<ActivityManager.RunningTaskInfo> getVisibleTasks(int displayId) { 332 ensureManageActivityTasksPermission(); 333 return getVisibleTasksInternal(displayId); 334 } 335 getVisibleTasksInternal()336 public List<ActivityManager.RunningTaskInfo> getVisibleTasksInternal() { 337 return getVisibleTasksInternal(Display.INVALID_DISPLAY); 338 } 339 340 /** Car service internal version without the permission enforcement. */ getVisibleTasksInternal(int displayId)341 public List<ActivityManager.RunningTaskInfo> getVisibleTasksInternal(int displayId) { 342 ArrayList<ActivityManager.RunningTaskInfo> tasksToReturn = new ArrayList<>(); 343 synchronized (mLock) { 344 for (ActivityManager.RunningTaskInfo taskInfo : mTasks.values()) { 345 // Activities launched in the private display or non-focusable display can't be 346 // focusable. So we just monitor all visible Activities/Tasks. 347 if (TaskInfoHelper.isVisible(taskInfo) 348 && (displayId == Display.INVALID_DISPLAY 349 || displayId == TaskInfoHelper.getDisplayId(taskInfo))) { 350 tasksToReturn.add(taskInfo); 351 } 352 } 353 } 354 // Reverse the order so that the resultant order is top to bottom. 355 Collections.reverse(tasksToReturn); 356 return tasksToReturn; 357 } 358 359 @Override startUserPickerOnDisplay(int displayId)360 public void startUserPickerOnDisplay(int displayId) { 361 CarServiceUtils.assertAnyPermission(mContext, INTERACT_ACROSS_USERS); 362 Preconditions.checkArgument(displayId != Display.INVALID_DISPLAY, "Invalid display"); 363 if (!isPlatformVersionAtLeastU()) { 364 return; 365 } 366 String userPickerName = mContext.getResources().getString( 367 R.string.config_userPickerActivity); 368 if (userPickerName.isEmpty()) { 369 Slogf.w(TAG, "Cannot launch user picker to display %d, component not specified", 370 displayId); 371 return; 372 } 373 CarServiceUtils.startUserPickerOnDisplay(mContext, displayId, userPickerName); 374 } 375 376 private abstract class MirroringToken extends Binder { MirroringToken()377 private MirroringToken() { 378 CarActivityService.this.registerMirroringToken(this); 379 } 380 getMirroredSurface(Rect outBounds)381 protected abstract SurfaceControl getMirroredSurface(Rect outBounds); 382 }; 383 registerMirroringToken(MirroringToken token)384 private void registerMirroringToken(MirroringToken token) { 385 synchronized (mLock) { 386 mMirroringTokens.add(token); 387 } 388 mHandler.postDelayed(() -> cleanUpMirroringToken(token), mMirroringTokenTimeoutMs); 389 } 390 cleanUpMirroringToken(MirroringToken token)391 private void cleanUpMirroringToken(MirroringToken token) { 392 synchronized (mLock) { 393 mMirroringTokens.remove(token); 394 } 395 } 396 397 @GuardedBy("mLock") assertMirroringTokenIsValidLocked(MirroringToken token)398 private void assertMirroringTokenIsValidLocked(MirroringToken token) { 399 if (!mMirroringTokens.contains(token)) { 400 throw new IllegalArgumentException("Invalid token: " + token); 401 } 402 } 403 404 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 405 private final class TaskMirroringToken extends MirroringToken { 406 private final int mTaskId; TaskMirroringToken(int taskId)407 private TaskMirroringToken(int taskId) { 408 super(); 409 mTaskId = taskId; 410 } 411 412 @Override 413 @GuardedBy("CarActivityService.this.mLock") getMirroredSurface(Rect outBounds)414 protected SurfaceControl getMirroredSurface(Rect outBounds) { 415 TaskInfo taskInfo = mTasks.get(mTaskId); 416 SurfaceControl taskSurface = mTaskToSurfaceMap.get(mTaskId); 417 if (taskInfo == null || taskSurface == null || !taskInfo.isVisible()) { 418 Slogf.e(TAG, "TaskMirroringToken#getMirroredSurface: no task=%s", taskInfo); 419 return null; 420 } 421 outBounds.set(TaskInfoHelper.getBounds(taskInfo)); 422 return SurfaceControlHelper.mirrorSurface(taskSurface); 423 } 424 425 @Override toString()426 public String toString() { 427 return TaskMirroringToken.class.getSimpleName() + "[taskid=" + mTaskId + "]"; 428 } 429 }; 430 431 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 432 private final class DisplayMirroringToken extends MirroringToken { 433 private final int mDisplayId; DisplayMirroringToken(int displayId)434 private DisplayMirroringToken(int displayId) { 435 super(); 436 mDisplayId = displayId; 437 } 438 439 @Override getMirroredSurface(Rect outBounds)440 protected SurfaceControl getMirroredSurface(Rect outBounds) { 441 Display display = mDisplayManager.getDisplay(mDisplayId); 442 Point point = new Point(); 443 display.getRealSize(point); 444 outBounds.set(0, 0, point.x, point.y); 445 return SurfaceControlHelper.mirrorDisplay(mDisplayId); 446 } 447 448 @Override toString()449 public String toString() { 450 return DisplayMirroringToken.class.getSimpleName() + "[displayId=" + mDisplayId + "]"; 451 } 452 }; 453 454 @Override createTaskMirroringToken(int taskId)455 public IBinder createTaskMirroringToken(int taskId) { 456 ensureManageActivityTasksPermission(); 457 if (!isPlatformVersionAtLeastU()) { 458 return null; 459 } 460 synchronized (mLock) { 461 if (!mTaskToSurfaceMap.contains(taskId)) { 462 throw new IllegalArgumentException("Non-existent Task#" + taskId); 463 } 464 } 465 return new TaskMirroringToken(taskId); 466 } 467 468 @Override createDisplayMirroringToken(int displayId)469 public IBinder createDisplayMirroringToken(int displayId) { 470 ensurePermission(Car.PERMISSION_MIRROR_DISPLAY); 471 if (!isPlatformVersionAtLeastU()) { 472 return null; 473 } 474 return new DisplayMirroringToken(displayId); 475 } 476 477 @Override 478 @Nullable getMirroredSurface(IBinder token, Rect outBounds)479 public SurfaceControl getMirroredSurface(IBinder token, Rect outBounds) { 480 if (!isPlatformVersionAtLeastU()) { 481 return null; 482 } 483 ensurePermission(Car.PERMISSION_ACCESS_MIRRORRED_SURFACE); 484 MirroringToken mirroringToken; 485 try { 486 mirroringToken = (MirroringToken) token; 487 } catch (ClassCastException e) { 488 throw new IllegalArgumentException("Bad token"); 489 } 490 synchronized (mLock) { 491 assertMirroringTokenIsValidLocked(mirroringToken); 492 return mirroringToken.getMirroredSurface(outBounds); 493 } 494 } 495 496 @Override registerCarSystemUIProxy(ICarSystemUIProxy carSystemUIProxy)497 public void registerCarSystemUIProxy(ICarSystemUIProxy carSystemUIProxy) { 498 if (DBG) { 499 Slogf.d(TAG, "registerCarSystemUIProxy %s", carSystemUIProxy.toString()); 500 } 501 ensurePermission(PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY); 502 if (!isPlatformVersionAtLeastU()) { 503 return; 504 } 505 synchronized (mLock) { 506 if (mCarSystemUIProxy != null) { 507 throw new UnsupportedOperationException("Car system UI proxy is already " 508 + "registered"); 509 } 510 511 mCarSystemUIProxy = carSystemUIProxy; 512 try { 513 mCarSystemUIProxy.asBinder().linkToDeath(new IBinder.DeathRecipient(){ 514 @Override 515 public void binderDied() { 516 synchronized (mLock) { 517 Slogf.d(TAG, "CarSystemUIProxy died %s", 518 mCarSystemUIProxy.toString()); 519 mCarSystemUIProxy.asBinder().unlinkToDeath(this, /* flags= */ 0); 520 mCarSystemUIProxy = null; 521 } 522 } 523 }, /* flags= */0); 524 } catch (RemoteException remoteException) { 525 mCarSystemUIProxy = null; 526 throw new IllegalStateException("Linking to binder death failed for " 527 + "ICarSystemUIProxy, the System UI might already died", remoteException); 528 } 529 530 if (DBG) { 531 Slogf.d(TAG, "CarSystemUIProxy registered."); 532 } 533 534 int numCallbacks = mCarSystemUIProxyCallbacks.beginBroadcast(); 535 if (DBG) { 536 Slogf.d(TAG, "Broadcasting CarSystemUIProxy connected to %d callbacks", 537 numCallbacks); 538 } 539 for (int i = 0; i < numCallbacks; i++) { 540 try { 541 mCarSystemUIProxyCallbacks.getBroadcastItem(i).onConnected( 542 mCarSystemUIProxy); 543 } catch (RemoteException remoteException) { 544 Slogf.e(TAG, "Error dispatching onConnected", remoteException); 545 } 546 } 547 mCarSystemUIProxyCallbacks.finishBroadcast(); 548 } 549 } 550 551 @Override isCarSystemUIProxyRegistered()552 public boolean isCarSystemUIProxyRegistered() { 553 if (!isPlatformVersionAtLeastU()) { 554 return false; 555 } 556 synchronized (mLock) { 557 return mCarSystemUIProxy != null; 558 } 559 } 560 561 @Override addCarSystemUIProxyCallback(ICarSystemUIProxyCallback callback)562 public void addCarSystemUIProxyCallback(ICarSystemUIProxyCallback callback) { 563 if (DBG) { 564 Slogf.d(TAG, "addCarSystemUIProxyCallback %s", callback.toString()); 565 } 566 ensurePermission(PERMISSION_MANAGE_CAR_SYSTEM_UI); 567 synchronized (mLock) { 568 boolean alreadyExists = mCarSystemUIProxyCallbacks.unregister(callback); 569 mCarSystemUIProxyCallbacks.register(callback); 570 571 if (alreadyExists) { 572 // Do not trigger onConnected() if the callback already exists because it is either 573 // already called or will be called when the mCarSystemUIProxy is registered. 574 Slogf.d(TAG, "Callback exists already, skip calling onConnected()"); 575 return; 576 } 577 578 // Trigger onConnected() on the callback. 579 if (mCarSystemUIProxy == null) { 580 if (DBG) { 581 Slogf.d(TAG, "Callback stored locally, car system ui proxy not " 582 + "registered."); 583 } 584 return; 585 } 586 try { 587 callback.onConnected(mCarSystemUIProxy); 588 } catch (RemoteException remoteException) { 589 Slogf.e(TAG, "Error when dispatching onConnected", remoteException); 590 } 591 } 592 } 593 594 @Override removeCarSystemUIProxyCallback(ICarSystemUIProxyCallback callback)595 public void removeCarSystemUIProxyCallback(ICarSystemUIProxyCallback callback) { 596 if (DBG) { 597 Slogf.d(TAG, "removeCarSystemUIProxyCallback %s", callback.toString()); 598 } 599 ensurePermission(PERMISSION_MANAGE_CAR_SYSTEM_UI); 600 synchronized (mLock) { 601 mCarSystemUIProxyCallbacks.unregister(callback); 602 } 603 } 604 605 /** 606 * Attempts to restart a task. 607 * 608 * <p>Restarts a task by removing the task and sending an empty intent with flag 609 * {@link Intent#FLAG_ACTIVITY_NEW_TASK} to its root activity. If the task does not exist, do 610 * nothing. 611 * 612 * @param taskId id of task to be restarted. 613 */ restartTask(int taskId)614 public void restartTask(int taskId) { 615 TaskInfo task; 616 synchronized (mLock) { 617 task = mTasks.get(taskId); 618 } 619 if (task == null) { 620 Slogf.e(CarLog.TAG_AM, "Could not find root activity with task id " + taskId); 621 return; 622 } 623 624 Intent intent = (Intent) task.baseIntent.clone(); 625 // Remove the task the root activity is running in and start it in a new task. 626 // This effectively leads to restarting of the root activity and removal all the other 627 // activities in the task. 628 // FLAG_ACTIVITY_CLEAR_TASK was being used earlier, but it has the side effect where the 629 // last activity in the existing task is visible for a moment until the root activity is 630 // started again. 631 ActivityManagerHelper.removeTask(taskId); 632 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 633 634 int userId = TaskInfoHelper.getUserId(task); 635 if (Slogf.isLoggable(CarLog.TAG_AM, Log.INFO)) { 636 Slogf.i(CarLog.TAG_AM, "restarting root activity with user id " + userId); 637 } 638 mContext.startActivityAsUser(intent, UserHandle.of(userId)); 639 } 640 getTaskInfoForTopActivity(ComponentName activity)641 public TaskInfo getTaskInfoForTopActivity(ComponentName activity) { 642 synchronized (mLock) { 643 for (ActivityManager.RunningTaskInfo info : mTasks.values()) { 644 if (activity.equals(info.topActivity)) { 645 return info; 646 } 647 } 648 } 649 return null; 650 } 651 652 /** 653 * Block the current task: Launch new activity with given Intent and finish the current task. 654 * 655 * @param currentTask task to finish 656 * @param newActivityIntent Intent for new Activity 657 */ blockActivity(TaskInfo currentTask, Intent newActivityIntent)658 public void blockActivity(TaskInfo currentTask, Intent newActivityIntent) { 659 mHandler.post(() -> handleBlockActivity(currentTask, newActivityIntent)); 660 } 661 662 /** 663 * block the current task with the provided new activity. 664 */ handleBlockActivity(TaskInfo currentTask, Intent newActivityIntent)665 private void handleBlockActivity(TaskInfo currentTask, Intent newActivityIntent) { 666 int displayId = newActivityIntent.getIntExtra(BLOCKING_INTENT_EXTRA_DISPLAY_ID, 667 Display.DEFAULT_DISPLAY); 668 if (Slogf.isLoggable(CarLog.TAG_AM, Log.DEBUG)) { 669 Slogf.d(CarLog.TAG_AM, "Launching blocking activity on display: " + displayId); 670 } 671 672 ActivityOptions options = ActivityOptions.makeBasic(); 673 options.setLaunchDisplayId(displayId); 674 // Starts ABA as User 0 consistenly since the target apps can be any users (User 0 - 675 // UserPicker, Driver/Passegners - general NDO apps) and launching ABA as passengers 676 // have some issue (b/294447050). 677 mContext.startActivity(newActivityIntent, options.toBundle()); 678 // Now make stack with new activity focused. 679 findTaskAndGrantFocus(newActivityIntent.getComponent()); 680 } 681 findTaskAndGrantFocus(ComponentName activity)682 private void findTaskAndGrantFocus(ComponentName activity) { 683 TaskInfo taskInfo = getTaskInfoForTopActivity(activity); 684 if (taskInfo != null) { 685 ActivityManagerHelper.setFocusedRootTask(taskInfo.taskId); 686 return; 687 } 688 Slogf.i(CarLog.TAG_AM, "cannot give focus, cannot find Activity:" + activity); 689 } 690 691 @Override moveRootTaskToDisplay(int taskId, int displayId)692 public void moveRootTaskToDisplay(int taskId, int displayId) { 693 ensurePermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH); 694 if (!isPlatformVersionAtLeastU()) { 695 return; 696 } 697 698 // Calls moveRootTaskToDisplay() with the system uid. 699 long identity = Binder.clearCallingIdentity(); 700 try { 701 ActivityManagerHelper.moveRootTaskToDisplay(taskId, displayId); 702 } finally { 703 Binder.restoreCallingIdentity(identity); 704 } 705 } 706 707 @Override 708 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)709 public void dump(IndentingPrintWriter writer) { 710 writer.println("*CarActivityService*"); 711 synchronized (mLock) { 712 writer.println(" CarSystemUIProxy registered:"); 713 writer.println(" " + (mCarSystemUIProxy != null)); 714 writer.println(" Tasks:"); 715 for (ActivityManager.RunningTaskInfo taskInfo : mTasks.values()) { 716 writer.println(" " + TaskInfoHelper.toString(taskInfo)); 717 } 718 writer.println(" Surfaces: " + mTaskToSurfaceMap.toString()); 719 } 720 } 721 } 722