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