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