summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java1
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java78
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java4
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java7
-rw-r--r--services/core/java/com/android/server/wm/Task.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java11
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