Make PINNED mode alwaysCreateStack

Now that we don't have a re-used pinned stack anymore, this is
how it should be. This also helps CTS align closer to actual
app usage.

Also don't perform transactions on detached containers (but log
an error). This is because CTS triggers various pip events which
fire callbacks after the test tears-down.

Bug: 153201813
Test: PinnedStackTests pass
Change-Id: I57bf4db35f7c05c18a69d2bf39b66ccca07d39f2
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index bcad758..d88d76c 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -783,6 +783,11 @@
         if (currentMode == WINDOWING_MODE_PINNED) {
             mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned();
         }
+        if (likelyResolvedMode == WINDOWING_MODE_PINNED
+                && taskDisplayArea.getRootPinnedTask() != null) {
+            // Can only have 1 pip at a time, so replace an existing pip
+            taskDisplayArea.getRootPinnedTask().dismissPip();
+        }
         if (likelyResolvedMode != WINDOWING_MODE_FULLSCREEN
                 && topActivity != null && !topActivity.noDisplay
                 && topActivity.isNonResizableOrForcedResizable(likelyResolvedMode)) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 80a1a45..8d46a67 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -25,6 +25,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -5028,6 +5029,7 @@
         return activityType == ACTIVITY_TYPE_STANDARD
                 && (windowingMode == WINDOWING_MODE_FULLSCREEN
                 || windowingMode == WINDOWING_MODE_FREEFORM
+                || windowingMode == WINDOWING_MODE_PINNED
                 || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
                 || windowingMode == WINDOWING_MODE_MULTI_WINDOW);
     }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f8ee09b..ee6cc9a 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3072,6 +3072,7 @@
         mForceShowForAllUsers = forceShowForAllUsers;
     }
 
+    @Override
     public boolean isAttached() {
         final TaskDisplayArea taskDisplayArea = getDisplayArea();
         return taskDisplayArea != null && !taskDisplayArea.isRemoved();
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index ea9a362..d71e561 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -879,6 +879,11 @@
                     + windowingMode);
         }
 
+        if (windowingMode == WINDOWING_MODE_PINNED && getRootPinnedTask() != null) {
+            // Only 1 stack can be PINNED at a time, so dismiss the existing one
+            getRootPinnedTask().dismissPip();
+        }
+
         final int stackId = getNextStackId();
         return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent,
                 createdByOrganizer);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index fba22dd..7219164 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -737,6 +737,10 @@
         return parent != null ? parent.getDisplayArea() : null;
     }
 
+    boolean isAttached() {
+        return getDisplayArea() != null;
+    }
+
     void setWaitingForDrawnIfResizingChanged() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 86bc013..d9c0219 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -129,6 +129,10 @@
                         final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
                                 entries.next();
                         final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
+                        if (!wc.isAttached()) {
+                            Slog.e(TAG, "Attempt to operate on detached container: " + wc);
+                            continue;
+                        }
                         int containerEffect = applyWindowContainerChange(wc, entry.getValue());
                         effects |= containerEffect;
 
@@ -146,6 +150,10 @@
                     for (int i = 0, n = hops.size(); i < n; ++i) {
                         final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
                         final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+                        if (!wc.isAttached()) {
+                            Slog.e(TAG, "Attempt to operate on detached container: " + wc);
+                            continue;
+                        }
                         effects |= sanitizeAndApplyHierarchyOp(wc, hop);
                     }
                     if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {