Introduced ReadOnlyWindowList

7th and Final step in making the modification of a display's
WindowList private to DisplayContent.
ReadOnlyWindowList provides an interface for external classes to
DisplayContent to access the window list without being able to
modify it. This will be important in upcoming CLs where it is
important for us to keep track of when the window list changes.

Bug: 30060889
Test: Manual testing and existing tests pass.

Change-Id: I4de0b258a40fd4b21ef9cc9e3401488f76d25f83
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 893749e..bde65ed 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -648,12 +648,11 @@
             }
 
             private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
-                DisplayContent displayContent = mWindowManagerService
-                        .getDefaultDisplayContentLocked();
-                WindowList windowList = displayContent.getWindowList();
+                final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked();
+                final ReadOnlyWindowList windowList = dc.getReadOnlyWindowList();
                 final int windowCount = windowList.size();
                 for (int i = 0; i < windowCount; i++) {
-                    WindowState windowState = windowList.get(i);
+                    final WindowState windowState = windowList.get(i);
                     if (windowState.isOnScreen() && windowState.isVisibleLw() &&
                             !windowState.mWinAnimator.mEnterAnimationPending) {
                         outWindows.put(windowState.mLayer, windowState);
@@ -1027,9 +1026,8 @@
                 Region unaccountedSpace = mTempRegion;
                 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
 
-                SparseArray<WindowState> visibleWindows = mTempWindowStates;
+                final SparseArray<WindowState> visibleWindows = mTempWindowStates;
                 populateVisibleWindowsOnScreenLocked(visibleWindows);
-
                 Set<IBinder> addedWindows = mTempBinderSet;
                 addedWindows.clear();
 
@@ -1299,12 +1297,11 @@
         }
 
         private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
-            DisplayContent displayContent = mWindowManagerService
-                    .getDefaultDisplayContentLocked();
-            WindowList windowList = displayContent.getWindowList();
+            final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked();
+            final ReadOnlyWindowList windowList = dc.getReadOnlyWindowList();
             final int windowCount = windowList.size();
             for (int i = 0; i < windowCount; i++) {
-                WindowState windowState = windowList.get(i);
+                final WindowState windowState = windowList.get(i);
                 if (windowState.isVisibleLw()) {
                     outWindows.put(windowState.mLayer, windowState);
                 }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a3f7ac6..e3588ff 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -946,8 +946,7 @@
 
             if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
                     "Removing starting window: " + tStartingWindow);
-            getDisplayContent().getWindowList().remove(tStartingWindow);
-            mService.mWindowsChanged = true;
+            getDisplayContent().removeFromWindowList(tStartingWindow);
             if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
                     "Removing starting " + tStartingWindow + " from " + fromToken);
             fromToken.removeChild(tStartingWindow);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0b39d65..8e36e85 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -29,9 +29,13 @@
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
+import static android.view.View.GONE;
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_TOP;
+import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
+import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -40,16 +44,23 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE;
 import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
@@ -69,18 +80,28 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
+import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
 import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
+import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.dipToPixel;
 import static com.android.server.wm.WindowManagerService.localLOGV;
+import static com.android.server.wm.WindowManagerService.logSurface;
 import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
 import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
@@ -91,6 +112,7 @@
 import android.annotation.NonNull;
 import android.app.ActivityManager.StackId;
 import android.content.res.Configuration;
+import android.graphics.Bitmap;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -107,13 +129,16 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.IWindow;
-import android.view.WindowManager;
+import android.view.InputChannel;
+import android.view.Surface;
+import android.view.SurfaceControl;
 import android.view.WindowManagerPolicy;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.view.IInputMethodClient;
+import com.android.server.input.InputWindowHandle;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -123,7 +148,11 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 /**
  * Utility class for keeping track of the WindowStates and other pertinent contents of a
@@ -204,6 +233,14 @@
     /** Used when rebuilding window list to keep track of windows that have been removed. */
     private WindowState[] mRebuildTmp = new WindowState[20];
 
+    /**
+     * Temporary list for comparison. Always clear this after use so we don't end up with
+     * orphaned windows references
+     */
+    private final ArrayList<WindowState> mTmpWindows = new ArrayList<>();
+
+    private final LinkedList<AppWindowToken> mTmpUpdateAllDrawn = new LinkedList();
+
     private final TaskForResizePointSearchResult mTmpTaskForResizePointSearchResult =
             new TaskForResizePointSearchResult();
     private final GetWindowOnDisplaySearchResult mTmpGetWindowOnDisplaySearchResult =
@@ -248,10 +285,6 @@
         return mDisplayId;
     }
 
-    WindowList getWindowList() {
-        return mWindows;
-    }
-
     WindowToken getWindowToken(IBinder binder) {
         return mTokenMap.get(binder);
     }
@@ -539,14 +572,6 @@
         throw new UnsupportedOperationException("See DisplayChildWindowContainer");
     }
 
-    /**
-     * Propagate the new bounds to all child stacks.
-     * @param contentRect The bounds to apply at the top level.
-     */
-    void resize(Rect contentRect) {
-        mContentRect.set(contentRect);
-    }
-
     int taskIdFromPoint(int x, int y) {
         for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
             final TaskStack stack = mTaskStackContainers.get(stackNdx);
@@ -618,9 +643,9 @@
     }
 
     void switchUser() {
-        final WindowList windows = getWindowList();
-        for (int i = 0; i < windows.size(); i++) {
-            final WindowState win = windows.get(i);
+        final int count = mWindows.size();
+        for (int i = 0; i < count; i++) {
+            final WindowState win = mWindows.get(i);
             if (win.isHiddenFromUserLocked()) {
                 if (DEBUG_VISIBILITY) Slog.w(TAG_WM, "user changing, hiding " + win
                         + ", attrs=" + win.mAttrs.type + ", belonging to " + win.mOwnerUid);
@@ -645,7 +670,7 @@
         return mDimLayerController.animateDimLayers();
     }
 
-    void resetDimming() {
+    private void resetDimming() {
         mDimLayerController.resetDimming();
     }
 
@@ -653,7 +678,7 @@
         return mDimLayerController.isDimming();
     }
 
-    void stopDimmingIfNeeded() {
+    private void stopDimmingIfNeeded() {
         mDimLayerController.stopDimmingIfNeeded();
     }
 
@@ -998,10 +1023,9 @@
 
     boolean canAddToastWindowForUid(int uid) {
         // We allow one toast window per UID being shown at a time.
-        WindowList windows = getWindowList();
-        final int windowCount = windows.size();
+        final int windowCount = mWindows.size();
         for (int i = 0; i < windowCount; i++) {
-            WindowState window = windows.get(i);
+            final WindowState window = mWindows.get(i);
             if (window.mAttrs.type == TYPE_TOAST && window.mOwnerUid == uid
                     && !window.mPermanentlyHidden && !window.mAnimatingExit
                     && !window.mRemoveOnExit) {
@@ -1016,11 +1040,10 @@
             return;
         }
         final int lostFocusUid = oldFocus.mOwnerUid;
-        final WindowList windows = getWindowList();
-        final int windowCount = windows.size();
+        final int windowCount = mWindows.size();
         final Handler handler = mService.mH;
         for (int i = 0; i < windowCount; i++) {
-            final WindowState window = windows.get(i);
+            final WindowState window = mWindows.get(i);
             if (window.mAttrs.type == TYPE_TOAST && window.mOwnerUid == lostFocusUid) {
                 if (!handler.hasMessages(WINDOW_HIDE_TIMEOUT, window)) {
                     handler.sendMessageDelayed(handler.obtainMessage(WINDOW_HIDE_TIMEOUT, window),
@@ -1205,34 +1228,35 @@
     }
 
     void addToWindowList(WindowState win, int index) {
+        mService.mWindowsChanged = true;
         mWindows.add(index, win);
     }
 
     boolean removeFromWindowList(WindowState win) {
+        mService.mWindowsChanged = true;
         return mWindows.remove(win);
     }
 
     private int removeWindowAndChildrenFromWindowList(WindowState win, int interestingPos) {
-        final WindowList windows = getWindowList();
-        int wpos = windows.indexOf(win);
+        int wpos = mWindows.indexOf(win);
         if (wpos < 0) {
             return interestingPos;
         }
 
         if (wpos < interestingPos) interestingPos--;
         if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Temp removing at " + wpos + ": " + this);
-        windows.remove(wpos);
+        mWindows.remove(wpos);
         mService.mWindowsChanged = true;
         int childWinCount = win.mChildren.size();
         while (childWinCount > 0) {
             childWinCount--;
             final WindowState cw = win.mChildren.get(childWinCount);
-            int cpos = windows.indexOf(cw);
+            int cpos = mWindows.indexOf(cw);
             if (cpos >= 0) {
                 if (cpos < interestingPos) interestingPos--;
                 if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM,
                         "Temp removing child at " + cpos + ": " + cw);
-                windows.remove(cpos);
+                mWindows.remove(cpos);
             }
         }
         return interestingPos;
@@ -1282,14 +1306,14 @@
 
     /** Updates the layer assignment of windows on this display. */
     void assignWindowLayers(boolean setLayoutNeeded) {
-        mLayersController.assignWindowLayers(mWindows);
+        mLayersController.assignWindowLayers(mWindows.getReadOnly());
         if (setLayoutNeeded) {
             setLayoutNeeded();
         }
     }
 
     void adjustWallpaperWindows() {
-        if (mWallpaperController.adjustWallpaperWindows(mWindows)) {
+        if (mWallpaperController.adjustWallpaperWindows(mWindows.getReadOnly())) {
             assignWindowLayers(true /*setLayoutNeeded*/);
         }
     }
@@ -1386,6 +1410,160 @@
         Arrays.fill(mRebuildTmp, null);
     }
 
+    /** Rebuilds the display's window list and does a relayout if something changed. */
+    void rebuildAppWindowsAndLayoutIfNeeded() {
+        mTmpWindows.clear();
+        mTmpWindows.addAll(mWindows);
+
+        rebuildAppWindowList();
+
+        // Set displayContent.mLayoutNeeded if window order changed.
+        final int tmpSize = mTmpWindows.size();
+        final int winSize = mWindows.size();
+        int tmpNdx = 0, winNdx = 0;
+        while (tmpNdx < tmpSize && winNdx < winSize) {
+            // Skip over all exiting windows, they've been moved out of order.
+            WindowState tmp;
+            do {
+                tmp = mTmpWindows.get(tmpNdx++);
+            } while (tmpNdx < tmpSize && tmp.mAppToken != null && tmp.mAppToken.mIsExiting);
+
+            WindowState win;
+            do {
+                win = mWindows.get(winNdx++);
+            } while (winNdx < winSize && win.mAppToken != null && win.mAppToken.mIsExiting);
+
+            if (tmp != win) {
+                // Window order changed.
+                setLayoutNeeded();
+                break;
+            }
+        }
+        if (tmpNdx != winNdx) {
+            // One list was different from the other.
+            setLayoutNeeded();
+        }
+        mTmpWindows.clear();
+
+        if (!mService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+                false /*updateInputWindows*/)) {
+            assignWindowLayers(false /* setLayoutNeeded */);
+        }
+
+        mService.mInputMonitor.setUpdateInputWindowsNeededLw();
+        mService.mWindowPlacerLocked.performSurfacePlacement();
+        mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+    }
+
+    void updateInputWindows(InputMonitor inputMonitor, WindowState inputFocus, boolean inDrag) {
+        final InputConsumerImpl navInputConsumer =
+                mService.mInputMonitor.getInputConsumer(INPUT_CONSUMER_NAVIGATION, mDisplayId);
+        final InputConsumerImpl pipInputConsumer =
+                mService.mInputMonitor.getInputConsumer(INPUT_CONSUMER_PIP, mDisplayId);
+        final InputConsumerImpl wallpaperInputConsumer =
+                mService.mInputMonitor.getInputConsumer(INPUT_CONSUMER_WALLPAPER, mDisplayId);
+        boolean addInputConsumerHandle = navInputConsumer != null;
+        boolean addPipInputConsumerHandle = pipInputConsumer != null;
+        boolean addWallpaperInputConsumerHandle = wallpaperInputConsumer != null;
+        final Rect pipTouchableBounds = addPipInputConsumerHandle ? new Rect() : null;
+        boolean disableWallpaperTouchEvents = false;
+
+        for (int winNdx = mWindows.size() - 1; winNdx >= 0; --winNdx) {
+            final WindowState child = mWindows.get(winNdx);
+            final InputChannel inputChannel = child.mInputChannel;
+            final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
+            if (inputChannel == null || inputWindowHandle == null || child.mRemoved
+                    || child.isAdjustedForMinimizedDock()) {
+                // Skip this window because it cannot possibly receive input.
+                continue;
+            }
+
+            if (addPipInputConsumerHandle
+                    && child.getStackId() == PINNED_STACK_ID
+                    && inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer) {
+                // Update the bounds of the Pip input consumer to match the Pinned stack
+                child.getStack().getBounds(pipTouchableBounds);
+                pipInputConsumer.mWindowHandle.touchableRegion.set(pipTouchableBounds);
+                inputMonitor.addInputWindowHandle(pipInputConsumer.mWindowHandle);
+                addPipInputConsumerHandle = false;
+            }
+
+            if (addInputConsumerHandle
+                    && inputWindowHandle.layer <= navInputConsumer.mWindowHandle.layer) {
+                inputMonitor.addInputWindowHandle(navInputConsumer.mWindowHandle);
+                addInputConsumerHandle = false;
+            }
+
+            if (addWallpaperInputConsumerHandle) {
+                if (child.mAttrs.type == TYPE_WALLPAPER && child.isVisibleLw()) {
+                    // Add the wallpaper input consumer above the first visible wallpaper.
+                    inputMonitor.addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+                    addWallpaperInputConsumerHandle = false;
+                }
+            }
+
+            final int flags = child.mAttrs.flags;
+            final int privateFlags = child.mAttrs.privateFlags;
+            final int type = child.mAttrs.type;
+
+            final boolean hasFocus = child == inputFocus;
+            final boolean isVisible = child.isVisibleLw();
+            if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) {
+                disableWallpaperTouchEvents = true;
+            }
+            final boolean hasWallpaper = mWallpaperController.isWallpaperTarget(child)
+                    && (privateFlags & PRIVATE_FLAG_KEYGUARD) == 0
+                    && !disableWallpaperTouchEvents;
+
+            // If there's a drag in progress and 'child' is a potential drop target,
+            // make sure it's been told about the drag
+            if (inDrag && isVisible && isDefaultDisplay) {
+                mService.mDragState.sendDragStartedIfNeededLw(child);
+            }
+
+            inputMonitor.addInputWindowHandle(
+                    inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper);
+        }
+
+        if (addWallpaperInputConsumerHandle) {
+            // No visible wallpaper found, add the wallpaper input consumer at the end.
+            inputMonitor.addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+        }
+    }
+
+    /** Returns true if a leaked surface was destroyed */
+    boolean destroyLeakedSurfaces() {
+        boolean leakedSurface = false;
+        final int numWindows = mWindows.size();
+        for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
+            final WindowState ws = mWindows.get(winNdx);
+            final WindowStateAnimator wsa = ws.mWinAnimator;
+            if (wsa.mSurfaceController == null) {
+                continue;
+            }
+            if (!mService.mSessions.contains(wsa.mSession)) {
+                Slog.w(TAG_WM, "LEAKED SURFACE (session doesn't exist): "
+                        + ws + " surface=" + wsa.mSurfaceController
+                        + " token=" + ws.mToken
+                        + " pid=" + ws.mSession.mPid
+                        + " uid=" + ws.mSession.mUid);
+                wsa.destroySurface();
+                mService.mForceRemoves.add(ws);
+                leakedSurface = true;
+            } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
+                Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): "
+                        + ws + " surface=" + wsa.mSurfaceController
+                        + " token=" + ws.mAppToken
+                        + " saved=" + ws.hasSavedSurface());
+                if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", false);
+                wsa.destroySurface();
+                leakedSurface = true;
+            }
+        }
+
+        return leakedSurface;
+    }
+
     /** Return the list of Windows on this display associated with the input token. */
     WindowList getTokenWindowsOnDisplay(WindowToken token) {
         final WindowList windowList = new WindowList();
@@ -1467,8 +1645,6 @@
         }
 
         // TODO(multidisplay): IMEs are only supported on the default display.
-        WindowList windows = mWindows;
-
         int imPos = findDesiredInputMethodWindowIndex(true);
         if (imPos >= 0) {
             // In this case, the input method windows are to be placed
@@ -1476,8 +1652,8 @@
 
             // First check to see if the input method windows are already
             // located here, and contiguous.
-            final int N = windows.size();
-            final WindowState firstImWin = imPos < N ? windows.get(imPos) : null;
+            final int N = mWindows.size();
+            final WindowState firstImWin = imPos < N ? mWindows.get(imPos) : null;
 
             // Figure out the actual input method window that should be
             // at the bottom of their stack.
@@ -1492,7 +1668,7 @@
                 // First find the top IM window.
                 int pos = imPos+1;
                 while (pos < N) {
-                    if (!(windows.get(pos)).mIsImWindow) {
+                    if (!(mWindows.get(pos)).mIsImWindow) {
                         break;
                     }
                     pos++;
@@ -1500,7 +1676,7 @@
                 pos++;
                 // Now there should be no more input method windows above.
                 while (pos < N) {
-                    if ((windows.get(pos)).mIsImWindow) {
+                    if ((mWindows.get(pos)).mIsImWindow) {
                         break;
                     }
                     pos++;
@@ -1513,17 +1689,17 @@
             if (imWin != null) {
                 if (DEBUG_INPUT_METHOD) {
                     Slog.v(TAG_WM, "Moving IM from " + imPos);
-                    logWindowList(windows, "  ");
+                    logWindowList(mWindows, "  ");
                 }
                 imPos = removeWindowAndChildrenFromWindowList(imWin, imPos);
                 if (DEBUG_INPUT_METHOD) {
                     Slog.v(TAG_WM, "List after removing with new pos " + imPos + ":");
-                    logWindowList(windows, "  ");
+                    logWindowList(mWindows, "  ");
                 }
                 imWin.reAddWindow(imPos);
                 if (DEBUG_INPUT_METHOD) {
                     Slog.v(TAG_WM, "List after moving IM to " + imPos + ":");
-                    logWindowList(windows, "  ");
+                    logWindowList(mWindows, "  ");
                 }
                 if (DN > 0) moveInputMethodDialogs(imPos+1);
             } else {
@@ -1540,7 +1716,7 @@
                 reAddToWindowList(imWin);
                 if (DEBUG_INPUT_METHOD) {
                     Slog.v(TAG_WM, "List with no IM target:");
-                    logWindowList(windows, "  ");
+                    logWindowList(mWindows, "  ");
                 }
                 if (DN > 0) moveInputMethodDialogs(-1);
             } else {
@@ -1565,11 +1741,10 @@
         // TODO(multidisplay): Needs some serious rethought when the target and IME are not on the
         // same display. Or even when the current IME/target are not on the same screen as the next
         // IME/target. For now only look for input windows on the main screen.
-        final WindowList windows = getWindowList();
         WindowState w = null;
         int i;
-        for (i = windows.size() - 1; i >= 0; --i) {
-            WindowState win = windows.get(i);
+        for (i = mWindows.size() - 1; i >= 0; --i) {
+            final WindowState win = mWindows.get(i);
 
             if (DEBUG_INPUT_METHOD && willMove) Slog.i(TAG_WM, "Checking window @" + i
                     + " " + win + " fl=0x" + Integer.toHexString(win.mAttrs.flags));
@@ -1582,7 +1757,7 @@
                 // is not actually looking to move the IME, look down below for a real window to
                 // target...
                 if (!willMove && w.mAttrs.type == TYPE_APPLICATION_STARTING && i > 0) {
-                    WindowState wb = windows.get(i-1);
+                    final WindowState wb = mWindows.get(i-1);
                     if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) {
                         i--;
                         w = wb;
@@ -1607,7 +1782,7 @@
                 && curTarget.isClosing()
                 && (w == null || curTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer)) {
             if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Current target higher, not changing");
-            return windows.indexOf(curTarget) + 1;
+            return mWindows.indexOf(curTarget) + 1;
         }
 
         if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Desired input method target="
@@ -1623,7 +1798,7 @@
                 WindowState highestTarget = null;
                 int highestPos = 0;
                 if (token.mAppAnimator.animating || token.mAppAnimator.animation != null) {
-                    WindowList curWindows = token.getDisplayContent().getWindowList();
+                    WindowList curWindows = token.getDisplayContent().mWindows;
                     int pos = curWindows.indexOf(curTarget);
                     while (pos >= 0) {
                         WindowState win = curWindows.get(pos);
@@ -1688,7 +1863,7 @@
             // docked divider. Unless it is already above the divider.
             final WindowState dockedDivider = mDividerControllerLocked.getWindow();
             if (dockedDivider != null && dockedDivider.isVisibleLw()) {
-                int dividerIndex = windows.indexOf(dockedDivider);
+                int dividerIndex = mWindows.indexOf(dockedDivider);
                 if (dividerIndex > 0 && dividerIndex > i) {
                     return dividerIndex + 1;
                 }
@@ -1741,7 +1916,6 @@
 
     boolean getNeedsMenu(WindowState win, WindowManagerPolicy.WindowState bottom) {
         int index = -1;
-        WindowList windows = getWindowList();
         while (true) {
             if (win.mAttrs.needsMenuKey != NEEDS_MENU_UNSET) {
                 return win.mAttrs.needsMenuKey == NEEDS_MENU_SET_TRUE;
@@ -1754,13 +1928,13 @@
             // The current window hasn't specified whether menu key is needed; look behind it.
             // First, we may need to determine the starting position.
             if (index < 0) {
-                index = windows.indexOf(win);
+                index = mWindows.indexOf(win);
             }
             index--;
             if (index < 0) {
                 return false;
             }
-            win = windows.get(index);
+            win = mWindows.get(index);
         }
     }
 
@@ -1769,7 +1943,7 @@
         mLayoutNeeded = true;
     }
 
-    void clearLayoutNeeded() {
+    private void clearLayoutNeeded() {
         if (DEBUG_LAYOUT) Slog.w(TAG_WM, "clearLayoutNeeded: callers=" + Debug.getCallers(3));
         mLayoutNeeded = false;
     }
@@ -1852,9 +2026,8 @@
 
     private void dumpWindows() {
         Slog.v(TAG_WM, " Display #" + mDisplayId);
-        final WindowList windows = getWindowList();
-        for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-            Slog.v(TAG_WM, "  #" + winNdx + ": " + windows.get(winNdx));
+        for (int winNdx = mWindows.size() - 1; winNdx >= 0; --winNdx) {
+            Slog.v(TAG_WM, "  #" + winNdx + ": " + mWindows.get(winNdx));
         }
     }
 
@@ -2424,6 +2597,673 @@
         }
     }
 
+    ReadOnlyWindowList getReadOnlyWindowList() {
+        return mWindows.getReadOnly();
+    }
+
+    void getWindows(WindowList output) {
+        output.addAll(mWindows);
+    }
+
+    // TODO: Super crazy long method that should be broken down...
+    boolean applySurfaceChangesTransaction(boolean recoveringMemory) {
+
+        boolean focusDisplayed = false;
+        boolean displayHasContent = false;
+        float preferredRefreshRate = 0;
+        int preferredModeId = 0;
+
+
+        final int dw = mDisplayInfo.logicalWidth;
+        final int dh = mDisplayInfo.logicalHeight;
+        final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
+
+        mTmpUpdateAllDrawn.clear();
+
+        int repeats = 0;
+        do {
+            repeats++;
+            if (repeats > 6) {
+                Slog.w(TAG, "Animation repeat aborted after too many iterations");
+                clearLayoutNeeded();
+                break;
+            }
+
+            if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("On entry to LockedInner",
+                    pendingLayoutChanges);
+
+            if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+                adjustWallpaperWindows();
+            }
+
+            if (isDefaultDisplay && (pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
+                if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
+                if (mService.updateOrientationFromAppTokensLocked(true, mDisplayId)) {
+                    setLayoutNeeded();
+                    mService.mH.sendEmptyMessage(SEND_NEW_CONFIGURATION);
+                }
+            }
+
+            if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+                setLayoutNeeded();
+            }
+
+            // FIRST LOOP: Perform a layout, if needed.
+            if (repeats < LAYOUT_REPEAT_THRESHOLD) {
+                performLayout(repeats == 1, false /* updateInputWindows */);
+            } else {
+                Slog.w(TAG, "Layout repeat skipped after too many iterations");
+            }
+
+            // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think it is animating.
+            pendingLayoutChanges = 0;
+
+            if (isDefaultDisplay) {
+                mService.mPolicy.beginPostLayoutPolicyLw(dw, dh);
+                for (int i = mWindows.size() - 1; i >= 0; i--) {
+                    final WindowState w = mWindows.get(i);
+                    if (w.mHasSurface) {
+                        mService.mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs, w.getParentWindow());
+                    }
+                }
+                pendingLayoutChanges |= mService.mPolicy.finishPostLayoutPolicyLw();
+                if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
+                        "after finishPostLayoutPolicyLw", pendingLayoutChanges);
+            }
+        } while (pendingLayoutChanges != 0);
+
+        RootWindowContainer root = mService.mRoot;
+        boolean obscured = false;
+        boolean syswin = false;
+        resetDimming();
+
+        // Only used if default window
+        final boolean someoneLosingFocus = !mService.mLosingFocus.isEmpty();
+
+        for (int i = mWindows.size() - 1; i >= 0; i--) {
+            final WindowState w = mWindows.get(i);
+            final Task task = w.getTask();
+            final boolean obscuredChanged = w.mObscured != obscured;
+
+            // Update effect.
+            w.mObscured = obscured;
+            if (!obscured) {
+                final boolean isDisplayed = w.isDisplayedLw();
+
+                if (isDisplayed && w.isObscuringFullscreen(mDisplayInfo)) {
+                    // This window completely covers everything behind it, so we want to leave all
+                    // of them as undimmed (for performance reasons).
+                    root.mObscuringWindow = w;
+                    obscured = true;
+                }
+
+                displayHasContent |= root.handleNotObscuredLocked(w, obscured, syswin);
+
+                if (w.mHasSurface && isDisplayed) {
+                    final int type = w.mAttrs.type;
+                    if (type == TYPE_SYSTEM_DIALOG || type == TYPE_SYSTEM_ERROR
+                            || (w.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+                        syswin = true;
+                    }
+                    if (preferredRefreshRate == 0 && w.mAttrs.preferredRefreshRate != 0) {
+                        preferredRefreshRate = w.mAttrs.preferredRefreshRate;
+                    }
+                    if (preferredModeId == 0 && w.mAttrs.preferredDisplayModeId != 0) {
+                        preferredModeId = w.mAttrs.preferredDisplayModeId;
+                    }
+                }
+            }
+
+            w.applyDimLayerIfNeeded();
+
+            if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
+                    && mWallpaperController.isWallpaperTarget(w)) {
+                // This is the wallpaper target and its obscured state changed... make sure the
+                // current wallpaper's visibility has been updated accordingly.
+                mWallpaperController.updateWallpaperVisibility();
+            }
+
+            w.handleWindowMovedIfNeeded();
+
+            final WindowStateAnimator winAnimator = w.mWinAnimator;
+
+            //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
+            w.mContentChanged = false;
+
+            // Moved from updateWindowsAndWallpaperLocked().
+            if (w.mHasSurface) {
+                // Take care of the window being ready to display.
+                final boolean committed = winAnimator.commitFinishDrawingLocked();
+                if (isDefaultDisplay && committed) {
+                    if (w.mAttrs.type == TYPE_DREAM) {
+                        // HACK: When a dream is shown, it may at that point hide the lock screen.
+                        // So we need to redo the layout to let the phone window manager make this
+                        // happen.
+                        pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
+                        if (DEBUG_LAYOUT_REPEATS) {
+                            surfacePlacer.debugLayoutRepeats(
+                                    "dream and commitFinishDrawingLocked true",
+                                    pendingLayoutChanges);
+                        }
+                    }
+                    if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
+                        if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
+                                "First draw done in potential wallpaper target " + w);
+                        root.mWallpaperMayChange = true;
+                        pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+                        if (DEBUG_LAYOUT_REPEATS) {
+                            surfacePlacer.debugLayoutRepeats(
+                                    "wallpaper and commitFinishDrawingLocked true",
+                                    pendingLayoutChanges);
+                        }
+                    }
+                }
+                if (!winAnimator.isAnimationStarting() && !winAnimator.isWaitingForOpening()) {
+                    // Updates the shown frame before we set up the surface. This is needed
+                    // because the resizing could change the top-left position (in addition to
+                    // size) of the window. setSurfaceBoundariesLocked uses mShownPosition to
+                    // position the surface.
+                    //
+                    // If an animation is being started, we can't call this method because the
+                    // animation hasn't processed its initial transformation yet, but in general
+                    // we do want to update the position if the window is animating.
+                    winAnimator.computeShownFrameLocked();
+                }
+                winAnimator.setSurfaceBoundariesLocked(recoveringMemory);
+            }
+
+            final AppWindowToken atoken = w.mAppToken;
+            if (atoken != null) {
+                final boolean updateAllDrawn = atoken.updateDrawnWindowStates(w);
+                if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(atoken)) {
+                    mTmpUpdateAllDrawn.add(atoken);
+                }
+            }
+
+            if (isDefaultDisplay && someoneLosingFocus && w == mService.mCurrentFocus
+                    && w.isDisplayedLw()) {
+                focusDisplayed = true;
+            }
+
+            w.updateResizingWindowIfNeeded();
+        }
+
+        mService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
+                displayHasContent,
+                preferredRefreshRate,
+                preferredModeId,
+                true /* inTraversal, must call performTraversalInTrans... below */);
+
+        stopDimmingIfNeeded();
+
+        while (!mTmpUpdateAllDrawn.isEmpty()) {
+            final AppWindowToken atoken = mTmpUpdateAllDrawn.removeLast();
+            // See if any windows have been drawn, so they (and others associated with them)
+            // can now be shown.
+            atoken.updateAllDrawn(this);
+        }
+
+        return focusDisplayed;
+    }
+
+    void performLayout(boolean initial, boolean updateInputWindows) {
+        if (!isLayoutNeeded()) {
+            return;
+        }
+        clearLayoutNeeded();
+
+        final int dw = mDisplayInfo.logicalWidth;
+        final int dh = mDisplayInfo.logicalHeight;
+
+        int i;
+
+        if (DEBUG_LAYOUT) {
+            Slog.v(TAG, "-------------------------------------");
+            Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw + " dh=" + dh);
+        }
+
+        mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mService.mRotation,
+                getConfiguration().uiMode);
+        if (isDefaultDisplay) {
+            // Not needed on non-default displays.
+            mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
+            mService.mScreenRect.set(0, 0, dw, dh);
+        }
+
+        mService.mPolicy.getContentRectLw(mContentRect);
+
+        int seq = mService.mLayoutSeq + 1;
+        if (seq < 0) seq = 0;
+        mService.mLayoutSeq = seq;
+
+        boolean behindDream = false;
+
+        // First perform layout of any root windows (not attached to another window).
+        int topAttached = -1;
+        for (i = mWindows.size() - 1; i >= 0; i--) {
+            final WindowState win = mWindows.get(i);
+
+            // Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
+            // wasting time and funky changes while a window is animating away.
+            final boolean gone = (behindDream && mService.mPolicy.canBeForceHidden(win, win.mAttrs))
+                    || win.isGoneForLayoutLw();
+
+            if (DEBUG_LAYOUT && !win.mLayoutAttached) {
+                Slog.v(TAG, "1ST PASS " + win + ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame
+                        + " mLayoutAttached=" + win.mLayoutAttached
+                        + " screen changed=" + win.isConfigChanged());
+                final AppWindowToken atoken = win.mAppToken;
+                if (gone) Slog.v(TAG, "  GONE: mViewVisibility=" + win.mViewVisibility
+                        + " mRelayoutCalled=" + win.mRelayoutCalled + " hidden=" + win.mToken.hidden
+                        + " hiddenRequested=" + (atoken != null && atoken.hiddenRequested)
+                        + " parentHidden=" + win.isParentWindowHidden());
+                else Slog.v(TAG, "  VIS: mViewVisibility=" + win.mViewVisibility
+                        + " mRelayoutCalled=" + win.mRelayoutCalled + " hidden=" + win.mToken.hidden
+                        + " hiddenRequested=" + (atoken != null && atoken.hiddenRequested)
+                        + " parentHidden=" + win.isParentWindowHidden());
+            }
+
+            // If this view is GONE, then skip it -- keep the current frame, and let the caller know
+            // so they can ignore it if they want.  (We do the normal layout for INVISIBLE windows,
+            // since that means "perform layout as normal, just don't display").
+            if (!gone || !win.mHaveFrame || win.mLayoutNeeded
+                    || ((win.isConfigChanged() || win.setReportResizeHints())
+                    && !win.isGoneForLayoutLw() &&
+                    ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 ||
+                            (win.mHasSurface && win.mAppToken != null &&
+                                    win.mAppToken.layoutConfigChanges)))) {
+                if (!win.mLayoutAttached) {
+                    if (initial) {
+                        //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
+                        win.mContentChanged = false;
+                    }
+                    if (win.mAttrs.type == TYPE_DREAM) {
+                        // Don't layout windows behind a dream, so that if it does stuff like hide
+                        // the status bar we won't get a bad transition when it goes away.
+                        behindDream = true;
+                    }
+                    win.mLayoutNeeded = false;
+                    win.prelayout();
+                    mService.mPolicy.layoutWindowLw(win, null);
+                    win.mLayoutSeq = seq;
+
+                    // Window frames may have changed. Update dim layer with the new bounds.
+                    final Task task = win.getTask();
+                    if (task != null) {
+                        mDimLayerController.updateDimLayer(task);
+                    }
+
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + win.mFrame
+                            + " mContainingFrame=" + win.mContainingFrame
+                            + " mDisplayFrame=" + win.mDisplayFrame);
+                } else {
+                    if (topAttached < 0) topAttached = i;
+                }
+            }
+        }
+
+        boolean attachedBehindDream = false;
+
+        // Now perform layout of attached windows, which usually depend on the position of the
+        // window they are attached to. XXX does not deal with windows that are attached to windows
+        // that are themselves attached.
+        for (i = topAttached; i >= 0; i--) {
+            final WindowState win = mWindows.get(i);
+
+            if (win.mLayoutAttached) {
+                if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + win + " mHaveFrame=" + win.mHaveFrame
+                        + " mViewVisibility=" + win.mViewVisibility
+                        + " mRelayoutCalled=" + win.mRelayoutCalled);
+                // If this view is GONE, then skip it -- keep the current frame, and let the caller
+                // know so they can ignore it if they want.  (We do the normal layout for INVISIBLE
+                // windows, since that means "perform layout as normal, just don't display").
+                if (attachedBehindDream && mService.mPolicy.canBeForceHidden(win, win.mAttrs)) {
+                    continue;
+                }
+                if ((win.mViewVisibility != GONE && win.mRelayoutCalled) || !win.mHaveFrame
+                        || win.mLayoutNeeded) {
+                    if (initial) {
+                        //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
+                        win.mContentChanged = false;
+                    }
+                    win.mLayoutNeeded = false;
+                    win.prelayout();
+                    mService.mPolicy.layoutWindowLw(win, win.getParentWindow());
+                    win.mLayoutSeq = seq;
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + win.mFrame
+                            + " mContainingFrame=" + win.mContainingFrame
+                            + " mDisplayFrame=" + win.mDisplayFrame);
+                }
+            } else if (win.mAttrs.type == TYPE_DREAM) {
+                // Don't layout windows behind a dream, so that if it does stuff like hide the
+                // status bar we won't get a bad transition when it goes away.
+                attachedBehindDream = behindDream;
+            }
+        }
+
+        // Window frames may have changed. Tell the input dispatcher about it.
+        mService.mInputMonitor.layoutInputConsumers(dw, dh);
+        mService.mInputMonitor.setUpdateInputWindowsNeededLw();
+        if (updateInputWindows) {
+            mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+        }
+
+        mService.mPolicy.finishLayoutLw();
+        mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
+    }
+
+    /**
+     * Takes a snapshot of the display.  In landscape mode this grabs the whole screen.
+     * In portrait mode, it grabs the full screenshot.
+     *
+     * @param width the width of the target bitmap
+     * @param height the height of the target bitmap
+     * @param includeFullDisplay true if the screen should not be cropped before capture
+     * @param frameScale the scale to apply to the frame, only used when width = -1 and height = -1
+     * @param config of the output bitmap
+     * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
+     */
+    Bitmap screenshotApplications(IBinder appToken, int displayId, int width, int height,
+            boolean includeFullDisplay, float frameScale, Bitmap.Config config,
+            boolean wallpaperOnly) {
+        int dw = mDisplayInfo.logicalWidth;
+        int dh = mDisplayInfo.logicalHeight;
+        if (dw == 0 || dh == 0) {
+            if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
+                    + ": returning null. logical widthxheight=" + dw + "x" + dh);
+            return null;
+        }
+
+        Bitmap bm = null;
+
+        int maxLayer = 0;
+        final Rect frame = new Rect();
+        final Rect stackBounds = new Rect();
+
+        boolean screenshotReady;
+        int minLayer;
+        if (appToken == null && !wallpaperOnly) {
+            screenshotReady = true;
+            minLayer = 0;
+        } else {
+            screenshotReady = false;
+            minLayer = Integer.MAX_VALUE;
+        }
+
+        WindowState appWin = null;
+
+        boolean includeImeInScreenshot;
+        synchronized(mService.mWindowMap) {
+            final AppWindowToken imeTargetAppToken = mService.mInputMethodTarget != null
+                    ? mService.mInputMethodTarget.mAppToken : null;
+            // We only include the Ime in the screenshot if the app we are screenshoting is the IME
+            // target and isn't in multi-window mode. We don't screenshot the IME in multi-window
+            // mode because the frame of the IME might not overlap with that of the app.
+            // E.g. IME target app at the top in split-screen mode and the IME at the bottom
+            // overlapping with the bottom app.
+            includeImeInScreenshot = imeTargetAppToken != null
+                    && imeTargetAppToken.appToken != null
+                    && imeTargetAppToken.appToken.asBinder() == appToken
+                    && !mService.mInputMethodTarget.isInMultiWindowMode();
+        }
+
+        final int aboveAppLayer = (mService.mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)
+                * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
+
+        synchronized(mService.mWindowMap) {
+            // Figure out the part of the screen that is actually the app.
+            appWin = null;
+            for (int i = mWindows.size() - 1; i >= 0; i--) {
+                final WindowState ws = mWindows.get(i);
+                if (!ws.mHasSurface) {
+                    continue;
+                }
+                if (ws.mLayer >= aboveAppLayer) {
+                    continue;
+                }
+                if (wallpaperOnly && !ws.mIsWallpaper) {
+                    continue;
+                }
+                if (ws.mIsImWindow) {
+                    if (!includeImeInScreenshot) {
+                        continue;
+                    }
+                } else if (ws.mIsWallpaper) {
+                    // If this is the wallpaper layer and we're only looking for the wallpaper layer
+                    // then the target window state is this one.
+                    if (wallpaperOnly) {
+                        appWin = ws;
+                    }
+
+                    if (appWin == null) {
+                        // We have not ran across the target window yet, so it is probably behind
+                        // the wallpaper. This can happen when the keyguard is up and all windows
+                        // are moved behind the wallpaper. We don't want to include the wallpaper
+                        // layer in the screenshot as it will cover-up the layer of the target
+                        // window.
+                        continue;
+                    }
+                    // Fall through. The target window is in front of the wallpaper. For this
+                    // case we want to include the wallpaper layer in the screenshot because
+                    // the target window might have some transparent areas.
+                } else if (appToken != null) {
+                    if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
+                        // This app window is of no interest if it is not associated with the
+                        // screenshot app.
+                        continue;
+                    }
+                    appWin = ws;
+                }
+
+                // Include this window.
+
+                final WindowStateAnimator winAnim = ws.mWinAnimator;
+                int layer = winAnim.mSurfaceController.getLayer();
+                if (maxLayer < layer) {
+                    maxLayer = layer;
+                }
+                if (minLayer > layer) {
+                    minLayer = layer;
+                }
+
+                // Don't include wallpaper in bounds calculation
+                if (!includeFullDisplay && !ws.mIsWallpaper) {
+                    final Rect wf = ws.mFrame;
+                    final Rect cr = ws.mContentInsets;
+                    int left = wf.left + cr.left;
+                    int top = wf.top + cr.top;
+                    int right = wf.right - cr.right;
+                    int bottom = wf.bottom - cr.bottom;
+                    frame.union(left, top, right, bottom);
+                    ws.getVisibleBounds(stackBounds);
+                    if (!Rect.intersects(frame, stackBounds)) {
+                        // Set frame empty if there's no intersection.
+                        frame.setEmpty();
+                    }
+                }
+
+                final boolean foundTargetWs =
+                        (ws.mAppToken != null && ws.mAppToken.token == appToken)
+                                || (appWin != null && wallpaperOnly);
+                if (foundTargetWs && ws.isDisplayedLw() && winAnim.getShown()) {
+                    screenshotReady = true;
+                }
+
+                if (ws.isObscuringFullscreen(mDisplayInfo)){
+                    break;
+                }
+            }
+
+            if (appToken != null && appWin == null) {
+                // Can't find a window to snapshot.
+                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM,
+                        "Screenshot: Couldn't find a surface matching " + appToken);
+                return null;
+            }
+
+            if (!screenshotReady) {
+                Slog.i(TAG_WM, "Failed to capture screenshot of " + appToken +
+                        " appWin=" + (appWin == null ? "null" : (appWin + " drawState=" +
+                        appWin.mWinAnimator.mDrawState)));
+                return null;
+            }
+
+            // Screenshot is ready to be taken. Everything from here below will continue
+            // through the bottom of the loop and return a value. We only stay in the loop
+            // because we don't want to release the mWindowMap lock until the screenshot is
+            // taken.
+
+            if (maxLayer == 0) {
+                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
+                        + ": returning null maxLayer=" + maxLayer);
+                return null;
+            }
+
+            if (!includeFullDisplay) {
+                // Constrain frame to the screen size.
+                if (!frame.intersect(0, 0, dw, dh)) {
+                    frame.setEmpty();
+                }
+            } else {
+                // Caller just wants entire display.
+                frame.set(0, 0, dw, dh);
+            }
+            if (frame.isEmpty()) {
+                return null;
+            }
+
+            if (width < 0) {
+                width = (int) (frame.width() * frameScale);
+            }
+            if (height < 0) {
+                height = (int) (frame.height() * frameScale);
+            }
+
+            // Tell surface flinger what part of the image to crop. Take the top
+            // right part of the application, and crop the larger dimension to fit.
+            Rect crop = new Rect(frame);
+            if (width / (float) frame.width() < height / (float) frame.height()) {
+                int cropWidth = (int)((float)width / (float)height * frame.height());
+                crop.right = crop.left + cropWidth;
+            } else {
+                int cropHeight = (int)((float)height / (float)width * frame.width());
+                crop.bottom = crop.top + cropHeight;
+            }
+
+            // The screenshot API does not apply the current screen rotation.
+            int rot = mDisplay.getRotation();
+
+            if (rot == ROTATION_90 || rot == ROTATION_270) {
+                rot = (rot == ROTATION_90) ? ROTATION_270 : ROTATION_90;
+            }
+
+            // Surfaceflinger is not aware of orientation, so convert our logical
+            // crop to surfaceflinger's portrait orientation.
+            convertCropForSurfaceFlinger(crop, rot, dw, dh);
+
+            if (DEBUG_SCREENSHOT) {
+                Slog.i(TAG_WM, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
+                        + maxLayer + " appToken=" + appToken);
+                for (int i = 0; i < mWindows.size(); i++) {
+                    final WindowState win = mWindows.get(i);
+                    final WindowSurfaceController controller = win.mWinAnimator.mSurfaceController;
+                    Slog.i(TAG_WM, win + ": " + win.mLayer
+                            + " animLayer=" + win.mWinAnimator.mAnimLayer
+                            + " surfaceLayer=" + ((controller == null)
+                            ? "null" : controller.getLayer()));
+                }
+            }
+
+            final ScreenRotationAnimation screenRotationAnimation =
+                    mService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
+            final boolean inRotation = screenRotationAnimation != null &&
+                    screenRotationAnimation.isAnimating();
+            if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM,
+                    "Taking screenshot while rotating");
+
+            // We force pending transactions to flush before taking
+            // the screenshot by pushing an empty synchronous transaction.
+            SurfaceControl.openTransaction();
+            SurfaceControl.closeTransactionSync();
+
+            bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer,
+                    inRotation, rot);
+            if (bm == null) {
+                Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
+                        + ") to layer " + maxLayer);
+                return null;
+            }
+        }
+
+        if (DEBUG_SCREENSHOT) {
+            // TEST IF IT's ALL BLACK
+            int[] buffer = new int[bm.getWidth() * bm.getHeight()];
+            bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
+            boolean allBlack = true;
+            final int firstColor = buffer[0];
+            for (int i = 0; i < buffer.length; i++) {
+                if (buffer[i] != firstColor) {
+                    allBlack = false;
+                    break;
+                }
+            }
+            if (allBlack) {
+                Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" +
+                        Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
+                        (appWin != null ?
+                                appWin.mWinAnimator.mSurfaceController.getLayer() : "null") +
+                        " minLayer=" + minLayer + " maxLayer=" + maxLayer);
+            }
+        }
+
+        // Create a copy of the screenshot that is immutable and backed in ashmem.
+        // This greatly reduces the overhead of passing the bitmap between processes.
+        Bitmap ret = bm.createAshmemBitmap(config);
+        bm.recycle();
+        return ret;
+    }
+
+    // TODO: Can this use createRotationMatrix()?
+    private static void convertCropForSurfaceFlinger(Rect crop, int rot, int dw, int dh) {
+        if (rot == Surface.ROTATION_90) {
+            final int tmp = crop.top;
+            crop.top = dw - crop.right;
+            crop.right = crop.bottom;
+            crop.bottom = dw - crop.left;
+            crop.left = tmp;
+        } else if (rot == Surface.ROTATION_180) {
+            int tmp = crop.top;
+            crop.top = dh - crop.bottom;
+            crop.bottom = dh - tmp;
+            tmp = crop.right;
+            crop.right = dw - crop.left;
+            crop.left = dw - tmp;
+        } else if (rot == Surface.ROTATION_270) {
+            final int tmp = crop.top;
+            crop.top = crop.left;
+            crop.left = dh - crop.bottom;
+            crop.bottom = crop.right;
+            crop.right = dh - tmp;
+        }
+    }
+
+    void onSeamlessRotationTimeout() {
+        boolean layoutNeeded = false;
+        for (int i = mWindows.size() - 1; i >= 0; i--) {
+            final WindowState w = mWindows.get(i);
+            if (!w.mSeamlesslyRotated) {
+                continue;
+            }
+            layoutNeeded = true;
+            w.setDisplayLayoutNeeded();
+            mService.markForSeamlessRotation(w, false);
+        }
+
+        if (layoutNeeded) {
+            mService.mWindowPlacerLocked.performSurfacePlacement();
+        }
+    }
+
     static final class GetWindowOnDisplaySearchResult {
         boolean reachedToken;
         WindowState foundWindow;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index f75f224..530fb1b 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -268,7 +268,7 @@
     }
 
     private void resetDragResizingChangeReported() {
-        final WindowList windowList = mDisplayContent.getWindowList();
+        final ReadOnlyWindowList windowList = mDisplayContent.getReadOnlyWindowList();
         for (int i = windowList.size() - 1; i >= 0; i--) {
             windowList.get(i).resetDragResizingChangeReported();
         }
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 588bb76..d52168c 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -271,7 +271,7 @@
             Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
         }
 
-        final WindowList windows = mDisplayContent.getWindowList();
+        final ReadOnlyWindowList windows = mDisplayContent.getReadOnlyWindowList();
         final int N = windows.size();
         for (int i = 0; i < N; i++) {
             sendDragStartedLw(windows.get(i), touchX, touchY, mDataDescription);
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 6a06ef3..a8eb75c 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -19,6 +19,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
@@ -109,7 +110,7 @@
         mService = service;
     }
 
-    void addInputConsumer(String name, InputConsumerImpl consumer) {
+    private void addInputConsumer(String name, InputConsumerImpl consumer) {
         mInputConsumers.put(name, consumer);
         updateInputWindowsLw(true /* force */);
     }
@@ -130,8 +131,9 @@
         return false;
     }
 
-    InputConsumerImpl getInputConsumer(String name) {
-        return mInputConsumers.get(name);
+    InputConsumerImpl getInputConsumer(String name, int displayId) {
+        // TODO(multi-display): Allow input consumers on non-default displays?
+        return (displayId == DEFAULT_DISPLAY) ? mInputConsumers.get(name) : null;
     }
 
     void layoutInputConsumers(int dw, int dh) {
@@ -165,8 +167,7 @@
             case INPUT_CONSUMER_PIP:
                 // The touchable region of the Pip input window is cropped to the bounds of the
                 // stack, and we need FLAG_NOT_TOUCH_MODAL to ensure other events fall through
-                consumer.mWindowHandle.layoutParamsFlags |=
-                        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+                consumer.mWindowHandle.layoutParamsFlags |= FLAG_NOT_TOUCH_MODAL;
                 break;
         }
         addInputConsumer(name, consumer);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 90e27ea..a28dc10 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import android.app.AppOpsManager;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.hardware.power.V1_0.PowerHint;
@@ -34,35 +33,26 @@
 import android.util.SparseIntArray;
 import android.view.Display;
 import android.view.DisplayInfo;
-import android.view.InputChannel;
 import android.view.WindowManager;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.EventLogTags;
-import com.android.server.input.InputWindowHandle;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.LinkedList;
 
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.OP_NONE;
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
-import static android.view.WindowManager.INPUT_CONSUMER_PIP;
-import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
-import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -72,7 +62,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
@@ -89,7 +78,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
 import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
-import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
@@ -104,8 +92,6 @@
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
 
 /** Root {@link WindowContainer} for the device. */
-// TODO: Several methods in here are accessing children of this container's children through various
-// references (WindowList I am looking at you :/). See if we can delegate instead.
 class RootWindowContainer extends WindowContainer<DisplayContent> {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "RootWindowContainer" : TAG_WM;
 
@@ -118,13 +104,6 @@
     private float mButtonBrightness = -1;
     private long mUserActivityTimeout = -1;
     private boolean mUpdateRotation = false;
-    private boolean mObscured = false;
-    private boolean mSyswin = false;
-    // Set to true when the display contains content to show the user.
-    // When false, the display manager may choose to mirror or blank the display.
-    private boolean mDisplayHasContent = false;
-    private float mPreferredRefreshRate = 0;
-    private int mPreferredModeId = 0;
     // Following variables are for debugging screen wakelock only.
     // Last window that requires screen wakelock
     WindowState mHoldScreenWindow = null;
@@ -147,8 +126,6 @@
 
     private final ArrayList<Integer> mChangedStackList = new ArrayList();
 
-    private final LinkedList<AppWindowToken> mTmpUpdateAllDrawn = new LinkedList();
-
     private final ArrayList<WindowToken> mTmpTokensList = new ArrayList();
 
     // Collection of binder tokens mapped to their window type we are allowed to create window
@@ -305,14 +282,14 @@
         final int count = mChildren.size();
         for (int i = 0; i < count; ++i) {
             final DisplayContent dc = mChildren.get(i);
-            output.addAll(dc.getWindowList());
+            dc.getWindows(output);
         }
     }
 
     void getWindows(WindowList output, boolean visibleOnly, boolean appsOnly) {
         final int numDisplays = mChildren.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final WindowList windowList = mChildren.get(displayNdx).getWindowList();
+            final ReadOnlyWindowList windowList = mChildren.get(displayNdx).getReadOnlyWindowList();
             for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) {
                 final WindowState w = windowList.get(winNdx);
                 if ((!visibleOnly || w.mWinAnimator.getShown())
@@ -333,7 +310,7 @@
         }
         final int numDisplays = mChildren.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final WindowList windowList = mChildren.get(displayNdx).getWindowList();
+            final ReadOnlyWindowList windowList = mChildren.get(displayNdx).getReadOnlyWindowList();
             for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) {
                 final WindowState w = windowList.get(winNdx);
                 if (name != null) {
@@ -350,7 +327,7 @@
     WindowState findWindow(int hashCode) {
         final int numDisplays = mChildren.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final WindowList windows = mChildren.get(displayNdx).getWindowList();
+            final ReadOnlyWindowList windows = mChildren.get(displayNdx).getReadOnlyWindowList();
             final int numWindows = windows.size();
             for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
                 final WindowState w = windows.get(winNdx);
@@ -645,7 +622,7 @@
 
     void setSecureSurfaceState(int userId, boolean disabled) {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowList windows = mChildren.get(i).getWindowList();
+            final ReadOnlyWindowList windows = mChildren.get(i).getReadOnlyWindowList();
             for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
                 final WindowState win = windows.get(winNdx);
                 if (win.mHasSurface && userId == UserHandle.getUserId(win.mOwnerUid)) {
@@ -658,17 +635,16 @@
     void updateAppOpsState() {
         final int count = mChildren.size();
         for (int i = 0; i < count; ++i) {
-            final WindowList windows = mChildren.get(i).getWindowList();
+            final ReadOnlyWindowList windows = mChildren.get(i).getReadOnlyWindowList();
             final int numWindows = windows.size();
             for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
                 final WindowState win = windows.get(winNdx);
-                if (win.mAppOp == AppOpsManager.OP_NONE) {
+                if (win.mAppOp == OP_NONE) {
                     continue;
                 }
                 final int mode = mService.mAppOps.checkOpNoThrow(win.mAppOp, win.getOwningUid(),
                         win.getOwningPackage());
-                win.setAppOpVisibilityLw(mode == AppOpsManager.MODE_ALLOWED ||
-                        mode == AppOpsManager.MODE_DEFAULT);
+                win.setAppOpVisibilityLw(mode == MODE_ALLOWED || mode == MODE_DEFAULT);
             }
         }
     }
@@ -676,7 +652,7 @@
     boolean canShowStrictModeViolation(int pid) {
         final int count = mChildren.size();
         for (int i = 0; i < count; ++i) {
-            final WindowList windows = mChildren.get(i).getWindowList();
+            final ReadOnlyWindowList windows = mChildren.get(i).getReadOnlyWindowList();
             final int numWindows = windows.size();
             for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
                 final WindowState ws = windows.get(winNdx);
@@ -691,7 +667,7 @@
     void closeSystemDialogs(String reason) {
         final int count = mChildren.size();
         for (int i = 0; i < count; ++i) {
-            final WindowList windows = mChildren.get(i).getWindowList();
+            final ReadOnlyWindowList windows = mChildren.get(i).getReadOnlyWindowList();
             final int numWindows = windows.size();
             for (int j = 0; j < numWindows; ++j) {
                 final WindowState w = windows.get(j);
@@ -711,7 +687,7 @@
         try {
             for (int i = mChildren.size() - 1; i >= 0; i--) {
                 DisplayContent dc = mChildren.get(i);
-                final WindowList windows = dc.getWindowList();
+                final ReadOnlyWindowList windows = mChildren.get(i).getReadOnlyWindowList();
                 for (int j = windows.size() - 1; j >= 0; j--) {
                     final WindowState win = windows.get(j);
                     final AppWindowToken aToken = win.mAppToken;
@@ -745,84 +721,10 @@
     }
 
     void updateInputWindows(InputMonitor inputMonitor, WindowState inputFocus, boolean inDrag) {
-        final InputConsumerImpl navInputConsumer =
-                mService.mInputMonitor.getInputConsumer(INPUT_CONSUMER_NAVIGATION);
-        final InputConsumerImpl pipInputConsumer =
-                mService.mInputMonitor.getInputConsumer(INPUT_CONSUMER_PIP);
-        final InputConsumerImpl wallpaperInputConsumer =
-                mService.mInputMonitor.getInputConsumer(INPUT_CONSUMER_WALLPAPER);
-        boolean addInputConsumerHandle = navInputConsumer != null;
-        boolean addPipInputConsumerHandle = pipInputConsumer != null;
-        boolean addWallpaperInputConsumerHandle = wallpaperInputConsumer != null;
-        final Rect pipTouchableBounds = addPipInputConsumerHandle ? new Rect() : null;
-        boolean disableWallpaperTouchEvents = false;
-
         final int count = mChildren.size();
         for (int i = 0; i < count; ++i) {
             final DisplayContent dc = mChildren.get(i);
-            final WindowList windows = dc.getWindowList();
-            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                final WindowState child = windows.get(winNdx);
-                final InputChannel inputChannel = child.mInputChannel;
-                final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
-                if (inputChannel == null || inputWindowHandle == null || child.mRemoved
-                        || child.isAdjustedForMinimizedDock()) {
-                    // Skip this window because it cannot possibly receive input.
-                    continue;
-                }
-
-                if (addPipInputConsumerHandle
-                        && child.getStackId() == PINNED_STACK_ID
-                        && inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer) {
-                    // Update the bounds of the Pip input consumer to match the Pinned stack
-                    child.getStack().getBounds(pipTouchableBounds);
-                    pipInputConsumer.mWindowHandle.touchableRegion.set(pipTouchableBounds);
-                    inputMonitor.addInputWindowHandle(pipInputConsumer.mWindowHandle);
-                    addPipInputConsumerHandle = false;
-                }
-
-                if (addInputConsumerHandle
-                        && inputWindowHandle.layer <= navInputConsumer.mWindowHandle.layer) {
-                    inputMonitor.addInputWindowHandle(navInputConsumer.mWindowHandle);
-                    addInputConsumerHandle = false;
-                }
-
-                if (addWallpaperInputConsumerHandle) {
-                    if (child.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER &&
-                            child.isVisibleLw()) {
-                        // Add the wallpaper input consumer above the first visible wallpaper.
-                        inputMonitor.addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
-                        addWallpaperInputConsumerHandle = false;
-                    }
-                }
-
-                final int flags = child.mAttrs.flags;
-                final int privateFlags = child.mAttrs.privateFlags;
-                final int type = child.mAttrs.type;
-
-                final boolean hasFocus = child == inputFocus;
-                final boolean isVisible = child.isVisibleLw();
-                if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) {
-                    disableWallpaperTouchEvents = true;
-                }
-                final boolean hasWallpaper = dc.mWallpaperController.isWallpaperTarget(child)
-                        && (privateFlags & PRIVATE_FLAG_KEYGUARD) == 0
-                        && !disableWallpaperTouchEvents;
-
-                // If there's a drag in progress and 'child' is a potential drop target,
-                // make sure it's been told about the drag
-                if (inDrag && isVisible && dc.isDefaultDisplay) {
-                    mService.mDragState.sendDragStartedIfNeededLw(child);
-                }
-
-                inputMonitor.addInputWindowHandle(
-                        inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper);
-            }
-        }
-
-        if (addWallpaperInputConsumerHandle) {
-            // No visible wallpaper found, add the wallpaper input consumer at the end.
-            inputMonitor.addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+            dc.updateInputWindows(inputMonitor, inputFocus, inDrag);
         }
     }
 
@@ -837,57 +739,33 @@
 
         final long callingIdentity = Binder.clearCallingIdentity();
         try {
-            // There was some problem...   first, do a sanity check of the window list to make sure
+            // There was some problem...first, do a sanity check of the window list to make sure
             // we haven't left any dangling surfaces around.
 
             Slog.i(TAG_WM, "Out of memory for surface!  Looking for leaks...");
             final int numDisplays = mChildren.size();
             for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-                final WindowList windows = mChildren.get(displayNdx).getWindowList();
-                final int numWindows = windows.size();
-                for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
-                    final WindowState ws = windows.get(winNdx);
-                    final WindowStateAnimator wsa = ws.mWinAnimator;
-                    if (wsa.mSurfaceController == null) {
-                        continue;
-                    }
-                    if (!mService.mSessions.contains(wsa.mSession)) {
-                        Slog.w(TAG_WM, "LEAKED SURFACE (session doesn't exist): "
-                                + ws + " surface=" + wsa.mSurfaceController
-                                + " token=" + ws.mToken
-                                + " pid=" + ws.mSession.mPid
-                                + " uid=" + ws.mSession.mUid);
-                        wsa.destroySurface();
-                        mService.mForceRemoves.add(ws);
-                        leakedSurface = true;
-                    } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
-                        Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): "
-                                + ws + " surface=" + wsa.mSurfaceController
-                                + " token=" + ws.mAppToken
-                                + " saved=" + ws.hasSavedSurface());
-                        if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", false);
-                        wsa.destroySurface();
-                        leakedSurface = true;
-                    }
-                }
+                leakedSurface |= mChildren.get(displayNdx).destroyLeakedSurfaces();
             }
 
             if (!leakedSurface) {
                 Slog.w(TAG_WM, "No leaked surfaces; killing applications!");
-                SparseIntArray pidCandidates = new SparseIntArray();
+                final SparseIntArray pidCandidates = new SparseIntArray();
                 for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-                    final WindowList windows = mChildren.get(displayNdx).getWindowList();
+                    final ReadOnlyWindowList windows =
+                            mChildren.get(displayNdx).getReadOnlyWindowList();
                     final int numWindows = windows.size();
                     for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
                         final WindowState ws = windows.get(winNdx);
                         if (mService.mForceRemoves.contains(ws)) {
                             continue;
                         }
-                        WindowStateAnimator wsa = ws.mWinAnimator;
+                        final WindowStateAnimator wsa = ws.mWinAnimator;
                         if (wsa.mSurfaceController != null) {
                             pidCandidates.append(wsa.mSession.mPid, wsa.mSession.mPid);
                         }
                     }
+
                     if (pidCandidates.size() > 0) {
                         int[] pids = new int[pidCandidates.size()];
                         for (int i = 0; i < pids.length; i++) {
@@ -1238,8 +1116,8 @@
                 "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
     }
 
-    // TODO: Super crazy long method that should be broken down...
-    private void applySurfaceChangesTransaction(boolean recoveringMemory, int defaultDw, int defaultDh) {
+    private void applySurfaceChangesTransaction(boolean recoveringMemory, int defaultDw,
+            int defaultDh) {
         mHoldScreenWindow = null;
         mObscuringWindow = null;
 
@@ -1262,214 +1140,31 @@
         final int count = mChildren.size();
         for (int j = 0; j < count; ++j) {
             final DisplayContent dc = mChildren.get(j);
-            WindowList windows = dc.getWindowList();
-            DisplayInfo displayInfo = dc.getDisplayInfo();
-            final int displayId = dc.getDisplayId();
-            final int dw = displayInfo.logicalWidth;
-            final int dh = displayInfo.logicalHeight;
-            final boolean isDefaultDisplay = (displayId == DEFAULT_DISPLAY);
-            final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
-
-            // Reset for each display.
-            mDisplayHasContent = false;
-            mPreferredRefreshRate = 0;
-            mPreferredModeId = 0;
-            mTmpUpdateAllDrawn.clear();
-
-            int repeats = 0;
-            do {
-                repeats++;
-                if (repeats > 6) {
-                    Slog.w(TAG, "Animation repeat aborted after too many iterations");
-                    dc.clearLayoutNeeded();
-                    break;
-                }
-
-                if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
-                        "On entry to LockedInner", dc.pendingLayoutChanges);
-
-                if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
-                    dc.adjustWallpaperWindows();
-                }
-
-                if (isDefaultDisplay
-                        && (dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
-                    if (mService.updateOrientationFromAppTokensLocked(true, displayId)) {
-                        dc.setLayoutNeeded();
-                        mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
-                    }
-                }
-
-                if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
-                    dc.setLayoutNeeded();
-                }
-
-                // FIRST LOOP: Perform a layout, if needed.
-                if (repeats < LAYOUT_REPEAT_THRESHOLD) {
-                    surfacePlacer.performLayoutLockedInner(dc, repeats == 1,
-                            false /* updateInputWindows */);
-                } else {
-                    Slog.w(TAG, "Layout repeat skipped after too many iterations");
-                }
-
-                // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
-                // it is animating.
-                dc.pendingLayoutChanges = 0;
-
-                if (isDefaultDisplay) {
-                    mService.mPolicy.beginPostLayoutPolicyLw(dw, dh);
-                    for (int i = windows.size() - 1; i >= 0; i--) {
-                        WindowState w = windows.get(i);
-                        if (w.mHasSurface) {
-                            mService.mPolicy.applyPostLayoutPolicyLw(
-                                    w, w.mAttrs, w.getParentWindow());
-                        }
-                    }
-                    dc.pendingLayoutChanges |=
-                            mService.mPolicy.finishPostLayoutPolicyLw();
-                    if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
-                            "after finishPostLayoutPolicyLw", dc.pendingLayoutChanges);
-                }
-            } while (dc.pendingLayoutChanges != 0);
-
-            mObscured = false;
-            mSyswin = false;
-            dc.resetDimming();
-
-            // Only used if default window
-            final boolean someoneLosingFocus = !mService.mLosingFocus.isEmpty();
-
-            for (int i = windows.size() - 1; i >= 0; i--) {
-                WindowState w = windows.get(i);
-                final Task task = w.getTask();
-                final boolean obscuredChanged = w.mObscured != mObscured;
-
-                // Update effect.
-                w.mObscured = mObscured;
-                if (!mObscured) {
-                    handleNotObscuredLocked(w, displayInfo);
-                }
-
-                w.applyDimLayerIfNeeded();
-
-                if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
-                        && dc.mWallpaperController.isWallpaperTarget(w)) {
-                    // This is the wallpaper target and its obscured state changed... make sure the
-                    // current wallpaper's visibility has been updated accordingly.
-                    dc.mWallpaperController.updateWallpaperVisibility();
-                }
-
-                w.handleWindowMovedIfNeeded();
-
-                final WindowStateAnimator winAnimator = w.mWinAnimator;
-
-                //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
-                w.mContentChanged = false;
-
-                // Moved from updateWindowsAndWallpaperLocked().
-                if (w.mHasSurface) {
-                    // Take care of the window being ready to display.
-                    final boolean committed = winAnimator.commitFinishDrawingLocked();
-                    if (isDefaultDisplay && committed) {
-                        if (w.mAttrs.type == TYPE_DREAM) {
-                            // HACK: When a dream is shown, it may at that point hide the lock
-                            // screen. So we need to redo the layout to let the phone window manager
-                            // make this happen.
-                            dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
-                            if (DEBUG_LAYOUT_REPEATS) {
-                                surfacePlacer.debugLayoutRepeats(
-                                        "dream and commitFinishDrawingLocked true",
-                                        dc.pendingLayoutChanges);
-                            }
-                        }
-                        if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
-                            if (DEBUG_WALLPAPER_LIGHT)
-                                Slog.v(TAG, "First draw done in potential wallpaper target " + w);
-                            mWallpaperMayChange = true;
-                            dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
-                            if (DEBUG_LAYOUT_REPEATS) {
-                                surfacePlacer.debugLayoutRepeats(
-                                        "wallpaper and commitFinishDrawingLocked true",
-                                        dc.pendingLayoutChanges);
-                            }
-                        }
-                    }
-                    if (!winAnimator.isAnimationStarting() && !winAnimator.isWaitingForOpening()) {
-                        // Updates the shown frame before we set up the surface. This is needed
-                        // because the resizing could change the top-left position (in addition to
-                        // size) of the window. setSurfaceBoundariesLocked uses mShownPosition to
-                        // position the surface.
-                        //
-                        // If an animation is being started, we can't call this method because the
-                        // animation hasn't processed its initial transformation yet, but in general
-                        // we do want to update the position if the window is animating.
-                        winAnimator.computeShownFrameLocked();
-                    }
-                    winAnimator.setSurfaceBoundariesLocked(recoveringMemory);
-                }
-
-                final AppWindowToken atoken = w.mAppToken;
-                if (atoken != null) {
-                    final boolean updateAllDrawn = atoken.updateDrawnWindowStates(w);
-                    if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(atoken)) {
-                        mTmpUpdateAllDrawn.add(atoken);
-                    }
-                }
-
-                if (isDefaultDisplay && someoneLosingFocus && w == mService.mCurrentFocus
-                        && w.isDisplayedLw()) {
-                    focusDisplayed = true;
-                }
-
-                w.updateResizingWindowIfNeeded();
-            }
-
-            mService.mDisplayManagerInternal.setDisplayProperties(displayId,
-                    mDisplayHasContent,
-                    mPreferredRefreshRate,
-                    mPreferredModeId,
-                    true /* inTraversal, must call performTraversalInTrans... below */);
-
-            dc.stopDimmingIfNeeded();
-
-            while (!mTmpUpdateAllDrawn.isEmpty()) {
-                final AppWindowToken atoken = mTmpUpdateAllDrawn.removeLast();
-                // See if any windows have been drawn, so they (and others associated with them)
-                // can now be shown.
-                atoken.updateAllDrawn(dc);
-            }
+            focusDisplayed |= dc.applySurfaceChangesTransaction(recoveringMemory);
         }
 
         if (focusDisplayed) {
             mService.mH.sendEmptyMessage(REPORT_LOSING_FOCUS);
         }
 
-        // Give the display manager a chance to adjust properties
-        // like display rotation if it needs to.
+        // Give the display manager a chance to adjust properties like display rotation if it needs
+        // to.
         mService.mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();
     }
 
     /**
      * @param w WindowState this method is applied to.
-     * @param dispInfo info of the display that the window's obscuring state is checked against.
+     * @param obscured True if there is a window on top of this obscuring the display.
+     * @param syswin System window?
+     * @return True when the display contains content to show the user. When false, the display
+     *          manager may choose to mirror or blank the display.
      */
-    private void handleNotObscuredLocked(final WindowState w, final DisplayInfo dispInfo) {
+    boolean handleNotObscuredLocked(WindowState w, boolean obscured, boolean syswin) {
         final WindowManager.LayoutParams attrs = w.mAttrs;
         final int attrFlags = attrs.flags;
         final boolean canBeSeen = w.isDisplayedLw();
         final int privateflags = attrs.privateFlags;
-
-        if (canBeSeen && w.isObscuringFullscreen(dispInfo)) {
-            // This window completely covers everything behind it,
-            // so we want to leave all of them as undimmed (for
-            // performance reasons).
-            if (!mObscured) {
-                mObscuringWindow = w;
-            }
-
-            mObscured = true;
-        }
+        boolean displayHasContent = false;
 
         if (w.mHasSurface && canBeSeen) {
             if ((attrFlags & FLAG_KEEP_SCREEN_ON) != 0) {
@@ -1480,22 +1175,17 @@
                         + "screen wakelock but no longer has FLAG_KEEP_SCREEN_ON!!! called by"
                         + Debug.getCallers(10));
             }
-            if (!mSyswin && w.mAttrs.screenBrightness >= 0 && mScreenBrightness < 0) {
+            if (!syswin && w.mAttrs.screenBrightness >= 0 && mScreenBrightness < 0) {
                 mScreenBrightness = w.mAttrs.screenBrightness;
             }
-            if (!mSyswin && w.mAttrs.buttonBrightness >= 0 && mButtonBrightness < 0) {
+            if (!syswin && w.mAttrs.buttonBrightness >= 0 && mButtonBrightness < 0) {
                 mButtonBrightness = w.mAttrs.buttonBrightness;
             }
-            if (!mSyswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
+            if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
                 mUserActivityTimeout = w.mAttrs.userActivityTimeout;
             }
 
             final int type = attrs.type;
-            if (type == TYPE_SYSTEM_DIALOG || type == TYPE_SYSTEM_ERROR
-                    || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
-                mSyswin = true;
-            }
-
             // This function assumes that the contents of the default display are processed first
             // before secondary displays.
             final DisplayContent displayContent = w.getDisplayContent();
@@ -1506,23 +1196,19 @@
                 if (type == TYPE_DREAM || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
                     mObscureApplicationContentOnSecondaryDisplays = true;
                 }
-                mDisplayHasContent = true;
+                displayHasContent = true;
             } else if (displayContent != null &&
                     (!mObscureApplicationContentOnSecondaryDisplays
-                            || (mObscured && type == TYPE_KEYGUARD_DIALOG))) {
+                            || (obscured && type == TYPE_KEYGUARD_DIALOG))) {
                 // Allow full screen keyguard presentation dialogs to be seen.
-                mDisplayHasContent = true;
-            }
-            if (mPreferredRefreshRate == 0 && w.mAttrs.preferredRefreshRate != 0) {
-                mPreferredRefreshRate = w.mAttrs.preferredRefreshRate;
-            }
-            if (mPreferredModeId == 0 && w.mAttrs.preferredDisplayModeId != 0) {
-                mPreferredModeId = w.mAttrs.preferredDisplayModeId;
+                displayHasContent = true;
             }
             if ((privateflags & PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE) != 0) {
                 mSustainedPerformanceModeCurrent = true;
             }
         }
+
+        return displayHasContent;
     }
 
     boolean copyAnimToLayoutParams() {
@@ -1619,7 +1305,7 @@
     void dumpWindowsNoHeader(PrintWriter pw, boolean dumpAll, ArrayList<WindowState> windows) {
         final int numDisplays = mChildren.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final WindowList windowList = mChildren.get(displayNdx).getWindowList();
+            final ReadOnlyWindowList windowList = mChildren.get(displayNdx).getReadOnlyWindowList();
             for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) {
                 final WindowState w = windowList.get(winNdx);
                 if (windows == null || windows.contains(w)) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 4ab8887..c9f1ffc 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -374,7 +374,7 @@
         return mWallpaperAnimLayerAdjustment;
     }
 
-    private void findWallpaperTarget(WindowList windows, FindWallpaperTargetResult result) {
+    private void findWallpaperTarget(ReadOnlyWindowList windows, FindWallpaperTargetResult result) {
         final WindowAnimator winAnimator = mService.mAnimator;
         result.reset();
         WindowState w = null;
@@ -457,7 +457,7 @@
 
     /** Updates the target wallpaper if needed and returns true if an update happened. */
     private boolean updateWallpaperWindowsTarget(
-            WindowList windows, FindWallpaperTargetResult result) {
+            ReadOnlyWindowList windows, FindWallpaperTargetResult result) {
 
         WindowState wallpaperTarget = result.wallpaperTarget;
         int wallpaperTargetIndex = result.wallpaperTargetIndex;
@@ -558,7 +558,7 @@
         return true;
     }
 
-    private boolean updateWallpaperWindowsTargetByLayer(WindowList windows,
+    private boolean updateWallpaperWindowsTargetByLayer(ReadOnlyWindowList windows,
             FindWallpaperTargetResult result) {
 
         WindowState wallpaperTarget = result.wallpaperTarget;
@@ -609,7 +609,7 @@
         return visible;
     }
 
-    private boolean updateWallpaperWindowsPlacement(WindowList windows,
+    private boolean updateWallpaperWindowsPlacement(ReadOnlyWindowList windows,
             WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible) {
 
         // TODO(multidisplay): Wallpapers on main screen only.
@@ -628,7 +628,7 @@
         return changed;
     }
 
-    boolean adjustWallpaperWindows(WindowList windows) {
+    boolean adjustWallpaperWindows(ReadOnlyWindowList windows) {
         mService.mRoot.mWallpaperMayChange = false;
 
         // First find top-most window that has asked to be on top of the wallpaper;
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index e184e39..d94094a 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -19,7 +19,6 @@
 import android.util.Slog;
 import android.view.Display;
 
-import java.io.PrintWriter;
 import java.util.ArrayDeque;
 
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
@@ -27,7 +26,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
 import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
 
 /**
@@ -63,7 +61,7 @@
     private WindowState mDockDivider = null;
     private ArrayDeque<WindowState> mReplacingWindows = new ArrayDeque<>();
 
-    final void assignWindowLayers(WindowList windows) {
+    final void assignWindowLayers(ReadOnlyWindowList windows) {
         if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based on windows=" + windows,
                 new RuntimeException("here").fillInStackTrace());
 
@@ -113,7 +111,7 @@
         if (DEBUG_LAYERS) logDebugLayers(windows);
     }
 
-    private void logDebugLayers(WindowList windows) {
+    private void logDebugLayers(ReadOnlyWindowList windows) {
         for (int i = 0, n = windows.size(); i < n; i++) {
             final WindowState w = windows.get(i);
             final WindowStateAnimator winAnimator = w.mWinAnimator;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1b08f16..1111b5e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -142,7 +142,6 @@
 import com.android.server.UiThread;
 import com.android.server.Watchdog;
 import com.android.server.input.InputManagerService;
-import com.android.server.policy.PhoneWindowManager;
 import com.android.server.power.ShutdownThread;
 
 import java.io.BufferedWriter;
@@ -170,7 +169,6 @@
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.StatusBarManager.DISABLE_MASK;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.DOCKED_INVALID;
@@ -186,12 +184,9 @@
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_DRAG;
-import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -204,6 +199,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.EventLogTags.WM_TASK_CREATED;
 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
@@ -219,7 +215,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
@@ -241,7 +236,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
@@ -620,10 +614,6 @@
     // window list before it is used whenever window container order changes.
     final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<>();
 
-    /** Temporary list for comparison. Always clear this after use so we don't end up with
-     * orphaned windows references */
-    final ArrayList<WindowState> mTmpWindows = new ArrayList<>();
-
     boolean mHardKeyboardAvailable;
     WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
     SettingsObserver mSettingsObserver;
@@ -3274,51 +3264,6 @@
         mH.sendMessage(m);
     }
 
-    /** Rebuilds the input display's window list and does a relayout if something changed. */
-    private void rebuildAppWindowsAndLayoutIfNeededLocked(DisplayContent displayContent) {
-        final WindowList windows = displayContent.getWindowList();
-        mTmpWindows.addAll(windows);
-
-        displayContent.rebuildAppWindowList();
-
-        // Set displayContent.mLayoutNeeded if window order changed.
-        final int tmpSize = mTmpWindows.size();
-        final int winSize = windows.size();
-        int tmpNdx = 0, winNdx = 0;
-        while (tmpNdx < tmpSize && winNdx < winSize) {
-            // Skip over all exiting windows, they've been moved out of order.
-            WindowState tmp;
-            do {
-                tmp = mTmpWindows.get(tmpNdx++);
-            } while (tmpNdx < tmpSize && tmp.mAppToken != null && tmp.mAppToken.mIsExiting);
-
-            WindowState win;
-            do {
-                win = windows.get(winNdx++);
-            } while (winNdx < winSize && win.mAppToken != null && win.mAppToken.mIsExiting);
-
-            if (tmp != win) {
-                // Window order changed.
-                displayContent.setLayoutNeeded();
-                break;
-            }
-        }
-        if (tmpNdx != winNdx) {
-            // One list was different from the other.
-            displayContent.setLayoutNeeded();
-        }
-        mTmpWindows.clear();
-
-        if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
-                false /*updateInputWindows*/)) {
-            displayContent.assignWindowLayers(false /* setLayoutNeeded */);
-        }
-
-        mInputMonitor.setUpdateInputWindowsNeededLw();
-        mWindowPlacerLocked.performSurfacePlacement();
-        mInputMonitor.updateInputWindowsLw(false /*force*/);
-    }
-
     public void moveTaskToTop(int taskId) {
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -3343,7 +3288,7 @@
                 if (mAppTransition.isTransitionSet()) {
                     task.setSendingToBottom(false);
                 }
-                rebuildAppWindowsAndLayoutIfNeededLocked(displayContent);
+                displayContent.rebuildAppWindowsAndLayoutIfNeeded();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -3365,7 +3310,7 @@
                 if (mAppTransition.isTransitionSet()) {
                     task.setSendingToBottom(true);
                 }
-                rebuildAppWindowsAndLayoutIfNeededLocked(stack.getDisplayContent());
+                stack.getDisplayContent().rebuildAppWindowsAndLayoutIfNeeded();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -4400,29 +4345,6 @@
         SystemProperties.set(StrictMode.VISUAL_PROPERTY, value);
     }
 
-    private static void convertCropForSurfaceFlinger(Rect crop, int rot, int dw, int dh) {
-        if (rot == Surface.ROTATION_90) {
-            final int tmp = crop.top;
-            crop.top = dw - crop.right;
-            crop.right = crop.bottom;
-            crop.bottom = dw - crop.left;
-            crop.left = tmp;
-        } else if (rot == Surface.ROTATION_180) {
-            int tmp = crop.top;
-            crop.top = dh - crop.bottom;
-            crop.bottom = dh - tmp;
-            tmp = crop.right;
-            crop.right = dw - crop.left;
-            crop.left = dw - tmp;
-        } else if (rot == Surface.ROTATION_270) {
-            final int tmp = crop.top;
-            crop.top = crop.left;
-            crop.left = dh - crop.bottom;
-            crop.bottom = crop.right;
-            crop.right = dh - tmp;
-        }
-    }
-
     @Override
     public Bitmap screenshotWallpaper() {
         if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER,
@@ -4501,8 +4423,8 @@
      * @param config of the output bitmap
      * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
      */
-    Bitmap screenshotApplicationsInner(IBinder appToken, int displayId, int width, int height,
-            boolean includeFullDisplay, float frameScale, Bitmap.Config config,
+    private Bitmap screenshotApplicationsInner(IBinder appToken, int displayId, int width,
+            int height, boolean includeFullDisplay, float frameScale, Bitmap.Config config,
             boolean wallpaperOnly) {
         final DisplayContent displayContent;
         synchronized(mWindowMap) {
@@ -4513,263 +4435,8 @@
                 return null;
             }
         }
-        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-        int dw = displayInfo.logicalWidth;
-        int dh = displayInfo.logicalHeight;
-        if (dw == 0 || dh == 0) {
-            if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
-                    + ": returning null. logical widthxheight=" + dw + "x" + dh);
-            return null;
-        }
-
-        Bitmap bm = null;
-
-        int maxLayer = 0;
-        final Rect frame = new Rect();
-        final Rect stackBounds = new Rect();
-
-        boolean screenshotReady;
-        int minLayer;
-        if (appToken == null && !wallpaperOnly) {
-            screenshotReady = true;
-            minLayer = 0;
-        } else {
-            screenshotReady = false;
-            minLayer = Integer.MAX_VALUE;
-        }
-
-        WindowState appWin = null;
-
-        boolean includeImeInScreenshot;
-        synchronized(mWindowMap) {
-            final AppWindowToken imeTargetAppToken =
-                    mInputMethodTarget != null ? mInputMethodTarget.mAppToken : null;
-            // We only include the Ime in the screenshot if the app we are screenshoting is the IME
-            // target and isn't in multi-window mode. We don't screenshot the IME in multi-window
-            // mode because the frame of the IME might not overlap with that of the app.
-            // E.g. IME target app at the top in split-screen mode and the IME at the bottom
-            // overlapping with the bottom app.
-            includeImeInScreenshot = imeTargetAppToken != null
-                    && imeTargetAppToken.appToken != null
-                    && imeTargetAppToken.appToken.asBinder() == appToken
-                    && !mInputMethodTarget.isInMultiWindowMode();
-        }
-
-        final int aboveAppLayer = (mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)
-                * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
-
-        synchronized(mWindowMap) {
-            // Figure out the part of the screen that is actually the app.
-            appWin = null;
-            final WindowList windows = displayContent.getWindowList();
-            for (int i = windows.size() - 1; i >= 0; i--) {
-                WindowState ws = windows.get(i);
-                if (!ws.mHasSurface) {
-                    continue;
-                }
-                if (ws.mLayer >= aboveAppLayer) {
-                    continue;
-                }
-                if (wallpaperOnly && !ws.mIsWallpaper) {
-                    continue;
-                }
-                if (ws.mIsImWindow) {
-                    if (!includeImeInScreenshot) {
-                        continue;
-                    }
-                } else if (ws.mIsWallpaper) {
-                    // If this is the wallpaper layer and we're only looking for the wallpaper layer
-                    // then the target window state is this one.
-                    if (wallpaperOnly) {
-                        appWin = ws;
-                    }
-
-                    if (appWin == null) {
-                        // We have not ran across the target window yet, so it is probably
-                        // behind the wallpaper. This can happen when the keyguard is up and
-                        // all windows are moved behind the wallpaper. We don't want to
-                        // include the wallpaper layer in the screenshot as it will coverup
-                        // the layer of the target window.
-                        continue;
-                    }
-                    // Fall through. The target window is in front of the wallpaper. For this
-                    // case we want to include the wallpaper layer in the screenshot because
-                    // the target window might have some transparent areas.
-                } else if (appToken != null) {
-                    if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
-                        // This app window is of no interest if it is not associated with the
-                        // screenshot app.
-                        continue;
-                    }
-                    appWin = ws;
-                }
-
-                // Include this window.
-
-                final WindowStateAnimator winAnim = ws.mWinAnimator;
-                int layer = winAnim.mSurfaceController.getLayer();
-                if (maxLayer < layer) {
-                    maxLayer = layer;
-                }
-                if (minLayer > layer) {
-                    minLayer = layer;
-                }
-
-                // Don't include wallpaper in bounds calculation
-                if (!includeFullDisplay && !ws.mIsWallpaper) {
-                    final Rect wf = ws.mFrame;
-                    final Rect cr = ws.mContentInsets;
-                    int left = wf.left + cr.left;
-                    int top = wf.top + cr.top;
-                    int right = wf.right - cr.right;
-                    int bottom = wf.bottom - cr.bottom;
-                    frame.union(left, top, right, bottom);
-                    ws.getVisibleBounds(stackBounds);
-                    if (!Rect.intersects(frame, stackBounds)) {
-                        // Set frame empty if there's no intersection.
-                        frame.setEmpty();
-                    }
-                }
-
-                final boolean foundTargetWs =
-                        (ws.mAppToken != null && ws.mAppToken.token == appToken)
-                        || (appWin != null && wallpaperOnly);
-                if (foundTargetWs && ws.isDisplayedLw() && winAnim.getShown()) {
-                    screenshotReady = true;
-                }
-
-                if (ws.isObscuringFullscreen(displayInfo)){
-                    break;
-                }
-            }
-
-            if (appToken != null && appWin == null) {
-                // Can't find a window to snapshot.
-                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM,
-                        "Screenshot: Couldn't find a surface matching " + appToken);
-                return null;
-            }
-
-            if (!screenshotReady) {
-                Slog.i(TAG_WM, "Failed to capture screenshot of " + appToken +
-                        " appWin=" + (appWin == null ? "null" : (appWin + " drawState=" +
-                        appWin.mWinAnimator.mDrawState)));
-                return null;
-            }
-
-            // Screenshot is ready to be taken. Everything from here below will continue
-            // through the bottom of the loop and return a value. We only stay in the loop
-            // because we don't want to release the mWindowMap lock until the screenshot is
-            // taken.
-
-            if (maxLayer == 0) {
-                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
-                        + ": returning null maxLayer=" + maxLayer);
-                return null;
-            }
-
-            if (!includeFullDisplay) {
-                // Constrain frame to the screen size.
-                if (!frame.intersect(0, 0, dw, dh)) {
-                    frame.setEmpty();
-                }
-            } else {
-                // Caller just wants entire display.
-                frame.set(0, 0, dw, dh);
-            }
-            if (frame.isEmpty()) {
-                return null;
-            }
-
-            if (width < 0) {
-                width = (int) (frame.width() * frameScale);
-            }
-            if (height < 0) {
-                height = (int) (frame.height() * frameScale);
-            }
-
-            // Tell surface flinger what part of the image to crop. Take the top
-            // right part of the application, and crop the larger dimension to fit.
-            Rect crop = new Rect(frame);
-            if (width / (float) frame.width() < height / (float) frame.height()) {
-                int cropWidth = (int)((float)width / (float)height * frame.height());
-                crop.right = crop.left + cropWidth;
-            } else {
-                int cropHeight = (int)((float)height / (float)width * frame.width());
-                crop.bottom = crop.top + cropHeight;
-            }
-
-            // The screenshot API does not apply the current screen rotation.
-            int rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
-
-            if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
-                rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
-            }
-
-            // Surfaceflinger is not aware of orientation, so convert our logical
-            // crop to surfaceflinger's portrait orientation.
-            convertCropForSurfaceFlinger(crop, rot, dw, dh);
-
-            if (DEBUG_SCREENSHOT) {
-                Slog.i(TAG_WM, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
-                        + maxLayer + " appToken=" + appToken);
-                for (int i = 0; i < windows.size(); i++) {
-                    WindowState win = windows.get(i);
-                    WindowSurfaceController controller = win.mWinAnimator.mSurfaceController;
-                    Slog.i(TAG_WM, win + ": " + win.mLayer
-                            + " animLayer=" + win.mWinAnimator.mAnimLayer
-                            + " surfaceLayer=" + ((controller == null)
-                                ? "null" : controller.getLayer()));
-                }
-            }
-
-            ScreenRotationAnimation screenRotationAnimation =
-                    mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
-            final boolean inRotation = screenRotationAnimation != null &&
-                    screenRotationAnimation.isAnimating();
-            if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM,
-                    "Taking screenshot while rotating");
-
-            // We force pending transactions to flush before taking
-            // the screenshot by pushing an empty synchronous transaction.
-            SurfaceControl.openTransaction();
-            SurfaceControl.closeTransactionSync();
-
-            bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer,
-                    inRotation, rot);
-            if (bm == null) {
-                Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
-                        + ") to layer " + maxLayer);
-                return null;
-            }
-        }
-
-        if (DEBUG_SCREENSHOT) {
-            // TEST IF IT's ALL BLACK
-            int[] buffer = new int[bm.getWidth() * bm.getHeight()];
-            bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
-            boolean allBlack = true;
-            final int firstColor = buffer[0];
-            for (int i = 0; i < buffer.length; i++) {
-                if (buffer[i] != firstColor) {
-                    allBlack = false;
-                    break;
-                }
-            }
-            if (allBlack) {
-                Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" +
-                        Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
-                        (appWin != null ?
-                                appWin.mWinAnimator.mSurfaceController.getLayer() : "null") +
-                        " minLayer=" + minLayer + " maxLayer=" + maxLayer);
-            }
-        }
-
-        // Create a copy of the screenshot that is immutable and backed in ashmem.
-        // This greatly reduces the overhead of passing the bitmap between processes.
-        Bitmap ret = bm.createAshmemBitmap(config);
-        bm.recycle();
-        return ret;
+        return displayContent.screenshotApplications(appToken, displayId, width, height,
+                includeFullDisplay, frameScale, config, wallpaperOnly);
     }
 
     /**
@@ -4895,6 +4562,8 @@
         }
     }
 
+
+    // TODO(multidisplay): Rotate any display? Move to DisplayContent
     /**
      * Updates the current rotation of the specified display.
      *
@@ -4922,7 +4591,8 @@
             // Even if the screen rotation animation has finished (e.g. isAnimating
             // returns false), there is still some time where we haven't yet unfrozen
             // the display. We also need to abort rotation here.
-            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, still finishing previous rotation");
+            if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+                    "Deferring rotation, still finishing previous rotation");
             return false;
         }
 
@@ -4933,7 +4603,7 @@
         }
 
         final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-        final WindowList windows = displayContent.getWindowList();
+        final ReadOnlyWindowList windows = displayContent.getReadOnlyWindowList();
 
         final int oldRotation = mRotation;
         int rotation = mPolicy.rotationForOrientationLw(mLastOrientation, mRotation);
@@ -4973,24 +4643,18 @@
         boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
                 mLastOrientation, rotation);
 
-        if (DEBUG_ORIENTATION) {
-            Slog.v(TAG_WM, "Selected orientation "
-                    + mLastOrientation + ", got rotation " + rotation
-                    + " which has " + (altOrientation ? "incompatible" : "compatible")
-                    + " metrics");
-        }
+        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Selected orientation " + mLastOrientation
+                + ", got rotation " + rotation + " which has "
+                + (altOrientation ? "incompatible" : "compatible") + " metrics");
 
         if (mRotation == rotation && mAltOrientation == altOrientation) {
             // No change.
             return false;
         }
 
-        if (DEBUG_ORIENTATION) {
-            Slog.v(TAG_WM,
-                "Rotation changed to " + rotation + (altOrientation ? " (alt)" : "")
-                + " from " + mRotation + (mAltOrientation ? " (alt)" : "")
-                + ", lastOrientation=" + mLastOrientation);
-        }
+        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Rotation changed to " + rotation
+                + (altOrientation ? " (alt)" : "") + " from " + mRotation
+                + (mAltOrientation ? " (alt)" : "") + ", lastOrientation=" + mLastOrientation);
 
         mRotation = rotation;
         mAltOrientation = altOrientation;
@@ -5082,6 +4746,7 @@
                 w.mLastFreezeDuration = 0;
             }
         }
+
         if (rotateSeamlessly) {
             mH.removeMessages(H.SEAMLESS_ROTATION_TIMEOUT);
             mH.sendEmptyMessageDelayed(H.SEAMLESS_ROTATION_TIMEOUT, SEAMLESS_ROTATION_TIMEOUT_DURATION);
@@ -6943,20 +6608,8 @@
                     // Rotation only supported on primary display.
                     // TODO(multi-display)
                     synchronized(mWindowMap) {
-                        final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                        final WindowList windows = displayContent.getWindowList();
-                        boolean layoutNeeded = false;
-                        for (int i = windows.size() - 1; i >= 0; i--) {
-                            WindowState w = windows.get(i);
-                            if (w.mSeamlesslyRotated) {
-                                layoutNeeded = true;
-                                w.setDisplayLayoutNeeded();
-                                markForSeamlessRotation(w, false);
-                            }
-                        }
-                        if (layoutNeeded) {
-                            mWindowPlacerLocked.performSurfacePlacement();
-                        }
+                        final DisplayContent dc = getDefaultDisplayContentLocked();
+                        dc.onSeamlessRotationTimeout();
                     }
                 }
                 break;
@@ -7451,7 +7104,7 @@
 
         dc.onAppTransitionDone();
 
-        changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+        changes |= FINISH_LAYOUT_REDO_LAYOUT;
         if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG_WM,
                 "Wallpaper layer changed: assigning layers + relayout");
         dc.moveInputMethodWindowsIfNeeded(true);
@@ -7536,6 +7189,7 @@
         }
     }
 
+    // TODO: Move to DisplayContent
     boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
         WindowState newFocus = mRoot.computeFocusedWindow();
         if (mCurrentFocus != newFocus) {
@@ -7565,9 +7219,8 @@
             if (imWindowChanged && oldFocus != mInputMethodWindow) {
                 // Focus of the input method window changed. Perform layout if needed.
                 if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
-                    mWindowPlacerLocked.performLayoutLockedInner(displayContent, true /*initial*/,
-                            updateInputWindows);
-                    focusChanged &= ~WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+                    displayContent.performLayout(true /*initial*/,  updateInputWindows);
+                    focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
                 } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                     // Client will do the layout, but we need to assign layers
                     // for handleNewWindowLocked() below.
@@ -7575,12 +7228,11 @@
                 }
             }
 
-            if ((focusChanged & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+            if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
                 // The change in focus caused us to need to do a layout.  Okay.
                 displayContent.setLayoutNeeded();
                 if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
-                    mWindowPlacerLocked.performLayoutLockedInner(displayContent, true /*initial*/,
-                            updateInputWindows);
+                    displayContent.performLayout(true /*initial*/, updateInputWindows);
                 }
             }
 
@@ -7592,11 +7244,10 @@
 
             displayContent.adjustForImeIfNeeded();
 
-            // We may need to schedule some toast windows to be removed. The
-            // toasts for an app that does not have input focus are removed
-            // within a timeout to prevent apps to redress other apps' UI.
-            getDefaultDisplayContentLocked().scheduleToastWindowsTimeoutIfNeededLocked(
-                        oldFocus, newFocus);
+            // We may need to schedule some toast windows to be removed. The toasts for an app that
+            // does not have input focus are removed within a timeout to prevent apps to redress
+            // other apps' UI.
+            displayContent.scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
 
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             return true;
@@ -8229,8 +7880,9 @@
         }
     }
 
-    boolean dumpWindows(PrintWriter pw, String name, String[] args, int opti, boolean dumpAll) {
-        WindowList windows = new WindowList();
+    private boolean dumpWindows(PrintWriter pw, String name, String[] args, int opti,
+            boolean dumpAll) {
+        final WindowList windows = new WindowList();
         if ("apps".equals(name) || "visible".equals(name) || "visible-apps".equals(name)) {
             final boolean appsOnly = name.contains("apps");
             final boolean visibleOnly = name.contains("visible");
@@ -8257,7 +7909,7 @@
         return true;
     }
 
-    void dumpLastANRLocked(PrintWriter pw) {
+    private void dumpLastANRLocked(PrintWriter pw) {
         pw.println("WINDOW MANAGER LAST ANR (dumpsys window lastanr)");
         if (mLastANRState == null) {
             pw.println("  <no ANR has occurred since boot>");
@@ -8275,8 +7927,7 @@
      * @param windowState The window that ANR'd, may be null.
      * @param reason The reason for the ANR, may be null.
      */
-    public void saveANRStateLocked(AppWindowToken appWindowToken, WindowState windowState,
-            String reason) {
+    void saveANRStateLocked(AppWindowToken appWindowToken, WindowState windowState, String reason) {
         StringWriter sw = new StringWriter();
         PrintWriter pw = new FastPrintWriter(sw, false, 1024);
         pw.println("  ANR time: " + DateFormat.getInstance().format(new Date()));
@@ -8474,6 +8125,8 @@
     }
 
     // There is an inherent assumption that this will never return null.
+    // TODO(multi-display): Inspect all the call-points of this method to see if they make sense to
+    // support non-default display.
     DisplayContent getDefaultDisplayContentLocked() {
         return mRoot.getDisplayContentOrCreate(DEFAULT_DISPLAY);
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a7b46111..d791454 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -136,9 +136,51 @@
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
 
 class WindowList extends ArrayList<WindowState> {
-    WindowList() {}
-    WindowList(WindowList windowList) {
-        super(windowList);
+
+    /**
+     * Read-only interface for the window list that the creator of the window list can pass-out to
+     * other users to prevent them from modifying the window list.
+     */
+    private ReadOnlyWindowList mReadOnly;
+
+    WindowList() {
+        mReadOnly = new ReadOnlyWindowList(this);
+    }
+
+    /** Returns the read-only interface for this window list. */
+    ReadOnlyWindowList getReadOnly() {
+        return mReadOnly;
+    }
+}
+
+/**
+ * Read-only interface for a list of windows. It is common for the owner of a list of windows to
+ * want to provide a way for external classes to iterate of its windows, but prevent them from
+ * modifying the list in any way. This call provides a way for them to do that by wrapping the
+ * original window list and only exposing the read-only APIs.
+ */
+final class ReadOnlyWindowList {
+    // List of windows this read-only class is tied to.
+    private final WindowList mWindows;
+
+    ReadOnlyWindowList(WindowList windows) {
+        mWindows = windows;
+    }
+
+    WindowState get(int index) {
+        return mWindows.get(index);
+    }
+
+    int indexOf(WindowState w) {
+        return mWindows.indexOf(w);
+    }
+
+    int size() {
+        return mWindows.size();
+    }
+
+    boolean isEmpty() {
+        return mWindows.isEmpty();
     }
 }
 
@@ -3873,7 +3915,6 @@
             dc.addToWindowList(this, index);
             index++;
         }
-        mService.mWindowsChanged = true;
         return index;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 04e00c4..63820e5 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -213,175 +213,6 @@
         return mInLayout;
     }
 
-    final void performLayoutLockedInner(final DisplayContent displayContent,
-            boolean initial, boolean updateInputWindows) {
-        if (!displayContent.isLayoutNeeded()) {
-            return;
-        }
-        displayContent.clearLayoutNeeded();
-        WindowList windows = displayContent.getWindowList();
-        boolean isDefaultDisplay = displayContent.isDefaultDisplay;
-
-        DisplayInfo displayInfo = displayContent.getDisplayInfo();
-        final int dw = displayInfo.logicalWidth;
-        final int dh = displayInfo.logicalHeight;
-
-        final int N = windows.size();
-        int i;
-
-        if (DEBUG_LAYOUT) {
-            Slog.v(TAG, "-------------------------------------");
-            Slog.v(TAG, "performLayout: needed=" + displayContent.isLayoutNeeded()
-                    + " dw=" + dw + " dh=" + dh);
-        }
-
-        mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mService.mRotation,
-                displayContent.getConfiguration().uiMode);
-        if (isDefaultDisplay) {
-            // Not needed on non-default displays.
-            mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
-            mService.mScreenRect.set(0, 0, dw, dh);
-        }
-
-        mService.mPolicy.getContentRectLw(mTmpContentRect);
-        displayContent.resize(mTmpContentRect);
-
-        int seq = mService.mLayoutSeq+1;
-        if (seq < 0) seq = 0;
-        mService.mLayoutSeq = seq;
-
-        boolean behindDream = false;
-
-        // First perform layout of any root windows (not attached
-        // to another window).
-        int topAttached = -1;
-        for (i = N-1; i >= 0; i--) {
-            final WindowState win = windows.get(i);
-
-            // Don't do layout of a window if it is not visible, or
-            // soon won't be visible, to avoid wasting time and funky
-            // changes while a window is animating away.
-            final boolean gone = (behindDream && mService.mPolicy.canBeForceHidden(win, win.mAttrs))
-                    || win.isGoneForLayoutLw();
-
-            if (DEBUG_LAYOUT && !win.mLayoutAttached) {
-                Slog.v(TAG, "1ST PASS " + win
-                        + ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame
-                        + " mLayoutAttached=" + win.mLayoutAttached
-                        + " screen changed=" + win.isConfigChanged());
-                final AppWindowToken atoken = win.mAppToken;
-                if (gone) Slog.v(TAG, "  GONE: mViewVisibility="
-                        + win.mViewVisibility + " mRelayoutCalled="
-                        + win.mRelayoutCalled + " hidden="
-                        + win.mToken.hidden + " hiddenRequested="
-                        + (atoken != null && atoken.hiddenRequested)
-                        + " parentHidden=" + win.isParentWindowHidden());
-                else Slog.v(TAG, "  VIS: mViewVisibility="
-                        + win.mViewVisibility + " mRelayoutCalled="
-                        + win.mRelayoutCalled + " hidden="
-                        + win.mToken.hidden + " hiddenRequested="
-                        + (atoken != null && atoken.hiddenRequested)
-                        + " parentHidden=" + win.isParentWindowHidden());
-            }
-
-            // If this view is GONE, then skip it -- keep the current
-            // frame, and let the caller know so they can ignore it
-            // if they want.  (We do the normal layout for INVISIBLE
-            // windows, since that means "perform layout as normal,
-            // just don't display").
-            if (!gone || !win.mHaveFrame || win.mLayoutNeeded
-                    || ((win.isConfigChanged() || win.setReportResizeHints())
-                            && !win.isGoneForLayoutLw() &&
-                            ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 ||
-                            (win.mHasSurface && win.mAppToken != null &&
-                            win.mAppToken.layoutConfigChanges)))) {
-                if (!win.mLayoutAttached) {
-                    if (initial) {
-                        //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
-                        win.mContentChanged = false;
-                    }
-                    if (win.mAttrs.type == TYPE_DREAM) {
-                        // Don't layout windows behind a dream, so that if it
-                        // does stuff like hide the status bar we won't get a
-                        // bad transition when it goes away.
-                        behindDream = true;
-                    }
-                    win.mLayoutNeeded = false;
-                    win.prelayout();
-                    mService.mPolicy.layoutWindowLw(win, null);
-                    win.mLayoutSeq = seq;
-
-                    // Window frames may have changed. Update dim layer with the new bounds.
-                    final Task task = win.getTask();
-                    if (task != null) {
-                        displayContent.mDimLayerController.updateDimLayer(task);
-                    }
-
-                    if (DEBUG_LAYOUT) Slog.v(TAG,
-                            "  LAYOUT: mFrame="
-                            + win.mFrame + " mContainingFrame="
-                            + win.mContainingFrame + " mDisplayFrame="
-                            + win.mDisplayFrame);
-                } else {
-                    if (topAttached < 0) topAttached = i;
-                }
-            }
-        }
-
-        boolean attachedBehindDream = false;
-
-        // Now perform layout of attached windows, which usually
-        // depend on the position of the window they are attached to.
-        // XXX does not deal with windows that are attached to windows
-        // that are themselves attached.
-        for (i = topAttached; i >= 0; i--) {
-            final WindowState win = windows.get(i);
-
-            if (win.mLayoutAttached) {
-                if (DEBUG_LAYOUT) Slog.v(TAG,
-                        "2ND PASS " + win + " mHaveFrame=" + win.mHaveFrame + " mViewVisibility="
-                        + win.mViewVisibility + " mRelayoutCalled=" + win.mRelayoutCalled);
-                // If this view is GONE, then skip it -- keep the current
-                // frame, and let the caller know so they can ignore it
-                // if they want.  (We do the normal layout for INVISIBLE
-                // windows, since that means "perform layout as normal,
-                // just don't display").
-                if (attachedBehindDream && mService.mPolicy.canBeForceHidden(win, win.mAttrs)) {
-                    continue;
-                }
-                if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
-                        || !win.mHaveFrame || win.mLayoutNeeded) {
-                    if (initial) {
-                        //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
-                        win.mContentChanged = false;
-                    }
-                    win.mLayoutNeeded = false;
-                    win.prelayout();
-                    mService.mPolicy.layoutWindowLw(win, win.getParentWindow());
-                    win.mLayoutSeq = seq;
-                    if (DEBUG_LAYOUT) Slog.v(TAG,
-                            "  LAYOUT: mFrame=" + win.mFrame + " mContainingFrame="
-                            + win.mContainingFrame + " mDisplayFrame=" + win.mDisplayFrame);
-                }
-            } else if (win.mAttrs.type == TYPE_DREAM) {
-                // Don't layout windows behind a dream, so that if it
-                // does stuff like hide the status bar we won't get a
-                // bad transition when it goes away.
-                attachedBehindDream = behindDream;
-            }
-        }
-
-        // Window frames may have changed. Tell the input dispatcher about it.
-        mService.mInputMonitor.layoutInputConsumers(dw, dh);
-        mService.mInputMonitor.setUpdateInputWindowsNeededLw();
-        if (updateInputWindows) {
-            mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
-        }
-
-        mService.mPolicy.finishLayoutLw();
-        mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
-    }
-
     /**
      * @return bitmap indicating if another pass through layout must be made.
      */
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 38225cc..38f25e0 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -235,7 +235,6 @@
         if (!mChildren.contains(win)) {
             addChild(win, null);
         }
-        mService.mWindowsChanged = true;
         mDisplayContent.moveInputMethodDialogs(pos + 1);
     }
 
@@ -338,8 +337,9 @@
         }
     }
 
-    boolean updateWallpaperWindowsPlacement(WindowList windowList, WindowState wallpaperTarget,
-            int wallpaperTargetIndex, boolean visible, int dw, int dh, int wallpaperAnimLayerAdj) {
+    boolean updateWallpaperWindowsPlacement(ReadOnlyWindowList windowList,
+            WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible, int dw, int dh,
+            int wallpaperAnimLayerAdj) {
 
         boolean changed = false;
         if (hidden == visible) {
@@ -379,8 +379,7 @@
             if (oldIndex >= 0) {
                 if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
                         "Wallpaper removing at " + oldIndex + ": " + wallpaper);
-                windowList.remove(oldIndex);
-                mService.mWindowsChanged = true;
+                mDisplayContent.removeFromWindowList(wallpaper);
                 if (oldIndex < wallpaperTargetIndex) {
                     wallpaperTargetIndex--;
                 }
@@ -403,8 +402,7 @@
                     || (DEBUG_ADD_REMOVE && oldIndex != insertionIndex)) Slog.v(TAG,
                     "Moving wallpaper " + wallpaper + " from " + oldIndex + " to " + insertionIndex);
 
-            windowList.add(insertionIndex, wallpaper);
-            mService.mWindowsChanged = true;
+            mDisplayContent.addToWindowList(wallpaper, insertionIndex);
             changed = true;
         }
 
@@ -415,7 +413,7 @@
      * @return The index in {@param windows} of the lowest window that is currently on screen and
      *         not hidden by the policy.
      */
-    private int findLowestWindowOnScreen(WindowList windowList) {
+    private int findLowestWindowOnScreen(ReadOnlyWindowList windowList) {
         final int size = windowList.size();
         for (int index = 0; index < size; index++) {
             final WindowState win = windowList.get(index);