blob: c2abdc69264e3f5c1965342ee590398bf75b7327 [file] [log] [blame]
Keun-young Park4727da32016-05-31 10:00:51 -07001/*
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 */
16package com.android.car;
17
Sudheer Shankaa4f49aa2016-11-10 16:39:13 -080018import android.app.ActivityManager;
Keun-young Park4727da32016-05-31 10:00:51 -070019import android.app.ActivityManager.StackInfo;
Yao, Yuxingcfcc59f2019-03-05 16:35:47 -080020import android.app.ActivityOptions;
Keun-young Park4727da32016-05-31 10:00:51 -070021import android.app.IActivityManager;
22import android.app.IProcessObserver;
Yorke Leec1265de2016-10-27 11:25:25 -070023import android.app.TaskStackListener;
Keun-young Park4727da32016-05-31 10:00:51 -070024import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.os.Handler;
28import android.os.HandlerThread;
29import android.os.Looper;
30import android.os.Message;
31import android.os.RemoteException;
32import android.os.UserHandle;
Yao Chened8609e2016-10-07 09:49:13 -070033import android.util.ArrayMap;
Keun-young Park4727da32016-05-31 10:00:51 -070034import android.util.ArraySet;
35import android.util.Log;
36import android.util.Pair;
37import android.util.SparseArray;
Yao, Yuxingcfcc59f2019-03-05 16:35:47 -080038import android.view.Display;
Keun-young Park4727da32016-05-31 10:00:51 -070039
40import java.io.PrintWriter;
41import java.util.Arrays;
Keun-young Park4727da32016-05-31 10:00:51 -070042import java.util.LinkedList;
43import java.util.List;
44import java.util.Map;
Yao Chened8609e2016-10-07 09:49:13 -070045import java.util.Objects;
Keun-young Park4727da32016-05-31 10:00:51 -070046import java.util.Set;
47
48/**
49 * Service to monitor AMS for new Activity or Service launching.
50 */
51public class SystemActivityMonitoringService implements CarServiceBase {
52
53 /**
54 * Container to hold info on top task in an Activity stack
55 */
56 public static class TopTaskInfoContainer {
57 public final ComponentName topActivity;
58 public final int taskId;
Yao, Yuxingcfcc59f2019-03-05 16:35:47 -080059 public final int displayId;
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -080060 public final int position;
Keun-young Park4727da32016-05-31 10:00:51 -070061 public final StackInfo stackInfo;
62
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -080063 private TopTaskInfoContainer(ComponentName topActivity, int taskId,
64 int displayId, int position, StackInfo stackInfo) {
Keun-young Park4727da32016-05-31 10:00:51 -070065 this.topActivity = topActivity;
66 this.taskId = taskId;
Yao, Yuxingcfcc59f2019-03-05 16:35:47 -080067 this.displayId = displayId;
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -080068 this.position = position;
Keun-young Park4727da32016-05-31 10:00:51 -070069 this.stackInfo = stackInfo;
70 }
71
Yao Chened8609e2016-10-07 09:49:13 -070072 public boolean isMatching(TopTaskInfoContainer taskInfo) {
73 return taskInfo != null
74 && Objects.equals(this.topActivity, taskInfo.topActivity)
75 && this.taskId == taskInfo.taskId
Yao, Yuxingcfcc59f2019-03-05 16:35:47 -080076 && this.displayId == taskInfo.displayId
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -080077 && this.position == taskInfo.position
Yao Chened8609e2016-10-07 09:49:13 -070078 && this.stackInfo.userId == taskInfo.stackInfo.userId;
Keun-young Park4727da32016-05-31 10:00:51 -070079 }
80
81 @Override
82 public String toString() {
83 return String.format(
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -080084 "TaskInfoContainer [topActivity=%s, taskId=%d, stackId=%d, userId=%d, "
85 + "displayId=%d, position=%d",
86 topActivity, taskId, stackInfo.stackId, stackInfo.userId, displayId, position);
Keun-young Park4727da32016-05-31 10:00:51 -070087 }
88 }
89
90 public interface ActivityLaunchListener {
91 /**
92 * Notify launch of activity.
93 * @param topTask Task information for what is currently launched.
94 */
95 void onActivityLaunch(TopTaskInfoContainer topTask);
96 }
97
Michal Palczewski7684dbb2018-11-13 18:36:06 -080098 private static final int INVALID_STACK_ID = -1;
Keun-young Park4727da32016-05-31 10:00:51 -070099 private final Context mContext;
100 private final IActivityManager mAm;
101 private final ProcessObserver mProcessObserver;
102 private final TaskListener mTaskListener;
103
104 private final HandlerThread mMonitorHandlerThread;
105 private final ActivityMonitorHandler mHandler;
106
107 /** K: stack id, V: top task */
108 private final SparseArray<TopTaskInfoContainer> mTopTasks = new SparseArray<>();
109 /** K: uid, V : list of pid */
Yao Chened8609e2016-10-07 09:49:13 -0700110 private final Map<Integer, Set<Integer>> mForegroundUidPids = new ArrayMap<>();
Michal Palczewski7684dbb2018-11-13 18:36:06 -0800111 private int mFocusedStackId = INVALID_STACK_ID;
Keun-young Park4727da32016-05-31 10:00:51 -0700112
113 /**
114 * Temporary container to dispatch tasks for onActivityLaunch. Only used in handler thread.
115 * can be accessed without lock. */
116 private final List<TopTaskInfoContainer> mTasksToDispatch = new LinkedList<>();
117 private ActivityLaunchListener mActivityLaunchListener;
118
119 public SystemActivityMonitoringService(Context context) {
120 mContext = context;
121 mMonitorHandlerThread = new HandlerThread(CarLog.TAG_AM);
122 mMonitorHandlerThread.start();
123 mHandler = new ActivityMonitorHandler(mMonitorHandlerThread.getLooper());
124 mProcessObserver = new ProcessObserver();
125 mTaskListener = new TaskListener();
Sudheer Shankaa4f49aa2016-11-10 16:39:13 -0800126 mAm = ActivityManager.getService();
Keun-young Park4727da32016-05-31 10:00:51 -0700127 // Monitoring both listeners are necessary as there are cases where one listener cannot
128 // monitor activity change.
129 try {
130 mAm.registerProcessObserver(mProcessObserver);
131 mAm.registerTaskStackListener(mTaskListener);
132 } catch (RemoteException e) {
133 Log.e(CarLog.TAG_AM, "cannot register activity monitoring", e);
134 throw new RuntimeException(e);
135 }
136 updateTasks();
137 }
138
139 @Override
140 public void init() {
141 }
142
143 @Override
144 public void release() {
145 }
146
147 @Override
148 public void dump(PrintWriter writer) {
149 writer.println("*SystemActivityMonitoringService*");
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -0800150 writer.println(" Top Tasks per display:");
Keun-young Park4727da32016-05-31 10:00:51 -0700151 synchronized (this) {
152 for (int i = 0; i < mTopTasks.size(); i++) {
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -0800153 int displayId = mTopTasks.keyAt(i);
Keun-young Park4727da32016-05-31 10:00:51 -0700154 TopTaskInfoContainer info = mTopTasks.valueAt(i);
155 if (info != null) {
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -0800156 writer.println("display id " + displayId + ": " + info);
Keun-young Park4727da32016-05-31 10:00:51 -0700157 }
158 }
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -0800159 writer.println(" Foreground uid-pids:");
Keun-young Park4727da32016-05-31 10:00:51 -0700160 for (Integer key : mForegroundUidPids.keySet()) {
161 Set<Integer> pids = mForegroundUidPids.get(key);
162 if (pids == null) {
163 continue;
164 }
165 writer.println("uid:" + key + ", pids:" + Arrays.toString(pids.toArray()));
166 }
167 writer.println(" focused stack:" + mFocusedStackId);
168 }
169 }
170
171 /**
172 * Block the current task: Launch new activity with given Intent and finish the current task.
173 * @param currentTask task to finish
174 * @param newActivityIntent Intent for new Activity
175 */
176 public void blockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent) {
177 mHandler.requestBlockActivity(currentTask, newActivityIntent);
178 }
179
180 public List<TopTaskInfoContainer> getTopTasks() {
181 LinkedList<TopTaskInfoContainer> tasks = new LinkedList<>();
182 synchronized (this) {
183 for (int i = 0; i < mTopTasks.size(); i++) {
184 tasks.add(mTopTasks.valueAt(i));
185 }
186 }
187 return tasks;
188 }
189
190 public boolean isInForeground(int pid, int uid) {
191 synchronized (this) {
192 Set<Integer> pids = mForegroundUidPids.get(uid);
193 if (pids == null) {
194 return false;
195 }
196 if (pids.contains(pid)) {
197 return true;
198 }
199 }
200 return false;
201 }
202
Yao, Yuxingd1d6a372018-05-08 10:37:43 -0700203 /**
204 * Attempts to restart a task.
205 *
206 * <p>Restarts a task by sending an empty intent with flag
207 * {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} to its root activity. If the task does not exist,
208 * do nothing.
209 *
210 * @param taskId id of task to be restarted.
211 */
212 public void restartTask(int taskId) {
213 String rootActivityName = null;
214 int userId = 0;
215 try {
216 findRootActivityName:
217 for (StackInfo info : mAm.getAllStackInfos()) {
218 for (int i = 0; i < info.taskIds.length; i++) {
219 if (info.taskIds[i] == taskId) {
220 rootActivityName = info.taskNames[i];
221 userId = info.userId;
222 if (Log.isLoggable(CarLog.TAG_AM, Log.DEBUG)) {
223 Log.d(CarLog.TAG_AM, "Root activity is " + rootActivityName);
224 Log.d(CarLog.TAG_AM, "User id is " + userId);
225 }
226 // Break out of nested loop.
227 break findRootActivityName;
228 }
229 }
230 }
231 } catch (RemoteException e) {
232 Log.e(CarLog.TAG_AM, "Could not get stack info", e);
233 return;
234 }
235
236 if (rootActivityName == null) {
237 Log.e(CarLog.TAG_AM, "Could not find root activity with task id " + taskId);
238 return;
239 }
240
241 Intent rootActivityIntent = new Intent();
242 rootActivityIntent.setComponent(ComponentName.unflattenFromString(rootActivityName));
243 // Clear the task the root activity is running in and start it in a new task.
244 // Effectively restart root activity.
245 rootActivityIntent.addFlags(
246 Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
247
248 if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
249 Log.i(CarLog.TAG_AM, "restarting root activity with user id " + userId);
250 }
251 mContext.startActivityAsUser(rootActivityIntent, new UserHandle(userId));
252 }
253
Keun-young Park4727da32016-05-31 10:00:51 -0700254 public void registerActivityLaunchListener(ActivityLaunchListener listener) {
255 synchronized (this) {
256 mActivityLaunchListener = listener;
257 }
258 }
259
260 private void updateTasks() {
261 List<StackInfo> infos;
262 try {
263 infos = mAm.getAllStackInfos();
264 } catch (RemoteException e) {
265 Log.e(CarLog.TAG_AM, "cannot getTasks", e);
266 return;
267 }
Michal Palczewski7684dbb2018-11-13 18:36:06 -0800268 int focusedStackId = INVALID_STACK_ID;
Keun-young Park4727da32016-05-31 10:00:51 -0700269 try {
Wale Ogunwale9a000b92017-09-26 11:05:02 -0700270 // TODO(b/66955160): Someone on the Auto-team should probably re-work the code in the
271 // synchronized block below based on this new API.
272 final StackInfo focusedStackInfo = mAm.getFocusedStackInfo();
273 if (focusedStackInfo != null) {
274 focusedStackId = focusedStackInfo.stackId;
275 }
Keun-young Park4727da32016-05-31 10:00:51 -0700276 } catch (RemoteException e) {
277 Log.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
278 return;
279 }
280 mTasksToDispatch.clear();
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -0800281
282 SparseArray<TopTaskInfoContainer> topTasks = new SparseArray<>();
Keun-young Park4727da32016-05-31 10:00:51 -0700283 ActivityLaunchListener listener;
284 synchronized (this) {
285 listener = mActivityLaunchListener;
286 for (StackInfo info : infos) {
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -0800287 int displayId = info.displayId;
Keun-young Park4727da32016-05-31 10:00:51 -0700288 if (info.taskNames.length == 0 || !info.visible) { // empty stack or not shown
Keun-young Park4727da32016-05-31 10:00:51 -0700289 continue;
290 }
Yao Chened8609e2016-10-07 09:49:13 -0700291 TopTaskInfoContainer newTopTaskInfo = new TopTaskInfoContainer(
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -0800292 info.topActivity, info.taskIds[info.taskIds.length - 1],
293 info.displayId, info.position, info);
294 TopTaskInfoContainer currentTopTaskInfo = topTasks.get(displayId);
Yao Chened8609e2016-10-07 09:49:13 -0700295
Keun-young Park4727da32016-05-31 10:00:51 -0700296 if (currentTopTaskInfo == null ||
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -0800297 newTopTaskInfo.position > currentTopTaskInfo.position) {
298 topTasks.put(displayId, newTopTaskInfo);
Yao, Yuxingd1d6a372018-05-08 10:37:43 -0700299 if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -0800300 Log.i(CarLog.TAG_AM, "Current top task " + newTopTaskInfo);
Keun-young Park4727da32016-05-31 10:00:51 -0700301 }
302 }
303 }
Yao, Yuxing4ddb2c62019-02-04 11:26:46 -0800304 // Assuming displays remains the same.
305 for (int i = 0; i < topTasks.size(); i++) {
306 int displayId = topTasks.keyAt(i);
307 TopTaskInfoContainer current = topTasks.get(displayId);
308 TopTaskInfoContainer previous = mTopTasks.get(displayId);
309 if (!current.isMatching(previous)) {
310 mTasksToDispatch.add(current);
311 mTopTasks.put(displayId, current);
Michal Palczewski7684dbb2018-11-13 18:36:06 -0800312 }
313 }
Keun-young Park4727da32016-05-31 10:00:51 -0700314 }
315 if (listener != null) {
316 for (TopTaskInfoContainer topTask : mTasksToDispatch) {
Yao, Yuxingd1d6a372018-05-08 10:37:43 -0700317 if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
Keun-young Park4727da32016-05-31 10:00:51 -0700318 Log.i(CarLog.TAG_AM, "activity launched:" + topTask.toString());
319 }
320 listener.onActivityLaunch(topTask);
321 }
322 }
323 }
324
325 public StackInfo getFocusedStackForTopActivity(ComponentName activity) {
Keun-young Park4727da32016-05-31 10:00:51 -0700326 StackInfo focusedStack;
327 try {
Wale Ogunwale9a000b92017-09-26 11:05:02 -0700328 focusedStack = mAm.getFocusedStackInfo();
Keun-young Park4727da32016-05-31 10:00:51 -0700329 } catch (RemoteException e) {
330 Log.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
331 return null;
332 }
333 if (focusedStack.taskNames.length == 0) { // nothing in focused stack
334 return null;
335 }
336 ComponentName topActivity = ComponentName.unflattenFromString(
337 focusedStack.taskNames[focusedStack.taskNames.length - 1]);
338 if (topActivity.equals(activity)) {
339 return focusedStack;
340 } else {
341 return null;
342 }
343 }
344
345 private void handleForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
346 synchronized (this) {
347 if (foregroundActivities) {
348 Set<Integer> pids = mForegroundUidPids.get(uid);
349 if (pids == null) {
350 pids = new ArraySet<Integer>();
351 mForegroundUidPids.put(uid, pids);
352 }
353 pids.add(pid);
354 } else {
355 doHandlePidGoneLocked(pid, uid);
356 }
357 }
358 }
359
360 private void handleProcessDied(int pid, int uid) {
361 synchronized (this) {
362 doHandlePidGoneLocked(pid, uid);
363 }
364 }
365
366 private void doHandlePidGoneLocked(int pid, int uid) {
367 Set<Integer> pids = mForegroundUidPids.get(uid);
368 if (pids != null) {
369 pids.remove(pid);
370 if (pids.isEmpty()) {
371 mForegroundUidPids.remove(uid);
372 }
373 }
374 }
375
Ram Periathiruvadiffcdb842018-04-16 17:17:39 -0700376 /**
377 * block the current task with the provided new activity.
378 */
Keun-young Park4727da32016-05-31 10:00:51 -0700379 private void handleBlockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent) {
Yao, Yuxingcfcc59f2019-03-05 16:35:47 -0800380 // Only block default display.
381 ActivityOptions options = ActivityOptions.makeBasic();
382 options.setLaunchDisplayId(Display.DEFAULT_DISPLAY);
383
384 mContext.startActivityAsUser(newActivityIntent, options.toBundle(),
Keun-young Park4727da32016-05-31 10:00:51 -0700385 new UserHandle(currentTask.stackInfo.userId));
Yao, Yuxingd1d6a372018-05-08 10:37:43 -0700386 // Now make stack with new activity focused.
Keun-young Park4727da32016-05-31 10:00:51 -0700387 findTaskAndGrantFocus(newActivityIntent.getComponent());
Keun-young Park4727da32016-05-31 10:00:51 -0700388 }
389
390 private void findTaskAndGrantFocus(ComponentName activity) {
391 List<StackInfo> infos;
392 try {
393 infos = mAm.getAllStackInfos();
394 } catch (RemoteException e) {
395 Log.e(CarLog.TAG_AM, "cannot getTasks", e);
396 return;
397 }
398 for (StackInfo info : infos) {
399 if (info.taskNames.length == 0) {
400 continue;
401 }
402 ComponentName topActivity = ComponentName.unflattenFromString(
403 info.taskNames[info.taskNames.length - 1]);
404 if (activity.equals(topActivity)) {
405 try {
406 mAm.setFocusedStack(info.stackId);
407 } catch (RemoteException e) {
408 Log.e(CarLog.TAG_AM, "cannot setFocusedStack to stack:" + info.stackId, e);
409 }
410 return;
411 }
412 }
413 Log.i(CarLog.TAG_AM, "cannot give focus, cannot find Activity:" + activity);
414 }
415
416 private class ProcessObserver extends IProcessObserver.Stub {
417 @Override
418 public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
Yao, Yuxingd1d6a372018-05-08 10:37:43 -0700419 if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
Keun-young Park4727da32016-05-31 10:00:51 -0700420 Log.i(CarLog.TAG_AM,
421 String.format("onForegroundActivitiesChanged uid %d pid %d fg %b",
422 uid, pid, foregroundActivities));
423 }
424 mHandler.requestForegroundActivitiesChanged(pid, uid, foregroundActivities);
425 }
426
427 @Override
Keun-young Park4727da32016-05-31 10:00:51 -0700428 public void onProcessDied(int pid, int uid) {
429 mHandler.requestProcessDied(pid, uid);
430 }
431 }
432
Yorke Leec1265de2016-10-27 11:25:25 -0700433 private class TaskListener extends TaskStackListener {
Keun-young Park4727da32016-05-31 10:00:51 -0700434 @Override
435 public void onTaskStackChanged() {
Yao, Yuxingd1d6a372018-05-08 10:37:43 -0700436 if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
Keun-young Park4727da32016-05-31 10:00:51 -0700437 Log.i(CarLog.TAG_AM, "onTaskStackChanged");
438 }
439 mHandler.requestUpdatingTask();
440 }
Keun-young Park4727da32016-05-31 10:00:51 -0700441 }
442
443 private class ActivityMonitorHandler extends Handler {
444 private static final int MSG_UPDATE_TASKS = 0;
445 private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 1;
446 private static final int MSG_PROCESS_DIED = 2;
447 private static final int MSG_BLOCK_ACTIVITY = 3;
448
449 private ActivityMonitorHandler(Looper looper) {
450 super(looper);
451 }
452
453 private void requestUpdatingTask() {
454 Message msg = obtainMessage(MSG_UPDATE_TASKS);
455 sendMessage(msg);
456 }
457
458 private void requestForegroundActivitiesChanged(int pid, int uid,
459 boolean foregroundActivities) {
460 Message msg = obtainMessage(MSG_FOREGROUND_ACTIVITIES_CHANGED, pid, uid,
461 Boolean.valueOf(foregroundActivities));
462 sendMessage(msg);
463 }
464
465 private void requestProcessDied(int pid, int uid) {
466 Message msg = obtainMessage(MSG_PROCESS_DIED, pid, uid);
467 sendMessage(msg);
468 }
469
470 private void requestBlockActivity(TopTaskInfoContainer currentTask,
471 Intent newActivityIntent) {
472 Message msg = obtainMessage(MSG_BLOCK_ACTIVITY,
473 new Pair<TopTaskInfoContainer, Intent>(currentTask, newActivityIntent));
474 sendMessage(msg);
475 }
476
477 @Override
478 public void handleMessage(Message msg) {
479 switch (msg.what) {
480 case MSG_UPDATE_TASKS:
481 updateTasks();
482 break;
483 case MSG_FOREGROUND_ACTIVITIES_CHANGED:
484 handleForegroundActivitiesChanged(msg.arg1, msg.arg2, (Boolean) msg.obj);
485 updateTasks();
486 break;
487 case MSG_PROCESS_DIED:
488 handleProcessDied(msg.arg1, msg.arg2);
489 break;
490 case MSG_BLOCK_ACTIVITY:
491 Pair<TopTaskInfoContainer, Intent> pair =
492 (Pair<TopTaskInfoContainer, Intent>) msg.obj;
493 handleBlockActivity(pair.first, pair.second);
494 break;
495 }
496 }
497 }
498}