1 /*
2  * Copyright (C) 2016 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 package com.android.car;
17 
18 import static com.android.car.pm.CarPackageManagerService.BLOCKING_INTENT_EXTRA_DISPLAY_ID;
19 
20 import android.app.ActivityManager;
21 import android.app.ActivityOptions;
22 import android.app.ActivityTaskManager.RootTaskInfo;
23 import android.app.IActivityManager;
24 import android.app.IProcessObserver;
25 import android.app.TaskStackListener;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.os.Handler;
30 import android.os.HandlerThread;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.os.RemoteException;
34 import android.os.UserHandle;
35 import android.util.ArrayMap;
36 import android.util.ArraySet;
37 import android.util.IndentingPrintWriter;
38 import android.util.Log;
39 import android.util.Pair;
40 import android.util.Slog;
41 import android.util.SparseArray;
42 import android.view.Display;
43 
44 import com.android.internal.annotations.GuardedBy;
45 
46 import java.lang.ref.WeakReference;
47 import java.util.Arrays;
48 import java.util.LinkedList;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.Objects;
52 import java.util.Set;
53 
54 /**
55  * Service to monitor AMS for new Activity or Service launching.
56  */
57 public class SystemActivityMonitoringService implements CarServiceBase {
58 
59     /**
60      * Container to hold info on top task in an Activity stack
61      */
62     public static class TopTaskInfoContainer {
63         public final ComponentName topActivity;
64         public final int taskId;
65         public final int displayId;
66         public final int position;
67         public final RootTaskInfo taskInfo;
68 
TopTaskInfoContainer(ComponentName topActivity, int taskId, int displayId, int position, RootTaskInfo taskInfo)69         private TopTaskInfoContainer(ComponentName topActivity, int taskId,
70                 int displayId, int position, RootTaskInfo taskInfo) {
71             this.topActivity = topActivity;
72             this.taskId = taskId;
73             this.displayId = displayId;
74             this.position = position;
75             this.taskInfo = taskInfo;
76         }
77 
isMatching(TopTaskInfoContainer taskInfo)78         public boolean isMatching(TopTaskInfoContainer taskInfo) {
79             return taskInfo != null
80                     && Objects.equals(this.topActivity, taskInfo.topActivity)
81                     && this.taskId == taskInfo.taskId
82                     && this.displayId == taskInfo.displayId
83                     && this.position == taskInfo.position
84                     && this.taskInfo.userId == taskInfo.taskInfo.userId;
85         }
86 
87         @Override
toString()88         public String toString() {
89             return String.format(
90                     "TaskInfoContainer [topActivity=%s, taskId=%d, stackId=%d, userId=%d, "
91                     + "displayId=%d, position=%d",
92                   topActivity, taskId, taskInfo.taskId, taskInfo.userId, displayId, position);
93         }
94     }
95 
96     public interface ActivityLaunchListener {
97         /**
98          * Notify launch of activity.
99          * @param topTask Task information for what is currently launched.
100          */
onActivityLaunch(TopTaskInfoContainer topTask)101         void onActivityLaunch(TopTaskInfoContainer topTask);
102     }
103 
104     private static final int INVALID_STACK_ID = -1;
105     private final Context mContext;
106     private final IActivityManager mAm;
107     private final ProcessObserver mProcessObserver;
108     private final TaskListener mTaskListener;
109 
110     private final HandlerThread mMonitorHandlerThread = CarServiceUtils.getHandlerThread(
111             getClass().getSimpleName());
112     private final ActivityMonitorHandler mHandler = new ActivityMonitorHandler(
113             mMonitorHandlerThread.getLooper(), this);
114 
115     private final Object mLock = new Object();
116 
117     /** K: display id, V: top task */
118     @GuardedBy("mLock")
119     private final SparseArray<TopTaskInfoContainer> mTopTasks = new SparseArray<>();
120     /** K: uid, V : list of pid */
121     @GuardedBy("mLock")
122     private final Map<Integer, Set<Integer>> mForegroundUidPids = new ArrayMap<>();
123     @GuardedBy("mLock")
124     private ActivityLaunchListener mActivityLaunchListener;
125 
SystemActivityMonitoringService(Context context)126     public SystemActivityMonitoringService(Context context) {
127         mContext = context;
128         mProcessObserver = new ProcessObserver();
129         mTaskListener = new TaskListener();
130         mAm = ActivityManager.getService();
131     }
132 
133     @Override
init()134     public void init() {
135         // Monitoring both listeners are necessary as there are cases where one listener cannot
136         // monitor activity change.
137         try {
138             mAm.registerProcessObserver(mProcessObserver);
139             mAm.registerTaskStackListener(mTaskListener);
140         } catch (RemoteException e) {
141             Slog.e(CarLog.TAG_AM, "cannot register activity monitoring", e);
142             throw new RuntimeException(e);
143         }
144         updateTasks();
145     }
146 
147     @Override
release()148     public void release() {
149         try {
150             mAm.unregisterProcessObserver(mProcessObserver);
151             mAm.unregisterTaskStackListener(mTaskListener);
152         } catch (RemoteException e) {
153             Slog.e(CarLog.TAG_AM, "Failed to unregister listeners", e);
154         }
155     }
156 
157     @Override
dump(IndentingPrintWriter writer)158     public void dump(IndentingPrintWriter writer) {
159         writer.println("*SystemActivityMonitoringService*");
160         writer.println(" Top Tasks per display:");
161         synchronized (mLock) {
162             for (int i = 0; i < mTopTasks.size(); i++) {
163                 int displayId = mTopTasks.keyAt(i);
164                 TopTaskInfoContainer info = mTopTasks.valueAt(i);
165                 if (info != null) {
166                     writer.println("display id " + displayId + ": " + info);
167                 }
168             }
169             writer.println(" Foreground uid-pids:");
170             for (Integer key : mForegroundUidPids.keySet()) {
171                 Set<Integer> pids = mForegroundUidPids.get(key);
172                 if (pids == null) {
173                     continue;
174                 }
175                 writer.println("uid:" + key + ", pids:" + Arrays.toString(pids.toArray()));
176             }
177         }
178     }
179 
180     /**
181      * Block the current task: Launch new activity with given Intent and finish the current task.
182      * @param currentTask task to finish
183      * @param newActivityIntent Intent for new Activity
184      */
blockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent)185     public void blockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent) {
186         mHandler.requestBlockActivity(currentTask, newActivityIntent);
187     }
188 
getTopTasks()189     public List<TopTaskInfoContainer> getTopTasks() {
190         LinkedList<TopTaskInfoContainer> tasks = new LinkedList<>();
191         synchronized (mLock) {
192             for (int i = 0; i < mTopTasks.size(); i++) {
193                 TopTaskInfoContainer topTask = mTopTasks.valueAt(i);
194                 if (topTask == null) {
195                     Slog.e(CarLog.TAG_AM, "Top tasks contains null. Full content is: "
196                             + mTopTasks.toString());
197                     continue;
198                 }
199                 tasks.add(topTask);
200             }
201         }
202         return tasks;
203     }
204 
isInForeground(int pid, int uid)205     public boolean isInForeground(int pid, int uid) {
206         synchronized (mLock) {
207             Set<Integer> pids = mForegroundUidPids.get(uid);
208             if (pids == null) {
209                 return false;
210             }
211             if (pids.contains(pid)) {
212                 return true;
213             }
214         }
215         return false;
216     }
217 
218     /**
219      * Attempts to restart a task.
220      *
221      * <p>Restarts a task by sending an empty intent with flag
222      * {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} to its root activity. If the task does not exist,
223      * do nothing.
224      *
225      * @param taskId id of task to be restarted.
226      */
restartTask(int taskId)227     public void restartTask(int taskId) {
228         String rootActivityName = null;
229         int userId = 0;
230         try {
231             findRootActivityName:
232             for (RootTaskInfo info : mAm.getAllRootTaskInfos()) {
233                 for (int i = 0; i < info.childTaskIds.length; i++) {
234                     if (info.childTaskIds[i] == taskId) {
235                         rootActivityName = info.childTaskNames[i];
236                         userId = info.userId;
237                         if (Log.isLoggable(CarLog.TAG_AM, Log.DEBUG)) {
238                             Slog.d(CarLog.TAG_AM, "Root activity is " + rootActivityName);
239                             Slog.d(CarLog.TAG_AM, "User id is " + userId);
240                         }
241                         // Break out of nested loop.
242                         break findRootActivityName;
243                     }
244                 }
245             }
246         } catch (RemoteException e) {
247             Slog.e(CarLog.TAG_AM, "Could not get stack info", e);
248             return;
249         }
250 
251         if (rootActivityName == null) {
252             Slog.e(CarLog.TAG_AM, "Could not find root activity with task id " + taskId);
253             return;
254         }
255 
256         Intent rootActivityIntent = new Intent();
257         rootActivityIntent.setComponent(ComponentName.unflattenFromString(rootActivityName));
258         // Clear the task the root activity is running in and start it in a new task.
259         // Effectively restart root activity.
260         rootActivityIntent.addFlags(
261                 Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
262 
263         if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
264             Slog.i(CarLog.TAG_AM, "restarting root activity with user id " + userId);
265         }
266         mContext.startActivityAsUser(rootActivityIntent, new UserHandle(userId));
267     }
268 
registerActivityLaunchListener(ActivityLaunchListener listener)269     public void registerActivityLaunchListener(ActivityLaunchListener listener) {
270         synchronized (mLock) {
271             mActivityLaunchListener = listener;
272         }
273     }
274 
updateTasks()275     private void updateTasks() {
276         List<RootTaskInfo> infos;
277         try {
278             infos = mAm.getAllRootTaskInfos();
279         } catch (RemoteException e) {
280             Slog.e(CarLog.TAG_AM, "cannot getTasks", e);
281             return;
282         }
283 
284         if (infos == null) {
285             return;
286         }
287 
288         int focusedStackId = INVALID_STACK_ID;
289         try {
290             // TODO(b/66955160): Someone on the Auto-team should probably re-work the code in the
291             // synchronized block below based on this new API.
292             final RootTaskInfo focusedTaskInfo = mAm.getFocusedRootTaskInfo();
293             if (focusedTaskInfo != null) {
294                 focusedStackId = focusedTaskInfo.taskId;
295             }
296         } catch (RemoteException e) {
297             Slog.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
298             return;
299         }
300 
301         SparseArray<TopTaskInfoContainer> topTasks = new SparseArray<>();
302         ActivityLaunchListener listener;
303         synchronized (mLock) {
304             mTopTasks.clear();
305             listener = mActivityLaunchListener;
306 
307             for (RootTaskInfo info : infos) {
308                 int displayId = info.displayId;
309                 if (info.childTaskNames.length == 0
310                         || !info.visible) { // empty stack or not shown
311                     continue;
312                 }
313                 TopTaskInfoContainer newTopTaskInfo = new TopTaskInfoContainer(
314                         info.topActivity, info.childTaskIds[info.childTaskIds.length - 1],
315                         info.displayId, info.position, info);
316                 TopTaskInfoContainer currentTopTaskInfo = topTasks.get(displayId);
317 
318                 if (currentTopTaskInfo == null ||
319                         newTopTaskInfo.position > currentTopTaskInfo.position) {
320                     topTasks.put(displayId, newTopTaskInfo);
321                     if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
322                         Slog.i(CarLog.TAG_AM, "Updating top task to: " + newTopTaskInfo);
323                     }
324                 }
325             }
326             // Assuming displays remains the same.
327             for (int i = 0; i < topTasks.size(); i++) {
328                 TopTaskInfoContainer topTask = topTasks.valueAt(i);
329 
330                 int displayId = topTasks.keyAt(i);
331                 mTopTasks.put(displayId, topTask);
332             }
333         }
334         if (listener != null) {
335             for (int i = 0; i < topTasks.size(); i++) {
336                 TopTaskInfoContainer topTask = topTasks.valueAt(i);
337 
338                 if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
339                     Slog.i(CarLog.TAG_AM, "Notifying about top task: " + topTask.toString());
340                 }
341                 listener.onActivityLaunch(topTask);
342             }
343         }
344     }
345 
getFocusedStackForTopActivity(ComponentName activity)346     public RootTaskInfo getFocusedStackForTopActivity(ComponentName activity) {
347         RootTaskInfo focusedStack;
348         try {
349             focusedStack = mAm.getFocusedRootTaskInfo();
350         } catch (RemoteException e) {
351             Slog.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
352             return null;
353         }
354         if (focusedStack.childTaskNames.length == 0) { // nothing in focused stack
355             return null;
356         }
357         ComponentName topActivity = ComponentName.unflattenFromString(
358                 focusedStack.childTaskNames[focusedStack.childTaskNames.length - 1]);
359         if (topActivity.equals(activity)) {
360             return focusedStack;
361         } else {
362             return null;
363         }
364     }
365 
handleForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities)366     private void handleForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
367         synchronized (mLock) {
368             if (foregroundActivities) {
369                 Set<Integer> pids = mForegroundUidPids.get(uid);
370                 if (pids == null) {
371                     pids = new ArraySet<Integer>();
372                     mForegroundUidPids.put(uid, pids);
373                 }
374                 pids.add(pid);
375             } else {
376                 doHandlePidGoneLocked(pid, uid);
377             }
378         }
379     }
380 
handleProcessDied(int pid, int uid)381     private void handleProcessDied(int pid, int uid) {
382         synchronized (mLock) {
383             doHandlePidGoneLocked(pid, uid);
384         }
385     }
386 
doHandlePidGoneLocked(int pid, int uid)387     private void doHandlePidGoneLocked(int pid, int uid) {
388         Set<Integer> pids = mForegroundUidPids.get(uid);
389         if (pids != null) {
390             pids.remove(pid);
391             if (pids.isEmpty()) {
392                 mForegroundUidPids.remove(uid);
393             }
394         }
395     }
396 
397     /**
398      * block the current task with the provided new activity.
399      */
handleBlockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent)400     private void handleBlockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent) {
401         int displayId = newActivityIntent.getIntExtra(BLOCKING_INTENT_EXTRA_DISPLAY_ID,
402                 Display.DEFAULT_DISPLAY);
403         if (Log.isLoggable(CarLog.TAG_AM, Log.DEBUG)) {
404             Slog.d(CarLog.TAG_AM, "Launching blocking activity on display: " + displayId);
405         }
406 
407         ActivityOptions options = ActivityOptions.makeBasic();
408         options.setLaunchDisplayId(displayId);
409         mContext.startActivityAsUser(newActivityIntent, options.toBundle(),
410                 new UserHandle(currentTask.taskInfo.userId));
411         // Now make stack with new activity focused.
412         findTaskAndGrantFocus(newActivityIntent.getComponent());
413     }
414 
findTaskAndGrantFocus(ComponentName activity)415     private void findTaskAndGrantFocus(ComponentName activity) {
416         List<RootTaskInfo> infos;
417         try {
418             infos = mAm.getAllRootTaskInfos();
419         } catch (RemoteException e) {
420             Slog.e(CarLog.TAG_AM, "cannot getTasks", e);
421             return;
422         }
423         for (RootTaskInfo info : infos) {
424             if (info.childTaskNames.length == 0) {
425                 continue;
426             }
427             ComponentName topActivity = ComponentName.unflattenFromString(
428                     info.childTaskNames[info.childTaskNames.length - 1]);
429             if (activity.equals(topActivity)) {
430                 try {
431                     mAm.setFocusedRootTask(info.taskId);
432                 } catch (RemoteException e) {
433                     Slog.e(CarLog.TAG_AM, "cannot setFocusedRootTask to task:" + info.taskId, e);
434                 }
435                 return;
436             }
437         }
438         Slog.i(CarLog.TAG_AM, "cannot give focus, cannot find Activity:" + activity);
439     }
440 
441     private class ProcessObserver extends IProcessObserver.Stub {
442         @Override
onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities)443         public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
444             if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
445                 Slog.i(CarLog.TAG_AM,
446                         String.format("onForegroundActivitiesChanged uid %d pid %d fg %b",
447                     uid, pid, foregroundActivities));
448             }
449             mHandler.requestForegroundActivitiesChanged(pid, uid, foregroundActivities);
450         }
451 
452         @Override
onForegroundServicesChanged(int pid, int uid, int fgServiceTypes)453         public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) {
454         }
455 
456         @Override
onProcessDied(int pid, int uid)457         public void onProcessDied(int pid, int uid) {
458             mHandler.requestProcessDied(pid, uid);
459         }
460     }
461 
462     private class TaskListener extends TaskStackListener {
463         @Override
onTaskStackChanged()464         public void onTaskStackChanged() {
465             if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
466                 Slog.i(CarLog.TAG_AM, "onTaskStackChanged");
467             }
468             mHandler.requestUpdatingTask();
469         }
470     }
471 
472     private static final class ActivityMonitorHandler extends Handler {
473         private  static final String TAG = ActivityMonitorHandler.class.getSimpleName();
474 
475         private static final int MSG_UPDATE_TASKS = 0;
476         private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 1;
477         private static final int MSG_PROCESS_DIED = 2;
478         private static final int MSG_BLOCK_ACTIVITY = 3;
479 
480         private final WeakReference<SystemActivityMonitoringService> mService;
481 
ActivityMonitorHandler(Looper looper, SystemActivityMonitoringService service)482         private ActivityMonitorHandler(Looper looper, SystemActivityMonitoringService service) {
483             super(looper);
484             mService = new WeakReference<SystemActivityMonitoringService>(service);
485         }
486 
requestUpdatingTask()487         private void requestUpdatingTask() {
488             Message msg = obtainMessage(MSG_UPDATE_TASKS);
489             sendMessage(msg);
490         }
491 
requestForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities)492         private void requestForegroundActivitiesChanged(int pid, int uid,
493                 boolean foregroundActivities) {
494             Message msg = obtainMessage(MSG_FOREGROUND_ACTIVITIES_CHANGED, pid, uid,
495                     Boolean.valueOf(foregroundActivities));
496             sendMessage(msg);
497         }
498 
requestProcessDied(int pid, int uid)499         private void requestProcessDied(int pid, int uid) {
500             Message msg = obtainMessage(MSG_PROCESS_DIED, pid, uid);
501             sendMessage(msg);
502         }
503 
requestBlockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent)504         private void requestBlockActivity(TopTaskInfoContainer currentTask,
505                 Intent newActivityIntent) {
506             Message msg = obtainMessage(MSG_BLOCK_ACTIVITY,
507                     new Pair<TopTaskInfoContainer, Intent>(currentTask, newActivityIntent));
508             sendMessage(msg);
509         }
510 
511         @Override
handleMessage(Message msg)512         public void handleMessage(Message msg) {
513             SystemActivityMonitoringService service = mService.get();
514             if (service == null) {
515                 Slog.i(TAG, "handleMessage null service");
516                 return;
517             }
518             switch (msg.what) {
519                 case MSG_UPDATE_TASKS:
520                     service.updateTasks();
521                     break;
522                 case MSG_FOREGROUND_ACTIVITIES_CHANGED:
523                     service.handleForegroundActivitiesChanged(msg.arg1, msg.arg2,
524                             (Boolean) msg.obj);
525                     service.updateTasks();
526                     break;
527                 case MSG_PROCESS_DIED:
528                     service.handleProcessDied(msg.arg1, msg.arg2);
529                     break;
530                 case MSG_BLOCK_ACTIVITY:
531                     Pair<TopTaskInfoContainer, Intent> pair =
532                         (Pair<TopTaskInfoContainer, Intent>) msg.obj;
533                     service.handleBlockActivity(pair.first, pair.second);
534                     break;
535             }
536         }
537     }
538 }
539