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