Optimize surface hierarchy

- Do not reset layer if layer doesn't change. JNI is expensive.
- Only assign child layers once if we have to do multiple passes.
- Fix issue where layouting the starting window caused the surface
to be resized, leading to a blocking SF transaction.

Test: go/wm-smoke
Test: adb logcat -b events | grep 319, inspect transition times.
Fixes: 69632880
Change-Id: I8fffca30591730fae87ed3aeb9df0bdf7d4b55dd
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ca8ffca..91cad46 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -135,6 +135,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.util.DisplayMetrics;
 import android.util.MutableBoolean;
 import android.util.Slog;
@@ -178,20 +179,20 @@
 
     /** The containers below are the only child containers the display can have. */
     // Contains all window containers that are related to apps (Activities)
-    private final TaskStackContainers mTaskStackContainers = new TaskStackContainers();
+    private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mService);
     // Contains all non-app window containers that should be displayed above the app containers
     // (e.g. Status bar)
     private final AboveAppWindowContainers mAboveAppWindowsContainers =
-            new AboveAppWindowContainers("mAboveAppWindowsContainers");
+            new AboveAppWindowContainers("mAboveAppWindowsContainers", mService);
     // Contains all non-app window containers that should be displayed below the app containers
     // (e.g. Wallpaper).
     private final NonAppWindowContainers mBelowAppWindowsContainers =
-            new NonAppWindowContainers("mBelowAppWindowsContainers");
+            new NonAppWindowContainers("mBelowAppWindowsContainers", mService);
     // Contains all IME window containers. Note that the z-ordering of the IME windows will depend
     // on the IME target. We mainly have this container grouping so we can keep track of all the IME
     // window containers together and move them in-sync if/when needed.
     private final NonAppWindowContainers mImeWindowsContainers =
-            new NonAppWindowContainers("mImeWindowsContainers");
+            new NonAppWindowContainers("mImeWindowsContainers", mService);
 
     private WindowState mTmpWindow;
     private WindowState mTmpWindow2;
@@ -317,8 +318,6 @@
     /** Used for handing back size of display */
     private final Rect mTmpBounds = new Rect();
 
-    WindowManagerService mService;
-
     /** Remove this display when animation on it has completed. */
     private boolean mDeferredRemoval;
 
@@ -765,6 +764,7 @@
      */
     DisplayContent(Display display, WindowManagerService service,
             WallpaperController wallpaperController) {
+        super(service);
         if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
             throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
                     + " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
@@ -777,7 +777,6 @@
         display.getDisplayInfo(mDisplayInfo);
         display.getMetrics(mDisplayMetrics);
         isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
-        mService = service;
         mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo);
         initializeDisplayBaseInfo();
         mDividerControllerLocked = new DockedStackDividerController(service, this);
@@ -2339,6 +2338,7 @@
 
     /** Updates the layer assignment of windows on this display. */
     void assignWindowLayers(boolean setLayoutNeeded) {
+        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers");
         assignChildLayers(getPendingTransaction());
         if (setLayoutNeeded) {
             setLayoutNeeded();
@@ -2349,6 +2349,7 @@
         // prepareSurfaces. This allows us to synchronize Z-ordering changes with
         // the hiding and showing of surfaces.
         scheduleAnimation();
+        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
     }
 
     // TODO: This should probably be called any time a visual change is made to the hierarchy like
@@ -3182,6 +3183,10 @@
      */
     static class DisplayChildWindowContainer<E extends WindowContainer> extends WindowContainer<E> {
 
+        DisplayChildWindowContainer(WindowManagerService service) {
+            super(service);
+        }
+
         @Override
         boolean fillsParent() {
             return true;
@@ -3209,6 +3214,10 @@
         private TaskStack mPinnedStack = null;
         private TaskStack mSplitScreenPrimaryStack = null;
 
+        TaskStackContainers(WindowManagerService service) {
+            super(service);
+        }
+
         /**
          * Returns the topmost stack on the display that is compatible with the input windowing mode
          * and activity type. Null is no compatible stack on the display.
@@ -3516,35 +3525,37 @@
 
         @Override
         void assignChildLayers(SurfaceControl.Transaction t) {
-            final int NORMAL_STACK_STATE = 0;
-            final int BOOSTED_STATE = 1;
-            final int ALWAYS_ON_TOP_STATE = 2;
+            int layer = 0;
 
             // We allow stacks to change visual order from the AM specified order due to
             // Z-boosting during animations. However we must take care to ensure TaskStacks
             // which are marked as alwaysOnTop remain that way.
-            int layer = 0;
-            for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
-                for (int i = 0; i < mChildren.size(); i++) {
-                    final TaskStack s = mChildren.get(i);
-                    layer++;
-                    if (state == NORMAL_STACK_STATE) {
-                        s.assignLayer(t, layer);
-                    } else if (state == BOOSTED_STATE && s.needsZBoost()) {
-                        s.assignLayer(t, layer);
-                    } else if (state == ALWAYS_ON_TOP_STATE &&
-                            s.isAlwaysOnTop()) {
-                        s.assignLayer(t, layer);
-                    }
-                    s.assignChildLayers(t);
+            for (int i = 0; i < mChildren.size(); i++) {
+                final TaskStack s = mChildren.get(i);
+                s.assignChildLayers();
+                if (!s.needsZBoost() && !s.isAlwaysOnTop()) {
+                    s.assignLayer(t, layer++);
                 }
-                // The appropriate place for App-Transitions to occur is right
-                // above all other animations but still below things in the Picture-and-Picture
-                // windowing mode.
-                if (state == BOOSTED_STATE && mAnimationLayer != null) {
-                    t.setLayer(mAnimationLayer, layer + 1);
+            }
+            for (int i = 0; i < mChildren.size(); i++) {
+                final TaskStack s = mChildren.get(i);
+                if (s.needsZBoost() && !s.isAlwaysOnTop()) {
+                    s.assignLayer(t, layer++);
                 }
             }
+            for (int i = 0; i < mChildren.size(); i++) {
+                final TaskStack s = mChildren.get(i);
+                if (s.isAlwaysOnTop()) {
+                    s.assignLayer(t, layer++);
+                }
+            }
+
+            // The appropriate place for App-Transitions to occur is right
+            // above all other animations but still below things in the Picture-and-Picture
+            // windowing mode.
+            if (mAnimationLayer != null) {
+                t.setLayer(mAnimationLayer, layer++);
+            }
         }
 
         @Override
@@ -3560,8 +3571,8 @@
     }
 
     private final class AboveAppWindowContainers extends NonAppWindowContainers {
-        AboveAppWindowContainers(String name) {
-            super(name);
+        AboveAppWindowContainers(String name, WindowManagerService service) {
+            super(name, service);
         }
 
         void assignChildLayers(SurfaceControl.Transaction t, WindowContainer imeContainer) {
@@ -3577,14 +3588,12 @@
 
                 if (needAssignIme && layer >= mService.mPolicy.getWindowLayerFromTypeLw(
                                 TYPE_INPUT_METHOD_DIALOG, true)) {
-                    t.setRelativeLayer(imeContainer.getSurfaceControl(),
-                            wt.getSurfaceControl(), -1);
+                    imeContainer.assignRelativeLayer(t, wt.getSurfaceControl(), -1);
                     needAssignIme = false;
                 }
             }
             if (needAssignIme) {
-                t.setRelativeLayer(imeContainer.getSurfaceControl(),
-                        getSurfaceControl(), Integer.MAX_VALUE);
+                imeContainer.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
             }
         }
     }
@@ -3618,7 +3627,8 @@
         };
 
         private final String mName;
-        NonAppWindowContainers(String name) {
+        NonAppWindowContainers(String name, WindowManagerService service) {
+            super(service);
             mName = name;
         }
 
@@ -3712,8 +3722,6 @@
 
     @Override
     void assignChildLayers(SurfaceControl.Transaction t) {
-        t.setLayer(mOverlayLayer, 1)
-                .setLayer(mWindowingLayer, 0);
 
         // These are layers as children of "mWindowingLayer"
         mBelowAppWindowsContainers.assignLayer(t, 0);
@@ -3737,8 +3745,7 @@
         // place it in the AboveAppWindowContainers.
         if (imeTarget != null && !imeTarget.inSplitScreenWindowingMode()
                 && (imeTarget.getSurfaceControl() != null)) {
-            t.setRelativeLayer(mImeWindowsContainers.getSurfaceControl(),
-                    imeTarget.getSurfaceControl(),
+            mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
                     // TODO: We need to use an extra level on the app surface to ensure
                     // this is always above SurfaceView but always below attached window.
                     1);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 4008811..b08eb18 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -101,8 +101,6 @@
     private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1;
     private static final int SET_USER_ACTIVITY_TIMEOUT = 2;
 
-    WindowManagerService mService;
-
     private boolean mWallpaperForceHidingChanged = false;
     private Object mLastWindowFreezeSource = null;
     private Session mHoldScreen = null;
@@ -160,7 +158,7 @@
     };
 
     RootWindowContainer(WindowManagerService service) {
-        mService = service;
+        super(service);
         mHandler = new MyHandler(service.mH.getLooper());
         mWallpaperController = new WallpaperController(mService);
     }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8aa129a..6ea8a47 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -59,7 +59,6 @@
     final int mTaskId;
     final int mUserId;
     private boolean mDeferRemoval = false;
-    final WindowManagerService mService;
 
     final Rect mPreparedFrozenBounds = new Rect();
     final Configuration mPreparedFrozenMergedConfig = new Configuration();
@@ -102,10 +101,10 @@
     Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode,
             boolean supportsPictureInPicture, TaskDescription taskDescription,
             TaskWindowContainerController controller) {
+        super(service);
         mTaskId = taskId;
         mStack = stack;
         mUserId = userId;
-        mService = service;
         mResizeMode = resizeMode;
         mSupportsPictureInPicture = supportsPictureInPicture;
         setController(controller);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 94fbd0e..259f8df 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -197,7 +197,7 @@
         }
         try {
             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
-                    View.VISIBLE, token.getDisplayContent().getDisplayId(), tmpRect, tmpRect,
+                    View.GONE, token.getDisplayContent().getDisplayId(), tmpRect, tmpRect,
                     tmpRect, tmpCutout, null);
             if (res < 0) {
                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 4a3a3fc..832d395 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -75,9 +75,6 @@
     /** Unique identifier */
     final int mStackId;
 
-    /** The service */
-    private final WindowManagerService mService;
-
     /** The display this stack sits under. */
     // TODO: Track parent marks like this in WindowContainer.
     private DisplayContent mDisplayContent;
@@ -151,7 +148,7 @@
     final Rect mTmpDimBoundsRect = new Rect();
 
     TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
-        mService = service;
+        super(service);
         mStackId = stackId;
         setController(controller);
         mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize(
diff --git a/services/core/java/com/android/server/wm/TransactionFactory.java b/services/core/java/com/android/server/wm/TransactionFactory.java
new file mode 100644
index 0000000..067f083
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TransactionFactory.java
@@ -0,0 +1,27 @@
+/*
+ * 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.wm;
+
+import android.view.SurfaceControl.Transaction;
+
+/**
+ * Helper class to inject custom transaction objects into window manager.
+ */
+interface TransactionFactory {
+    Transaction make();
+};
+
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index c371e88..d6329bf 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -72,18 +72,25 @@
      WindowContainerController mController;
 
     protected SurfaceControl mSurfaceControl;
+    private int mLastLayer = 0;
+    private SurfaceControl mLastRelativeToLayer = null;
 
     /**
      * Applied as part of the animation pass in "prepareSurfaces".
      */
-    private Transaction mPendingTransaction = new Transaction();
+    private final Transaction mPendingTransaction;
+    protected final WindowManagerService mService;
+
+    WindowContainer(WindowManagerService service) {
+        mService = service;
+        mPendingTransaction = service.mTransactionFactory.make();
+    }
 
     @Override
     final protected WindowContainer getParent() {
         return mParent;
     }
 
-
     @Override
     protected int getChildCount() {
         return mChildren.size();
@@ -756,34 +763,46 @@
     }
 
     void assignLayer(Transaction t, int layer) {
-        if (mSurfaceControl != null) {
+        final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
+        if (mSurfaceControl != null && changed) {
             t.setLayer(mSurfaceControl, layer);
+            mLastLayer = layer;
+            mLastRelativeToLayer = null;
+        }
+    }
+
+    void assignRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
+        final boolean changed = layer != mLastLayer || mLastRelativeToLayer != relativeTo;
+        if (mSurfaceControl != null && changed) {
+            t.setRelativeLayer(mSurfaceControl, relativeTo, layer);
+            mLastLayer = layer;
+            mLastRelativeToLayer = relativeTo;
         }
     }
 
     void assignChildLayers(Transaction t) {
         int layer = 0;
-        boolean boosting = false;
 
         // We use two passes as a way to promote children which
         // need Z-boosting to the end of the list.
-        for (int i = 0; i < 2; i++ ) {
-            for (int j = 0; j < mChildren.size(); ++j) {
-                final WindowContainer wc = mChildren.get(j);
-                if (wc.needsZBoost() && !boosting) {
-                    continue;
-                }
-                wc.assignLayer(t, layer);
-                wc.assignChildLayers(t);
-
-                layer++;
+        for (int j = 0; j < mChildren.size(); ++j) {
+            final WindowContainer wc = mChildren.get(j);
+            wc.assignChildLayers(t);
+            if (!wc.needsZBoost()) {
+                wc.assignLayer(t, layer++);
             }
-            boosting = true;
+        }
+        for (int j = 0; j < mChildren.size(); ++j) {
+            final WindowContainer wc = mChildren.get(j);
+            if (wc.needsZBoost()) {
+                wc.assignLayer(t, layer++);
+            }
         }
     }
 
     void assignChildLayers() {
         assignChildLayers(getPendingTransaction());
+        scheduleAnimation();
     }
 
     boolean needsZBoost() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3ad4df7..e598224 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -215,6 +215,7 @@
 import android.view.PointerIcon;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.view.SurfaceControl.Builder;
 import android.view.SurfaceSession;
 import android.view.View;
 import android.view.WindowContentFrameStats;
@@ -806,12 +807,8 @@
     static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
             new WindowManagerThreadPriorityBooster();
 
-    class DefaultSurfaceBuilderFactory implements SurfaceBuilderFactory {
-        public SurfaceControl.Builder make(SurfaceSession s) {
-            return new SurfaceControl.Builder(s);
-        }
-    };
-    SurfaceBuilderFactory mSurfaceBuilderFactory = new DefaultSurfaceBuilderFactory();
+    SurfaceBuilderFactory mSurfaceBuilderFactory = SurfaceControl.Builder::new;
+    TransactionFactory mTransactionFactory = SurfaceControl.Transaction::new;
 
     static void boostPriorityForLockedSection() {
         sThreadPriorityBooster.boost();
@@ -1502,7 +1499,7 @@
 
             // Don't do layout here, the window must call
             // relayout to be displayed, so we'll do it there.
-            displayContent.assignWindowLayers(false /* setLayoutNeeded */);
+            win.getParent().assignChildLayers();
 
             if (focusChanged) {
                 mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
@@ -1972,6 +1969,13 @@
                         + " newVis=" + viewVisibility, stack);
             }
 
+            win.setDisplayLayoutNeeded();
+            win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
+
+            // We may be deferring layout passes at the moment, but since the client is interested
+            // in the new out values right now we need to force a layout.
+            mWindowPlacerLocked.performSurfacePlacement(true /* force */);
+
             // We should only relayout if the view is visible, it is a starting window, or the
             // associated appToken is not hidden.
             final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
@@ -1981,15 +1985,6 @@
             if (shouldRelayout) {
                 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
 
-                // We are about to create a surface, but we didn't run a layout yet. So better run
-                // a layout now that we already know the right size, as a resize call will make the
-                // surface transaction blocking until next vsync and slow us down.
-                // TODO: Ideally we'd create the surface after running layout a bit further down,
-                // but moving this seems to be too risky at this point in the release.
-                if (win.mLayoutSeq == -1) {
-                    win.setDisplayLayoutNeeded();
-                    mWindowPlacerLocked.performSurfacePlacement(true);
-                }
                 result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility);
 
                 try {
@@ -2091,16 +2086,11 @@
                 mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken);
             }
 
-            win.setDisplayLayoutNeeded();
-            win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                     "relayoutWindow: updateOrientationFromAppTokens");
             configChanged = updateOrientationFromAppTokensLocked(false, displayId);
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
-            // We may be deferring layout passes at the moment, but since the client is interested
-            // in the new out values right now we need to force a layout.
-            mWindowPlacerLocked.performSurfacePlacement(true /* force */);
             if (toBeDisplayed && win.mIsWallpaper) {
                 DisplayInfo displayInfo = win.getDisplayContent().getDisplayInfo();
                 dc.mWallpaperController.updateWallpaperOffset(
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d23a6c7..e38605d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -186,7 +186,6 @@
     // to capture touch events in that area.
     static final int RESIZE_HANDLE_WIDTH_IN_DP = 30;
 
-    final WindowManagerService mService;
     final WindowManagerPolicy mPolicy;
     final Context mContext;
     final Session mSession;
@@ -627,7 +626,7 @@
     WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
            int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
-        mService = service;
+        super(service);
         mSession = s;
         mClient = c;
         mAppOp = appOp;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index a3d4b71..5bcf59c 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -46,9 +46,6 @@
 class WindowToken extends WindowContainer<WindowState> {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowToken" : TAG_WM;
 
-    // The window manager!
-    protected final WindowManagerService mService;
-
     // The actual token.
     final IBinder token;
 
@@ -107,7 +104,7 @@
 
     WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
             DisplayContent dc, boolean ownerCanManageAppTokens) {
-        mService = service;
+        super(service);
         token = _token;
         windowType = type;
         mPersistOnEmpty = persistOnEmpty;