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