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