Reparent bounds layer if surface was replaced.

In a normal case, the app will get either a visibility changed to
false or stopped from system server to indicate that the content
should be cleaned up. In those cases, it will request a relayout
to WM, which will notify VRI that the surface is gone. Since the
VRI surface is removed, the SV and bounds layer will get notified
that they should be destroyed. On the next relayout, the VRI will
get a new surface and notify SV and the bounds layer to create
new surfaces.

The scenario in the bug happens when VRI receives a visibility change to
false, but doesn't process the information fast enough. By the time it
calls relayout, WMS is in the state where the client is visible again. This
time the relayout will return a valid surface so the SV and bounds layer
will not get cleaned up. The reason this causes issues is VRI now has a
new main surface. The bounds layer is parented to the old layer that's been
deleted so it's not parented to anything. The SurfaceView is a child of
the bounds layer so it also renders offscreen.

The reparent needs to be done on the client side since WMS won't
reparent the children to the new surface if it thinks the app is
closing. WMS gets the signal that the app is stopping, but on the client
side, it doesn't get stopped since it's restarted quick enough. WMS
doesn't want to keep around old children since they will leak when the
client creates new children.

This change will reparent the bounds layer to the new VRI surface. It's
possible WMS will take care of this in certain cases where the app
isn't stopped. But for those cases, the reparent will not have
any effect since the bounds layer will be correctly parented.

Fixes: 159545768
Test: Can no longer repro black SV in maps
Test: Test app with SV and delay in onPause no longer has the same issue
Change-Id: I616652e1cc4380ebbcb386586d8d5889fcc19497
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a586425..814293a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1824,13 +1824,19 @@
     /**
      * Called after window layout to update the bounds surface. If the surface insets have changed
      * or the surface has resized, update the bounds surface.
+     *
+     * @param shouldReparent Whether it should reparent the bounds layer to the main SurfaceControl.
      */
-    private void updateBoundsLayer() {
+    private void updateBoundsLayer(boolean shouldReparent) {
         if (mBoundsLayer != null) {
             setBoundsLayerCrop();
-            mTransaction.deferTransactionUntil(mBoundsLayer,
-                    getRenderSurfaceControl(), mSurface.getNextFrameNumber())
-                    .apply();
+            mTransaction.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(),
+                    mSurface.getNextFrameNumber());
+
+            if (shouldReparent) {
+                mTransaction.reparent(mBoundsLayer, getRenderSurfaceControl());
+            }
+            mTransaction.apply();
         }
     }
 
@@ -2913,7 +2919,16 @@
         }
 
         if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) {
-            updateBoundsLayer();
+            // If the surface has been replaced, there's a chance the bounds layer is not parented
+            // to the new layer. When updating bounds layer, also reparent to the main VRI
+            // SurfaceControl to ensure it's correctly placed in the hierarchy.
+            //
+            // This needs to be done on the client side since WMS won't reparent the children to the
+            // new surface if it thinks the app is closing. WMS gets the signal that the app is
+            // stopping, but on the client side it doesn't get stopped since it's restarted quick
+            // enough. WMS doesn't want to keep around old children since they will leak when the
+            // client creates new children.
+            updateBoundsLayer(surfaceReplaced);
         }
 
         final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);