Remove window container from empty task when destroying.

Previously, the window container was being removed whenever we were
removing the task in a destroy mode. However, this caused issues
where an activity may still be present in the task record, leading to
a subequent change to limit this to tasks with overlays. This led to
the situation where the window container would not be destroyed when
it was supposed to, such as moving an activity to recents, but
otherwise destroying.

This changelist addresses the issue by always removing the window
container from tasks when removed from their parent stack in a
destroy mode and empty. In the recents flow, this will fire and
cleanup when the activity is destroyed.

Change-Id: I87548d6ff8e4d77b6294504d7cc78370cd5d31fa
Fixes: 37301159
Test: bit FrameworksServicesTests:com.android.server.am.ActivityStackTests
(cherry picked from commit 840c566d13b8c84ad5edb37006176d6731bad250)
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index b998314..007e3b7 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -116,6 +116,7 @@
 import android.util.SparseArray;
 import android.view.Display;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.Watchdog;
@@ -230,9 +231,10 @@
     // activities and there is a specific combination of stacks.
     private static final int STACK_VISIBLE_ACTIVITY_BEHIND = 2;
 
+    @VisibleForTesting
     /* The various modes for the method {@link #removeTask}. */
     // Task is being completely removed from all stacks in the system.
-    private static final int REMOVE_TASK_MODE_DESTROYING = 0;
+    protected static final int REMOVE_TASK_MODE_DESTROYING = 0;
     // Task is being removed from this stack so we can add it to another stack. In the case we are
     // moving we don't want to perform some operations on the task like removing it from window
     // manager or recents.
@@ -3984,7 +3986,6 @@
                 //       where we are destroying the task, move this back into removeTask()
                 mStackSupervisor.removeTaskByIdLocked(task.taskId, false /* killProcess */,
                         !REMOVE_FROM_RECENTS, PAUSE_IMMEDIATELY);
-                task.removeWindowContainer();
             }
             removeTask(task, reason, REMOVE_TASK_MODE_DESTROYING);
         }
@@ -5076,6 +5077,8 @@
                 mRecentTasks.remove(task);
                 task.removedFromRecents();
             }
+
+            task.removeWindowContainer();
         }
 
         if (mTaskHistory.isEmpty()) {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
new file mode 100644
index 0000000..1d80b33
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.ComponentName;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link ActivityStack} class.
+ *
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.am.ActivityStackTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActivityStackTests extends ActivityTestsBase {
+    private final ComponentName testActivityComponent =
+            ComponentName.unflattenFromString("com.foo/.BarActivity");
+
+    @Test
+    public void testEmptyTaskCleanupOnRemove() throws Exception {
+        final ActivityManagerService service = createActivityManagerService();
+        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
+        final TaskRecord task = createTask(service, testActivityComponent, testStack);
+        assertNotNull(task.getWindowContainerController());
+        testStack.removeTask(task, "testEmptyTaskCleanupOnRemove",
+                ActivityStack.REMOVE_TASK_MODE_DESTROYING);
+        assertNull(task.getWindowContainerController());
+    }
+    @Test
+    public void testOccupiedTaskCleanupOnRemove() throws Exception {
+        final ActivityManagerService service = createActivityManagerService();
+        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
+        final TaskRecord task = createTask(service, testActivityComponent, testStack);
+        final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task);
+        assertNotNull(task.getWindowContainerController());
+        testStack.removeTask(task, "testOccupiedTaskCleanupOnRemove",
+                ActivityStack.REMOVE_TASK_MODE_DESTROYING);
+        assertNotNull(task.getWindowContainerController());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 5240586..3bf0e5f 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -18,6 +18,7 @@
 
 import static org.mockito.Mockito.mock;
 
+import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -62,14 +63,16 @@
     }
 
     protected ActivityManagerService createActivityManagerService() {
-        return new TestActivityManagerService(mContext);
+        final ActivityManagerService service = new TestActivityManagerService(mContext);
+        service.mWindowManager = WindowTestUtils.getWindowManagerService(mContext);
+        return service;
     }
 
     protected static TestActivityStack createActivityStack(ActivityManagerService service,
             int stackId, int displayId, boolean onTop) {
         if (service.mStackSupervisor instanceof TestActivityStackSupervisor) {
             final TestActivityStack stack = ((TestActivityStackSupervisor) service.mStackSupervisor)
-                    .createTestStack(stackId, onTop);
+                    .createTestStack(service, stackId, onTop);
             return stack;
         }
 
@@ -109,7 +112,7 @@
         intent.setComponent(component);
 
         final TaskRecord task = new TaskRecord(service, 0, aInfo, intent /*intent*/,
-                null /*_taskDescription*/, null /*thumbnailInfo*/);
+                null /*_taskDescription*/, new ActivityManager.TaskThumbnailInfo());
         stack.addTask(task, true, "creating test task");
         task.setStack(stack);
         task.createWindowContainer(true, true);
@@ -146,25 +149,39 @@
         void updateUIDsPresentOnDisplay() {
         }
 
-        public TestActivityStack createTestStack(int stackId, boolean onTop) {
+        public TestActivityStack createTestStack(ActivityManagerService service, int stackId,
+                boolean onTop) {
             final ActivityDisplay display = new ActivityDisplay();
             final TestActivityContainer container =
-                    new TestActivityContainer(stackId, display, onTop);
+                    new TestActivityContainer(service, stackId, display, onTop);
             return container.getStack();
         }
 
         private class TestActivityContainer extends ActivityContainer {
+            private ActivityManagerService mService;
             private TestActivityStack mStack;
-            TestActivityContainer(int stackId, ActivityDisplay activityDisplay, boolean onTop) {
+            private boolean mOnTop;
+
+            TestActivityContainer(ActivityManagerService service, int stackId,
+                    ActivityDisplay activityDisplay, boolean onTop) {
                 super(stackId, activityDisplay, onTop);
+                mService = service;
             }
 
             @Override
             protected void createStack(int stackId, boolean onTop) {
-                mStack = new TestActivityStack(this, null /*recentTasks*/, onTop);
+                // normally stack creation is done here. However we need to do it on demand since
+                // we cannot set {@link mService} by the time the super constructor calling this
+                // method is invoked.
+                mOnTop = onTop;
             }
 
             public TestActivityStack getStack() {
+                if (mStack == null) {
+                    mStack = new TestActivityStack(this,
+                            new RecentTasks(mService, mService.mStackSupervisor), mOnTop);
+                }
+
                 return mStack;
             }
         }