Merge "Fixing rebase problem."
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 4fe866f..06eb829 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -18,6 +18,12 @@
     hwui/MinikinUtils.cpp \
     hwui/PaintImpl.cpp \
     hwui/Typeface.cpp \
+    pipeline/skia/GLFunctorDrawable.cpp \
+    pipeline/skia/LayerDrawable.cpp \
+    pipeline/skia/RenderNodeDrawable.cpp \
+    pipeline/skia/ReorderBarrierDrawables.cpp \
+    pipeline/skia/SkiaDisplayList.cpp \
+    pipeline/skia/SkiaRecordingCanvas.cpp \
     renderstate/Blend.cpp \
     renderstate/MeshState.cpp \
     renderstate/OffscreenBufferPool.cpp \
@@ -94,7 +100,6 @@
     ShadowTessellator.cpp \
     SkiaCanvas.cpp \
     SkiaCanvasProxy.cpp \
-    SkiaDisplayList.cpp \
     SkiaShader.cpp \
     Snapshot.cpp \
     SpotShadow.cpp \
@@ -169,6 +174,7 @@
 hwui_c_includes += \
     external/skia/include/private \
     external/skia/src/core \
+    external/skia/src/effects \
     external/harfbuzz_ng/src \
     external/freetype/include
 
@@ -284,6 +290,7 @@
     tests/unit/MeshStateTests.cpp \
     tests/unit/OffscreenBufferPoolTests.cpp \
     tests/unit/OpDumperTests.cpp \
+    tests/unit/RenderNodeDrawableTests.cpp \
     tests/unit/RecordingCanvasTests.cpp \
     tests/unit/RenderNodeTests.cpp \
     tests/unit/RenderPropertiesTests.cpp \
diff --git a/libs/hwui/NinePatchUtils.h b/libs/hwui/NinePatchUtils.h
index 7a271b7..e989a46 100644
--- a/libs/hwui/NinePatchUtils.h
+++ b/libs/hwui/NinePatchUtils.h
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#pragma once
+
 namespace android {
 namespace NinePatchUtils {
 
@@ -34,5 +36,61 @@
     }
 }
 
+static inline int NumDistinctRects(const SkCanvas::Lattice& lattice) {
+    int xRects;
+    if (lattice.fXCount > 0) {
+        xRects = (0 == lattice.fXDivs[0]) ? lattice.fXCount : lattice.fXCount + 1;
+    } else {
+        xRects = 1;
+    }
+
+    int yRects;
+    if (lattice.fYCount > 0) {
+        yRects = (0 == lattice.fYDivs[0]) ? lattice.fYCount : lattice.fYCount + 1;
+    } else {
+        yRects = 1;
+    }
+    return xRects * yRects;
+}
+
+static inline void SetLatticeFlags(SkCanvas::Lattice* lattice, SkCanvas::Lattice::Flags* flags,
+        int numFlags, const Res_png_9patch& chunk) {
+    lattice->fFlags = flags;
+    sk_bzero(flags, numFlags * sizeof(SkCanvas::Lattice::Flags));
+
+    bool needPadRow = lattice->fYCount > 0 && 0 == lattice->fYDivs[0];
+    bool needPadCol = lattice->fXCount > 0 && 0 == lattice->fXDivs[0];
+
+    int yCount = lattice->fYCount;
+    if (needPadRow) {
+        // Skip flags for the degenerate first row of rects.
+        flags += lattice->fXCount + 1;
+        yCount--;
+    }
+
+    int i = 0;
+    bool setFlags = false;
+    for (int y = 0; y < yCount + 1; y++) {
+        for (int x = 0; x < lattice->fXCount + 1; x++) {
+            if (0 == x && needPadCol) {
+                // First rect of each column is degenerate, skip the flag.
+                flags++;
+                continue;
+            }
+
+            if (0 == chunk.getColors()[i++]) {
+                *flags = SkCanvas::Lattice::kTransparent_Flags;
+                setFlags = true;
+            }
+
+            flags++;
+        }
+    }
+
+    if (!setFlags) {
+        lattice->fFlags = nullptr;
+    }
+}
+
 }; // namespace NinePatchUtils
 }; // namespace android
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index a05c744..3819c5e 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -32,7 +32,7 @@
 #include "DisplayList.h"
 #include "Matrix.h"
 #include "RenderProperties.h"
-#include "SkiaDisplayList.h"
+#include "pipeline/skia/SkiaDisplayList.h"
 
 #include <vector>
 
@@ -50,7 +50,6 @@
 class FrameBuilder;
 class OffscreenBuffer;
 class Rect;
-class SkiaDisplayList;
 class SkiaShader;
 struct RenderNodeOp;
 
@@ -61,6 +60,10 @@
 class RenderNode;
 }
 
+namespace skiapipeline {
+    class SkiaDisplayList;
+}
+
 /**
  * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
  *
@@ -294,14 +297,14 @@
      * Detach and transfer ownership of an already allocated displayList for use
      * in recording updated content for this renderNode
      */
-    std::unique_ptr<SkiaDisplayList> detachAvailableList() {
+    std::unique_ptr<skiapipeline::SkiaDisplayList> detachAvailableList() {
         return std::move(mAvailableDisplayList);
     }
 
     /**
      * Attach unused displayList to this node for potential future reuse.
      */
-    void attachAvailableList(SkiaDisplayList* skiaDisplayList) {
+    void attachAvailableList(skiapipeline::SkiaDisplayList* skiaDisplayList) {
         mAvailableDisplayList.reset(skiaDisplayList);
     }
 
@@ -337,7 +340,7 @@
      *  2) It is replaced with the displayList from the next completed frame
      *  3) It is detached and used to to record a new displayList for a later frame
      */
-    std::unique_ptr<SkiaDisplayList> mAvailableDisplayList;
+    std::unique_ptr<skiapipeline::SkiaDisplayList> mAvailableDisplayList;
 
     /**
      * An offscreen rendering target used to contain the contents this RenderNode
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index c48b4dc..b6ac48f 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -21,6 +21,7 @@
 #include "VectorDrawable.h"
 #include "hwui/Bitmap.h"
 #include "hwui/MinikinUtils.h"
+#include "pipeline/skia/AnimatedDrawables.h"
 
 #include <SkDrawable.h>
 #include <SkDevice.h>
@@ -57,6 +58,8 @@
     mCanvas.reset(new SkCanvas(bitmap));
 }
 
+SkiaCanvas::~SkiaCanvas() {}
+
 void SkiaCanvas::reset(SkCanvas* skiaCanvas) {
     mCanvas.reset(SkRef(skiaCanvas));
     mSaveStack.reset(nullptr);
@@ -671,62 +674,6 @@
                          indexCount, tmpPaint);
 }
 
-static inline int num_distinct_rects(const SkCanvas::Lattice& lattice) {
-    int xRects;
-    if (lattice.fXCount > 0) {
-        xRects = (0 == lattice.fXDivs[0]) ? lattice.fXCount : lattice.fXCount + 1;
-    } else {
-        xRects = 1;
-    }
-
-    int yRects;
-    if (lattice.fYCount > 0) {
-        yRects = (0 == lattice.fYDivs[0]) ? lattice.fYCount : lattice.fYCount + 1;
-    } else {
-        yRects = 1;
-    }
-    return xRects * yRects;
-}
-
-static inline void set_lattice_flags(SkCanvas::Lattice* lattice, SkCanvas::Lattice::Flags* flags,
-                                     int numFlags, const Res_png_9patch& chunk) {
-    lattice->fFlags = flags;
-    sk_bzero(flags, numFlags * sizeof(SkCanvas::Lattice::Flags));
-
-    bool needPadRow = lattice->fYCount > 0 && 0 == lattice->fYDivs[0];
-    bool needPadCol = lattice->fXCount > 0 && 0 == lattice->fXDivs[0];
-
-    int yCount = lattice->fYCount;
-    if (needPadRow) {
-        // Skip flags for the degenerate first row of rects.
-        flags += lattice->fXCount + 1;
-        yCount--;
-    }
-
-    int i = 0;
-    bool setFlags = false;
-    for (int y = 0; y < yCount + 1; y++) {
-        for (int x = 0; x < lattice->fXCount + 1; x++) {
-            if (0 == x && needPadCol) {
-                // First rect of each column is degenerate, skip the flag.
-                flags++;
-                continue;
-            }
-
-            if (0 == chunk.getColors()[i++]) {
-                *flags = SkCanvas::Lattice::kTransparent_Flags;
-                setFlags = true;
-            }
-
-            flags++;
-        }
-    }
-
-    if (!setFlags) {
-        lattice->fFlags = nullptr;
-    }
-}
-
 void SkiaCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk,
         float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
 
@@ -738,7 +685,7 @@
 
     lattice.fFlags = nullptr;
     int numFlags = 0;
-    if (chunk.numColors > 0 && chunk.numColors == num_distinct_rects(lattice)) {
+    if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) {
         // We can expect the framework to give us a color for every distinct rect.
         // Skia requires a flag for every rect.
         numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
@@ -746,7 +693,7 @@
 
     SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags);
     if (numFlags > 0) {
-        set_lattice_flags(&lattice, flags.get(), numFlags, chunk);
+        NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk);
     }
 
     lattice.fBounds = nullptr;
@@ -820,69 +767,18 @@
 // Canvas draw operations: Animations
 // ----------------------------------------------------------------------------
 
-class AnimatedRoundRect : public SkDrawable {
- public:
-    AnimatedRoundRect(uirenderer::CanvasPropertyPrimitive* left,
-            uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
-            uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
-            uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* p) :
-            mLeft(left), mTop(top), mRight(right), mBottom(bottom), mRx(rx), mRy(ry), mPaint(p) {}
-
- protected:
-     virtual SkRect onGetBounds() override {
-         return SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
-     }
-     virtual void onDraw(SkCanvas* canvas) override {
-         SkRect rect = SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
-         canvas->drawRoundRect(rect, mRx->value, mRy->value, mPaint->value);
-     }
-
- private:
-    sp<uirenderer::CanvasPropertyPrimitive> mLeft;
-    sp<uirenderer::CanvasPropertyPrimitive> mTop;
-    sp<uirenderer::CanvasPropertyPrimitive> mRight;
-    sp<uirenderer::CanvasPropertyPrimitive> mBottom;
-    sp<uirenderer::CanvasPropertyPrimitive> mRx;
-    sp<uirenderer::CanvasPropertyPrimitive> mRy;
-    sp<uirenderer::CanvasPropertyPaint> mPaint;
-};
-
-class AnimatedCircle : public SkDrawable {
- public:
-    AnimatedCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y,
-            uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint) :
-            mX(x), mY(y), mRadius(radius), mPaint(paint) {}
-
- protected:
-     virtual SkRect onGetBounds() override {
-         const float x = mX->value;
-         const float y = mY->value;
-         const float radius = mRadius->value;
-         return SkRect::MakeLTRB(x - radius, y - radius, x + radius, y + radius);
-     }
-     virtual void onDraw(SkCanvas* canvas) override {
-         canvas->drawCircle(mX->value, mY->value, mRadius->value, mPaint->value);
-     }
-
- private:
-    sp<uirenderer::CanvasPropertyPrimitive> mX;
-    sp<uirenderer::CanvasPropertyPrimitive> mY;
-    sp<uirenderer::CanvasPropertyPrimitive> mRadius;
-    sp<uirenderer::CanvasPropertyPaint> mPaint;
-};
-
 void SkiaCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
         uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
         uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
         uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) {
-    sk_sp<AnimatedRoundRect> drawable(
-            new AnimatedRoundRect(left, top, right, bottom, rx, ry, paint));
+    sk_sp<uirenderer::skiapipeline::AnimatedRoundRect> drawable(
+            new uirenderer::skiapipeline::AnimatedRoundRect(left, top, right, bottom, rx, ry, paint));
     mCanvas->drawDrawable(drawable.get());
 }
 
 void SkiaCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y,
         uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint) {
-    sk_sp<AnimatedCircle> drawable(new AnimatedCircle(x, y, radius, paint));
+    sk_sp<uirenderer::skiapipeline::AnimatedCircle> drawable(new uirenderer::skiapipeline::AnimatedCircle(x, y, radius, paint));
     mCanvas->drawDrawable(drawable.get());
 }
 
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index d1edff9..a0cdfcb 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -40,6 +40,8 @@
      */
     explicit SkiaCanvas(SkCanvas* canvas);
 
+    virtual ~SkiaCanvas();
+
     virtual SkCanvas* asSkCanvas() override {
         return mCanvas.get();
     }
diff --git a/libs/hwui/SkiaDrawables.h b/libs/hwui/SkiaDrawables.h
deleted file mode 100644
index a1ceeaa..0000000
--- a/libs/hwui/SkiaDrawables.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "Layer.h"
-#include "RenderNode.h"
-
-#include <SkCanvas.h>
-#include <SkDrawable.h>
-#include <SkMatrix.h>
-
-#include <utils/RefBase.h>
-#include <utils/FatVector.h>
-#include <utils/Functor.h>
-
-namespace android {
-
-class Functor;
-
-namespace uirenderer {
-
-
-class RenderProperties;
-class OffscreenBuffer;
-class GlFunctorLifecycleListener;
-class SkiaDisplayList;
-
-/**
- * This drawable wraps a RenderNode and enables it to be recorded into a list
- * of Skia drawing commands.
- */
-class RenderNodeDrawable : public SkDrawable {
-public:
-    explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas)
-            : mRenderNode(node)
-            , mRecordedTransform(canvas->getTotalMatrix()) {}
-
-    /**
-     * The renderNode (and its properties) that is to be drawn
-     */
-    RenderNode* getRenderNode() const { return mRenderNode.get(); }
-
-    /**
-     * Returns the transform on the canvas at time of recording and is used for
-     * computing total transform without rerunning DL contents.
-     */
-    const SkMatrix& getRecordedMatrix() const { return mRecordedTransform; }
-
-protected:
-    virtual SkRect onGetBounds() override {
-        // We don't want to enable a record time quick reject because the properties
-        // of the RenderNode may be updated on subsequent frames.
-        return SkRect::MakeLargest();
-    }
-    virtual void onDraw(SkCanvas* canvas) override { /* TODO */ }
-
-private:
-    sp<RenderNode> mRenderNode;
-    const SkMatrix mRecordedTransform;
-};
-
-/**
- * This drawable wraps a OpenGL functor enabling it to be recorded into a list
- * of Skia drawing commands.
- */
-class GLFunctorDrawable : public SkDrawable {
-public:
-    GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
-            : mFunctor(functor)
-            , mListener(listener) {
-        canvas->getClipBounds(&mBounds);
-    }
-    virtual ~GLFunctorDrawable() {}
-
-    void syncFunctor() const { (*mFunctor)(DrawGlInfo::kModeSync, nullptr); }
-
- protected:
-    virtual SkRect onGetBounds() override { return mBounds; }
-    virtual void onDraw(SkCanvas* canvas) override { /* TODO */ }
-
- private:
-     Functor* mFunctor;
-     sp<GlFunctorLifecycleListener> mListener;
-     SkRect mBounds;
-};
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h
new file mode 100644
index 0000000..44c494f
--- /dev/null
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "CanvasProperty.h"
+#include <utils/RefBase.h>
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class AnimatedRoundRect : public SkDrawable {
+public:
+    AnimatedRoundRect(uirenderer::CanvasPropertyPrimitive* left,
+            uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
+            uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
+            uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* p)
+            : mLeft(left)
+            , mTop(top)
+            , mRight(right)
+            , mBottom(bottom)
+            , mRx(rx)
+            , mRy(ry)
+            , mPaint(p) {}
+
+protected:
+    virtual SkRect onGetBounds() override {
+        return SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
+    }
+    virtual void onDraw(SkCanvas* canvas) override {
+        SkRect rect = SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
+        canvas->drawRoundRect(rect, mRx->value, mRy->value, mPaint->value);
+    }
+
+private:
+    sp<uirenderer::CanvasPropertyPrimitive> mLeft;
+    sp<uirenderer::CanvasPropertyPrimitive> mTop;
+    sp<uirenderer::CanvasPropertyPrimitive> mRight;
+    sp<uirenderer::CanvasPropertyPrimitive> mBottom;
+    sp<uirenderer::CanvasPropertyPrimitive> mRx;
+    sp<uirenderer::CanvasPropertyPrimitive> mRy;
+    sp<uirenderer::CanvasPropertyPaint> mPaint;
+};
+
+class AnimatedCircle : public SkDrawable {
+public:
+    AnimatedCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y,
+            uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint)
+            : mX(x)
+            , mY(y)
+            , mRadius(radius)
+            , mPaint(paint) {}
+
+protected:
+    virtual SkRect onGetBounds() override {
+        const float x = mX->value;
+        const float y = mY->value;
+        const float radius = mRadius->value;
+        return SkRect::MakeLTRB(x - radius, y - radius, x + radius, y + radius);
+    }
+    virtual void onDraw(SkCanvas* canvas) override {
+        canvas->drawCircle(mX->value, mY->value, mRadius->value, mPaint->value);
+    }
+
+private:
+    sp<uirenderer::CanvasPropertyPrimitive> mX;
+    sp<uirenderer::CanvasPropertyPrimitive> mY;
+    sp<uirenderer::CanvasPropertyPrimitive> mRadius;
+    sp<uirenderer::CanvasPropertyPaint> mPaint;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
new file mode 100644
index 0000000..fb2134c
--- /dev/null
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GLFunctorDrawable.h"
+#include "GlFunctorLifecycleListener.h"
+#include "RenderNode.h"
+#include "SkClipStack.h"
+#include <private/hwui/DrawGlInfo.h>
+#include <SkPath.h>
+#include <GrContext.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+GLFunctorDrawable::~GLFunctorDrawable() {
+    if(mListener.get() != nullptr) {
+        mListener->onGlFunctorReleased(mFunctor);
+    }
+}
+
+void GLFunctorDrawable::syncFunctor() const {
+    (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
+}
+
+static void setScissor(int viewportHeight, const SkIRect& clip) {
+    SkASSERT(!clip.isEmpty());
+    // transform to Y-flipped GL space, and prevent negatives
+    GLint y = viewportHeight - clip.fBottom;
+    GLint height = (viewportHeight - clip.fTop) - y;
+    glScissor(clip.fLeft, y, clip.width(), height);
+}
+
+void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
+    if (canvas->getGrContext() == nullptr) {
+        SkDEBUGF(("Attempting to draw GLFunctor into an unsupported surface"));
+        return;
+    }
+
+    canvas->flush();
+
+    SkImageInfo canvasInfo = canvas->imageInfo();
+    SkMatrix44 mat4(canvas->getTotalMatrix());
+
+    SkIRect ibounds;
+    canvas->getClipDeviceBounds(&ibounds);
+
+    DrawGlInfo info;
+    info.clipLeft = ibounds.fLeft;
+    info.clipTop = ibounds.fTop;
+    info.clipRight = ibounds.fRight;
+    info.clipBottom = ibounds.fBottom;
+    //   info.isLayer = hasLayer();
+    info.isLayer = false;
+    info.width = canvasInfo.width();
+    info.height = canvasInfo.height();
+    mat4.asColMajorf(&info.transform[0]);
+
+    //apply a simple clip with a scissor or a complex clip with a stencil
+    SkRegion clipRegion;
+    SkPath path;
+    canvas->getClipStack()->asPath(&path);
+    clipRegion.setPath(path, SkRegion(ibounds));
+    if (CC_UNLIKELY(clipRegion.isComplex())) {
+        //It is only a temporary solution to use a scissor to draw the stencil.
+        //There is a bug 31489986 to implement efficiently non-rectangular clips.
+        glDisable(GL_SCISSOR_TEST);
+        glDisable(GL_STENCIL_TEST);
+        glStencilMask(0xff);
+        glClearStencil(0);
+        glClear(GL_STENCIL_BUFFER_BIT);
+        glEnable(GL_SCISSOR_TEST);
+        SkRegion::Cliperator it(clipRegion, ibounds);
+        while (!it.done()) {
+            setScissor(info.height, it.rect());
+            glClearStencil(0x1);
+            glClear(GL_STENCIL_BUFFER_BIT);
+            it.next();
+        }
+        glDisable(GL_SCISSOR_TEST);
+        glStencilFunc(GL_EQUAL, 0x1, 0xff);
+        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+        glEnable(GL_STENCIL_TEST);
+    } else if (clipRegion.isEmpty()) {
+        glDisable(GL_STENCIL_TEST);
+        glDisable(GL_SCISSOR_TEST);
+    } else {
+        glDisable(GL_STENCIL_TEST);
+        glEnable(GL_SCISSOR_TEST);
+        setScissor(info.height, clipRegion.getBounds());
+    }
+
+    (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+
+    canvas->getGrContext()->resetContext();
+ }
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
new file mode 100644
index 0000000..bf39dad
--- /dev/null
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+#include <utils/RefBase.h>
+#include <utils/Functor.h>
+
+namespace android {
+namespace uirenderer {
+
+class GlFunctorLifecycleListener;
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a OpenGL functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class GLFunctorDrawable : public SkDrawable {
+public:
+    GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
+            : mFunctor(functor)
+            , mListener(listener) {
+        canvas->getClipBounds(&mBounds);
+    }
+    virtual ~GLFunctorDrawable();
+
+    void syncFunctor() const;
+
+ protected:
+    virtual SkRect onGetBounds() override { return mBounds; }
+    virtual void onDraw(SkCanvas* canvas) override;
+
+ private:
+     Functor* mFunctor;
+     sp<GlFunctorLifecycleListener> mListener;
+     SkRect mBounds;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
new file mode 100644
index 0000000..f8a181f
--- /dev/null
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerDrawable.h"
+#include "gl/GrGLTypes.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+void LayerDrawable::onDraw(SkCanvas* canvas) {
+    // transform the matrix based on the layer
+    int saveCount = -1;
+    if (!mLayer->getTransform().isIdentity()) {
+        saveCount = canvas->save();
+        SkMatrix transform;
+        mLayer->getTransform().copyTo(transform);
+        canvas->concat(transform);
+    }
+    GrGLTextureInfo externalTexture;
+    externalTexture.fTarget = mLayer->getRenderTarget();
+    externalTexture.fID = mLayer->getTextureId();
+    GrContext* context = canvas->getGrContext();
+    GrBackendTextureDesc textureDescription;
+    textureDescription.fWidth = mLayer->getWidth();
+    textureDescription.fHeight = mLayer->getHeight();
+    textureDescription.fConfig = kRGBA_8888_GrPixelConfig;
+    textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin;
+    textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);
+    sk_sp<SkImage> layerImage(SkImage::NewFromTexture(context, textureDescription));
+    if (layerImage) {
+        SkPaint paint;
+        paint.setAlpha(mLayer->getAlpha());
+        paint.setBlendMode(mLayer->getMode());
+        paint.setColorFilter(mLayer->getColorFilter());
+        canvas->drawImage(layerImage, 0, 0, &paint);
+    }
+    // restore the original matrix
+    if (saveCount >= 0) {
+        canvas->restoreToCount(saveCount);
+    }
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
new file mode 100644
index 0000000..91e2744
--- /dev/null
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Layer.h"
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/*
+ * Draws a layer backed by an OpenGL texture into a SkCanvas.
+ */
+class LayerDrawable : public SkDrawable {
+ public:
+    explicit LayerDrawable(Layer* layer)
+            : mLayer(layer) {}
+
+ protected:
+     virtual SkRect onGetBounds() override {
+         return SkRect::MakeWH(mLayer->getWidth(), mLayer->getHeight());
+     }
+     virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+    sp<Layer> mLayer;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
new file mode 100644
index 0000000..cefa893
--- /dev/null
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "RenderNodeDrawable.h"
+#include "RenderNode.h"
+#include "SkiaDisplayList.h"
+#include "SkiaFrameRenderer.h"
+#include "utils/TraceUtils.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+static void clipOutline(const Outline& outline, SkCanvas* canvas, const SkRect* pendingClip) {
+    SkASSERT(outline.willClip());
+    Rect possibleRect;
+    float radius;
+    LOG_ALWAYS_FATAL_IF(!outline.getAsRoundRect(&possibleRect, &radius),
+            "clipping outlines should be at most roundedRects");
+    SkRect rect = possibleRect.toSkRect();
+    if (radius != 0.0f) {
+        if (pendingClip && !pendingClip->contains(rect)) {
+            canvas->clipRect(*pendingClip);
+        }
+        canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), SkRegion::kIntersect_Op, true);
+    } else {
+        if (pendingClip) {
+            (void)rect.intersect(*pendingClip);
+        }
+        canvas->clipRect(rect);
+    }
+}
+
+const RenderProperties& RenderNodeDrawable::getNodeProperties() const {
+    return mRenderNode->properties();
+}
+
+void RenderNodeDrawable::onDraw(SkCanvas* canvas) {
+    //negative and positive Z order are drawn out of order
+    if (MathUtils::isZero(mRenderNode->properties().getZ())) {
+        this->forceDraw(canvas);
+    }
+}
+
+void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
+    RenderNode* renderNode = mRenderNode.get();
+    if (SkiaFrameRenderer::skpCaptureEnabled()) {
+        SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
+        canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
+    }
+
+    // We only respect the nothingToDraw check when we are composing a layer. This
+    // ensures that we paint the layer even if it is not currently visible in the
+    // event that the properties change and it becomes visible.
+    if (!renderNode->isRenderable() || (renderNode->nothingToDraw() && mComposeLayer)) {
+        return;
+    }
+
+    SkASSERT(renderNode->getDisplayList()->isSkiaDL());
+    SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
+
+    SkAutoCanvasRestore acr(canvas, true);
+
+    const RenderProperties& properties = this->getNodeProperties();
+    if (displayList->mIsProjectionReceiver) {
+        // this node is a projection receiver. We will gather the projected nodes as we draw our
+        // children, and then draw them on top of this node's content.
+        std::vector<ProjectedChild> newList;
+        for (auto& child : displayList->mChildNodes) {
+            // our direct children are not supposed to project into us (nodes project to, at the
+            // nearest, their grandparents). So we "delay" the list's activation one level by
+            // passing it into mNextProjectedChildrenTarget rather than mProjectedChildrenTarget.
+            child.mProjectedChildrenTarget = mNextProjectedChildrenTarget;
+            child.mNextProjectedChildrenTarget = &newList;
+        }
+        // draw ourselves and our children. As a side effect, this will add projected nodes to
+        // newList.
+        this->drawContent(canvas);
+        bool willClip = properties.getOutline().willClip();
+        if (willClip) {
+            canvas->save();
+            clipOutline(properties.getOutline(), canvas, nullptr);
+        }
+        // draw the collected projected nodes
+        for (auto& projectedChild : newList) {
+            canvas->setMatrix(projectedChild.matrix);
+            projectedChild.node->drawContent(canvas);
+        }
+        if (willClip) {
+            canvas->restore();
+        }
+    } else {
+        if (properties.getProjectBackwards() && mProjectedChildrenTarget) {
+            // We are supposed to project this node, so add it to the list and do not actually draw
+            // yet. It will be drawn by its projection receiver.
+            mProjectedChildrenTarget->push_back({ this, canvas->getTotalMatrix() });
+            return;
+        }
+        for (auto& child : displayList->mChildNodes) {
+            // storing these values in the nodes themselves is a bit ugly; they should "really" be
+            // function parameters, but we have to go through the preexisting draw() method and
+            // therefore cannot add additional parameters to it
+            child.mProjectedChildrenTarget = mNextProjectedChildrenTarget;
+            child.mNextProjectedChildrenTarget = mNextProjectedChildrenTarget;
+        }
+        this->drawContent(canvas);
+    }
+    mProjectedChildrenTarget = nullptr;
+    mNextProjectedChildrenTarget = nullptr;
+}
+
+static bool layerNeedsPaint(const LayerProperties& properties,
+                            float alphaMultiplier, SkPaint* paint) {
+    if (alphaMultiplier < 1.0f
+            || properties.alpha() < 255
+            || properties.xferMode() != SkBlendMode::kSrcOver
+            || properties.colorFilter() != nullptr) {
+        paint->setAlpha(properties.alpha() * alphaMultiplier);
+        paint->setBlendMode(properties.xferMode());
+        paint->setColorFilter(properties.colorFilter());
+        return true;
+    }
+    return false;
+}
+
+void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
+    RenderNode* renderNode = mRenderNode.get();
+    float alphaMultiplier = 1.0f;
+    const RenderProperties& properties = renderNode->properties();
+
+    // If we are drawing the contents of layer, we don't want to apply any of
+    // the RenderNode's properties during this pass. Those will all be applied
+    // when the layer is composited.
+    if (mComposeLayer) {
+        setViewProperties(properties, canvas, &alphaMultiplier);
+    }
+
+    //TODO should we let the bound of the drawable do this for us?
+    const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
+    bool quickRejected = properties.getClipToBounds() && canvas->quickReject(bounds);
+    if (!quickRejected) {
+        SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
+        const LayerProperties& layerProperties = properties.layerProperties();
+        // composing a hardware layer
+        if (renderNode->getLayerSurface() && mComposeLayer) {
+            SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
+            SkPaint* paint = nullptr;
+            SkPaint tmpPaint;
+            if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) {
+                paint = &tmpPaint;
+            }
+            renderNode->getLayerSurface()->draw(canvas, 0, 0, paint);
+        // composing a software layer with alpha
+        } else if (properties.effectiveLayerType() == LayerType::Software) {
+            SkPaint paint;
+            bool needsLayer = layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
+            if (needsLayer) {
+                canvas->saveLayer(bounds, &paint);
+            }
+            canvas->drawDrawable(displayList->mDrawable.get());
+            if (needsLayer) {
+                canvas->restore();
+            }
+        } else {
+            canvas->drawDrawable(displayList->mDrawable.get());
+        }
+    }
+}
+
+void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
+        float* alphaMultiplier) {
+    if (properties.getLeft() != 0 || properties.getTop() != 0) {
+        canvas->translate(properties.getLeft(), properties.getTop());
+    }
+    if (properties.getStaticMatrix()) {
+        canvas->concat(*properties.getStaticMatrix());
+    } else if (properties.getAnimationMatrix()) {
+        canvas->concat(*properties.getAnimationMatrix());
+    }
+    if (properties.hasTransformMatrix()) {
+        if (properties.isTransformTranslateOnly()) {
+            canvas->translate(properties.getTranslationX(), properties.getTranslationY());
+        } else {
+            canvas->concat(*properties.getTransformMatrix());
+        }
+    }
+    const bool isLayer = properties.effectiveLayerType() != LayerType::None;
+    int clipFlags = properties.getClippingFlags();
+    if (properties.getAlpha() < 1) {
+        if (isLayer) {
+            clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
+        }
+        if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
+            *alphaMultiplier = properties.getAlpha();
+        } else {
+            // savelayer needed to create an offscreen buffer
+            Rect layerBounds(0, 0, properties.getWidth(), properties.getHeight());
+            if (clipFlags) {
+                properties.getClippingRectForFlags(clipFlags, &layerBounds);
+                clipFlags = 0; // all clipping done by savelayer
+            }
+            SkRect bounds = SkRect::MakeLTRB(layerBounds.left, layerBounds.top,
+                    layerBounds.right, layerBounds.bottom);
+            canvas->saveLayerAlpha(&bounds, (int) (properties.getAlpha() * 255));
+        }
+
+        if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) {
+            // pretend alpha always causes savelayer to warn about
+            // performance problem affecting old versions
+            ATRACE_FORMAT("alpha caused saveLayer %dx%d", properties.getWidth(),
+                    properties.getHeight());
+        }
+    }
+
+    const SkRect* pendingClip = nullptr;
+    SkRect clipRect;
+
+    if (clipFlags) {
+        Rect tmpRect;
+        properties.getClippingRectForFlags(clipFlags, &tmpRect);
+        clipRect = tmpRect.toSkRect();
+        pendingClip = &clipRect;
+    }
+
+    if (properties.getRevealClip().willClip()) {
+        canvas->clipPath(*properties.getRevealClip().getPath(), SkRegion::kIntersect_Op, true);
+    } else if (properties.getOutline().willClip()) {
+        clipOutline(properties.getOutline(), canvas, pendingClip);
+        pendingClip = nullptr;
+    }
+
+    if (pendingClip) {
+        canvas->clipRect(*pendingClip);
+    }
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
new file mode 100644
index 0000000..0762f37
--- /dev/null
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+#include <SkMatrix.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace uirenderer {
+
+class RenderNode;
+class RenderProperties;
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a RenderNode and enables it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class RenderNodeDrawable : public SkDrawable {
+public:
+    /**
+     * This struct contains a pointer to a node that is to be
+     * projected into the drawing order of its closest ancestor
+     * (excluding its parent) that is marked as a projection
+     * receiver. The matrix is used to ensure that the node is
+     * drawn with same matrix as it would have prior to projection.
+     */
+    struct ProjectedChild {
+        const RenderNodeDrawable* node;
+        const SkMatrix matrix;
+    };
+
+    /**
+     * Creates a new RenderNodeDrawable backed by a render node.
+     *
+     * @param node that has to be drawn
+     * @param canvas is a recording canvas used to extract its matrix
+     * @param composeLayer if the node's layer type is RenderLayer this flag determines whether
+     *      we should draw into the contents of the layer or compose the existing contents of the
+     *      layer into the canvas.
+     */
+    explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer = true)
+            : mRenderNode(node)
+            , mRecordedTransform(canvas->getTotalMatrix())
+            , mComposeLayer(composeLayer) {}
+
+    /**
+     * Draws into the canvas this render node and its children. If the node is marked as a
+     * projection receiver then all projected children (excluding direct children) will be drawn
+     * last. Any projected node not matching those requirements will not be drawn by this function.
+     */
+    void forceDraw(SkCanvas* canvas);
+
+    /**
+     * Returns readonly render properties for this render node.
+     */
+    const RenderProperties& getNodeProperties() const;
+
+    /**
+     * The renderNode (and its properties) that is to be drawn
+     */
+    RenderNode* getRenderNode() const { return mRenderNode.get(); }
+
+    /**
+     * Returns the transform on the canvas at time of recording and is used for
+     * computing total transform without rerunning DL contents.
+     */
+    const SkMatrix& getRecordedMatrix() const { return mRecordedTransform; }
+
+protected:
+    /*
+     * Return the (conservative) bounds of what the drawable will draw.
+     */
+    virtual SkRect onGetBounds() override {
+        // We don't want to enable a record time quick reject because the properties
+        // of the RenderNode may be updated on subsequent frames.
+        return SkRect::MakeLargest();
+    }
+    /**
+     * This function draws into a canvas as forceDraw, but does nothing if the render node has a
+     * non-zero elevation.
+     */
+    virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+    /*
+     * Render node that is wrapped by this class.
+     */
+    sp<RenderNode> mRenderNode;
+
+    /**
+     * Applies the rendering properties of a view onto a SkCanvas.
+     */
+    static void setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
+            float* alphaMultiplier);
+
+    /**
+     * Stores transform on the canvas at time of recording and is used for
+     * computing total transform without rerunning DL contents.
+     */
+    const SkMatrix mRecordedTransform;
+
+    /**
+     * If mRenderNode's layer type is RenderLayer this flag determines whether we
+     * should draw into the contents of the layer or compose the existing contents
+     * of the layer into the canvas.
+     */
+    const bool mComposeLayer;
+
+    /**
+     * List to which we will add any projected children we encounter while walking our descendents.
+     * This pointer is valid only while the node (including its children) is actively being drawn.
+     */
+    std::vector<ProjectedChild>* mProjectedChildrenTarget = nullptr;
+
+    /**
+     * The value to which we should set our children's mProjectedChildrenTarget. We use two pointers
+     * (mProjectedChildrenTarget and mNextProjectedChildrenTarget) because we need to skip over our
+     * parent when looking for a projection receiver.
+     */
+    std::vector<ProjectedChild>* mNextProjectedChildrenTarget = nullptr;
+
+    /*
+     *  Draw the content into a canvas, depending on the render node layer type and mComposeLayer.
+     */
+    void drawContent(SkCanvas* canvas) const;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
new file mode 100644
index 0000000..8d77938
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -0,0 +1,704 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReorderBarrierDrawables.h"
+#include "RenderNode.h"
+#include "SkiaDisplayList.h"
+#include "SkiaFrameRenderer.h"
+
+#include <SkBlurMask.h>
+#include <SkBlurMaskFilter.h>
+#include <SkGaussianEdgeShader.h>
+#include <SkPathOps.h>
+#include <SkRRectsGaussianEdgeShader.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data)
+        : mEndChildIndex(0)
+        , mBeginChildIndex(data->mChildNodes.size())
+        , mDisplayList(data) {
+}
+
+void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
+    if (mChildren.empty()) {
+        //mChildren is allocated and initialized only the first time onDraw is called and cached for
+        //subsequent calls
+        mChildren.reserve(mEndChildIndex - mBeginChildIndex + 1);
+        for (unsigned int i = mBeginChildIndex; i <= mEndChildIndex; i++) {
+            mChildren.push_back(const_cast<RenderNodeDrawable*>(&mDisplayList->mChildNodes[i]));
+        }
+    }
+    std::stable_sort(mChildren.begin(), mChildren.end(),
+        [](RenderNodeDrawable* a, RenderNodeDrawable* b) {
+            const float aZValue = a->getNodeProperties().getZ();
+            const float bZValue = b->getNodeProperties().getZ();
+            return aZValue < bZValue;
+        });
+
+    SkASSERT(!mChildren.empty());
+
+    size_t drawIndex = 0;
+    const size_t endIndex = mChildren.size();
+    while (drawIndex < endIndex) {
+        RenderNodeDrawable* childNode = mChildren[drawIndex];
+        SkASSERT(childNode);
+        const float casterZ = childNode->getNodeProperties().getZ();
+        if (casterZ >= -NON_ZERO_EPSILON) { //draw only children with negative Z
+            return;
+        }
+        childNode->forceDraw(canvas);
+        drawIndex++;
+    }
+}
+
+EndReorderBarrierDrawable::EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier)
+        : mStartBarrier(startBarrier) {
+    mStartBarrier->mEndChildIndex = mStartBarrier->mDisplayList->mChildNodes.size() - 1;
+}
+
+#define SHADOW_DELTA 0.1f
+
+void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
+    auto& zChildren = mStartBarrier->mChildren;
+    SkASSERT(!zChildren.empty());
+
+    /**
+     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
+     * with very similar Z heights to draw together.
+     *
+     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
+     * underneath both, and neither's shadow is drawn on top of the other.
+     */
+    size_t drawIndex = 0;
+
+    const size_t endIndex = zChildren.size();
+    while (drawIndex < endIndex     //draw only children with positive Z
+            && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON) drawIndex++;
+    size_t shadowIndex = drawIndex;
+
+    float lastCasterZ = 0.0f;
+    while (shadowIndex < endIndex || drawIndex < endIndex) {
+        if (shadowIndex < endIndex) {
+            const float casterZ = zChildren[shadowIndex]->getNodeProperties().getZ();
+
+            // attempt to render the shadow if the caster about to be drawn is its caster,
+            // OR if its caster's Z value is similar to the previous potential caster
+            if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
+                this->drawShadow(canvas, zChildren[shadowIndex]);
+                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+                shadowIndex++;
+                continue;
+            }
+        }
+
+        RenderNodeDrawable* childNode = zChildren[drawIndex];
+        SkASSERT(childNode);
+        childNode->forceDraw(canvas);
+
+        drawIndex++;
+    }
+}
+
+/**
+ * @param canvas             the destination for the shadow draws
+ * @param shape              the shape casting the shadow
+ * @param casterZValue       the Z value of the caster RRect
+ * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
+ * @param draw               the function used to draw 'shape'
+ */
+template <typename Shape, typename F>
+static void DrawAmbientShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue,
+        float ambientAlpha, F&& draw) {
+    if (ambientAlpha <= 0) {
+        return;
+    }
+
+    const float kHeightFactor = 1.f/128.f;
+    const float kGeomFactor = 64;
+
+    float umbraAlpha = 1 / (1 + SkMaxScalar(casterZValue*kHeightFactor, 0));
+    float radius = casterZValue*kHeightFactor*kGeomFactor;
+
+    sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
+            SkBlurMask::ConvertRadiusToSigma(radius), SkBlurMaskFilter::kNone_BlurFlag);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setMaskFilter(std::move(mf));
+    paint.setARGB(ambientAlpha*umbraAlpha, 0, 0, 0);
+
+    draw(shape, paint);
+}
+
+/**
+ * @param canvas             the destination for the shadow draws
+ * @param shape              the shape casting the shadow
+ * @param casterZValue       the Z value of the caster RRect
+ * @param lightPos           the position of the light casting the shadow
+ * @param lightWidth
+ * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
+ * @param draw               the function used to draw 'shape'
+ */
+template <typename Shape, typename F>
+static void DrawSpotShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue,
+        float spotAlpha, F&& draw) {
+    if (spotAlpha <= 0) {
+        return;
+    }
+
+    const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
+    float zRatio = casterZValue / (lightPos.z - casterZValue);
+    // clamp
+    if (zRatio < 0.0f) {
+        zRatio = 0.0f;
+    } else if (zRatio > 0.95f) {
+        zRatio = 0.95f;
+    }
+
+    float blurRadius = SkiaFrameRenderer::getLightRadius()*zRatio;
+
+    SkAutoCanvasRestore acr(canvas, true);
+
+    sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
+            SkBlurMask::ConvertRadiusToSigma(blurRadius), SkBlurMaskFilter::kNone_BlurFlag);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setMaskFilter(std::move(mf));
+    paint.setARGB(spotAlpha, 0, 0, 0);
+
+    // approximate projection by translating and scaling projected offset of bounds center
+    // TODO: compute the actual 2D projection
+    SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
+    canvas->scale(scale, scale);
+    SkPoint center = SkPoint::Make(shape.getBounds().centerX(), shape.getBounds().centerY());
+    SkMatrix ctmInverse;
+    if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+        ALOGW("Matrix is degenerate. Will not render shadow!");
+        return;
+    }
+    SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
+    ctmInverse.mapPoints(&lightPos2D, 1);
+    canvas->translate(zRatio*(center.fX - lightPos2D.fX), zRatio*(center.fY - lightPos2D.fY));
+
+    draw(shape, paint);
+}
+
+#define MAX_BLUR_RADIUS 16383.75f
+#define MAX_PAD         64
+
+/**
+ * @param casterRect         the rectangle bounds of the RRect casting the shadow
+ * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
+ * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
+ * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
+ * @param casterAlpha        the alpha value of the RRect casting the shadow (0.0-1.0 range)
+ * @param casterZValue       the Z value of the caster RRect
+ * @param scaleFactor        the scale needed to map from src-space to device-space
+ * @param canvas             the destination for the shadow draws
+ */
+static void DrawRRectShadows(const SkRect& casterRect, SkScalar casterCornerRadius,
+        SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue,
+        SkScalar scaleFactor, SkCanvas* canvas) {
+    SkASSERT(cornerRadius >= 0.0f);
+
+    // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
+    const SkScalar minRadius = 0.5f / scaleFactor;
+
+    const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()),
+            SkScalarHalf(casterRect.height()));
+    const bool isRect = casterCornerRadius <= minRadius;
+
+    sk_sp<SkShader> edgeShader(SkGaussianEdgeShader::Make());
+
+    if (ambientAlpha > 0.0f) {
+        static const float kHeightFactor = 1.0f / 128.0f;
+        static const float kGeomFactor = 64.0f;
+
+        SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor;
+        // the device-space radius sent to the blur shader must fit in 14.2 fixed point
+        if (srcSpaceAmbientRadius*scaleFactor > MAX_BLUR_RADIUS) {
+            srcSpaceAmbientRadius = MAX_BLUR_RADIUS/scaleFactor;
+        }
+        const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f));
+        const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
+
+        // For the ambient rrect, we inset the offset rect by half the srcSpaceAmbientRadius
+        // to get our stroke shape.
+        SkScalar ambientPathOutset = std::max(ambientOffset - srcSpaceAmbientRadius * 0.5f,
+                minRadius);
+
+        SkRRect ambientRRect;
+        const SkRect temp = casterRect.makeOutset(ambientPathOutset, ambientPathOutset);
+        if (isOval) {
+            ambientRRect = SkRRect::MakeOval(temp);
+        } else if (isRect) {
+            ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
+        } else {
+            ambientRRect = SkRRect::MakeRectXY(temp, casterCornerRadius + ambientPathOutset,
+                    casterCornerRadius + ambientPathOutset);
+        }
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+        // we outset the stroke a little to cover up AA on the interior edge
+        float pad = 0.5f;
+        paint.setStrokeWidth(srcSpaceAmbientRadius + 2.0f * pad);
+        // handle scale of radius and pad due to CTM
+        pad *= scaleFactor;
+        const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
+        SkASSERT(devSpaceAmbientRadius <= MAX_BLUR_RADIUS);
+        SkASSERT(pad < MAX_PAD);
+        // convert devSpaceAmbientRadius to 14.2 fixed point and place in the R & G components
+        // convert pad to 6.2 fixed point and place in the B component
+        uint16_t iDevSpaceAmbientRadius = (uint16_t)(4.0f * devSpaceAmbientRadius);
+        paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, iDevSpaceAmbientRadius >> 8,
+                iDevSpaceAmbientRadius & 0xff, (unsigned char)(4.0f * pad)));
+
+        paint.setShader(edgeShader);
+        canvas->drawRRect(ambientRRect, paint);
+    }
+
+    if (spotAlpha > 0.0f) {
+        const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
+        float zRatio = casterZValue / (lightPos.z - casterZValue);
+        // clamp
+        if (zRatio < 0.0f) {
+            zRatio = 0.0f;
+        } else if (zRatio > 0.95f) {
+            zRatio = 0.95f;
+        }
+
+        const SkScalar lightWidth = SkiaFrameRenderer::getLightRadius();
+        SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
+        // the device-space radius sent to the blur shader must fit in 14.2 fixed point
+        if (srcSpaceSpotRadius*scaleFactor > MAX_BLUR_RADIUS) {
+            srcSpaceSpotRadius = MAX_BLUR_RADIUS/scaleFactor;
+        }
+
+        SkRRect spotRRect;
+        if (isOval) {
+            spotRRect = SkRRect::MakeOval(casterRect);
+        } else if (isRect) {
+            spotRRect = SkRRect::MakeRectXY(casterRect, minRadius, minRadius);
+        } else {
+            spotRRect = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius);
+        }
+
+        SkRRect spotShadowRRect;
+        // Compute the scale and translation for the spot shadow.
+        const SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
+        spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
+
+        SkPoint center = SkPoint::Make(spotShadowRRect.rect().centerX(),
+                                       spotShadowRRect.rect().centerY());
+        SkMatrix ctmInverse;
+        if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+            ALOGW("Matrix is degenerate. Will not render spot shadow!");
+            return;
+        }
+        SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
+        ctmInverse.mapPoints(&lightPos2D, 1);
+        const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+                zRatio*(center.fY - lightPos2D.fY));
+
+        SkAutoCanvasRestore acr(canvas, true);
+
+        // We want to extend the stroked area in so that it meets up with the caster
+        // geometry. The stroked geometry will, by definition already be inset half the
+        // stroke width but we also have to account for the scaling.
+        // We also add 1/2 to cover up AA on the interior edge.
+        SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(casterRect.fLeft),
+                SkTAbs(casterRect.fRight)), SkTMax(SkTAbs(casterRect.fTop),
+                SkTAbs(casterRect.fBottom)));
+        SkScalar insetAmount = spotOffset.length() - (0.5f * srcSpaceSpotRadius) +
+                scaleOffset + 0.5f;
+
+        // Compute area
+        SkScalar strokeWidth = srcSpaceSpotRadius + insetAmount;
+        SkScalar strokedArea = 2.0f*strokeWidth * (spotShadowRRect.width()
+                + spotShadowRRect.height());
+        SkScalar filledArea = (spotShadowRRect.height() + srcSpaceSpotRadius)
+                * (spotShadowRRect.width() + srcSpaceSpotRadius);
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+
+        // If the area of the stroked geometry is larger than the fill geometry, just fill it.
+        if (strokedArea > filledArea || casterAlpha < 1.0f) {
+            paint.setStyle(SkPaint::kStrokeAndFill_Style);
+            paint.setStrokeWidth(srcSpaceSpotRadius);
+        } else {
+            // Since we can't have unequal strokes, inset the shadow rect so the inner
+            // and outer edges of the stroke will land where we want.
+            SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount/2.0f, insetAmount/2.0f);
+            SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount/2.0f,
+                    minRadius);
+            spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
+            paint.setStyle(SkPaint::kStroke_Style);
+            paint.setStrokeWidth(strokeWidth);
+        }
+
+        // handle scale of radius and pad due to CTM
+        const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
+        SkASSERT(devSpaceSpotRadius <= MAX_BLUR_RADIUS);
+
+        const SkScalar devSpaceSpotPad = 0;
+        SkASSERT(devSpaceSpotPad < MAX_PAD);
+
+        // convert devSpaceSpotRadius to 14.2 fixed point and place in the R & G
+        // components convert devSpaceSpotPad to 6.2 fixed point and place in the B component
+        uint16_t iDevSpaceSpotRadius = (uint16_t)(4.0f * devSpaceSpotRadius);
+        paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, iDevSpaceSpotRadius >> 8,
+                iDevSpaceSpotRadius & 0xff, (unsigned char)(4.0f * devSpaceSpotPad)));
+        paint.setShader(edgeShader);
+
+        canvas->translate(spotOffset.fX, spotOffset.fY);
+        canvas->drawRRect(spotShadowRRect, paint);
+    }
+}
+
+/**
+ * @param casterRect         the rectangle bounds of the RRect casting the shadow
+ * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
+ * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
+ * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
+ * @param casterZValue       the Z value of the caster RRect
+ * @param scaleFactor        the scale needed to map from src-space to device-space
+ * @param clipRR             the oval or rect with which the drawn roundrect must be intersected
+ * @param canvas             the destination for the shadow draws
+ */
+static void DrawRRectShadowsWithClip(const SkRect& casterRect, SkScalar casterCornerRadius,
+        SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterZValue, SkScalar scaleFactor,
+        const SkRRect& clipRR, SkCanvas* canvas) {
+    SkASSERT(cornerRadius >= 0.0f);
+
+    const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()),
+            SkScalarHalf(casterRect.height()));
+
+    if (ambientAlpha > 0.0f) {
+        static const float kHeightFactor = 1.0f / 128.0f;
+        static const float kGeomFactor = 64.0f;
+
+        const SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor;
+        const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
+
+        const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f));
+        const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
+
+        const SkRect srcSpaceAmbientRect = casterRect.makeOutset(ambientOffset, ambientOffset);
+        SkRect devSpaceAmbientRect;
+        canvas->getTotalMatrix().mapRect(&devSpaceAmbientRect, srcSpaceAmbientRect);
+
+        SkRRect devSpaceAmbientRRect;
+        if (isOval) {
+            devSpaceAmbientRRect = SkRRect::MakeOval(devSpaceAmbientRect);
+        } else {
+            const SkScalar devSpaceCornerRadius = scaleFactor * (casterCornerRadius + ambientOffset);
+            devSpaceAmbientRRect = SkRRect::MakeRectXY(devSpaceAmbientRect, devSpaceCornerRadius,
+                    devSpaceCornerRadius);
+        }
+
+        const SkRect srcSpaceAmbClipRect = clipRR.rect().makeOutset(ambientOffset, ambientOffset);
+        SkRect devSpaceAmbClipRect;
+        canvas->getTotalMatrix().mapRect(&devSpaceAmbClipRect, srcSpaceAmbClipRect);
+        SkRRect devSpaceAmbientClipRR;
+        if (clipRR.isOval()) {
+            devSpaceAmbientClipRR = SkRRect::MakeOval(devSpaceAmbClipRect);
+        } else {
+            SkASSERT(clipRR.isRect());
+            devSpaceAmbientClipRR = SkRRect::MakeRect(devSpaceAmbClipRect);
+        }
+
+        SkRect cover = srcSpaceAmbClipRect;
+        if (!cover.intersect(srcSpaceAmbientRect)) {
+            return;
+        }
+
+        SkPaint paint;
+        paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, 0, 0, 0));
+        paint.setShader(SkRRectsGaussianEdgeShader::Make(devSpaceAmbientRRect,
+                devSpaceAmbientClipRR, devSpaceAmbientRadius));
+        canvas->drawRect(cover, paint);
+    }
+
+    if (spotAlpha > 0.0f) {
+        const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
+        float zRatio = casterZValue / (lightPos.z - casterZValue);
+        // clamp
+        if (zRatio < 0.0f) {
+            zRatio = 0.0f;
+        } else if (zRatio > 0.95f) {
+            zRatio = 0.95f;
+        }
+
+        const SkScalar lightWidth = SkiaFrameRenderer::getLightRadius();
+        const SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
+        const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
+
+        // Compute the scale and translation for the spot shadow.
+        const SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
+        const SkMatrix spotMatrix = SkMatrix::MakeScale(scale, scale);
+
+        SkRect srcSpaceScaledRect = casterRect;
+        spotMatrix.mapRect(&srcSpaceScaledRect);
+        srcSpaceScaledRect.outset(SkScalarHalf(srcSpaceSpotRadius),
+                SkScalarHalf(srcSpaceSpotRadius));
+
+        SkRRect srcSpaceSpotRRect;
+        if (isOval) {
+            srcSpaceSpotRRect = SkRRect::MakeOval(srcSpaceScaledRect);
+        } else {
+            srcSpaceSpotRRect = SkRRect::MakeRectXY(srcSpaceScaledRect, casterCornerRadius * scale,
+                    casterCornerRadius * scale);
+        }
+
+        SkPoint center = SkPoint::Make(srcSpaceSpotRRect.rect().centerX(),
+                srcSpaceSpotRRect.rect().centerY());
+        SkMatrix ctmInverse;
+        if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+            ALOGW("Matrix is degenerate. Will not render spot shadow!");
+            return;
+        }
+        SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
+        ctmInverse.mapPoints(&lightPos2D, 1);
+        const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+                zRatio*(center.fY - lightPos2D.fY));
+
+        SkAutoCanvasRestore acr(canvas, true);
+        canvas->translate(spotOffset.fX, spotOffset.fY);
+
+        SkRect devSpaceScaledRect;
+        canvas->getTotalMatrix().mapRect(&devSpaceScaledRect, srcSpaceScaledRect);
+
+        SkRRect devSpaceSpotRRect;
+        if (isOval) {
+            devSpaceSpotRRect = SkRRect::MakeOval(devSpaceScaledRect);
+        } else {
+            const SkScalar devSpaceScaledCornerRadius = casterCornerRadius * scale * scaleFactor;
+            devSpaceSpotRRect = SkRRect::MakeRectXY(devSpaceScaledRect, devSpaceScaledCornerRadius,
+                    devSpaceScaledCornerRadius);
+        }
+
+        SkPaint paint;
+        paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, 0, 0, 0));
+
+        SkRect srcSpaceScaledClipRect = clipRR.rect();
+        spotMatrix.mapRect(&srcSpaceScaledClipRect);
+        srcSpaceScaledClipRect.outset(SkScalarHalf(srcSpaceSpotRadius),
+                SkScalarHalf(srcSpaceSpotRadius));
+
+        SkRect devSpaceScaledClipRect;
+        canvas->getTotalMatrix().mapRect(&devSpaceScaledClipRect, srcSpaceScaledClipRect);
+        SkRRect devSpaceSpotClipRR;
+        if (clipRR.isOval()) {
+            devSpaceSpotClipRR = SkRRect::MakeOval(devSpaceScaledClipRect);
+        } else {
+            SkASSERT(clipRR.isRect());
+            devSpaceSpotClipRR = SkRRect::MakeRect(devSpaceScaledClipRect);
+        }
+
+        paint.setShader(SkRRectsGaussianEdgeShader::Make(devSpaceSpotRRect, devSpaceSpotClipRR,
+                devSpaceSpotRadius));
+
+        SkRect cover = srcSpaceScaledClipRect;
+        if (!cover.intersect(srcSpaceSpotRRect.rect())) {
+            return;
+        }
+
+        canvas->drawRect(cover, paint);
+    }
+}
+
+/**
+ * @param casterRect         the rectangle bounds of the RRect casting the shadow
+ * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
+ * @param casterClipRect     a rectangular clip that must be intersected with the
+ *                           shadow-casting RRect prior to casting the shadow
+ * @param revealClip         a circular clip that must be interested with the castClipRect
+ *                           and the shadow-casting rect prior to casting the shadow
+ * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
+ * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
+ * @param casterAlpha        the alpha value of the RRect casting the shadow (0.0-1.0 range)
+ * @param casterZValue       the Z value of the caster RRect
+ * @param canvas             the destination for the shadow draws
+ *
+ * We have special cases for 4 round rect shadow draws:
+ *    1) a RRect clipped by a reveal animation
+ *    2) a RRect clipped by a rectangle
+ *    3) an unclipped RRect with non-uniform scale
+ *    4) an unclipped RRect with uniform scale
+ * 1,2 and 4 require that the scale is uniform.
+ * 1 and 2 require that rects stay rects.
+ */
+static bool DrawShadowsAsRRects(const SkRect& casterRect, SkScalar casterCornerRadius,
+        const SkRect& casterClipRect, const RevealClip& revealClip, SkScalar ambientAlpha,
+        SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue, SkCanvas* canvas) {
+    SkScalar scaleFactors[2];
+    if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
+        ALOGW("Matrix is degenerate. Will not render shadow!");
+        return false;
+    }
+
+    // The casterClipRect will contain the casterRect when bounds clipping is disabled
+    bool casterIsClippedByRect = !casterClipRect.contains(casterRect);
+    bool uniformScale = scaleFactors[0] == scaleFactors[1];
+
+    if (revealClip.willClip()) {
+        if (casterIsClippedByRect || !uniformScale || !canvas->getTotalMatrix().rectStaysRect()) {
+            return false;  // Fall back to the slow path since PathOps are required
+        }
+
+        const float revealRadius = revealClip.getRadius();
+        SkRect revealClipRect = SkRect::MakeLTRB(revealClip.getX()-revealRadius,
+                revealClip.getY()-revealRadius, revealClip.getX()+revealRadius,
+                revealClip.getY()+revealRadius);
+        SkRRect revealClipRR = SkRRect::MakeOval(revealClipRect);
+
+        DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha,
+                casterZValue, scaleFactors[0], revealClipRR, canvas);
+        return true;
+    }
+
+    if (casterIsClippedByRect) {
+        if (!uniformScale || !canvas->getTotalMatrix().rectStaysRect()) {
+            return false;  // Fall back to the slow path since PathOps are required
+        }
+
+        SkRRect casterClipRR = SkRRect::MakeRect(casterClipRect);
+
+        DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha,
+                casterZValue, scaleFactors[0], casterClipRR, canvas);
+        return true;
+    }
+
+    // The fast path needs uniform scale
+    if (!uniformScale) {
+        SkRRect casterRR = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius);
+        DrawAmbientShadowGeneral(canvas, casterRR, casterZValue, ambientAlpha,
+                [&](const SkRRect& rrect, const SkPaint& paint) {
+                    canvas->drawRRect(rrect, paint);
+                });
+        DrawSpotShadowGeneral(canvas, casterRR, casterZValue, spotAlpha,
+                [&](const SkRRect& rrect, const SkPaint& paint) {
+                canvas->drawRRect(rrect, paint);
+                });
+        return true;
+    }
+
+    DrawRRectShadows(casterRect, casterCornerRadius, ambientAlpha, spotAlpha, casterAlpha,
+            casterZValue, scaleFactors[0], canvas);
+    return true;
+}
+
+// copied from FrameBuilder::deferShadow
+void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) {
+    const RenderProperties& casterProperties = caster->getNodeProperties();
+
+    if (casterProperties.getAlpha() <= 0.0f
+            || casterProperties.getOutline().getAlpha() <= 0.0f
+            || !casterProperties.getOutline().getPath()
+            || casterProperties.getScaleX() == 0
+            || casterProperties.getScaleY() == 0) {
+        // no shadow to draw
+        return;
+    }
+
+    const SkScalar casterAlpha = casterProperties.getAlpha()
+            * casterProperties.getOutline().getAlpha();
+    if (casterAlpha <= 0.0f) {
+        return;
+    }
+
+    float ambientAlpha = SkiaFrameRenderer::getAmbientShadowAlpha()*casterAlpha;
+    float spotAlpha = SkiaFrameRenderer::getSpotShadowAlpha()*casterAlpha;
+    const float casterZValue = casterProperties.getZ();
+
+    const RevealClip& revealClip = casterProperties.getRevealClip();
+    const SkPath* revealClipPath = revealClip.getPath();
+    if (revealClipPath && revealClipPath->isEmpty()) {
+        // An empty reveal clip means nothing is drawn
+        return;
+    }
+
+    bool clippedToBounds = casterProperties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS;
+
+    SkRect casterClipRect = SkRect::MakeLargest();
+    if (clippedToBounds) {
+        Rect clipBounds;
+        casterProperties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
+        casterClipRect = clipBounds.toSkRect();
+    }
+
+    SkAutoCanvasRestore acr(canvas, true);
+
+    SkMatrix shadowMatrix;
+    mat4 hwuiMatrix(caster->getRecordedMatrix());
+    // TODO we don't pass the optional boolean to treat it as a 4x4 matrix
+    caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix);
+    hwuiMatrix.copyTo(shadowMatrix);
+    canvas->concat(shadowMatrix);
+
+    const Outline& casterOutline = casterProperties.getOutline();
+    Rect possibleRect;
+    float radius;
+    if (casterOutline.getAsRoundRect(&possibleRect, &radius)) {
+        if (DrawShadowsAsRRects(possibleRect.toSkRect(), radius, casterClipRect, revealClip,
+                ambientAlpha, spotAlpha, casterAlpha, casterZValue, canvas)) {
+            return;
+        }
+    }
+
+    // Hard cases and calls to general shadow code
+    const SkPath* casterOutlinePath = casterProperties.getOutline().getPath();
+
+    // holds temporary SkPath to store the result of intersections
+    SkPath tmpPath;
+    const SkPath* casterPath = casterOutlinePath;
+
+    // TODO: In to following course of code that calculates the final shape, is there an optimal
+    //       of doing the Op calculations?
+    // intersect the shadow-casting path with the reveal, if present
+    if (revealClipPath) {
+        Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath);
+        casterPath = &tmpPath;
+    }
+
+    // intersect the shadow-casting path with the clipBounds, if present
+    if (clippedToBounds) {
+        SkPath clipBoundsPath;
+        clipBoundsPath.addRect(casterClipRect);
+        Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, &tmpPath);
+        casterPath = &tmpPath;
+    }
+
+    DrawAmbientShadowGeneral(canvas, *casterPath, casterZValue, ambientAlpha,
+            [&](const SkPath& path, const SkPaint& paint) {
+                canvas->drawPath(path, paint);
+            });
+
+    DrawSpotShadowGeneral(canvas, *casterPath, casterZValue, spotAlpha,
+            [&](const SkPath& path, const SkPaint& paint) {
+                canvas->drawPath(path, paint);
+            });
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
new file mode 100644
index 0000000..298a732
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "RenderNodeDrawable.h"
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+#include <utils/FatVector.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaDisplayList;
+class EndReorderBarrierDrawable;
+
+/**
+ * StartReorderBarrierDrawable and EndReorderBarrierDrawable work together to define
+ * a sub-list in a display list that need to be drawn out-of-order sorted instead by render
+ * node Z index.
+ * StartReorderBarrierDrawable will sort the entire range and it will draw
+ * render nodes in the range with negative Z index.
+ */
+class StartReorderBarrierDrawable : public SkDrawable {
+public:
+    explicit StartReorderBarrierDrawable(SkiaDisplayList* data);
+
+protected:
+    virtual SkRect onGetBounds() override {
+        return SkRect::MakeLargest();
+    }
+    virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+    size_t mEndChildIndex;
+    size_t mBeginChildIndex;
+    FatVector<RenderNodeDrawable*, 16> mChildren;
+    SkiaDisplayList* mDisplayList;
+
+    friend class EndReorderBarrierDrawable;
+};
+
+/**
+ * See StartReorderBarrierDrawable.
+ * EndReorderBarrierDrawable relies on StartReorderBarrierDrawable to host and sort the render
+ * nodes by Z index. When EndReorderBarrierDrawable is drawn it will draw all render nodes in the
+ * range with positive Z index. It is also responsible for drawing shadows for the nodes
+ * corresponding to their z-index.
+ */
+class EndReorderBarrierDrawable : public SkDrawable {
+public:
+    explicit EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier);
+protected:
+    virtual SkRect onGetBounds() override {
+        return SkRect::MakeLargest();
+    }
+    virtual void onDraw(SkCanvas* canvas) override;
+private:
+    void drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster);
+    StartReorderBarrierDrawable* mStartBarrier;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
similarity index 98%
rename from libs/hwui/SkiaDisplayList.cpp
rename to libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index d10f306..c734097e 100644
--- a/libs/hwui/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -24,6 +24,7 @@
 
 namespace android {
 namespace uirenderer {
+namespace skiapipeline {
 
 SkiaDisplayList::SkiaDisplayList(SkRect bounds) : mDrawable(SkLiteDL::New(bounds)) {
     SkASSERT(projectionReceiveIndex == -1);
@@ -130,5 +131,6 @@
     new (&allocator) LinearAllocator();
 }
 
+}; // namespace skiapipeline
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
similarity index 97%
rename from libs/hwui/SkiaDisplayList.h
rename to libs/hwui/pipeline/skia/SkiaDisplayList.h
index c8a82bd..734aae4a 100644
--- a/libs/hwui/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -17,7 +17,8 @@
 #pragma once
 
 #include "DisplayList.h"
-#include "SkiaDrawables.h"
+#include "GLFunctorDrawable.h"
+#include "RenderNodeDrawable.h"
 
 #include <deque>
 #include <SkLiteDL.h>
@@ -25,6 +26,7 @@
 
 namespace android {
 namespace uirenderer {
+namespace skiapipeline {
 
 /**
  * This class is intended to be self contained, but still subclasses from
@@ -148,5 +150,6 @@
     bool mPinnedImages = false;
 };
 
+}; // namespace skiapipeline
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaFrameRenderer.h b/libs/hwui/pipeline/skia/SkiaFrameRenderer.h
new file mode 100644
index 0000000..70207c1
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaFrameRenderer.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/**
+ * TODO: this is a stub that will be added in a subsquent CL
+ */
+class SkiaFrameRenderer {
+public:
+
+    static bool skpCaptureEnabled() { return false; }
+
+    // TODO avoids unused compile error but we need to pass this to the reorder drawables!
+    static float getLightRadius() {
+        return 1.0f;
+    }
+
+    static uint8_t getAmbientShadowAlpha() {
+        return 1;
+    }
+
+    static uint8_t getSpotShadowAlpha() {
+        return 1;
+    }
+
+    static Vector3 getLightCenter() {
+        Vector3 result;
+        result.x = result.y = result.z = 1.0f;
+        return result;
+    }
+
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
new file mode 100644
index 0000000..8a42983
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaRecordingCanvas.h"
+
+#include "Layer.h"
+#include "RenderNode.h"
+#include "LayerDrawable.h"
+#include "NinePatchUtils.h"
+#include "pipeline/skia/AnimatedDrawables.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+// ----------------------------------------------------------------------------
+// Recording Canvas Setup
+// ----------------------------------------------------------------------------
+
+void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, int width,
+        int height) {
+    mBarrierPending = false;
+    mCurrentBarrier = nullptr;
+    SkASSERT(mDisplayList.get() == nullptr);
+
+    if (renderNode) {
+        mDisplayList = renderNode->detachAvailableList();
+    }
+    SkRect bounds = SkRect::MakeWH(width, height);
+    if (mDisplayList) {
+        mDisplayList->reset(nullptr, bounds);
+    } else {
+        mDisplayList.reset(new SkiaDisplayList(bounds));
+    }
+
+    mRecorder.reset(mDisplayList->mDrawable.get());
+    SkiaCanvas::reset(&mRecorder);
+}
+
+uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() {
+    // close any existing chunks if necessary
+    insertReorderBarrier(false);
+    mRecorder.restoreToCount(1);
+    return mDisplayList.release();
+}
+
+// ----------------------------------------------------------------------------
+// Recording Canvas draw operations: View System
+// ----------------------------------------------------------------------------
+
+void SkiaRecordingCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
+        uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
+        uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
+        uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) {
+    drawDrawable(mDisplayList->allocateDrawable<AnimatedRoundRect>(left, top, right, bottom,
+            rx, ry, paint));
+}
+
+void SkiaRecordingCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x,
+        uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius,
+        uirenderer::CanvasPropertyPaint* paint) {
+    drawDrawable(mDisplayList->allocateDrawable<AnimatedCircle>(x, y, radius, paint));
+}
+
+void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
+    mBarrierPending = enableReorder;
+
+    if (nullptr != mCurrentBarrier) {
+        // finish off the existing chunk
+        SkDrawable* drawable =
+                mDisplayList->allocateDrawable<EndReorderBarrierDrawable>(
+                mCurrentBarrier);
+        mCurrentBarrier = nullptr;
+        drawDrawable(drawable);
+    }
+}
+
+void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) {
+    if (layerUpdater != nullptr && layerUpdater->backingLayer() != nullptr) {
+        uirenderer::Layer* layer = layerUpdater->backingLayer();
+        sk_sp<SkDrawable> drawable(new LayerDrawable(layer));
+        drawDrawable(drawable.get());
+    }
+}
+
+void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
+    // lazily create the chunk if needed
+    if (mBarrierPending) {
+        mCurrentBarrier = (StartReorderBarrierDrawable*)
+                mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
+                mDisplayList.get());
+        drawDrawable(mCurrentBarrier);
+        mBarrierPending = false;
+    }
+
+    // record the child node
+    mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas());
+    drawDrawable(&mDisplayList->mChildNodes.back());
+
+    // use staging property, since recording on UI thread
+    if (renderNode->stagingProperties().isProjectionReceiver()) {
+        mDisplayList->mIsProjectionReceiver = true;
+        // set projectionReceiveIndex so that RenderNode.hasProjectionReceiver returns true
+        mDisplayList->projectionReceiveIndex = mDisplayList->mChildNodes.size() - 1;
+    }
+}
+
+void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
+        uirenderer::GlFunctorLifecycleListener* listener) {
+    mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas());
+    drawDrawable(&mDisplayList->mChildFunctors.back());
+}
+
+class VectorDrawable : public SkDrawable {
+ public:
+    VectorDrawable(VectorDrawableRoot* tree) : mRoot(tree) {}
+
+ protected:
+     virtual SkRect onGetBounds() override {
+         return SkRect::MakeLargest();
+     }
+     virtual void onDraw(SkCanvas* canvas) override {
+         Bitmap& hwuiBitmap = mRoot->getBitmapUpdateIfDirty();
+         SkBitmap bitmap;
+         hwuiBitmap.getSkBitmap(&bitmap);
+         SkPaint* paint = mRoot->getPaint();
+         canvas->drawBitmapRect(bitmap, mRoot->mutateProperties()->getBounds(), paint);
+         /*
+          * TODO we can draw this directly but need to address the following...
+          *
+          * 1) Add drawDirect(SkCanvas*) to VectorDrawableRoot
+          * 2) fix VectorDrawable.cpp's Path::draw to not make a temporary path
+          *    so that we don't break caching
+          * 3) figure out how to set path's as volatile during animation
+          * 4) if mRoot->getPaint() != null either promote to layer (during
+          *    animation) or cache in SkSurface (for static content)
+          *
+          */
+     }
+
+ private:
+    sp<VectorDrawableRoot> mRoot;
+};
+
+void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
+    drawDrawable(mDisplayList->allocateDrawable<VectorDrawable>(tree));
+    mDisplayList->mVectorDrawables.push_back(tree);
+}
+
+// ----------------------------------------------------------------------------
+// Recording Canvas draw operations: Bitmaps
+// ----------------------------------------------------------------------------
+
+inline static const SkPaint* nonAAPaint(const SkPaint* origPaint, SkPaint* tmpPaint) {
+    if (origPaint && origPaint->isAntiAlias()) {
+        *tmpPaint = *origPaint;
+        tmpPaint->setAntiAlias(false);
+        return tmpPaint;
+    } else {
+        return origPaint;
+    }
+}
+
+void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
+    SkBitmap skBitmap;
+    bitmap.getSkBitmap(&skBitmap);
+
+    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
+    if (!skBitmap.isImmutable()) {
+        mDisplayList->mMutableImages.push_back(image.get());
+    }
+    SkPaint tmpPaint;
+    mRecorder.drawImage(image, left, top, nonAAPaint(paint, &tmpPaint));
+}
+
+void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix,
+        const SkPaint* paint) {
+    SkBitmap bitmap;
+    hwuiBitmap.getSkBitmap(&bitmap);
+    SkAutoCanvasRestore acr(&mRecorder, true);
+    concat(matrix);
+    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+    if (!bitmap.isImmutable()) {
+        mDisplayList->mMutableImages.push_back(image.get());
+    }
+    SkPaint tmpPaint;
+    mRecorder.drawImage(image, 0, 0, nonAAPaint(paint, &tmpPaint));
+}
+
+void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop,
+        float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight,
+        float dstBottom, const SkPaint* paint) {
+    SkBitmap bitmap;
+    hwuiBitmap.getSkBitmap(&bitmap);
+    SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
+    SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+    if (!bitmap.isImmutable()) {
+        mDisplayList->mMutableImages.push_back(image.get());
+    }
+    SkPaint tmpPaint;
+    mRecorder.drawImageRect(image, srcRect, dstRect, nonAAPaint(paint, &tmpPaint));
+}
+
+void SkiaRecordingCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk,
+        float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
+    SkBitmap bitmap;
+    hwuiBitmap.getSkBitmap(&bitmap);
+
+    SkCanvas::Lattice lattice;
+    NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height());
+
+    lattice.fFlags = nullptr;
+    int numFlags = 0;
+    if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) {
+        // We can expect the framework to give us a color for every distinct rect.
+        // Skia requires placeholder flags for degenerate rects.
+        numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
+    }
+
+    SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags);
+    if (numFlags > 0) {
+        NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk);
+    }
+
+    lattice.fBounds = nullptr;
+    SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+    if (!bitmap.isImmutable()) {
+        mDisplayList->mMutableImages.push_back(image.get());
+    }
+
+    SkPaint tmpPaint;
+    mRecorder.drawImageLattice(image.get(), lattice, dst, nonAAPaint(paint, &tmpPaint));
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
new file mode 100644
index 0000000..8aef97f
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "SkiaCanvas.h"
+#include "SkiaDisplayList.h"
+#include "ReorderBarrierDrawables.h"
+#include <SkLiteRecorder.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/**
+ * A SkiaCanvas implementation that records drawing operations for deferred rendering backed by a
+ * SkLiteRecorder and a SkiaDisplayList.
+ */
+class SkiaRecordingCanvas : public SkiaCanvas {
+ public:
+    explicit SkiaRecordingCanvas(uirenderer::RenderNode* renderNode, int width, int height) {
+        initDisplayList(renderNode, width, height);
+    }
+
+    virtual void setBitmap(const SkBitmap& bitmap) override {
+        LOG_ALWAYS_FATAL("DisplayListCanvas is not backed by a bitmap.");
+    }
+
+    virtual void resetRecording(int width, int height,
+            uirenderer::RenderNode* renderNode) override {
+        initDisplayList(renderNode, width, height);
+    }
+
+    virtual uirenderer::DisplayList* finishRecording() override;
+
+    virtual void drawBitmap(Bitmap& bitmap, float left, float top,
+            const SkPaint* paint) override;
+    virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix,
+            const SkPaint* paint) override;
+    virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
+            float srcRight, float srcBottom, float dstLeft, float dstTop,
+            float dstRight, float dstBottom, const SkPaint* paint) override;
+    virtual void drawNinePatch(Bitmap& hwuiBitmap, const android::Res_png_9patch& chunk,
+            float dstLeft, float dstTop, float dstRight, float dstBottom,
+            const SkPaint* paint) override;
+
+    virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
+            uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
+            uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
+            uirenderer::CanvasPropertyPrimitive* ry,
+            uirenderer::CanvasPropertyPaint* paint) override;
+    virtual void drawCircle(uirenderer::CanvasPropertyPrimitive* x,
+            uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius,
+            uirenderer::CanvasPropertyPaint* paint) override;
+
+    virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
+
+    virtual void insertReorderBarrier(bool enableReorder) override;
+    virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
+    virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
+    virtual void callDrawGLFunction(Functor* functor,
+                                    uirenderer::GlFunctorLifecycleListener* listener) override;
+
+private:
+    SkLiteRecorder mRecorder;
+    std::unique_ptr<SkiaDisplayList> mDisplayList;
+    bool mBarrierPending;
+    StartReorderBarrierDrawable* mCurrentBarrier;
+
+    /**
+     *  A new SkiaDisplayList is created or recycled if available.
+     *
+     *  @param renderNode is optional and used to recycle an old display list.
+     *  @param width used to calculate recording bounds.
+     *  @param height used to calculate recording bounds.
+     */
+    void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height);
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 5b9b003..9530c79 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -125,5 +125,44 @@
     return utf16;
 }
 
+SkColor TestUtils::getColor(const sk_sp<SkSurface>& surface, int x, int y) {
+    SkPixmap pixmap;
+    if (!surface->peekPixels(&pixmap)) {
+        return 0;
+    }
+    switch (pixmap.colorType()) {
+        case kGray_8_SkColorType: {
+            const uint8_t* addr = pixmap.addr8(x, y);
+            return SkColorSetRGB(*addr, *addr, *addr);
+        }
+        case kAlpha_8_SkColorType: {
+            const uint8_t* addr = pixmap.addr8(x, y);
+            return SkColorSetA(0, addr[0]);
+        }
+        case kRGB_565_SkColorType: {
+            const uint16_t* addr = pixmap.addr16(x, y);
+            return SkPixel16ToColor(addr[0]);
+        }
+        case kARGB_4444_SkColorType: {
+            const uint16_t* addr = pixmap.addr16(x, y);
+            SkPMColor c = SkPixel4444ToPixel32(addr[0]);
+            return SkUnPreMultiply::PMColorToColor(c);
+        }
+        case kBGRA_8888_SkColorType: {
+            const uint32_t* addr = pixmap.addr32(x, y);
+            SkPMColor c = SkSwizzle_BGRA_to_PMColor(addr[0]);
+            return SkUnPreMultiply::PMColorToColor(c);
+        }
+        case kRGBA_8888_SkColorType: {
+            const uint32_t* addr = pixmap.addr32(x, y);
+            SkPMColor c = SkSwizzle_RGBA_to_PMColor(addr[0]);
+            return SkUnPreMultiply::PMColorToColor(c);
+        }
+        default:
+            return 0;
+    }
+    return 0;
+}
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index f6fe7d2..0be5a3b 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -260,6 +260,8 @@
          int mLastMode = -1;
      };
 
+    static SkColor getColor(const sk_sp<SkSurface>& surface, int x, int y);
+
 private:
     static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
         node->syncProperties();
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
new file mode 100644
index 0000000..19c311c
--- /dev/null
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <VectorDrawable.h>
+
+#include "AnimationContext.h"
+#include "DamageAccumulator.h"
+#include "IContextFactory.h"
+#include "pipeline/skia/SkiaDisplayList.h"
+#include "pipeline/skia/SkiaRecordingCanvas.h"
+#include "renderthread/CanvasContext.h"
+#include "tests/common/TestUtils.h"
+#include "SkiaCanvas.h"
+#include <SkLiteRecorder.h>
+#include <string.h>
+
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::skiapipeline;
+
+static sp<RenderNode> createSkiaNode(int left, int top, int right, int bottom,
+        std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup,
+        const char* name = nullptr, SkiaDisplayList* displayList = nullptr) {
+#if HWUI_NULL_GPU
+    // if RenderNodes are being sync'd/used, device info will be needed, since
+    // DeviceInfo::maxTextureSize() affects layer property
+    DeviceInfo::initialize();
+#endif
+    sp<RenderNode> node = new RenderNode();
+    if (name) {
+        node->setName(name);
+    }
+    RenderProperties& props = node->mutateStagingProperties();
+    props.setLeftTopRightBottom(left, top, right, bottom);
+    if (displayList) {
+        node->setStagingDisplayList(displayList, nullptr);
+    }
+    if (setup) {
+        std::unique_ptr<SkiaRecordingCanvas> canvas(new SkiaRecordingCanvas(nullptr,
+            props.getWidth(), props.getHeight()));
+        setup(props, *canvas.get());
+        node->setStagingDisplayList(canvas->finishRecording(), nullptr);
+    }
+    node->setPropertyFieldsDirty(0xFFFFFFFF);
+    TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+    return node;
+}
+
+TEST(RenderNodeDrawable, create) {
+    auto rootNode = TestUtils::createNode(0, 0, 200, 400,
+            [](RenderProperties& props, Canvas& canvas) {
+                canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
+            });
+
+    auto skLiteDL = SkLiteDL::New(SkRect::MakeWH(1, 1));
+    SkLiteRecorder canvas;
+    canvas.reset(skLiteDL.get());
+    canvas.translate(100, 100);
+    RenderNodeDrawable drawable(rootNode.get(), &canvas);
+
+    ASSERT_EQ(drawable.getRenderNode(), rootNode.get());
+    ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties());
+    ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix());
+}
+
+TEST(RenderNodeDrawable, drawContent) {
+    auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+    SkCanvas& canvas = *surface->getCanvas();
+    canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+
+    //create a RenderNodeDrawable backed by a RenderNode backed by a SkLiteRecorder
+    auto rootNode = createSkiaNode(0, 0, 1, 1,
+        [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
+            recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+        });
+    RenderNodeDrawable drawable(rootNode.get(), &canvas, false);
+
+    //negative and positive Z order are drawn out of order
+    rootNode->animatorProperties().setElevation(10.0f);
+    canvas.drawDrawable(&drawable);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+    rootNode->animatorProperties().setElevation(-10.0f);
+    canvas.drawDrawable(&drawable);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+
+    //zero Z are drawn immediately
+    rootNode->animatorProperties().setElevation(0.0f);
+    canvas.drawDrawable(&drawable);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
+}
+
+//TODO: another test that verifies equal z values are drawn in order, and barriers prevent Z
+//intermixing (model after FrameBuilder zReorder)
+TEST(RenderNodeDrawable, drawAndReorder) {
+    //this test exercises StartReorderBarrierDrawable, EndReorderBarrierDrawable and
+    //SkiaRecordingCanvas
+    auto surface = SkSurface::MakeRasterN32Premul(4, 4);
+    SkCanvas& canvas = *surface->getCanvas();
+
+    canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
+
+    //-z draws to all 4 pixels (RED)
+    auto redNode = createSkiaNode(0, 0, 4, 4,
+        [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+            redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+            props.setElevation(-10.0f);
+        }, "redNode");
+
+    //0z draws to bottom 2 pixels (GREEN)
+    auto bottomHalfGreenNode = createSkiaNode(0, 0, 4, 4,
+            [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
+                SkPaint greenPaint;
+                greenPaint.setColor(SK_ColorGREEN);
+                greenPaint.setStyle(SkPaint::kFill_Style);
+                bottomHalfGreenCanvas.drawRect(0, 2, 4, 4, greenPaint);
+                props.setElevation(0.0f);
+            }, "bottomHalfGreenNode");
+
+    //+z draws to right 2 pixels (BLUE)
+    auto rightHalfBlueNode = createSkiaNode(0, 0, 4, 4,
+        [](RenderProperties& props, SkiaRecordingCanvas& rightHalfBlueCanvas) {
+            SkPaint bluePaint;
+            bluePaint.setColor(SK_ColorBLUE);
+            bluePaint.setStyle(SkPaint::kFill_Style);
+            rightHalfBlueCanvas.drawRect(2, 0, 4, 4, bluePaint);
+            props.setElevation(10.0f);
+        }, "rightHalfBlueNode");
+
+    auto rootNode = createSkiaNode(0, 0, 4, 4,
+            [&](RenderProperties& props, SkiaRecordingCanvas& rootRecorder) {
+                rootRecorder.insertReorderBarrier(true);
+                //draw in reverse Z order, so Z alters draw order
+                rootRecorder.drawRenderNode(rightHalfBlueNode.get());
+                rootRecorder.drawRenderNode(bottomHalfGreenNode.get());
+                rootRecorder.drawRenderNode(redNode.get());
+            }, "rootNode");
+
+    RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
+    canvas.drawDrawable(&drawable3);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 3), SK_ColorGREEN);
+    ASSERT_EQ(TestUtils::getColor(surface, 3, 3), SK_ColorBLUE);
+}
+
+TEST(RenderNodeDrawable, composeOnLayer)
+{
+    auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+    SkCanvas& canvas = *surface->getCanvas();
+    canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+
+    auto rootNode = createSkiaNode(0, 0, 1, 1,
+        [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
+            recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+        });
+
+    //attach a layer to the render node
+    auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
+    auto canvas2 = surfaceLayer->getCanvas();
+    canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+    rootNode->setLayerSurface( surfaceLayer  );
+
+    RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
+    canvas.drawDrawable(&drawable1);
+    ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
+
+    RenderNodeDrawable drawable2(rootNode.get(), &canvas, true);
+    canvas.drawDrawable(&drawable2);
+    ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0));
+
+    RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
+    canvas.drawDrawable(&drawable3);
+    ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
+
+    rootNode->setLayerSurface( sk_sp<SkSurface>()  );
+}
+
+//TODO: refactor to cover test cases from FrameBuilderTests_projectionReorder
+//validate with bounds and projection path mask.
+//TODO: research if we could hook in and mock/validate different aspects of the drawing,
+//instead of validating pixels
+TEST(RenderNodeDrawable, projectDraw) {
+    auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+    SkCanvas& canvas = *surface->getCanvas();
+    canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+
+    auto redNode = createSkiaNode(0, 0, 1, 1,
+        [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+            redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+        }, "redNode");
+
+    auto greenNodeWithRedChild = createSkiaNode(0, 0, 1, 1,
+        [&](RenderProperties& props, SkiaRecordingCanvas& greenCanvasWithRedChild) {
+            greenCanvasWithRedChild.drawRenderNode(redNode.get());
+            greenCanvasWithRedChild.drawColor(SK_ColorGREEN, SkBlendMode::kSrcOver);
+        }, "greenNodeWithRedChild");
+
+    auto rootNode = createSkiaNode(0, 0, 1, 1,
+        [&](RenderProperties& props, SkiaRecordingCanvas& rootCanvas) {
+            rootCanvas.drawRenderNode(greenNodeWithRedChild.get());
+        }, "rootNode");
+    SkiaDisplayList* rootDisplayList = static_cast<SkiaDisplayList*>(
+        (const_cast<DisplayList*>(rootNode->getDisplayList())));
+
+    RenderNodeDrawable rootDrawable(rootNode.get(), &canvas, false);
+    canvas.drawDrawable(&rootDrawable);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorGREEN);
+
+    //project redNode on rootNode, which will change the test outcome,
+    //because redNode will draw after greenNodeWithRedChild
+    rootDisplayList->mIsProjectionReceiver = true;
+    redNode->animatorProperties().setProjectBackwards(true);
+    canvas.drawDrawable(&rootDrawable);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
+}
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 72029b9..fe6cea6 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -20,14 +20,14 @@
 #include "AnimationContext.h"
 #include "DamageAccumulator.h"
 #include "IContextFactory.h"
-#include "SkiaDisplayList.h"
+#include "pipeline/skia/SkiaDisplayList.h"
 #include "renderthread/CanvasContext.h"
 #include "tests/common/TestUtils.h"
 
-
 using namespace android;
 using namespace android::uirenderer;
 using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::skiapipeline;
 
 TEST(SkiaDisplayList, create) {
     SkRect bounds = SkRect::MakeWH(200, 200);
diff --git a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
index b04948b..ebb5d19 100644
--- a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
@@ -82,11 +82,10 @@
 
         final String PROC_VERSION_REGEX =
                 "Linux version (\\S+) " + /* group 1: "3.0.31-g6fb96c9" */
-                "\\((\\S+?)\\) " +        /* group 2: "x@y.com" (kernel builder) */
-                "(?:\\(gcc.+? \\)) " +    /* ignore: GCC version information */
-                "(#\\d+) " +              /* group 3: "#1" */
-                "(?:.*?)?" +              /* ignore: optional SMP, PREEMPT, and any CONFIG_FLAGS */
-                "((Sun|Mon|Tue|Wed|Thu|Fri|Sat).+)"; /* group 4: "Thu Jun 28 11:02:39 PDT 2012" */
+                "\\((\\S+)\\)" +          /* group 2: "x@y.com" (kernel builder) */
+                ".*(#\\d+)" +             /* group 3: "#1" */
+                /* group 4: "Thu Jun 28 11:02:39 PDT 2012" */
+                ".*((?:Sun|Mon|Tue|Wed|Thu|Fri|Sat).+)";
 
         Matcher m = Pattern.compile(PROC_VERSION_REGEX).matcher(rawKernelVersion);
         if (!m.matches()) {
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 a99bad2..793d2fe 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
@@ -205,6 +234,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 =
@@ -250,10 +287,6 @@
         return mDisplayId;
     }
 
-    WindowList getWindowList() {
-        return mWindows;
-    }
-
     WindowToken getWindowToken(IBinder binder) {
         return mTokenMap.get(binder);
     }
@@ -546,14 +579,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);
@@ -625,9 +650,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);
@@ -652,7 +677,7 @@
         return mDimLayerController.animateDimLayers();
     }
 
-    void resetDimming() {
+    private void resetDimming() {
         mDimLayerController.resetDimming();
     }
 
@@ -660,7 +685,7 @@
         return mDimLayerController.isDimming();
     }
 
-    void stopDimmingIfNeeded() {
+    private void stopDimmingIfNeeded() {
         mDimLayerController.stopDimmingIfNeeded();
     }
 
@@ -1008,10 +1033,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) {
@@ -1026,11 +1050,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),
@@ -1215,34 +1238,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;
@@ -1292,14 +1316,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*/);
         }
     }
@@ -1396,6 +1420,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();
@@ -1477,8 +1655,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
@@ -1486,8 +1662,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.
@@ -1502,7 +1678,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++;
@@ -1510,7 +1686,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++;
@@ -1523,17 +1699,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 {
@@ -1550,7 +1726,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 {
@@ -1575,11 +1751,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));
@@ -1592,7 +1767,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;
@@ -1617,7 +1792,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="
@@ -1633,7 +1808,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);
@@ -1698,7 +1873,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;
                 }
@@ -1751,7 +1926,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;
@@ -1764,13 +1938,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);
         }
     }
 
@@ -1779,7 +1953,7 @@
         mLayoutNeeded = true;
     }
 
-    void clearLayoutNeeded() {
+    private void clearLayoutNeeded() {
         if (DEBUG_LAYOUT) Slog.w(TAG_WM, "clearLayoutNeeded: callers=" + Debug.getCallers(3));
         mLayoutNeeded = false;
     }
@@ -1862,9 +2036,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));
         }
     }
 
@@ -2434,6 +2607,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 70b0201..7759705 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -143,7 +143,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;
@@ -172,7 +171,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.util.TypedValue.COMPLEX_UNIT_DIP;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -189,12 +187,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;
@@ -207,6 +202,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;
@@ -222,7 +218,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;
@@ -244,7 +239,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
@@ -624,10 +618,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;
@@ -3278,51 +3268,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 {
@@ -3347,7 +3292,7 @@
                 if (mAppTransition.isTransitionSet()) {
                     task.setSendingToBottom(false);
                 }
-                rebuildAppWindowsAndLayoutIfNeededLocked(displayContent);
+                displayContent.rebuildAppWindowsAndLayoutIfNeeded();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -3369,7 +3314,7 @@
                 if (mAppTransition.isTransitionSet()) {
                     task.setSendingToBottom(true);
                 }
-                rebuildAppWindowsAndLayoutIfNeededLocked(stack.getDisplayContent());
+                stack.getDisplayContent().rebuildAppWindowsAndLayoutIfNeeded();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -4434,29 +4379,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,
@@ -4535,8 +4457,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) {
@@ -4547,263 +4469,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);
     }
 
     /**
@@ -4929,6 +4596,8 @@
         }
     }
 
+
+    // TODO(multidisplay): Rotate any display? Move to DisplayContent
     /**
      * Updates the current rotation of the specified display.
      *
@@ -4956,7 +4625,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;
         }
 
@@ -4967,7 +4637,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);
@@ -5007,24 +4677,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;
@@ -5116,6 +4780,7 @@
                 w.mLastFreezeDuration = 0;
             }
         }
+
         if (rotateSeamlessly) {
             mH.removeMessages(H.SEAMLESS_ROTATION_TIMEOUT);
             mH.sendEmptyMessageDelayed(H.SEAMLESS_ROTATION_TIMEOUT, SEAMLESS_ROTATION_TIMEOUT_DURATION);
@@ -6977,20 +6642,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;
@@ -7485,7 +7138,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);
@@ -7570,6 +7223,7 @@
         }
     }
 
+    // TODO: Move to DisplayContent
     boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
         WindowState newFocus = mRoot.computeFocusedWindow();
         if (mCurrentFocus != newFocus) {
@@ -7599,9 +7253,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.
@@ -7609,12 +7262,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);
                 }
             }
 
@@ -7626,11 +7278,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;
@@ -8263,8 +7914,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");
@@ -8291,7 +7943,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>");
@@ -8309,8 +7961,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()));
@@ -8521,6 +8172,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);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index 128317c..0ccd0ad 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -42,9 +42,8 @@
 /**
  * Test class for {@link WindowContainer}.
  *
- * Build: mmma -j32 frameworks/base/services/tests/servicestests
- * Install: adb install -r out/target/product/$TARGET_PRODUCT/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- * Run: adb shell am instrument -w -e class com.android.server.wm.WindowContainerTests com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.wm.WindowContainerTests
  */
 @SmallTest
 @Presubmit