Save window when an app died while it's visible

- If an app died visible, keep the dead window and leave it on screen.

- Apply 50% dim over the dead window to indicate it's no longer active.

- Monitor touch inputs on the dead window and restart the app on tap.

bug: 24913379
Change-Id: I911da4e6135f2bffaf3b1bbe6f911ff689a278ff
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c1fa78a..984a353 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -340,6 +340,12 @@
     boolean mRemoveOnExit;
 
     /**
+     * Whether the app died while it was visible, if true we might need
+     * to continue to show it until it's restarted.
+     */
+    boolean mAppDied;
+
+    /**
      * Set when the orientation is changing and this window has not yet
      * been updated for the new orientation.
      */
@@ -362,6 +368,7 @@
     // Input channel and input window handle used by the input dispatcher.
     final InputWindowHandle mInputWindowHandle;
     InputChannel mInputChannel;
+    InputChannel mClientChannel;
 
     // Used to improve performance of toString()
     String mStringNameCache;
@@ -1274,16 +1281,28 @@
         mConfigHasChanged = false;
     }
 
-    void setInputChannel(InputChannel inputChannel) {
+    void openInputChannel(InputChannel outInputChannel) {
         if (mInputChannel != null) {
             throw new IllegalStateException("Window already has an input channel.");
         }
-
-        mInputChannel = inputChannel;
-        mInputWindowHandle.inputChannel = inputChannel;
+        String name = makeInputChannelName();
+        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
+        mInputChannel = inputChannels[0];
+        mClientChannel = inputChannels[1];
+        mInputWindowHandle.inputChannel = inputChannels[0];
+        if (outInputChannel != null) {
+            mClientChannel.transferTo(outInputChannel);
+            mClientChannel.dispose();
+            mClientChannel = null;
+        }
+        mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
     }
 
     void disposeInputChannel() {
+        if (mClientChannel != null) {
+            mClientChannel.dispose();
+            mClientChannel = null;
+        }
         if (mInputChannel != null) {
             mService.mInputManager.unregisterInputChannel(mInputChannel);
 
@@ -1294,10 +1313,13 @@
         mInputWindowHandle.inputChannel = null;
     }
 
-    void handleFlagDimBehind() {
-        if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 && mDisplayContent != null && !mExiting
-                && isDisplayedLw()) {
-            mDisplayContent.mDimBehindController.applyDimBehind(getDimLayerUser(), mWinAnimator);
+    void applyDimLayerIfNeeded() {
+        if (!mExiting && mAppDied) {
+            // If app died visible, apply a dim over the window to indicate that it's inactive
+            mDisplayContent.mDimLayerController.applyDimAbove(getDimLayerUser(), mWinAnimator);
+        } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
+                && mDisplayContent != null && !mExiting && isDisplayedLw()) {
+            mDisplayContent.mDimLayerController.applyDimBehind(getDimLayerUser(), mWinAnimator);
         }
     }
 
@@ -1375,6 +1397,9 @@
                     WindowState win = mService.windowForClientLocked(mSession, mClient, false);
                     Slog.i(TAG, "WIN DEATH: " + win);
                     if (win != null) {
+                        if (win.mAppToken != null && !win.mAppToken.clientHidden) {
+                            win.mAppToken.appDied = true;
+                        }
                         mService.removeWindowLocked(win);
                     } else if (mHasSurface) {
                         Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
@@ -1574,7 +1599,7 @@
     public boolean isDimming() {
         final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
         return dimLayerUser != null && mDisplayContent != null &&
-                mDisplayContent.mDimBehindController.isDimming(dimLayerUser, mWinAnimator);
+                mDisplayContent.mDimLayerController.isDimming(dimLayerUser, mWinAnimator);
     }
 
     public void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
@@ -1833,7 +1858,8 @@
             pw.print(prefix); pw.print("mToken="); pw.println(mToken);
             pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken);
             if (mAppToken != null) {
-                pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
+                pw.print(prefix); pw.print("mAppToken="); pw.print(mAppToken);
+                pw.print(" mAppDied=");pw.println(mAppDied);
             }
             if (mTargetAppToken != null) {
                 pw.print(prefix); pw.print("mTargetAppToken="); pw.println(mTargetAppToken);