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