• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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