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 android.app.ActivityManager;
19 import android.app.ActivityManager.StackInfo;
20 import android.app.IActivityManager;
21 import android.app.IProcessObserver;
22 import android.app.TaskStackListener;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.os.Handler;
27 import android.os.HandlerThread;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.os.RemoteException;
31 import android.os.UserHandle;
32 import android.util.ArrayMap;
33 import android.util.ArraySet;
34 import android.util.Log;
35 import android.util.Pair;
36 import android.util.SparseArray;
37 
38 import java.io.PrintWriter;
39 import java.util.Arrays;
40 import java.util.LinkedList;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Objects;
44 import java.util.Set;
45 
46 /**
47  * Service to monitor AMS for new Activity or Service launching.
48  */
49 public class SystemActivityMonitoringService implements CarServiceBase {
50 
51     /**
52      * Container to hold info on top task in an Activity stack
53      */
54     public static class TopTaskInfoContainer {
55         public final ComponentName topActivity;
56         public final int taskId;
57         public final StackInfo stackInfo;
58 
TopTaskInfoContainer(ComponentName topActivity, int taskId, StackInfo stackInfo)59         private TopTaskInfoContainer(ComponentName topActivity, int taskId, StackInfo stackInfo) {
60             this.topActivity = topActivity;
61             this.taskId = taskId;
62             this.stackInfo = stackInfo;
63         }
64 
isMatching(TopTaskInfoContainer taskInfo)65         public boolean isMatching(TopTaskInfoContainer taskInfo) {
66             return taskInfo != null
67                     && Objects.equals(this.topActivity, taskInfo.topActivity)
68                     && this.taskId == taskInfo.taskId
69                     && this.stackInfo.userId == taskInfo.stackInfo.userId;
70         }
71 
72         @Override
toString()73         public String toString() {
74             return String.format(
75                     "TaskInfoContainer [topActivity=%s, taskId=%d, stackId=%d, userId=%d",
76                     topActivity, taskId, stackInfo.stackId, stackInfo.userId);
77         }
78     }
79 
80     public interface ActivityLaunchListener {
81         /**
82          * Notify launch of activity.
83          * @param topTask Task information for what is currently launched.
84          */
onActivityLaunch(TopTaskInfoContainer topTask)85         void onActivityLaunch(TopTaskInfoContainer topTask);
86     }
87 
88     private static final boolean DBG = false;
89 
90     private static final int NUM_MAX_TASK_TO_FETCH = 10;
91 
92     private final Context mContext;
93     private final IActivityManager mAm;
94     private final ProcessObserver mProcessObserver;
95     private final TaskListener mTaskListener;
96 
97     private final HandlerThread mMonitorHandlerThread;
98     private final ActivityMonitorHandler mHandler;
99 
100     /** K: stack id, V: top task */
101     private final SparseArray<TopTaskInfoContainer> mTopTasks = new SparseArray<>();
102     /** K: uid, V : list of pid */
103     private final Map<Integer, Set<Integer>> mForegroundUidPids = new ArrayMap<>();
104     private int mFocusedStackId = -1;
105 
106     /**
107      * Temporary container to dispatch tasks for onActivityLaunch. Only used in handler thread.
108      * can be accessed without lock. */
109     private final List<TopTaskInfoContainer> mTasksToDispatch = new LinkedList<>();
110     private ActivityLaunchListener mActivityLaunchListener;
111 
SystemActivityMonitoringService(Context context)112     public SystemActivityMonitoringService(Context context) {
113         mContext = context;
114         mMonitorHandlerThread = new HandlerThread(CarLog.TAG_AM);
115         mMonitorHandlerThread.start();
116         mHandler = new ActivityMonitorHandler(mMonitorHandlerThread.getLooper());
117         mProcessObserver = new ProcessObserver();
118         mTaskListener = new TaskListener();
119         mAm = ActivityManager.getService();
120         // Monitoring both listeners are necessary as there are cases where one listener cannot
121         // monitor activity change.
122         try {
123             mAm.registerProcessObserver(mProcessObserver);
124             mAm.registerTaskStackListener(mTaskListener);
125         } catch (RemoteException e) {
126             Log.e(CarLog.TAG_AM, "cannot register activity monitoring", e);
127             throw new RuntimeException(e);
128         }
129         updateTasks();
130     }
131 
132     @Override
init()133     public void init() {
134     }
135 
136     @Override
release()137     public void release() {
138     }
139 
140     @Override
dump(PrintWriter writer)141     public void dump(PrintWriter writer) {
142         writer.println("*SystemActivityMonitoringService*");
143         writer.println(" Top Tasks:");
144         synchronized (this) {
145             for (int i = 0; i < mTopTasks.size(); i++) {
146                 TopTaskInfoContainer info = mTopTasks.valueAt(i);
147                 if (info != null) {
148                     writer.println(info);
149                 }
150             }
151             writer.println(" Foregroud uid-pids:");
152             for (Integer key : mForegroundUidPids.keySet()) {
153                 Set<Integer> pids = mForegroundUidPids.get(key);
154                 if (pids == null) {
155                     continue;
156                 }
157                 writer.println("uid:" + key + ", pids:" + Arrays.toString(pids.toArray()));
158             }
159             writer.println(" focused stack:" + mFocusedStackId);
160         }
161     }
162 
163     /**
164      * Block the current task: Launch new activity with given Intent and finish the current task.
165      * @param currentTask task to finish
166      * @param newActivityIntent Intent for new Activity
167      */
blockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent)168     public void blockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent) {
169         mHandler.requestBlockActivity(currentTask, newActivityIntent);
170     }
171 
getTopTasks()172     public List<TopTaskInfoContainer> getTopTasks() {
173         LinkedList<TopTaskInfoContainer> tasks = new LinkedList<>();
174         synchronized (this) {
175             for (int i = 0; i < mTopTasks.size(); i++) {
176                 tasks.add(mTopTasks.valueAt(i));
177             }
178         }
179         return tasks;
180     }
181 
isInForeground(int pid, int uid)182     public boolean isInForeground(int pid, int uid) {
183         synchronized (this) {
184             Set<Integer> pids = mForegroundUidPids.get(uid);
185             if (pids == null) {
186                 return false;
187             }
188             if (pids.contains(pid)) {
189                 return true;
190             }
191         }
192         return false;
193     }
194 
registerActivityLaunchListener(ActivityLaunchListener listener)195     public void registerActivityLaunchListener(ActivityLaunchListener listener) {
196         synchronized (this) {
197             mActivityLaunchListener = listener;
198         }
199     }
200 
updateTasks()201     private void updateTasks() {
202         List<StackInfo> infos;
203         try {
204             infos = mAm.getAllStackInfos();
205         } catch (RemoteException e) {
206             Log.e(CarLog.TAG_AM, "cannot getTasks", e);
207             return;
208         }
209         int focusedStackId = -1;
210         try {
211             focusedStackId = mAm.getFocusedStackId();
212         } catch (RemoteException e) {
213             Log.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
214             return;
215         }
216         mTasksToDispatch.clear();
217         ActivityLaunchListener listener;
218         synchronized (this) {
219             listener = mActivityLaunchListener;
220             for (StackInfo info : infos) {
221                 int stackId = info.stackId;
222                 if (info.taskNames.length == 0 || !info.visible) { // empty stack or not shown
223                     mTopTasks.remove(stackId);
224                     continue;
225                 }
226                 TopTaskInfoContainer newTopTaskInfo = new TopTaskInfoContainer(
227                         info.topActivity, info.taskIds[info.taskIds.length - 1], info);
228                 TopTaskInfoContainer currentTopTaskInfo = mTopTasks.get(stackId);
229 
230                 // if a new task is added to stack or focused stack changes, should notify
231                 if (currentTopTaskInfo == null ||
232                         !currentTopTaskInfo.isMatching(newTopTaskInfo) ||
233                         (focusedStackId == stackId && focusedStackId != mFocusedStackId)) {
234                     mTopTasks.put(stackId, newTopTaskInfo);
235                     mTasksToDispatch.add(newTopTaskInfo);
236                     if (DBG) {
237                         Log.i(CarLog.TAG_AM, "New top task: " + newTopTaskInfo);
238                     }
239                 }
240             }
241             mFocusedStackId = focusedStackId;
242         }
243         if (listener != null) {
244             for (TopTaskInfoContainer topTask : mTasksToDispatch) {
245                 if (DBG) {
246                     Log.i(CarLog.TAG_AM, "activity launched:" + topTask.toString());
247                 }
248                 listener.onActivityLaunch(topTask);
249             }
250         }
251     }
252 
getFocusedStackForTopActivity(ComponentName activity)253     public StackInfo getFocusedStackForTopActivity(ComponentName activity) {
254         int focusedStackId = -1;
255         try {
256             focusedStackId = mAm.getFocusedStackId();
257         } catch (RemoteException e) {
258             Log.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
259             return null;
260         }
261         StackInfo focusedStack;
262         try {
263             focusedStack = mAm.getStackInfo(focusedStackId);
264         } catch (RemoteException e) {
265             Log.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
266             return null;
267         }
268         if (focusedStack.taskNames.length == 0) { // nothing in focused stack
269             return null;
270         }
271         ComponentName topActivity = ComponentName.unflattenFromString(
272                 focusedStack.taskNames[focusedStack.taskNames.length - 1]);
273         if (topActivity.equals(activity)) {
274             return focusedStack;
275         } else {
276             return null;
277         }
278     }
279 
handleForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities)280     private void handleForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
281         synchronized (this) {
282             if (foregroundActivities) {
283                 Set<Integer> pids = mForegroundUidPids.get(uid);
284                 if (pids == null) {
285                     pids = new ArraySet<Integer>();
286                     mForegroundUidPids.put(uid, pids);
287                 }
288                 pids.add(pid);
289             } else {
290                 doHandlePidGoneLocked(pid, uid);
291             }
292         }
293     }
294 
handleProcessDied(int pid, int uid)295     private void handleProcessDied(int pid, int uid) {
296         synchronized (this) {
297             doHandlePidGoneLocked(pid, uid);
298         }
299     }
300 
doHandlePidGoneLocked(int pid, int uid)301     private void doHandlePidGoneLocked(int pid, int uid) {
302         Set<Integer> pids = mForegroundUidPids.get(uid);
303         if (pids != null) {
304             pids.remove(pid);
305             if (pids.isEmpty()) {
306                 mForegroundUidPids.remove(uid);
307             }
308         }
309     }
310 
handleBlockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent)311     private void handleBlockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent) {
312         Log.i(CarLog.TAG_AM, String.format("stopping activity %s with taskid:%d",
313                 currentTask.topActivity, currentTask.taskId));
314         // Put launcher in the activity stack, so that we have something safe to show after the
315         // block activity finishes.
316         Intent launcherIntent = new Intent();
317         launcherIntent.setComponent(ComponentName.unflattenFromString(
318                 mContext.getString(R.string.defaultHomeActivity)));
319         mContext.startActivity(launcherIntent);
320 
321         newActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
322 
323         mContext.startActivityAsUser(newActivityIntent,
324                 new UserHandle(currentTask.stackInfo.userId));
325         // now make stack with new activity focused.
326         findTaskAndGrantFocus(newActivityIntent.getComponent());
327         try {
328             mAm.removeTask(currentTask.taskId);
329         } catch (RemoteException e) {
330             Log.w(CarLog.TAG_AM, "cannot remove task:" + currentTask.taskId, e);
331         }
332     }
333 
findTaskAndGrantFocus(ComponentName activity)334     private void findTaskAndGrantFocus(ComponentName activity) {
335         List<StackInfo> infos;
336         try {
337             infos = mAm.getAllStackInfos();
338         } catch (RemoteException e) {
339             Log.e(CarLog.TAG_AM, "cannot getTasks", e);
340             return;
341         }
342         for (StackInfo info : infos) {
343             if (info.taskNames.length == 0) {
344                 continue;
345             }
346             ComponentName topActivity = ComponentName.unflattenFromString(
347                     info.taskNames[info.taskNames.length - 1]);
348             if (activity.equals(topActivity)) {
349                 try {
350                     mAm.setFocusedStack(info.stackId);
351                 } catch (RemoteException e) {
352                     Log.e(CarLog.TAG_AM, "cannot setFocusedStack to stack:" + info.stackId, e);
353                 }
354                 return;
355             }
356         }
357         Log.i(CarLog.TAG_AM, "cannot give focus, cannot find Activity:" + activity);
358     }
359 
360     private class ProcessObserver extends IProcessObserver.Stub {
361         @Override
onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities)362         public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
363             if (DBG) {
364                 Log.i(CarLog.TAG_AM,
365                         String.format("onForegroundActivitiesChanged uid %d pid %d fg %b",
366                     uid, pid, foregroundActivities));
367             }
368             mHandler.requestForegroundActivitiesChanged(pid, uid, foregroundActivities);
369         }
370 
371         @Override
onProcessDied(int pid, int uid)372         public void onProcessDied(int pid, int uid) {
373             mHandler.requestProcessDied(pid, uid);
374         }
375     }
376 
377     private class TaskListener extends TaskStackListener {
378         @Override
onTaskStackChanged()379         public void onTaskStackChanged() {
380             if (DBG) {
381                 Log.i(CarLog.TAG_AM, "onTaskStackChanged");
382             }
383             mHandler.requestUpdatingTask();
384         }
385     }
386 
387     private class ActivityMonitorHandler extends Handler {
388         private static final int MSG_UPDATE_TASKS = 0;
389         private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 1;
390         private static final int MSG_PROCESS_DIED = 2;
391         private static final int MSG_BLOCK_ACTIVITY = 3;
392 
ActivityMonitorHandler(Looper looper)393         private ActivityMonitorHandler(Looper looper) {
394             super(looper);
395         }
396 
requestUpdatingTask()397         private void requestUpdatingTask() {
398             Message msg = obtainMessage(MSG_UPDATE_TASKS);
399             sendMessage(msg);
400         }
401 
requestForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities)402         private void requestForegroundActivitiesChanged(int pid, int uid,
403                 boolean foregroundActivities) {
404             Message msg = obtainMessage(MSG_FOREGROUND_ACTIVITIES_CHANGED, pid, uid,
405                     Boolean.valueOf(foregroundActivities));
406             sendMessage(msg);
407         }
408 
requestProcessDied(int pid, int uid)409         private void requestProcessDied(int pid, int uid) {
410             Message msg = obtainMessage(MSG_PROCESS_DIED, pid, uid);
411             sendMessage(msg);
412         }
413 
requestBlockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent)414         private void requestBlockActivity(TopTaskInfoContainer currentTask,
415                 Intent newActivityIntent) {
416             Message msg = obtainMessage(MSG_BLOCK_ACTIVITY,
417                     new Pair<TopTaskInfoContainer, Intent>(currentTask, newActivityIntent));
418             sendMessage(msg);
419         }
420 
421         @Override
handleMessage(Message msg)422         public void handleMessage(Message msg) {
423             switch (msg.what) {
424                 case MSG_UPDATE_TASKS:
425                     updateTasks();
426                     break;
427                 case MSG_FOREGROUND_ACTIVITIES_CHANGED:
428                     handleForegroundActivitiesChanged(msg.arg1, msg.arg2, (Boolean) msg.obj);
429                     updateTasks();
430                     break;
431                 case MSG_PROCESS_DIED:
432                     handleProcessDied(msg.arg1, msg.arg2);
433                     break;
434                 case MSG_BLOCK_ACTIVITY:
435                     Pair<TopTaskInfoContainer, Intent> pair =
436                         (Pair<TopTaskInfoContainer, Intent>) msg.obj;
437                     handleBlockActivity(pair.first, pair.second);
438                     break;
439             }
440         }
441     }
442 }
443