blob: a20e7fc650629c2e193f9af52f1903d03caf680a [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;
Keun-young Park4727da32016-05-31 10:00:51 -070060 public final StackInfo stackInfo;
61
Yao, Yuxingcfcc59f2019-03-05 16:35:47 -080062 private TopTaskInfoContainer(ComponentName topActivity, int taskId, int displayId,
63 StackInfo stackInfo) {
Keun-young Park4727da32016-05-31 10:00:51 -070064 this.topActivity = topActivity;
65 this.taskId = taskId;
Yao, Yuxingcfcc59f2019-03-05 16:35:47 -080066 this.displayId = displayId;
Keun-young Park4727da32016-05-31 10:00:51 -070067 this.stackInfo = stackInfo;
68 }
69
Yao Chened8609e2016-10-07 09:49:13 -070070 public boolean isMatching(TopTaskInfoContainer taskInfo) {
71 return taskInfo != null
72 && Objects.equals(this.topActivity, taskInfo.topActivity)
73 && this.taskId == taskInfo.taskId
Yao, Yuxingcfcc59f2019-03-05 16:35:47 -080074 && this.displayId == taskInfo.displayId
Yao Chened8609e2016-10-07 09:49:13 -070075 && this.stackInfo.userId == taskInfo.stackInfo.userId;
Keun-young Park4727da32016-05-31 10:00:51 -070076 }
77
78 @Override
79 public String toString() {
80 return String.format(
Yao, Yuxingcfcc59f2019-03-05 16:35:47 -080081 "TaskInfoContainer [topActivity=%s, taskId=%d, stackId=%d, userId=%d,"
82 + " displayId=%d]",
83 topActivity, taskId, stackInfo.stackId, stackInfo.userId, displayId);
Keun-young Park4727da32016-05-31 10:00:51 -070084 }
85 }
86
87 public interface ActivityLaunchListener {
88 /**
89 * Notify launch of activity.
90 * @param topTask Task information for what is currently launched.
91 */
92 void onActivityLaunch(TopTaskInfoContainer topTask);
93 }
94
Michal Palczewski7684dbb2018-11-13 18:36:06 -080095 private static final int INVALID_STACK_ID = -1;
Keun-young Park4727da32016-05-31 10:00:51 -070096 private final Context mContext;
97 private final IActivityManager mAm;
98 private final ProcessObserver mProcessObserver;
99 private final TaskListener mTaskListener;
100
101 private final HandlerThread mMonitorHandlerThread;
102 private final ActivityMonitorHandler mHandler;
103
104 /** K: stack id, V: top task */
105 private final SparseArray<TopTaskInfoContainer> mTopTasks = new SparseArray<>();
106 /** K: uid, V : list of pid */
Yao Chened8609e2016-10-07 09:49:13 -0700107 private final Map<Integer, Set<Integer>> mForegroundUidPids = new ArrayMap<>();
Michal Palczewski7684dbb2018-11-13 18:36:06 -0800108 private int mFocusedStackId = INVALID_STACK_ID;
Keun-young Park4727da32016-05-31 10:00:51 -0700109
110 /**
111 * Temporary container to dispatch tasks for onActivityLaunch. Only used in handler thread.
112 * can be accessed without lock. */
113 private final List<TopTaskInfoContainer> mTasksToDispatch = new LinkedList<>();
114 private ActivityLaunchListener mActivityLaunchListener;
115
116 public SystemActivityMonitoringService(Context context) {
117 mContext = context;
118 mMonitorHandlerThread = new HandlerThread(CarLog.TAG_AM);
119 mMonitorHandlerThread.start();
120 mHandler = new ActivityMonitorHandler(mMonitorHandlerThread.getLooper());
121 mProcessObserver = new ProcessObserver();
122 mTaskListener = new TaskListener();
Sudheer Shankaa4f49aa2016-11-10 16:39:13 -0800123 mAm = ActivityManager.getService();
Keun-young Park4727da32016-05-31 10:00:51 -0700124 // Monitoring both listeners are necessary as there are cases where one listener cannot
125 // monitor activity change.
126 try {
127 mAm.registerProcessObserver(mProcessObserver);
128 mAm.registerTaskStackListener(mTaskListener);
129 } catch (RemoteException e) {
130 Log.e(CarLog.TAG_AM, "cannot register activity monitoring", e);
131 throw new RuntimeException(e);
132 }
133 updateTasks();
134 }
135
136 @Override
137 public void init() {
138 }
139
140 @Override
141 public void release() {
142 }
143
144 @Override
145 public void dump(PrintWriter writer) {
146 writer.println("*SystemActivityMonitoringService*");
147 writer.println(" Top Tasks:");
148 synchronized (this) {
149 for (int i = 0; i < mTopTasks.size(); i++) {
150 TopTaskInfoContainer info = mTopTasks.valueAt(i);
151 if (info != null) {
152 writer.println(info);
153 }
154 }
155 writer.println(" Foregroud uid-pids:");
156 for (Integer key : mForegroundUidPids.keySet()) {
157 Set<Integer> pids = mForegroundUidPids.get(key);
158 if (pids == null) {
159 continue;
160 }
161 writer.println("uid:" + key + ", pids:" + Arrays.toString(pids.toArray()));
162 }
163 writer.println(" focused stack:" + mFocusedStackId);
164 }
165 }
166
167 /**
168 * Block the current task: Launch new activity with given Intent and finish the current task.
169 * @param currentTask task to finish
170 * @param newActivityIntent Intent for new Activity
171 */
172 public void blockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent) {
173 mHandler.requestBlockActivity(currentTask, newActivityIntent);
174 }
175
176 public List<TopTaskInfoContainer> getTopTasks() {
177 LinkedList<TopTaskInfoContainer> tasks = new LinkedList<>();
178 synchronized (this) {
179 for (int i = 0; i < mTopTasks.size(); i++) {
180 tasks.add(mTopTasks.valueAt(i));
181 }
182 }
183 return tasks;
184 }
185
186 public boolean isInForeground(int pid, int uid) {
187 synchronized (this) {
188 Set<Integer> pids = mForegroundUidPids.get(uid);
189 if (pids == null) {
190 return false;
191 }
192 if (pids.contains(pid)) {
193 return true;
194 }
195 }
196 return false;
197 }
198
Yao, Yuxingd1d6a372018-05-08 10:37:43 -0700199 /**
200 * Attempts to restart a task.
201 *
202 * <p>Restarts a task by sending an empty intent with flag
203 * {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} to its root activity. If the task does not exist,
204 * do nothing.
205 *
206 * @param taskId id of task to be restarted.
207 */
208 public void restartTask(int taskId) {
209 String rootActivityName = null;
210 int userId = 0;
211 try {
212 findRootActivityName:
213 for (StackInfo info : mAm.getAllStackInfos()) {
214 for (int i = 0; i < info.taskIds.length; i++) {
215 if (info.taskIds[i] == taskId) {
216 rootActivityName = info.taskNames[i];
217 userId = info.userId;
218 if (Log.isLoggable(CarLog.TAG_AM, Log.DEBUG)) {
219 Log.d(CarLog.TAG_AM, "Root activity is " + rootActivityName);
220 Log.d(CarLog.TAG_AM, "User id is " + userId);
221 }
222 // Break out of nested loop.
223 break findRootActivityName;
224 }
225 }
226 }
227 } catch (RemoteException e) {
228 Log.e(CarLog.TAG_AM, "Could not get stack info", e);
229 return;
230 }
231
232 if (rootActivityName == null) {
233 Log.e(CarLog.TAG_AM, "Could not find root activity with task id " + taskId);
234 return;
235 }
236
237 Intent rootActivityIntent = new Intent();
238 rootActivityIntent.setComponent(ComponentName.unflattenFromString(rootActivityName));
239 // Clear the task the root activity is running in and start it in a new task.
240 // Effectively restart root activity.
241 rootActivityIntent.addFlags(
242 Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
243
244 if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
245 Log.i(CarLog.TAG_AM, "restarting root activity with user id " + userId);
246 }
247 mContext.startActivityAsUser(rootActivityIntent, new UserHandle(userId));
248 }
249
Keun-young Park4727da32016-05-31 10:00:51 -0700250 public void registerActivityLaunchListener(ActivityLaunchListener listener) {
251 synchronized (this) {
252 mActivityLaunchListener = listener;
253 }
254 }
255
256 private void updateTasks() {
257 List<StackInfo> infos;
258 try {
259 infos = mAm.getAllStackInfos();
260 } catch (RemoteException e) {
261 Log.e(CarLog.TAG_AM, "cannot getTasks", e);
262 return;
263 }
Michal Palczewski7684dbb2018-11-13 18:36:06 -0800264 int focusedStackId = INVALID_STACK_ID;
Keun-young Park4727da32016-05-31 10:00:51 -0700265 try {
Wale Ogunwale9a000b92017-09-26 11:05:02 -0700266 // TODO(b/66955160): Someone on the Auto-team should probably re-work the code in the
267 // synchronized block below based on this new API.
268 final StackInfo focusedStackInfo = mAm.getFocusedStackInfo();
269 if (focusedStackInfo != null) {
270 focusedStackId = focusedStackInfo.stackId;
271 }
Keun-young Park4727da32016-05-31 10:00:51 -0700272 } catch (RemoteException e) {
273 Log.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
274 return;
275 }
276 mTasksToDispatch.clear();
277 ActivityLaunchListener listener;
278 synchronized (this) {
279 listener = mActivityLaunchListener;
Michal Palczewski7684dbb2018-11-13 18:36:06 -0800280 Set<Integer> allStackIds = new ArraySet<>(infos.size());
281 Set<Integer> stackIdsToRemove = new ArraySet<>(infos.size());
Keun-young Park4727da32016-05-31 10:00:51 -0700282 for (StackInfo info : infos) {
283 int stackId = info.stackId;
Michal Palczewski7684dbb2018-11-13 18:36:06 -0800284 allStackIds.add(info.stackId);
Keun-young Park4727da32016-05-31 10:00:51 -0700285 if (info.taskNames.length == 0 || !info.visible) { // empty stack or not shown
Michal Palczewski7684dbb2018-11-13 18:36:06 -0800286 stackIdsToRemove.add(stackId);
Keun-young Park4727da32016-05-31 10:00:51 -0700287 continue;
288 }
Yao Chened8609e2016-10-07 09:49:13 -0700289 TopTaskInfoContainer newTopTaskInfo = new TopTaskInfoContainer(
Yao, Yuxingcfcc59f2019-03-05 16:35:47 -0800290 info.topActivity, info.taskIds[info.taskIds.length - 1], info.displayId,
291 info);
Keun-young Park4727da32016-05-31 10:00:51 -0700292 TopTaskInfoContainer currentTopTaskInfo = mTopTasks.get(stackId);
Yao Chened8609e2016-10-07 09:49:13 -0700293
Keun-young Park4727da32016-05-31 10:00:51 -0700294 // if a new task is added to stack or focused stack changes, should notify
295 if (currentTopTaskInfo == null ||
Yao Chened8609e2016-10-07 09:49:13 -0700296 !currentTopTaskInfo.isMatching(newTopTaskInfo) ||
Keun-young Park4727da32016-05-31 10:00:51 -0700297 (focusedStackId == stackId && focusedStackId != mFocusedStackId)) {
Yao Chened8609e2016-10-07 09:49:13 -0700298 mTopTasks.put(stackId, newTopTaskInfo);
299 mTasksToDispatch.add(newTopTaskInfo);
Yao, Yuxingd1d6a372018-05-08 10:37:43 -0700300 if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
Yao Chened8609e2016-10-07 09:49:13 -0700301 Log.i(CarLog.TAG_AM, "New top task: " + newTopTaskInfo);
Keun-young Park4727da32016-05-31 10:00:51 -0700302 }
303 }
304 }
Michal Palczewski7684dbb2018-11-13 18:36:06 -0800305 for (int i = 0; i < mTopTasks.size(); i++) {
306 TopTaskInfoContainer topTask = mTopTasks.valueAt(i);
307 if (topTask == null) {
308 Log.wtf(CarLog.TAG_AM, "unexpected null value in sparse array");
309 continue;
310 }
311 if (!allStackIds.contains(mTopTasks.keyAt(i))) {
312 stackIdsToRemove.add(mTopTasks.keyAt(i));
313 }
314 }
315 for (int stackIdToRemove : stackIdsToRemove) {
316 mTopTasks.remove(stackIdToRemove);
317 }
Keun-young Park4727da32016-05-31 10:00:51 -0700318 mFocusedStackId = focusedStackId;
319 }
320 if (listener != null) {
321 for (TopTaskInfoContainer topTask : mTasksToDispatch) {
Yao, Yuxingd1d6a372018-05-08 10:37:43 -0700322 if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
Keun-young Park4727da32016-05-31 10:00:51 -0700323 Log.i(CarLog.TAG_AM, "activity launched:" + topTask.toString());
324 }
325 listener.onActivityLaunch(topTask);
326 }
327 }
328 }
329
330 public StackInfo getFocusedStackForTopActivity(ComponentName activity) {
Keun-young Park4727da32016-05-31 10:00:51 -0700331 StackInfo focusedStack;
332 try {
Wale Ogunwale9a000b92017-09-26 11:05:02 -0700333 focusedStack = mAm.getFocusedStackInfo();
Keun-young Park4727da32016-05-31 10:00:51 -0700334 } catch (RemoteException e) {
335 Log.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
336 return null;
337 }
338 if (focusedStack.taskNames.length == 0) { // nothing in focused stack
339 return null;
340 }
341 ComponentName topActivity = ComponentName.unflattenFromString(
342 focusedStack.taskNames[focusedStack.taskNames.length - 1]);
343 if (topActivity.equals(activity)) {
344 return focusedStack;
345 } else {
346 return null;
347 }
348 }
349
350 private void handleForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
351 synchronized (this) {
352 if (foregroundActivities) {
353 Set<Integer> pids = mForegroundUidPids.get(uid);
354 if (pids == null) {
355 pids = new ArraySet<Integer>();
356 mForegroundUidPids.put(uid, pids);
357 }
358 pids.add(pid);
359 } else {
360 doHandlePidGoneLocked(pid, uid);
361 }
362 }
363 }
364
365 private void handleProcessDied(int pid, int uid) {
366 synchronized (this) {
367 doHandlePidGoneLocked(pid, uid);
368 }
369 }
370
371 private void doHandlePidGoneLocked(int pid, int uid) {
372 Set<Integer> pids = mForegroundUidPids.get(uid);
373 if (pids != null) {
374 pids.remove(pid);
375 if (pids.isEmpty()) {
376 mForegroundUidPids.remove(uid);
377 }
378 }
379 }
380
Ram Periathiruvadiffcdb842018-04-16 17:17:39 -0700381 /**
382 * block the current task with the provided new activity.
383 */
Keun-young Park4727da32016-05-31 10:00:51 -0700384 private void handleBlockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent) {
Yao, Yuxingcfcc59f2019-03-05 16:35:47 -0800385 // Only block default display.
386 ActivityOptions options = ActivityOptions.makeBasic();
387 options.setLaunchDisplayId(Display.DEFAULT_DISPLAY);
388
389 mContext.startActivityAsUser(newActivityIntent, options.toBundle(),
Keun-young Park4727da32016-05-31 10:00:51 -0700390 new UserHandle(currentTask.stackInfo.userId));
Yao, Yuxingd1d6a372018-05-08 10:37:43 -0700391 // Now make stack with new activity focused.
Keun-young Park4727da32016-05-31 10:00:51 -0700392 findTaskAndGrantFocus(newActivityIntent.getComponent());
Keun-young Park4727da32016-05-31 10:00:51 -0700393 }
394
395 private void findTaskAndGrantFocus(ComponentName activity) {
396 List<StackInfo> infos;
397 try {
398 infos = mAm.getAllStackInfos();
399 } catch (RemoteException e) {
400 Log.e(CarLog.TAG_AM, "cannot getTasks", e);
401 return;
402 }
403 for (StackInfo info : infos) {
404 if (info.taskNames.length == 0) {
405 continue;
406 }
407 ComponentName topActivity = ComponentName.unflattenFromString(
408 info.taskNames[info.taskNames.length - 1]);
409 if (activity.equals(topActivity)) {
410 try {
411 mAm.setFocusedStack(info.stackId);
412 } catch (RemoteException e) {
413 Log.e(CarLog.TAG_AM, "cannot setFocusedStack to stack:" + info.stackId, e);
414 }
415 return;
416 }
417 }
418 Log.i(CarLog.TAG_AM, "cannot give focus, cannot find Activity:" + activity);
419 }
420
421 private class ProcessObserver extends IProcessObserver.Stub {
422 @Override
423 public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
Yao, Yuxingd1d6a372018-05-08 10:37:43 -0700424 if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
Keun-young Park4727da32016-05-31 10:00:51 -0700425 Log.i(CarLog.TAG_AM,
426 String.format("onForegroundActivitiesChanged uid %d pid %d fg %b",
427 uid, pid, foregroundActivities));
428 }
429 mHandler.requestForegroundActivitiesChanged(pid, uid, foregroundActivities);
430 }
431
432 @Override
Keun-young Park4727da32016-05-31 10:00:51 -0700433 public void onProcessDied(int pid, int uid) {
434 mHandler.requestProcessDied(pid, uid);
435 }
436 }
437
Yorke Leec1265de2016-10-27 11:25:25 -0700438 private class TaskListener extends TaskStackListener {
Keun-young Park4727da32016-05-31 10:00:51 -0700439 @Override
440 public void onTaskStackChanged() {
Yao, Yuxingd1d6a372018-05-08 10:37:43 -0700441 if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
Keun-young Park4727da32016-05-31 10:00:51 -0700442 Log.i(CarLog.TAG_AM, "onTaskStackChanged");
443 }
444 mHandler.requestUpdatingTask();
445 }
Keun-young Park4727da32016-05-31 10:00:51 -0700446 }
447
448 private class ActivityMonitorHandler extends Handler {
449 private static final int MSG_UPDATE_TASKS = 0;
450 private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 1;
451 private static final int MSG_PROCESS_DIED = 2;
452 private static final int MSG_BLOCK_ACTIVITY = 3;
453
454 private ActivityMonitorHandler(Looper looper) {
455 super(looper);
456 }
457
458 private void requestUpdatingTask() {
459 Message msg = obtainMessage(MSG_UPDATE_TASKS);
460 sendMessage(msg);
461 }
462
463 private void requestForegroundActivitiesChanged(int pid, int uid,
464 boolean foregroundActivities) {
465 Message msg = obtainMessage(MSG_FOREGROUND_ACTIVITIES_CHANGED, pid, uid,
466 Boolean.valueOf(foregroundActivities));
467 sendMessage(msg);
468 }
469
470 private void requestProcessDied(int pid, int uid) {
471 Message msg = obtainMessage(MSG_PROCESS_DIED, pid, uid);
472 sendMessage(msg);
473 }
474
475 private void requestBlockActivity(TopTaskInfoContainer currentTask,
476 Intent newActivityIntent) {
477 Message msg = obtainMessage(MSG_BLOCK_ACTIVITY,
478 new Pair<TopTaskInfoContainer, Intent>(currentTask, newActivityIntent));
479 sendMessage(msg);
480 }
481
482 @Override
483 public void handleMessage(Message msg) {
484 switch (msg.what) {
485 case MSG_UPDATE_TASKS:
486 updateTasks();
487 break;
488 case MSG_FOREGROUND_ACTIVITIES_CHANGED:
489 handleForegroundActivitiesChanged(msg.arg1, msg.arg2, (Boolean) msg.obj);
490 updateTasks();
491 break;
492 case MSG_PROCESS_DIED:
493 handleProcessDied(msg.arg1, msg.arg2);
494 break;
495 case MSG_BLOCK_ACTIVITY:
496 Pair<TopTaskInfoContainer, Intent> pair =
497 (Pair<TopTaskInfoContainer, Intent>) msg.obj;
498 handleBlockActivity(pair.first, pair.second);
499 break;
500 }
501 }
502 }
503}