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