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