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