diff options
6 files changed, 110 insertions, 15 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index f5bc8ff02e68..6ef8610a0c85 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3978,6 +3978,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A "Reported destroyed for activity that is not destroying: r=" + this); } + mTaskSupervisor.killTaskProcessesOnDestroyedIfNeeded(task); if (isInRootTaskLocked()) { cleanUp(true /* cleanServices */, false /* setState */); removeFromHistory(reason); diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index eaf55838afe7..be503fc61c4c 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -190,12 +190,19 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { /** How long we wait until giving up on the activity telling us it released the top state. */ private static final int TOP_RESUMED_STATE_LOSS_TIMEOUT = 500; + /** + * The timeout to kill task processes if its activity didn't complete destruction in time + * when there is a request to remove the task with killProcess=true. + */ + private static final int KILL_TASK_PROCESSES_TIMEOUT_MS = 1000; + private static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG; private static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_TASK_MSG + 1; private static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_TASK_MSG + 2; private static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG + 3; private static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG + 4; private static final int PROCESS_STOPPING_AND_FINISHING_MSG = FIRST_SUPERVISOR_TASK_MSG + 5; + private static final int KILL_TASK_PROCESSES_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG + 6; private static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_TASK_MSG + 12; private static final int RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG + 13; private static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_TASK_MSG + 14; @@ -1642,10 +1649,32 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { return; } task.mTransitionController.requestCloseTransitionIfNeeded(task); + // Consume the stopping activities immediately so activity manager won't skip killing + // the process because it is still foreground state, i.e. RESUMED -> PAUSING set from + // removeActivities -> finishIfPossible. + if (killProcess) { + ArrayList<ActivityRecord> activities = null; + for (int i = mStoppingActivities.size() - 1; i >= 0; i--) { + final ActivityRecord r = mStoppingActivities.get(i); + if (r.getTask() == task) { + if (activities == null) { + activities = new ArrayList<>(); + } + activities.add(r); + mStoppingActivities.remove(i); + } + } + if (activities != null) { + // This can update to background state. + for (int i = activities.size() - 1; i >= 0; i--) { + activities.get(i).stopIfPossible(); + } + } + } task.mInRemoveTask = true; try { task.removeActivities(reason, false /* excludingTaskOverlay */); - cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents); + cleanUpRemovedTask(task, killProcess, removeFromRecents); mService.getLockTaskController().clearLockedTask(task); mService.getTaskChangeNotificationController().notifyTaskStackChanged(); if (task.isPersistable) { @@ -1825,11 +1854,13 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } } - void cleanUpRemovedTaskLocked(Task task, boolean killProcess, boolean removeFromRecents) { + /** This method should only be called for leaf task. */ + private void cleanUpRemovedTask(Task task, boolean killProcess, boolean removeFromRecents) { if (removeFromRecents) { mRecentTasks.remove(task); } - ComponentName component = task.getBaseIntent().getComponent(); + final Intent baseIntent = task.getBaseIntent(); + final ComponentName component = baseIntent != null ? baseIntent.getComponent() : null; if (component == null) { Slog.w(TAG, "No component for base intent of task: " + task); return; @@ -1837,16 +1868,38 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // Find any running services associated with this app and stop if needed. final Message msg = PooledLambda.obtainMessage(ActivityManagerInternal::cleanUpServices, - mService.mAmInternal, task.mUserId, component, new Intent(task.getBaseIntent())); + mService.mAmInternal, task.mUserId, component, new Intent(baseIntent)); mService.mH.sendMessage(msg); if (!killProcess) { return; } + // Give a chance for the client to handle Activity#onStop(). The timeout waits for + // onDestroy because the client defers to report completion of stopped, the callback from + // DestroyActivityItem may be called first. + final ActivityRecord top = task.getTopMostActivity(); + if (top != null && top.finishing && !top.mAppStopped && top.lastVisibleTime > 0 + && !task.mKillProcessesOnDestroyed) { + task.mKillProcessesOnDestroyed = true; + mHandler.sendMessageDelayed( + mHandler.obtainMessage(KILL_TASK_PROCESSES_TIMEOUT_MSG, task), + KILL_TASK_PROCESSES_TIMEOUT_MS); + return; + } + killTaskProcessesIfPossible(task); + } + + void killTaskProcessesOnDestroyedIfNeeded(Task task) { + if (task == null || !task.mKillProcessesOnDestroyed) return; + mHandler.removeMessages(KILL_TASK_PROCESSES_TIMEOUT_MSG, task); + killTaskProcessesIfPossible(task); + } - // Determine if the process(es) for this task should be killed. - final String pkg = component.getPackageName(); - ArrayList<Object> procsToKill = new ArrayList<>(); + /** Kills the processes in the task if it doesn't contain perceptible components. */ + private void killTaskProcessesIfPossible(Task task) { + task.mKillProcessesOnDestroyed = false; + final String pkg = task.getBasePackageName(); + ArrayList<Object> procsToKill = null; ArrayMap<String, SparseArray<WindowProcessController>> pmap = mService.mProcessNames.getMap(); for (int i = 0; i < pmap.size(); i++) { @@ -1878,10 +1931,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { return; } + if (procsToKill == null) { + procsToKill = new ArrayList<>(); + } // Add process to kill list. procsToKill.add(proc); } } + if (procsToKill == null) return; // Kill the running processes. Post on handle since we don't want to hold the service lock // while calling into AM. @@ -2677,6 +2734,13 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { processStoppingAndFinishingActivities(null /* launchedActivity */, false /* processPausingActivities */, "transit"); } break; + case KILL_TASK_PROCESSES_TIMEOUT_MSG: { + final Task task = (Task) msg.obj; + if (task.mKillProcessesOnDestroyed) { + Slog.i(TAG, "Destroy timeout of remove-task, attempt to kill " + task); + killTaskProcessesIfPossible(task); + } + } break; case LAUNCH_TASK_BEHIND_COMPLETE: { final ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj); if (r != null) { diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index dcb7fe3fbc8b..0c98fb5000d5 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -1014,9 +1014,7 @@ public class LockTaskController { */ boolean isBaseOfLockedTask(String packageName) { for (int i = 0; i < mLockTaskModeTasks.size(); i++) { - final Intent bi = mLockTaskModeTasks.get(i).getBaseIntent(); - if (bi != null && packageName.equals(bi.getComponent() - .getPackageName())) { + if (packageName.equals(mLockTaskModeTasks.get(i).getBasePackageName())) { return true; } } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index cb94146b2ff2..dda0d6c3c3f2 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -682,10 +682,8 @@ class RecentTasks { void removeTasksByPackageName(String packageName, int userId) { for (int i = mTasks.size() - 1; i >= 0; --i) { final Task task = mTasks.get(i); - final String taskPackageName = - task.getBaseIntent().getComponent().getPackageName(); if (task.mUserId != userId) continue; - if (!taskPackageName.equals(packageName)) continue; + if (!task.getBasePackageName().equals(packageName)) continue; mSupervisor.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-package-task"); } @@ -859,8 +857,7 @@ class RecentTasks { if (task.effectiveUid != callingUid) { continue; } - Intent intent = task.getBaseIntent(); - if (intent == null || !callingPackage.equals(intent.getComponent().getPackageName())) { + if (!callingPackage.equals(task.getBasePackageName())) { continue; } AppTaskImpl taskImpl = new AppTaskImpl(mService, task.mTaskId, callingUid); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 68b2d0fc50d5..f446e4214fc2 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -374,6 +374,13 @@ class Task extends TaskFragment { * determining the order when restoring. */ long mLastTimeMoved; + /** + * If it is set, the processes belong to the task will be killed when one of its activity + * reports that Activity#onDestroy is done and the task no longer contains perceptible + * components. This should only be set on a leaf task. + */ + boolean mKillProcessesOnDestroyed; + /** If original intent did not allow relinquishing task identity, save that information */ private boolean mNeverRelinquishIdentity = true; @@ -1325,6 +1332,20 @@ class Task extends TaskFragment { return (topTask != this && topTask != null) ? topTask.getBaseIntent() : null; } + /** + * Returns the package name which stands for this task. It is empty string if no activities + * have been added to this task. + */ + @NonNull + String getBasePackageName() { + final Intent intent = getBaseIntent(); + if (intent == null) { + return ""; + } + final ComponentName componentName = intent.getComponent(); + return componentName != null ? componentName.getPackageName() : ""; + } + /** Returns the first non-finishing activity from the bottom. */ ActivityRecord getRootActivity() { // TODO: Figure out why we historical ignore relinquish identity for this case... @@ -3660,6 +3681,9 @@ class Task extends TaskFragment { if (mSharedStartingData != null) { pw.println(prefix + "mSharedStartingData=" + mSharedStartingData); } + if (mKillProcessesOnDestroyed) { + pw.println(prefix + "mKillProcessesOnDestroyed=true"); + } pw.print(prefix); pw.print("taskId=" + mTaskId); pw.println(" rootTaskId=" + getRootTaskId()); pw.print(prefix); pw.println("hasChildPipActivity=" + (mChildPipActivity != null)); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 12f9a9ee0ea6..9ebc7307418d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -1032,6 +1032,17 @@ public class RecentTasksTest extends WindowTestsBase { fail("Expected com.android.pkg1 tasks to be removed"); } } + + // If the task has a non-stopped activity, the removal will wait for its onDestroy. + final Task task = tasks.get(0); + final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).build(); + top.lastVisibleTime = 123; + top.setState(ActivityRecord.State.RESUMED, "test"); + mRecentTasks.removeTasksByPackageName(task.getBasePackageName(), TEST_USER_0_ID); + assertTrue(task.mKillProcessesOnDestroyed); + top.setState(ActivityRecord.State.DESTROYING, "test"); + top.destroyed("test"); + assertFalse(task.mKillProcessesOnDestroyed); } @Test |