blob: 0ea89dce761bee48f47f7497fbf8caad6f45d4d2 [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;
Keun-young Park4727da32016-05-31 10:00:51 -070020import android.app.IActivityManager;
21import android.app.IProcessObserver;
Yorke Leec1265de2016-10-27 11:25:25 -070022import android.app.TaskStackListener;
Keun-young Park4727da32016-05-31 10:00:51 -070023import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.os.Handler;
27import android.os.HandlerThread;
28import android.os.Looper;
29import android.os.Message;
30import android.os.RemoteException;
31import android.os.UserHandle;
Yao Chened8609e2016-10-07 09:49:13 -070032import android.util.ArrayMap;
Keun-young Park4727da32016-05-31 10:00:51 -070033import android.util.ArraySet;
34import android.util.Log;
35import android.util.Pair;
36import android.util.SparseArray;
37
38import java.io.PrintWriter;
39import java.util.Arrays;
Keun-young Park4727da32016-05-31 10:00:51 -070040import java.util.LinkedList;
41import java.util.List;
42import java.util.Map;
Yao Chened8609e2016-10-07 09:49:13 -070043import java.util.Objects;
Keun-young Park4727da32016-05-31 10:00:51 -070044import java.util.Set;
45
46/**
47 * Service to monitor AMS for new Activity or Service launching.
48 */
49public 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
59 private TopTaskInfoContainer(ComponentName topActivity, int taskId, StackInfo stackInfo) {
60 this.topActivity = topActivity;
61 this.taskId = taskId;
62 this.stackInfo = stackInfo;
63 }
64
Yao Chened8609e2016-10-07 09:49:13 -070065 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;
Keun-young Park4727da32016-05-31 10:00:51 -070070 }
71
72 @Override
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 */
85 void onActivityLaunch(TopTaskInfoContainer topTask);
86 }
87
Vitalii Tomkiv1b1247b2016-09-30 11:27:19 -070088 private static final boolean DBG = false;
Keun-young Park4727da32016-05-31 10:00:51 -070089
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 */
Yao Chened8609e2016-10-07 09:49:13 -0700103 private final Map<Integer, Set<Integer>> mForegroundUidPids = new ArrayMap<>();
Keun-young Park4727da32016-05-31 10:00:51 -0700104 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
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();
Sudheer Shankaa4f49aa2016-11-10 16:39:13 -0800119 mAm = ActivityManager.getService();
Keun-young Park4727da32016-05-31 10:00:51 -0700120 // 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
133 public void init() {
134 }
135
136 @Override
137 public void release() {
138 }
139
140 @Override
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 */
168 public void blockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent) {
169 mHandler.requestBlockActivity(currentTask, newActivityIntent);
170 }
171
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
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
195 public void registerActivityLaunchListener(ActivityLaunchListener listener) {
196 synchronized (this) {
197 mActivityLaunchListener = listener;
198 }
199 }
200
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 }
Yao Chened8609e2016-10-07 09:49:13 -0700226 TopTaskInfoContainer newTopTaskInfo = new TopTaskInfoContainer(
227 info.topActivity, info.taskIds[info.taskIds.length - 1], info);
Keun-young Park4727da32016-05-31 10:00:51 -0700228 TopTaskInfoContainer currentTopTaskInfo = mTopTasks.get(stackId);
Yao Chened8609e2016-10-07 09:49:13 -0700229
Keun-young Park4727da32016-05-31 10:00:51 -0700230 // if a new task is added to stack or focused stack changes, should notify
231 if (currentTopTaskInfo == null ||
Yao Chened8609e2016-10-07 09:49:13 -0700232 !currentTopTaskInfo.isMatching(newTopTaskInfo) ||
Keun-young Park4727da32016-05-31 10:00:51 -0700233 (focusedStackId == stackId && focusedStackId != mFocusedStackId)) {
Yao Chened8609e2016-10-07 09:49:13 -0700234 mTopTasks.put(stackId, newTopTaskInfo);
235 mTasksToDispatch.add(newTopTaskInfo);
Keun-young Park4727da32016-05-31 10:00:51 -0700236 if (DBG) {
Yao Chened8609e2016-10-07 09:49:13 -0700237 Log.i(CarLog.TAG_AM, "New top task: " + newTopTaskInfo);
Keun-young Park4727da32016-05-31 10:00:51 -0700238 }
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
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
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
295 private void handleProcessDied(int pid, int uid) {
296 synchronized (this) {
297 doHandlePidGoneLocked(pid, uid);
298 }
299 }
300
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
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));
Yao Chenb982e642016-10-13 14:22:56 -0700314 // 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
Keun-young Park4727da32016-05-31 10:00:51 -0700321 newActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
322 mContext.startActivityAsUser(newActivityIntent,
323 new UserHandle(currentTask.stackInfo.userId));
324 // now make stack with new activity focused.
325 findTaskAndGrantFocus(newActivityIntent.getComponent());
326 try {
327 mAm.removeTask(currentTask.taskId);
328 } catch (RemoteException e) {
329 Log.w(CarLog.TAG_AM, "cannot remove task:" + currentTask.taskId, e);
330 }
331 }
332
333 private void findTaskAndGrantFocus(ComponentName activity) {
334 List<StackInfo> infos;
335 try {
336 infos = mAm.getAllStackInfos();
337 } catch (RemoteException e) {
338 Log.e(CarLog.TAG_AM, "cannot getTasks", e);
339 return;
340 }
341 for (StackInfo info : infos) {
342 if (info.taskNames.length == 0) {
343 continue;
344 }
345 ComponentName topActivity = ComponentName.unflattenFromString(
346 info.taskNames[info.taskNames.length - 1]);
347 if (activity.equals(topActivity)) {
348 try {
349 mAm.setFocusedStack(info.stackId);
350 } catch (RemoteException e) {
351 Log.e(CarLog.TAG_AM, "cannot setFocusedStack to stack:" + info.stackId, e);
352 }
353 return;
354 }
355 }
356 Log.i(CarLog.TAG_AM, "cannot give focus, cannot find Activity:" + activity);
357 }
358
359 private class ProcessObserver extends IProcessObserver.Stub {
360 @Override
361 public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
362 if (DBG) {
363 Log.i(CarLog.TAG_AM,
364 String.format("onForegroundActivitiesChanged uid %d pid %d fg %b",
365 uid, pid, foregroundActivities));
366 }
367 mHandler.requestForegroundActivitiesChanged(pid, uid, foregroundActivities);
368 }
369
370 @Override
Keun-young Park4727da32016-05-31 10:00:51 -0700371 public void onProcessDied(int pid, int uid) {
372 mHandler.requestProcessDied(pid, uid);
373 }
374 }
375
Yorke Leec1265de2016-10-27 11:25:25 -0700376 private class TaskListener extends TaskStackListener {
Keun-young Park4727da32016-05-31 10:00:51 -0700377 @Override
378 public void onTaskStackChanged() {
379 if (DBG) {
380 Log.i(CarLog.TAG_AM, "onTaskStackChanged");
381 }
382 mHandler.requestUpdatingTask();
383 }
Keun-young Park4727da32016-05-31 10:00:51 -0700384 }
385
386 private class ActivityMonitorHandler extends Handler {
387 private static final int MSG_UPDATE_TASKS = 0;
388 private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 1;
389 private static final int MSG_PROCESS_DIED = 2;
390 private static final int MSG_BLOCK_ACTIVITY = 3;
391
392 private ActivityMonitorHandler(Looper looper) {
393 super(looper);
394 }
395
396 private void requestUpdatingTask() {
397 Message msg = obtainMessage(MSG_UPDATE_TASKS);
398 sendMessage(msg);
399 }
400
401 private void requestForegroundActivitiesChanged(int pid, int uid,
402 boolean foregroundActivities) {
403 Message msg = obtainMessage(MSG_FOREGROUND_ACTIVITIES_CHANGED, pid, uid,
404 Boolean.valueOf(foregroundActivities));
405 sendMessage(msg);
406 }
407
408 private void requestProcessDied(int pid, int uid) {
409 Message msg = obtainMessage(MSG_PROCESS_DIED, pid, uid);
410 sendMessage(msg);
411 }
412
413 private void requestBlockActivity(TopTaskInfoContainer currentTask,
414 Intent newActivityIntent) {
415 Message msg = obtainMessage(MSG_BLOCK_ACTIVITY,
416 new Pair<TopTaskInfoContainer, Intent>(currentTask, newActivityIntent));
417 sendMessage(msg);
418 }
419
420 @Override
421 public void handleMessage(Message msg) {
422 switch (msg.what) {
423 case MSG_UPDATE_TASKS:
424 updateTasks();
425 break;
426 case MSG_FOREGROUND_ACTIVITIES_CHANGED:
427 handleForegroundActivitiesChanged(msg.arg1, msg.arg2, (Boolean) msg.obj);
428 updateTasks();
429 break;
430 case MSG_PROCESS_DIED:
431 handleProcessDied(msg.arg1, msg.arg2);
432 break;
433 case MSG_BLOCK_ACTIVITY:
434 Pair<TopTaskInfoContainer, Intent> pair =
435 (Pair<TopTaskInfoContainer, Intent>) msg.obj;
436 handleBlockActivity(pair.first, pair.second);
437 break;
438 }
439 }
440 }
441}