summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Riddle Hsu <riddlehsu@google.com> 2020-07-30 19:02:16 +0800
committer Riddle Hsu <riddlehsu@google.com> 2020-08-01 00:04:24 +0800
commit7fce33931b8867337bc88cdf0f4ddf4a1098f5ad (patch)
tree5aabb08acc0eda7aeed5ff1a10d1562d4203823b
parent013b3a20901ed6e3e6d42ef378f900c729649327 (diff)
Simplify handling process died for activity
WindowProcessController should maintain the activity list belonging to it, so there is no need to go through all stacks to match the died process. Now it is simpler and more efficient by just iterating the activities of the process directly. Bug: 161447610 Test: RootWindowContainerTests#testForceStopPackage ActivityRecordTests#testRemoveFromHistory ActivityStackTests Change-Id: I21a647408046bbf7d52813996c263bc992a1deb8
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java73
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java13
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java18
-rw-r--r--services/core/java/com/android/server/wm/Task.java115
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java77
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java25
9 files changed, 173 insertions, 163 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 024ef874757b..c86c85530c06 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2865,7 +2865,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (hasProcess()) {
if (removeFromApp) {
- app.removeActivity(this);
+ app.removeActivity(this, true /* keepAssociation */);
if (!app.hasActivities()) {
mAtmService.clearHeavyWeightProcessIfEquals(app);
// Update any services we are bound to that might care about whether
@@ -2914,7 +2914,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
setState(DESTROYED,
"destroyActivityLocked. not finishing or skipping destroy");
if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + this);
- app = null;
+ detachFromProcess();
}
} else {
// Remove this record from the history.
@@ -2963,13 +2963,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
setState(DESTROYED, "removeFromHistory");
if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + this);
- app = null;
+ detachFromProcess();
removeAppTokenFromDisplay();
cleanUpActivityServices();
removeUriPermissionsLocked();
}
+ void detachFromProcess() {
+ if (app != null) {
+ app.removeActivity(this, false /* keepAssociation */);
+ }
+ app = null;
+ }
+
void makeFinishingLocked() {
if (finishing) {
return;
@@ -3019,7 +3026,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (setState) {
setState(DESTROYED, "cleanUp");
if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + this);
- app = null;
+ detachFromProcess();
}
// Inform supervisor the activity has been removed.
@@ -3160,6 +3167,64 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mServiceConnectionsHolder = null;
}
+ /**
+ * Detach this activity from process and clear the references to it. If the activity is
+ * finishing or has no saved state or crashed many times, it will also be removed from history.
+ */
+ void handleAppDied() {
+ final boolean remove;
+ if ((mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
+ || mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
+ && launchCount < 3 && !finishing) {
+ // If the process crashed during a resize, always try to relaunch it, unless it has
+ // failed more than twice. Skip activities that's already finishing cleanly by itself.
+ remove = false;
+ } else if ((!mHaveState && !stateNotNeeded
+ && !isState(ActivityState.RESTARTING_PROCESS)) || finishing) {
+ // Don't currently have state for the activity, or it is finishing -- always remove it.
+ remove = true;
+ } else if (!mVisibleRequested && launchCount > 2
+ && lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
+ // We have launched this activity too many times since it was able to run, so give up
+ // and remove it. (Note if the activity is visible, we don't remove the record. We leave
+ // the dead window on the screen but the process will not be restarted unless user
+ // explicitly tap on it.)
+ remove = true;
+ } else {
+ // The process may be gone, but the activity lives on!
+ remove = false;
+ }
+ if (remove) {
+ if (ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE || DEBUG_CLEANUP) {
+ Slog.i(TAG_ADD_REMOVE, "Removing activity " + this
+ + " hasSavedState=" + mHaveState + " stateNotNeeded=" + stateNotNeeded
+ + " finishing=" + finishing + " state=" + mState
+ + " callers=" + Debug.getCallers(5));
+ }
+ if (!finishing || (app != null && app.isRemoved())) {
+ Slog.w(TAG, "Force removing " + this + ": app died, no saved state");
+ EventLogTags.writeWmFinishActivity(mUserId, System.identityHashCode(this),
+ task != null ? task.mTaskId : -1, shortComponentName,
+ "proc died without state saved");
+ }
+ } else {
+ // We have the current state for this activity, so it can be restarted later
+ // when needed.
+ if (DEBUG_APP) {
+ Slog.v(TAG_APP, "Keeping entry during removeHistory for activity " + this);
+ }
+ // Set nowVisible to previous visible state. If the app was visible while it died, we
+ // leave the dead window on screen so it's basically visible. This is needed when user
+ // later tap on the dead window, we need to stop other apps when user transfers focus
+ // to the restarted activity.
+ nowVisible = mVisibleRequested;
+ }
+ cleanUp(true /* cleanServices */, true /* setState */);
+ if (remove) {
+ removeFromHistory("appDied");
+ }
+ }
+
@Override
void removeImmediately() {
onRemovedFromDisplay();
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 04b1edc3eede..ed1ea353eb3e 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -891,7 +891,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// This is the first time we failed -- restart process and
// retry.
r.launchFailed = true;
- proc.removeActivity(r);
+ proc.removeActivity(r, true /* keepAssociation */);
throw e;
}
} finally {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index c4af3e2c04c9..f91ff6f1c76c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6789,11 +6789,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public void handleAppDied(WindowProcessController wpc, boolean restarting,
Runnable finishInstrumentationCallback) {
synchronized (mGlobalLockWithoutBoost) {
- // Remove this application's activities from active lists.
- boolean hasVisibleActivities = mRootWindowContainer.handleAppDied(wpc);
-
- wpc.clearRecentTasks();
- wpc.clearActivities();
+ mStackSupervisor.beginDeferResume();
+ final boolean hasVisibleActivities;
+ try {
+ // Remove this application's activities from active lists.
+ hasVisibleActivities = wpc.handleAppDied();
+ } finally {
+ mStackSupervisor.endDeferResume();
+ }
if (wpc.isInstrumenting()) {
finishInstrumentationCallback.run();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 06dec7c8023d..526ec08930ea 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2751,7 +2751,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (r.app != app) return;
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- r.app = null;
+ r.detachFromProcess();
r.getDisplay().mDisplayContent.prepareAppTransition(
TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
r.destroyIfPossible("handleAppCrashed");
@@ -3104,22 +3104,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return null;
}
- boolean handleAppDied(WindowProcessController app) {
- if (app.isRemoved()) {
- // The package of the died process should be force-stopped, so make its activities as
- // finishing to prevent the process from being started again if the next top (or being
- // visible) activity also resides in the same process.
- app.makeFinishingForProcessRemoved();
- }
- return reduceOnAllTaskDisplayAreas((taskDisplayArea, result) -> {
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final Task stack = taskDisplayArea.getStackAt(sNdx);
- result |= stack.handleAppDied(app);
- }
- return result;
- }, false /* initValue */);
- }
-
void closeSystemDialogActivities(String reason) {
forAllActivities((r) -> {
if ((r.info.flags & ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 897b68073bd2..f3620050bed2 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -86,8 +86,6 @@ import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
@@ -115,8 +113,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIB
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
-import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
-import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -746,111 +742,6 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- // TODO: Can we just loop through WindowProcessController#mActivities instead of doing this?
- private final RemoveHistoryRecordsForApp mRemoveHistoryRecordsForApp =
- new RemoveHistoryRecordsForApp();
- private class RemoveHistoryRecordsForApp {
- private boolean mHasVisibleActivities;
- private boolean mIsProcessRemoved;
- private WindowProcessController mApp;
- private ArrayList<ActivityRecord> mToRemove = new ArrayList<>();
-
- boolean process(WindowProcessController app) {
- mToRemove.clear();
- mHasVisibleActivities = false;
- mApp = app;
- mIsProcessRemoved = app.isRemoved();
-
- final PooledConsumer c = PooledLambda.obtainConsumer(
- RemoveHistoryRecordsForApp::addActivityToRemove, this,
- PooledLambda.__(ActivityRecord.class));
- forAllActivities(c);
- c.recycle();
-
- while (!mToRemove.isEmpty()) {
- processActivity(mToRemove.remove(0));
- }
-
- mApp = null;
- return mHasVisibleActivities;
- }
-
- private void addActivityToRemove(ActivityRecord r) {
- if (r.app == mApp) {
- mToRemove.add(r);
- }
- }
-
- private void processActivity(ActivityRecord r) {
- if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "Record " + r + ": app=" + r.app);
-
- if (r.app != mApp) {
- return;
- }
- if (r.isVisible() || r.mVisibleRequested) {
- // While an activity launches a new activity, it's possible that the old
- // activity is already requested to be hidden (mVisibleRequested=false), but
- // this visibility is not yet committed, so isVisible()=true.
- mHasVisibleActivities = true;
- }
- final boolean remove;
- if ((r.mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
- || r.mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
- && r.launchCount < 3 && !r.finishing) {
- // If the process crashed during a resize, always try to relaunch it, unless
- // it has failed more than twice. Skip activities that's already finishing
- // cleanly by itself.
- remove = false;
- } else if ((!r.hasSavedState() && !r.stateNotNeeded
- && !r.isState(ActivityState.RESTARTING_PROCESS)) || r.finishing) {
- // Don't currently have state for the activity, or
- // it is finishing -- always remove it.
- remove = true;
- } else if (!r.mVisibleRequested && r.launchCount > 2
- && r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
- // We have launched this activity too many times since it was
- // able to run, so give up and remove it.
- // (Note if the activity is visible, we don't remove the record.
- // We leave the dead window on the screen but the process will
- // not be restarted unless user explicitly tap on it.)
- remove = true;
- } else {
- // The process may be gone, but the activity lives on!
- remove = false;
- }
- if (remove) {
- if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) Slog.i(TAG_ADD_REMOVE,
- "Removing activity " + r + " from stack "
- + ": hasSavedState=" + r.hasSavedState()
- + " stateNotNeeded=" + r.stateNotNeeded
- + " finishing=" + r.finishing
- + " state=" + r.getState() + " callers=" + Debug.getCallers(5));
- if (!r.finishing || mIsProcessRemoved) {
- Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
- EventLogTags.writeWmFinishActivity(r.mUserId,
- System.identityHashCode(r), r.getTask().mTaskId,
- r.shortComponentName, "proc died without state saved");
- }
- } else {
- // We have the current state for this activity, so
- // it can be restarted later when needed.
- if (DEBUG_ALL) Slog.v(TAG, "Keeping entry, setting app to null");
- if (DEBUG_APP) Slog.v(TAG_APP,
- "Clearing app during removeHistory for activity " + r);
- r.app = null;
- // Set nowVisible to previous visible state. If the app was visible while
- // it died, we leave the dead window on screen so it's basically visible.
- // This is needed when user later tap on the dead window, we need to stop
- // other apps when user transfers focus to the restarted activity.
- r.nowVisible = r.mVisibleRequested;
- }
- r.cleanUp(true /* cleanServices */, true /* setState */);
- if (remove) {
- r.removeFromHistory("appDied");
- }
- }
- }
-
private final FindRootHelper mFindRootHelper = new FindRootHelper();
private class FindRootHelper {
private ActivityRecord mRoot;
@@ -7167,9 +7058,8 @@ class Task extends WindowContainer<WindowContainer> {
/**
* Reset local parameters because an app's activity died.
* @param app The app of the activity that died.
- * @return result from removeHistoryRecordsForAppLocked.
*/
- boolean handleAppDied(WindowProcessController app) {
+ void handleAppDied(WindowProcessController app) {
if (mPausingActivity != null && mPausingActivity.app == app) {
if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG_PAUSE,
"App died while pausing: " + mPausingActivity);
@@ -7179,9 +7069,6 @@ class Task extends WindowContainer<WindowContainer> {
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
-
- mStackSupervisor.removeHistoryRecords(app);
- return mRemoveHistoryRecordsForApp.process(app);
}
boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 6ba8769842f6..df59c56f5aff 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -177,8 +177,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// Whether this process has ever started a service with the BIND_INPUT_METHOD permission.
private volatile boolean mHasImeService;
- // all activities running in the process
+ /** All activities running in the process (exclude destroying). */
private final ArrayList<ActivityRecord> mActivities = new ArrayList<>();
+ /** The activities will be removed but still belong to this process. */
+ private ArrayList<ActivityRecord> mInactiveActivities;
// any tasks this process had run root activities in
private final ArrayList<Task> mRecentTasks = new ArrayList<>();
// The most recent top-most activity that was resumed in the process for pre-Q app.
@@ -635,21 +637,39 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return;
}
mActivities.add(r);
+ if (mInactiveActivities != null) {
+ mInactiveActivities.remove(r);
+ }
updateActivityConfigurationListener();
}
- void removeActivity(ActivityRecord r) {
+ /**
+ * Indicates that the given activity is no longer active in this process.
+ *
+ * @param r The running activity to be removed.
+ * @param keepAssociation {@code true} if the activity still belongs to this process but will
+ * be removed soon, e.g. destroying. From the perspective of process
+ * priority, the process is not important if it only contains activities
+ * that are being destroyed. But the association is still needed to
+ * ensure all activities are reachable from this process.
+ */
+ void removeActivity(ActivityRecord r, boolean keepAssociation) {
+ if (keepAssociation) {
+ if (mInactiveActivities == null) {
+ mInactiveActivities = new ArrayList<>();
+ mInactiveActivities.add(r);
+ } else if (!mInactiveActivities.contains(r)) {
+ mInactiveActivities.add(r);
+ }
+ } else if (mInactiveActivities != null) {
+ mInactiveActivities.remove(r);
+ }
mActivities.remove(r);
updateActivityConfigurationListener();
}
- void makeFinishingForProcessRemoved() {
- for (int i = mActivities.size() - 1; i >= 0; --i) {
- mActivities.get(i).makeFinishingLocked();
- }
- }
-
void clearActivities() {
+ mInactiveActivities = null;
mActivities.clear();
updateActivityConfigurationListener();
}
@@ -1142,6 +1162,47 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mAtm.mH.sendMessage(m);
}
+ /**
+ * Clean up the activities belonging to this process.
+ *
+ * @return {@code true} if the process has any visible activity.
+ */
+ boolean handleAppDied() {
+ mAtm.mStackSupervisor.removeHistoryRecords(this);
+
+ final boolean isRemoved = isRemoved();
+ boolean hasVisibleActivities = false;
+ if (mInactiveActivities != null && !mInactiveActivities.isEmpty()) {
+ // Make sure that all activities in this process are handled.
+ mActivities.addAll(mInactiveActivities);
+ }
+ for (int i = mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = mActivities.get(i);
+ if (r.mVisibleRequested || r.isVisible()) {
+ // While an activity launches a new activity, it's possible that the old activity
+ // is already requested to be hidden (mVisibleRequested=false), but this visibility
+ // is not yet committed, so isVisible()=true.
+ hasVisibleActivities = true;
+ }
+ if (isRemoved) {
+ // The package of the died process should be force-stopped, so make its activities
+ // as finishing to prevent the process from being started again if the next top (or
+ // being visible) activity also resides in the same process.
+ r.makeFinishingLocked();
+ }
+
+ final Task rootTask = r.getRootTask();
+ if (rootTask != null) {
+ rootTask.handleAppDied(this);
+ }
+ r.handleAppDied();
+ }
+ clearRecentTasks();
+ clearActivities();
+
+ return hasVisibleActivities;
+ }
+
void registerDisplayConfigurationListener(DisplayContent displayContent) {
if (displayContent == null) {
return;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index e45ced64c5b6..2f020736d059 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1327,12 +1327,15 @@ public class ActivityRecordTests extends ActivityTestsBase {
public void testRemoveFromHistory() {
final Task stack = mActivity.getRootTask();
final Task task = mActivity.getTask();
+ final WindowProcessController wpc = mActivity.app;
+ assertTrue(wpc.hasActivities());
mActivity.removeFromHistory("test");
assertEquals(DESTROYED, mActivity.getState());
assertNull(mActivity.app);
assertNull(mActivity.getTask());
+ assertFalse(wpc.hasActivities());
assertEquals(0, task.getChildCount());
assertEquals(task.getRootTask(), task);
assertEquals(0, stack.getChildCount());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 775df74f88e0..dfdf686e5502 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1072,7 +1072,7 @@ public class ActivityStackTests extends ActivityTestsBase {
assertEquals(2, mTask.getChildCount());
- mRootWindowContainer.handleAppDied(secondActivity.app);
+ secondActivity.app.handleAppDied();
assertFalse(mTask.hasChild());
assertFalse(mStack.hasChild());
@@ -1086,7 +1086,7 @@ public class ActivityStackTests extends ActivityTestsBase {
activity.launchCount = 1;
activity.setSavedState(null /* savedState */);
- mRootWindowContainer.handleAppDied(activity.app);
+ activity.app.handleAppDied();
assertEquals(1, mTask.getChildCount());
assertEquals(1, mStack.getChildCount());
@@ -1100,7 +1100,7 @@ public class ActivityStackTests extends ActivityTestsBase {
activity.launchCount = 3;
activity.setSavedState(null /* savedState */);
- mRootWindowContainer.handleAppDied(activity.app);
+ activity.app.handleAppDied();
assertFalse(mTask.hasChild());
assertFalse(mStack.hasChild());
@@ -1114,7 +1114,7 @@ public class ActivityStackTests extends ActivityTestsBase {
activity.launchCount = 1;
activity.setSavedState(null /* savedState */);
- mRootWindowContainer.handleAppDied(activity.app);
+ activity.app.handleAppDied();
assertEquals(1, mTask.getChildCount());
assertEquals(1, mStack.getChildCount());
@@ -1128,7 +1128,7 @@ public class ActivityStackTests extends ActivityTestsBase {
activity.launchCount = 3;
activity.setSavedState(null /* savedState */);
- mRootWindowContainer.handleAppDied(activity.app);
+ activity.app.handleAppDied();
assertFalse(mTask.hasChild());
assertFalse(mStack.hasChild());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 2e4c9ea747c5..d9d07d9beb2b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -175,23 +175,30 @@ public class RootWindowContainerTests extends WindowTestsBase {
@Test
public void testForceStopPackage() {
final Task task = new ActivityTestsBase.StackBuilder(mWm.mRoot).build();
- final ActivityRecord activity1 = task.getTopMostActivity();
- final ActivityRecord activity2 =
- new ActivityTestsBase.ActivityBuilder(mWm.mAtmService).setStack(task).build();
- final WindowProcessController wpc = activity1.app;
+ final ActivityRecord activity = task.getTopMostActivity();
+ final WindowProcessController wpc = activity.app;
+ final ActivityRecord[] activities = {
+ activity,
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(task).setUseProcess(wpc).build(),
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(task).setUseProcess(wpc).build()
+ };
+ activities[0].detachFromProcess();
+ activities[1].finishing = true;
+ activities[1].destroyImmediately(true /* removeFromApp */, "test");
spyOn(wpc);
- activity1.app = null;
- activity2.setProcess(wpc);
doReturn(true).when(wpc).isRemoved();
mWm.mAtmService.mInternal.onForceStopPackage(wpc.mInfo.packageName, true /* doit */,
false /* evenPersistent */, wpc.mUserId);
// The activity without process should be removed.
- assertEquals(1, task.getChildCount());
+ assertEquals(2, task.getChildCount());
- mWm.mRoot.handleAppDied(wpc);
- // The activity with process should be removed because WindowProcessController#isRemoved.
+ wpc.handleAppDied();
+ // The activities with process should be removed because WindowProcessController#isRemoved.
assertFalse(task.hasChild());
+ assertFalse(wpc.hasActivities());
}
}