summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/IPinnedStackController.aidl5
-rw-r--r--core/java/com/android/internal/policy/PipSnapAlgorithm.java10
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp4
-rw-r--r--core/jni/android/graphics/Graphics.cpp2
-rw-r--r--core/res/res/values-watch/config.xml3
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--libs/hwui/Android.mk4
-rw-r--r--libs/hwui/DeviceInfo.cpp7
-rw-r--r--libs/hwui/DeviceInfo.h1
-rw-r--r--libs/hwui/RenderNode.h30
-rw-r--r--libs/hwui/SkiaCanvasProxy.cpp38
-rw-r--r--libs/hwui/Texture.cpp2
-rw-r--r--libs/hwui/VectorDrawable.cpp2
-rw-r--r--libs/hwui/hwui/Bitmap.cpp6
-rw-r--r--libs/hwui/hwui/Canvas.cpp5
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.cpp4
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp18
-rw-r--r--libs/hwui/pipeline/skia/SkiaLayer.h (renamed from libs/hwui/pipeline/skia/SkiaFrameRenderer.h)40
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp170
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h58
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp258
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.h112
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp144
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.h62
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp27
-rw-r--r--libs/hwui/renderthread/EglManager.h6
-rw-r--r--libs/hwui/tests/common/TestUtils.h30
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp50
-rw-r--r--libs/hwui/tests/unit/SkiaBehaviorTests.cpp4
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp181
-rw-r--r--libs/hwui/utils/TestWindowContext.cpp2
-rw-r--r--packages/SystemUI/AndroidManifest.xml12
-rw-r--r--packages/SystemUI/res/layout/pip_menu_activity.xml36
-rw-r--r--packages/SystemUI/res/values/strings.xml19
-rw-r--r--packages/SystemUI/res/values/styles.xml18
-rw-r--r--packages/SystemUI/res/xml/tuner_prefs.xml12
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java137
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java126
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java113
-rw-r--r--services/core/java/com/android/server/LockSettingsService.java14
-rw-r--r--services/core/java/com/android/server/wm/PinnedStackController.java7
-rwxr-xr-xservices/tests/runtests.py4
-rw-r--r--tools/aapt2/Android.mk4
-rw-r--r--tools/aapt2/jni/Aapt2.java44
-rw-r--r--tools/aapt2/jni/Makefile25
-rw-r--r--tools/aapt2/jni/ScopedUtfChars.h84
-rw-r--r--tools/aapt2/jni/aapt2_jni.cpp11
-rw-r--r--tools/aapt2/jni/com_android_tools_aapt2_Aapt2.h8
49 files changed, 1768 insertions, 200 deletions
diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl
index 830591d5b503..a81eef831f4e 100644
--- a/core/java/android/view/IPinnedStackController.aidl
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -30,4 +30,9 @@ interface IPinnedStackController {
* Notifies the controller that the user is currently interacting with the PIP.
*/
oneway void setInInteractiveMode(boolean inInteractiveMode);
+
+ /**
+ * Notifies the controller that the desired snap mode is to the closest edge.
+ */
+ oneway void setSnapToEdge(boolean snapToEdge);
}
diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
index 45b7b0183fd8..cbacf269a0f0 100644
--- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -46,7 +46,8 @@ public class PipSnapAlgorithm {
private final Context mContext;
private final ArrayList<Integer> mSnapGravities = new ArrayList<>();
- private final int mSnapMode = SNAP_MODE_CORNERS_ONLY;
+ private final int mDefaultSnapMode = SNAP_MODE_CORNERS_ONLY;
+ private int mSnapMode = mDefaultSnapMode;
private Scroller mScroller;
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
@@ -65,6 +66,13 @@ public class PipSnapAlgorithm {
}
/**
+ * Enables snapping to the closest edge.
+ */
+ public void setSnapToEdge(boolean snapToEdge) {
+ mSnapMode = snapToEdge ? SNAP_MODE_EDGE : mDefaultSnapMode;
+ }
+
+ /**
* @return the closest absolute snap stack bounds for the given {@param stackBounds} moving at
* the given {@param velocityX} and {@param velocityY}. The {@param movementBounds} should be
* those for the given {@param stackBounds}.
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index a47062e4c828..a489421bd7ed 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -806,7 +806,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
std::unique_ptr<SkBitmap> bitmap(new SkBitmap);
if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType,
- isSRGB ? SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named) : nullptr), rowBytes)) {
+ isSRGB ? SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named) : nullptr), rowBytes)) {
return NULL;
}
@@ -921,7 +921,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle);
bitmapWrapper->getSkBitmap(&bitmap);
- sk_sp<SkColorSpace> sRGB = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
+ sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
bool isSRGB = bitmap.colorSpace() == sRGB.get();
p->writeInt32(isMutable);
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index dffb63c774a6..322eed572940 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -437,7 +437,7 @@ android::Bitmap* GraphicsJNI::mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap,
sk_sp<SkColorSpace> GraphicsJNI::defaultColorSpace() {
#ifdef ANDROID_ENABLE_LINEAR_BLENDING
- return SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
+ return SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
#else
return nullptr;
#endif
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 7c05b7a8e362..d13d15468825 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -62,4 +62,7 @@
ListView/GridView are notably absent since this is their default anyway.
Set to true for watch devices. -->
<bool name="config_focusScrollContainersInTouchMode">true</bool>
+
+ <!-- The small screens of watch devices makes multi-window support undesireable. -->
+ <bool name="config_supportsMultiWindow">false</bool>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0678dfd1e759..6dc3f3eb1614 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2175,9 +2175,9 @@
-->
<string-array translatable="false" name="config_telephonyHardware">
<!-- modem -->
- <item>"0,modem,0,0,0,1,1,1"</item>
+ <item>0,modem,0,0,0,1,1,1</item>
<!-- sim -->
- <item>"1,sim,0,modem"</item>
+ <item>1,sim,0,modem</item>
</string-array>
<!-- This string array can be overriden to add an additional DRM support for WebView EME. -->
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 461936aaa493..e1b7788801d4 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -23,7 +23,10 @@ hwui_src_files := \
pipeline/skia/RenderNodeDrawable.cpp \
pipeline/skia/ReorderBarrierDrawables.cpp \
pipeline/skia/SkiaDisplayList.cpp \
+ pipeline/skia/SkiaOpenGLPipeline.cpp \
+ pipeline/skia/SkiaPipeline.cpp \
pipeline/skia/SkiaRecordingCanvas.cpp \
+ pipeline/skia/SkiaVulkanPipeline.cpp \
renderstate/Blend.cpp \
renderstate/MeshState.cpp \
renderstate/OffscreenBufferPool.cpp \
@@ -296,6 +299,7 @@ LOCAL_SRC_FILES += \
tests/unit/RenderPropertiesTests.cpp \
tests/unit/SkiaBehaviorTests.cpp \
tests/unit/SkiaDisplayListTests.cpp \
+ tests/unit/SkiaPipelineTests.cpp \
tests/unit/SkiaCanvasTests.cpp \
tests/unit/SnapshotTests.cpp \
tests/unit/StringUtilsTests.cpp \
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 4cfbb2a43198..700642ed7334 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -41,6 +41,13 @@ void DeviceInfo::initialize() {
});
}
+void DeviceInfo::initialize(int maxTextureSize) {
+ std::call_once(sInitializedFlag, [maxTextureSize]() {
+ sDeviceInfo = new DeviceInfo();
+ sDeviceInfo->mMaxTextureSize = maxTextureSize;
+ });
+}
+
void DeviceInfo::load() {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
}
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index e551eb9bb8c0..aff84b02d85a 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -32,6 +32,7 @@ public:
// only call this after GL has been initialized, or at any point if compiled
// with HWUI_NULL_GPU
static void initialize();
+ static void initialize(int maxTextureSize);
int maxTextureSize() const { return mMaxTextureSize; }
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 3819c5e9187d..b8964f0f16a0 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -33,6 +33,7 @@
#include "Matrix.h"
#include "RenderProperties.h"
#include "pipeline/skia/SkiaDisplayList.h"
+#include "pipeline/skia/SkiaLayer.h"
#include <vector>
@@ -60,10 +61,6 @@ namespace proto {
class RenderNode;
}
-namespace skiapipeline {
- class SkiaDisplayList;
-}
-
/**
* Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
*
@@ -312,14 +309,23 @@ public:
* Returns true if an offscreen layer from any renderPipeline is attached
* to this node.
*/
- bool hasLayer() const { return mLayer || mLayerSurface.get(); }
+ bool hasLayer() const { return mLayer || mSkiaLayer.get(); }
/**
* Used by the RenderPipeline to attach an offscreen surface to the RenderNode.
* The surface is then will be used to store the contents of a layer.
*/
- void setLayerSurface(sk_sp<SkSurface> layer) { mLayerSurface = layer; }
-
+ void setLayerSurface(sk_sp<SkSurface> layer) {
+ if (layer.get()) {
+ if (!mSkiaLayer.get()) {
+ mSkiaLayer = std::make_unique<skiapipeline::SkiaLayer>();
+ }
+ mSkiaLayer->layerSurface = std::move(layer);
+ mSkiaLayer->inverseTransformInWindow.loadIdentity();
+ } else {
+ mSkiaLayer.reset();
+ }
+ }
/**
* If the RenderNode is of type LayerType::RenderLayer then this method will
@@ -330,7 +336,13 @@ public:
* NOTE: this function is only guaranteed to return accurate results after
* prepareTree has been run for this RenderNode
*/
- SkSurface* getLayerSurface() const { return mLayerSurface.get(); }
+ SkSurface* getLayerSurface() const {
+ return mSkiaLayer.get() ? mSkiaLayer->layerSurface.get() : nullptr;
+ }
+
+ skiapipeline::SkiaLayer* getSkiaLayer() const {
+ return mSkiaLayer.get();
+ }
private:
/**
@@ -346,7 +358,7 @@ private:
* An offscreen rendering target used to contain the contents this RenderNode
* when it has been set to draw as a LayerType::RenderLayer.
*/
- sk_sp<SkSurface> mLayerSurface;
+ std::unique_ptr<skiapipeline::SkiaLayer> mSkiaLayer;
}; // class RenderNode
} /* namespace uirenderer */
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index f67408698426..5978abc9efaa 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -27,6 +27,7 @@
#include <SkRRect.h>
#include <SkRSXform.h>
#include <SkSurface.h>
+#include <SkTextBlobRunIterator.h>
#include <memory>
@@ -371,7 +372,42 @@ void SkiaCanvasProxy::onDrawTextRSXform(const void* text, size_t byteLength,
void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
const SkPaint& paint) {
- SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextBlob is not supported");
+ SkPaint runPaint = paint;
+
+ SkTextBlobRunIterator it(blob);
+ for (;!it.done(); it.next()) {
+ size_t textLen = it.glyphCount() * sizeof(uint16_t);
+ const SkPoint& offset = it.offset();
+ // applyFontToPaint() always overwrites the exact same attributes,
+ // so it is safe to not re-seed the paint for this reason.
+ it.applyFontToPaint(&runPaint);
+
+ switch (it.positioning()) {
+ case SkTextBlob::kDefault_Positioning:
+ this->drawText(it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
+ break;
+ case SkTextBlob::kHorizontal_Positioning: {
+ std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
+ for (size_t i = 0; i < it.glyphCount(); i++) {
+ pts[i].set(x + offset.x() + it.pos()[i], y + offset.y());
+ }
+ this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
+ break;
+ }
+ case SkTextBlob::kFull_Positioning: {
+ std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
+ for (size_t i = 0; i < it.glyphCount(); i++) {
+ const size_t xIndex = i*2;
+ const size_t yIndex = xIndex + 1;
+ pts[i].set(x + offset.x() + it.pos()[xIndex], y + offset.y() + it.pos()[yIndex]);
+ }
+ this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
+ break;
+ }
+ default:
+ SkFAIL("unhandled positioning mode");
+ }
+ }
}
void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 0a60a8ea4c99..5b5b74e1c3f3 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -272,7 +272,7 @@ void Texture::upload(Bitmap& bitmap) {
setDefaultParams = true;
}
- sk_sp<SkColorSpace> sRGB = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
+ sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
bool needSRGB = bitmap.info().colorSpace() == sRGB.get();
GLint internalFormat, format, type;
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index b50647adc0be..97b7dd722e08 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -564,7 +564,7 @@ bool Tree::allocateBitmapIfNeeded(Cache& cache, int width, int height) {
#ifndef ANDROID_ENABLE_LINEAR_BLENDING
sk_sp<SkColorSpace> colorSpace = nullptr;
#else
- sk_sp<SkColorSpace> colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
+ sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
#endif
SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace);
cache.bitmap = Bitmap::allocateHeapBitmap(info);
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index be0b22ee8d31..b8f7d9f01697 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -217,7 +217,7 @@ sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(uirenderer::renderthread::RenderThr
return nullptr;
}
- sk_sp<SkColorSpace> sRGB = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
+ sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
bool needSRGB = skBitmap.info().colorSpace() == sRGB.get();
bool hasSRGB = caches.extensions().hasSRGB();
GLint format, type, internalFormat;
@@ -307,7 +307,7 @@ void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes, SkColorTab
}
mRowBytes = rowBytes;
if (mColorTable.get() != ctable) {
- mColorTable.reset(ctable);
+ mColorTable.reset(SkSafeRef(ctable));
}
// Need to validate the alpha type to filter against the color type
@@ -482,4 +482,4 @@ GraphicBuffer* Bitmap::graphicBuffer() {
return nullptr;
}
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 1ea8bd2fcdbf..2dc8ce8fe774 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -20,6 +20,8 @@
#include "RenderNode.h"
#include "MinikinUtils.h"
#include "Paint.h"
+#include "Properties.h"
+#include "pipeline/skia/SkiaRecordingCanvas.h"
#include "Typeface.h"
#include <SkDrawFilter.h>
@@ -27,6 +29,9 @@
namespace android {
Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) {
+ if (uirenderer::Properties::isSkiaEnabled()) {
+ return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
+ }
return new uirenderer::RecordingCanvas(width, height);
}
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index b656d6737ab3..b971856c6e73 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -17,7 +17,7 @@
#include "RenderNodeDrawable.h"
#include "RenderNode.h"
#include "SkiaDisplayList.h"
-#include "SkiaFrameRenderer.h"
+#include "SkiaPipeline.h"
#include "utils/TraceUtils.h"
namespace android {
@@ -57,7 +57,7 @@ void RenderNodeDrawable::onDraw(SkCanvas* canvas) {
void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
RenderNode* renderNode = mRenderNode.get();
- if (SkiaFrameRenderer::skpCaptureEnabled()) {
+ if (SkiaPipeline::skpCaptureEnabled()) {
SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
}
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index ac185878b9ca..a204d5cb8f71 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -17,7 +17,7 @@
#include "ReorderBarrierDrawables.h"
#include "RenderNode.h"
#include "SkiaDisplayList.h"
-#include "SkiaFrameRenderer.h"
+#include "SkiaPipeline.h"
#include <SkBlurMask.h>
#include <SkBlurMaskFilter.h>
@@ -161,7 +161,7 @@ static void DrawSpotShadowGeneral(SkCanvas* canvas, const Shape& shape, float ca
return;
}
- const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
+ const Vector3 lightPos = SkiaPipeline::getLightCenter();
float zRatio = casterZValue / (lightPos.z - casterZValue);
// clamp
if (zRatio < 0.0f) {
@@ -170,7 +170,7 @@ static void DrawSpotShadowGeneral(SkCanvas* canvas, const Shape& shape, float ca
zRatio = 0.95f;
}
- float blurRadius = SkiaFrameRenderer::getLightRadius()*zRatio;
+ float blurRadius = SkiaPipeline::getLightRadius()*zRatio;
SkAutoCanvasRestore acr(canvas, true);
@@ -276,7 +276,7 @@ static void DrawRRectShadows(const SkRect& casterRect, SkScalar casterCornerRadi
}
if (spotAlpha > 0.0f) {
- const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
+ const Vector3 lightPos = SkiaPipeline::getLightCenter();
float zRatio = casterZValue / (lightPos.z - casterZValue);
// clamp
if (zRatio < 0.0f) {
@@ -285,7 +285,7 @@ static void DrawRRectShadows(const SkRect& casterRect, SkScalar casterCornerRadi
zRatio = 0.95f;
}
- const SkScalar lightWidth = SkiaFrameRenderer::getLightRadius();
+ const SkScalar lightWidth = SkiaPipeline::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) {
@@ -439,7 +439,7 @@ static void DrawRRectShadowsWithClip(const SkRect& casterRect, SkScalar casterCo
}
if (spotAlpha > 0.0f) {
- const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
+ const Vector3 lightPos = SkiaPipeline::getLightCenter();
float zRatio = casterZValue / (lightPos.z - casterZValue);
// clamp
if (zRatio < 0.0f) {
@@ -448,7 +448,7 @@ static void DrawRRectShadowsWithClip(const SkRect& casterRect, SkScalar casterCo
zRatio = 0.95f;
}
- const SkScalar lightWidth = SkiaFrameRenderer::getLightRadius();
+ const SkScalar lightWidth = SkiaPipeline::getLightRadius();
const SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
@@ -626,8 +626,8 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable*
return;
}
- float ambientAlpha = SkiaFrameRenderer::getAmbientShadowAlpha()*casterAlpha;
- float spotAlpha = SkiaFrameRenderer::getSpotShadowAlpha()*casterAlpha;
+ float ambientAlpha = SkiaPipeline::getAmbientShadowAlpha()*casterAlpha;
+ float spotAlpha = SkiaPipeline::getSpotShadowAlpha()*casterAlpha;
const float casterZValue = casterProperties.getZ();
const RevealClip& revealClip = casterProperties.getRevealClip();
diff --git a/libs/hwui/pipeline/skia/SkiaFrameRenderer.h b/libs/hwui/pipeline/skia/SkiaLayer.h
index 70207c1280ab..0988d7eda89d 100644
--- a/libs/hwui/pipeline/skia/SkiaFrameRenderer.h
+++ b/libs/hwui/pipeline/skia/SkiaLayer.h
@@ -16,39 +16,23 @@
#pragma once
+#include <SkSurface.h>
+#include "Matrix.h"
+
namespace android {
namespace uirenderer {
namespace skiapipeline {
/**
- * TODO: this is a stub that will be added in a subsquent CL
+ * An offscreen rendering target used to contain the contents a RenderNode.
*/
-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;
- }
-
+struct SkiaLayer
+{
+ sk_sp<SkSurface> layerSurface;
+ Matrix4 inverseTransformInWindow;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
new file mode 100644
index 000000000000..519de967794d
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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 "SkiaOpenGLPipeline.h"
+
+#include "DeferredLayerUpdater.h"
+#include "renderthread/EglManager.h"
+#include "renderstate/RenderState.h"
+#include "Readback.h"
+#include "utils/TraceUtils.h"
+
+#include <android/native_window.h>
+#include <cutils/properties.h>
+#include <strings.h>
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+SkiaOpenGLPipeline::SkiaOpenGLPipeline(RenderThread& thread)
+ : SkiaPipeline(thread)
+ , mEglManager(thread.eglManager()) {
+}
+
+MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
+ // TODO: Figure out why this workaround is needed, see b/13913604
+ // In the meantime this matches the behavior of GLRenderer, so it is not a regression
+ EGLint error = 0;
+ if (!mEglManager.makeCurrent(mEglSurface, &error)) {
+ return MakeCurrentResult::AlreadyCurrent;
+ }
+ return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded;
+}
+
+Frame SkiaOpenGLPipeline::getFrame() {
+ LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+ "drawRenderNode called on a context with no surface!");
+ return mEglManager.beginFrame(mEglSurface);
+}
+
+bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty,
+ const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector<sp<RenderNode>>& renderNodes,
+ FrameInfoVisualizer* profiler) {
+
+ mEglManager.damageFrame(frame, dirty);
+
+ // setup surface for fbo0
+ GrBackendRenderTargetDesc renderTargetDesc;
+ renderTargetDesc.fWidth = frame.width();
+ renderTargetDesc.fHeight = frame.height();
+ renderTargetDesc.fConfig = kRGBA_8888_GrPixelConfig;
+ renderTargetDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+ renderTargetDesc.fSampleCnt = 0;
+ renderTargetDesc.fStencilBits = STENCIL_BUFFER_SIZE;
+ renderTargetDesc.fRenderTargetHandle = 0;
+
+ SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+
+ SkASSERT(mRenderThread.getGrContext() != nullptr);
+ sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
+ mRenderThread.getGrContext(), renderTargetDesc, &props));
+
+ SkiaPipeline::updateLighting(lightGeometry, lightInfo);
+ renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+ layerUpdateQueue->clear();
+ return true;
+}
+
+bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) {
+
+ GL_CHECKPOINT(LOW);
+
+ // Even if we decided to cancel the frame, from the perspective of jank
+ // metrics the frame was swapped at this point
+ currentFrameInfo->markSwapBuffers();
+
+ *requireSwap = drew || mEglManager.damageRequiresSwap();
+
+ if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
+ return false;
+ }
+
+ return *requireSwap;
+}
+
+bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
+ layer->apply();
+ return Readback::copyTextureLayerInto(mRenderThread, *(layer->backingLayer()), bitmap)
+ == CopyResult::Success;
+}
+
+DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() {
+ mEglManager.initialize();
+ Layer* layer = new Layer(mRenderThread.renderState(), 0, 0);
+ layer->generateTexture();
+ return new DeferredLayerUpdater(layer);
+}
+
+void SkiaOpenGLPipeline::onStop() {
+ if (mEglManager.isCurrent(mEglSurface)) {
+ mEglManager.makeCurrent(EGL_NO_SURFACE);
+ }
+}
+
+bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
+
+ if (mEglSurface != EGL_NO_SURFACE) {
+ mEglManager.destroySurface(mEglSurface);
+ mEglSurface = EGL_NO_SURFACE;
+ }
+
+ if (surface) {
+ mEglSurface = mEglManager.createSurface(surface);
+ }
+
+ if (mEglSurface != EGL_NO_SURFACE) {
+ const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
+ mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
+ return true;
+ }
+
+ return false;
+}
+
+bool SkiaOpenGLPipeline::isSurfaceReady() {
+ return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE);
+}
+
+bool SkiaOpenGLPipeline::isContextReady() {
+ return CC_LIKELY(mEglManager.hasEglContext());
+}
+
+void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ if (thread.eglManager().hasEglContext()) {
+ mode = DrawGlInfo::kModeProcess;
+ }
+
+ (*functor)(mode, nullptr);
+
+ // If there's no context we don't need to reset as there's no gl state to save/restore
+ if (mode != DrawGlInfo::kModeProcessNoContext) {
+ thread.getGrContext()->resetContext();
+ }
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
new file mode 100644
index 000000000000..36685ddb17a7
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -0,0 +1,58 @@
+/*
+ * 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 "SkiaPipeline.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaOpenGLPipeline : public SkiaPipeline {
+public:
+ SkiaOpenGLPipeline(renderthread::RenderThread& thread);
+ virtual ~SkiaOpenGLPipeline() {}
+
+ renderthread::MakeCurrentResult makeCurrent() override;
+ renderthread::Frame getFrame() override;
+ bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector< sp<RenderNode> >& renderNodes,
+ FrameInfoVisualizer* profiler) override;
+ bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
+ FrameInfo* currentFrameInfo, bool* requireSwap) override;
+ bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
+ DeferredLayerUpdater* createTextureLayer() override;
+ bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override;
+ void onStop() override;
+ bool isSurfaceReady() override;
+ bool isContextReady() override;
+
+ static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
+
+private:
+ renderthread::EglManager& mEglManager;
+ EGLSurface mEglSurface = EGL_NO_SURFACE;
+ bool mBufferPreserved = false;
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
new file mode 100644
index 000000000000..03fa266a5c20
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -0,0 +1,258 @@
+/*
+ * 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 "SkiaPipeline.h"
+
+#include "utils/TraceUtils.h"
+#include <SkOSFile.h>
+#include <SkPicture.h>
+#include <SkPictureRecorder.h>
+#include <SkPixelSerializer.h>
+#include <SkStream.h>
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+float SkiaPipeline::mLightRadius = 0;
+uint8_t SkiaPipeline::mAmbientShadowAlpha = 0;
+uint8_t SkiaPipeline::mSpotShadowAlpha = 0;
+
+Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN};
+
+SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) { }
+
+TaskManager* SkiaPipeline::getTaskManager() {
+ return &mTaskManager;
+}
+
+void SkiaPipeline::onDestroyHardwareResources() {
+ // No need to flush the caches here. There is a timer
+ // which will flush temporary resources over time.
+}
+
+void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo) {
+ updateLighting(lightGeometry, lightInfo);
+ ATRACE_NAME("draw layers");
+ renderLayersImpl(*layerUpdateQueue, opaque);
+ layerUpdateQueue->clear();
+}
+
+void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
+ // Render all layers that need to be updated, in order.
+ for (size_t i = 0; i < layers.entries().size(); i++) {
+ RenderNode* layerNode = layers.entries()[i].renderNode;
+ // only schedule repaint if node still on layer - possible it may have been
+ // removed during a dropped frame, but layers may still remain scheduled so
+ // as not to lose info on what portion is damaged
+ if (CC_LIKELY(layerNode->getLayerSurface() != nullptr)) {
+ SkASSERT(layerNode->getLayerSurface());
+ SkASSERT(layerNode->getDisplayList()->isSkiaDL());
+ SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList();
+ if (!displayList || displayList->isEmpty()) {
+ SkDEBUGF(("%p drawLayers(%s) : missing drawable", this, layerNode->getName()));
+ return;
+ }
+
+ const Rect& layerDamage = layers.entries()[i].damage;
+
+ SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
+
+ int saveCount = layerCanvas->save();
+ SkASSERT(saveCount == 1);
+
+ layerCanvas->clipRect(layerDamage.toSkRect(), SkRegion::kReplace_Op);
+
+ auto savedLightCenter = mLightCenter;
+ // map current light center into RenderNode's coordinate space
+ layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(mLightCenter);
+
+ const RenderProperties& properties = layerNode->properties();
+ const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
+ if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) {
+ return;
+ }
+
+ layerCanvas->clear(SK_ColorTRANSPARENT);
+
+ RenderNodeDrawable root(layerNode, layerCanvas, false);
+ root.forceDraw(layerCanvas);
+ layerCanvas->restoreToCount(saveCount);
+ layerCanvas->flush();
+ mLightCenter = savedLightCenter;
+ }
+ }
+}
+
+bool SkiaPipeline::createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) {
+ SkSurface* layer = node->getLayerSurface();
+ if (!layer || layer->width() != node->getWidth() || layer->height() != node->getHeight()) {
+ SkImageInfo info = SkImageInfo::MakeN32Premul(node->getWidth(), node->getHeight());
+ SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+ SkASSERT(mRenderThread.getGrContext() != nullptr);
+ node->setLayerSurface(
+ SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
+ info, 0, &props));
+ if (node->getLayerSurface()) {
+ // update the transform in window of the layer to reset its origin wrt light source
+ // position
+ Matrix4 windowTransform;
+ damageAccumulator.computeCurrentTransform(&windowTransform);
+ node->getSkiaLayer()->inverseTransformInWindow = windowTransform;
+ }
+ return true;
+ }
+ return false;
+}
+
+void SkiaPipeline::destroyLayer(RenderNode* node) {
+ node->setLayerSurface(nullptr);
+}
+
+void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
+ GrContext* context = thread.getGrContext();
+ if (context) {
+ ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
+ SkBitmap skiaBitmap;
+ bitmap->getSkBitmap(&skiaBitmap);
+ sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
+ SkImage_pinAsTexture(image.get(), context);
+ SkImage_unpinAsTexture(image.get(), context);
+ }
+}
+
+// Encodes to PNG, unless there is already encoded data, in which case that gets
+// used.
+class PngPixelSerializer : public SkPixelSerializer {
+public:
+ bool onUseEncodedData(const void*, size_t) override { return true; }
+ SkData* onEncode(const SkPixmap& pixmap) override {
+ return SkImageEncoder::EncodeData(pixmap.info(), pixmap.addr(), pixmap.rowBytes(),
+ SkImageEncoder::kPNG_Type, 100);
+ }
+};
+
+void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
+ const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
+ sk_sp<SkSurface> surface) {
+
+ // unpin all mutable images that were attached to nodes deleted while on the UI thread
+ SkiaDisplayList::cleanupImages(surface->getCanvas()->getGrContext());
+
+ // draw all layers up front
+ renderLayersImpl(layers, opaque);
+
+ // initialize the canvas for the current frame
+ SkCanvas* canvas = surface->getCanvas();
+
+ std::unique_ptr<SkPictureRecorder> recorder;
+ bool recordingPicture = false;
+ char prop[PROPERTY_VALUE_MAX];
+ if (skpCaptureEnabled()) {
+ property_get("debug.hwui.capture_frame_as_skp", prop, "0");
+ recordingPicture = prop[0] != '0' && !sk_exists(prop);
+ if (recordingPicture) {
+ recorder.reset(new SkPictureRecorder());
+ canvas = recorder->beginRecording(surface->width(), surface->height(),
+ nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
+ }
+ }
+
+ canvas->clipRect(clip, SkRegion::kReplace_Op);
+
+ if (!opaque) {
+ canvas->clear(SK_ColorTRANSPARENT);
+ }
+
+ // If there are multiple render nodes, they are laid out as follows:
+ // #0 - backdrop (content + caption)
+ // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
+ // #2 - additional overlay nodes
+ // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
+ // resizing however it might become partially visible. The following render loop will crop the
+ // backdrop against the content and draw the remaining part of it. It will then draw the content
+ // cropped to the backdrop (since that indicates a shrinking of the window).
+ //
+ // Additional nodes will be drawn on top with no particular clipping semantics.
+
+ // The bounds of the backdrop against which the content should be clipped.
+ Rect backdropBounds = contentDrawBounds;
+ // Usually the contents bounds should be mContentDrawBounds - however - we will
+ // move it towards the fixed edge to give it a more stable appearance (for the moment).
+ // If there is no content bounds we ignore the layering as stated above and start with 2.
+ int layer = (contentDrawBounds.isEmpty() || nodes.size() == 1) ? 2 : 0;
+
+ for (const sp<RenderNode>& node : nodes) {
+ if (node->nothingToDraw()) continue;
+
+ SkASSERT(node->getDisplayList()->isSkiaDL());
+
+ int count = canvas->save();
+
+ if (layer == 0) {
+ const RenderProperties& properties = node->properties();
+ Rect targetBounds(properties.getLeft(), properties.getTop(),
+ properties.getRight(), properties.getBottom());
+ // Move the content bounds towards the fixed corner of the backdrop.
+ const int x = targetBounds.left;
+ const int y = targetBounds.top;
+ // Remember the intersection of the target bounds and the intersection bounds against
+ // which we have to crop the content.
+ backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
+ backdropBounds.doIntersect(targetBounds);
+ } else if (layer == 1) {
+ // We shift and clip the content to match its final location in the window.
+ const SkRect clip = SkRect::MakeXYWH(contentDrawBounds.left, contentDrawBounds.top,
+ backdropBounds.getWidth(), backdropBounds.getHeight());
+ const float dx = backdropBounds.left - contentDrawBounds.left;
+ const float dy = backdropBounds.top - contentDrawBounds.top;
+ canvas->translate(dx, dy);
+ // It gets cropped against the bounds of the backdrop to stay inside.
+ canvas->clipRect(clip, SkRegion::kIntersect_Op);
+ }
+
+ RenderNodeDrawable root(node.get(), canvas);
+ root.draw(canvas);
+ canvas->restoreToCount(count);
+ layer++;
+ }
+
+ if (skpCaptureEnabled() && recordingPicture) {
+ sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture();
+ if (picture->approximateOpCount() > 0) {
+ SkFILEWStream stream(prop);
+ if (stream.isValid()) {
+ PngPixelSerializer serializer;
+ picture->serialize(&stream, &serializer);
+ stream.flush();
+ SkDebugf("Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), prop);
+ }
+ }
+ surface->getCanvas()->drawPicture(picture);
+ }
+
+ ATRACE_NAME("flush commands");
+ canvas->flush();
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
new file mode 100644
index 000000000000..160046af63a6
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -0,0 +1,112 @@
+/*
+ * 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 "renderthread/CanvasContext.h"
+#include "FrameBuilder.h"
+#include "renderthread/IRenderPipeline.h"
+#include <SkSurface.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaPipeline : public renderthread::IRenderPipeline {
+public:
+ SkiaPipeline(renderthread::RenderThread& thread);
+ virtual ~SkiaPipeline() {}
+
+ TaskManager* getTaskManager() override;
+
+ void onDestroyHardwareResources() override;
+
+ void renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo) override;
+
+ bool createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) override;
+
+ void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
+ const std::vector< sp<RenderNode> >& nodes, bool opaque, const Rect &contentDrawBounds,
+ sk_sp<SkSurface> surface);
+
+ static void destroyLayer(RenderNode* node);
+
+ static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
+
+ static void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque);
+
+ static bool skpCaptureEnabled() { return false; }
+
+ static float getLightRadius() {
+ if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) {
+ return Properties::overrideLightRadius;
+ }
+ return mLightRadius;
+ }
+
+ static uint8_t getAmbientShadowAlpha() {
+ if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
+ return Properties::overrideAmbientShadowStrength;
+ }
+ return mAmbientShadowAlpha;
+ }
+
+ static uint8_t getSpotShadowAlpha() {
+ if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
+ return Properties::overrideSpotShadowStrength;
+ }
+ return mSpotShadowAlpha;
+ }
+
+ static Vector3 getLightCenter() {
+ if (CC_UNLIKELY(Properties::overrideLightPosY > 0 || Properties::overrideLightPosZ > 0)) {
+ Vector3 adjustedLightCenter = mLightCenter;
+ if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) {
+ // negated since this shifts up
+ adjustedLightCenter.y = - Properties::overrideLightPosY;
+ }
+ if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) {
+ adjustedLightCenter.z = Properties::overrideLightPosZ;
+ }
+ return adjustedLightCenter;
+ }
+ return mLightCenter;
+ }
+
+ static void updateLighting(const FrameBuilder::LightGeometry& lightGeometry,
+ const BakedOpRenderer::LightInfo& lightInfo) {
+ mLightRadius = lightGeometry.radius;
+ mAmbientShadowAlpha = lightInfo.ambientShadowAlpha;
+ mSpotShadowAlpha = lightInfo.spotShadowAlpha;
+ mLightCenter = lightGeometry.center;
+ }
+protected:
+ renderthread::RenderThread& mRenderThread;
+
+private:
+ TaskManager mTaskManager;
+ static float mLightRadius;
+ static uint8_t mAmbientShadowAlpha;
+ static uint8_t mSpotShadowAlpha;
+ static Vector3 mLightCenter;
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
new file mode 100644
index 000000000000..520309a50f53
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -0,0 +1,144 @@
+/*
+ * 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 "SkiaVulkanPipeline.h"
+
+#include "DeferredLayerUpdater.h"
+#include "renderthread/EglManager.h" // needed for Frame
+#include "Readback.h"
+#include "renderstate/RenderState.h"
+
+#include <SkTypes.h>
+#include <WindowContextFactory_android.h>
+#include <VulkanWindowContext.h>
+
+#include <android/native_window.h>
+#include <cutils/properties.h>
+#include <strings.h>
+
+using namespace android::uirenderer::renderthread;
+using namespace sk_app;
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+MakeCurrentResult SkiaVulkanPipeline::makeCurrent() {
+ return (mWindowContext != nullptr) ?
+ MakeCurrentResult::AlreadyCurrent : MakeCurrentResult::Failed;
+}
+
+Frame SkiaVulkanPipeline::getFrame() {
+ LOG_ALWAYS_FATAL_IF(mWindowContext == nullptr, "Tried to draw into null vulkan context!");
+ mBackbuffer = mWindowContext->getBackbufferSurface();
+ if (mBackbuffer.get() == nullptr) {
+ // try recreating the context?
+ SkDebugf("failed to get backbuffer");
+ return Frame(-1, -1, 0);
+ }
+
+ // TODO: support buffer age if Vulkan API can do it
+ Frame frame(mBackbuffer->width(), mBackbuffer->height(), 0);
+ return frame;
+}
+
+bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty,
+ const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector<sp<RenderNode>>& renderNodes,
+ FrameInfoVisualizer* profiler) {
+
+ if (mBackbuffer.get() == nullptr) {
+ return false;
+ }
+ renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, mBackbuffer);
+ layerUpdateQueue->clear();
+ return true;
+}
+
+bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) {
+
+ *requireSwap = drew;
+
+ // Even if we decided to cancel the frame, from the perspective of jank
+ // metrics the frame was swapped at this point
+ currentFrameInfo->markSwapBuffers();
+
+ if (*requireSwap) {
+ mWindowContext->swapBuffers();
+ }
+
+ mBackbuffer.reset();
+
+ return *requireSwap;
+}
+
+bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
+ // TODO: implement copyLayerInto for vulkan.
+ return false;
+}
+
+DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
+ Layer* layer = new Layer(mRenderThread.renderState(), 0, 0);
+ return new DeferredLayerUpdater(layer);
+}
+
+void SkiaVulkanPipeline::onStop() {
+}
+
+bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
+
+ if (mWindowContext) {
+ delete mWindowContext;
+ mWindowContext = nullptr;
+ }
+
+ if (surface) {
+ DisplayParams displayParams;
+ mWindowContext = window_context_factory::NewVulkanForAndroid(surface, displayParams);
+ if (mWindowContext) {
+ DeviceInfo::initialize(mWindowContext->getGrContext()->caps()->maxRenderTargetSize());
+ }
+ }
+
+
+ // this doesn't work for if there is more than one CanvasContext available at one time!
+ mRenderThread.setGrContext(mWindowContext ? mWindowContext->getGrContext() : nullptr);
+
+ return mWindowContext != nullptr;
+}
+
+bool SkiaVulkanPipeline::isSurfaceReady() {
+ return CC_LIKELY(mWindowContext != nullptr) && mWindowContext->isValid();
+}
+
+bool SkiaVulkanPipeline::isContextReady() {
+ return CC_LIKELY(mWindowContext != nullptr);
+}
+
+void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
+ // TODO: we currently don't support OpenGL WebView's
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ (*functor)(mode, nullptr);
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
new file mode 100644
index 000000000000..cdc869245be3
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -0,0 +1,62 @@
+/*
+ * 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 "SkiaPipeline.h"
+#include <SkSurface.h>
+
+namespace sk_app {
+class WindowContext;
+}
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaVulkanPipeline : public SkiaPipeline {
+public:
+ SkiaVulkanPipeline(renderthread::RenderThread& thread) : SkiaPipeline(thread) {}
+ virtual ~SkiaVulkanPipeline() {}
+
+ renderthread::MakeCurrentResult makeCurrent() override;
+ renderthread::Frame getFrame() override;
+ bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector< sp<RenderNode> >& renderNodes,
+ FrameInfoVisualizer* profiler) override;
+ bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
+ FrameInfo* currentFrameInfo, bool* requireSwap) override;
+ bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
+ DeferredLayerUpdater* createTextureLayer() override;
+ bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override;
+ void onStop() override;
+ bool isSurfaceReady() override;
+ bool isContextReady() override;
+
+ static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
+
+private:
+ sk_app::WindowContext* mWindowContext = nullptr;
+ sk_sp<SkSurface> mBackbuffer;
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 03deb0ac213b..c561c86460c6 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -28,6 +28,9 @@
#include "renderstate/Stencil.h"
#include "protos/hwui.pb.h"
#include "OpenGLPipeline.h"
+#include "pipeline/skia/SkiaOpenGLPipeline.h"
+#include "pipeline/skia/SkiaPipeline.h"
+#include "pipeline/skia/SkiaVulkanPipeline.h"
#include "utils/GLUtils.h"
#include "utils/TimeUtils.h"
@@ -69,13 +72,11 @@ CanvasContext* CanvasContext::create(RenderThread& thread,
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
std::make_unique<OpenGLPipeline>(thread));
case RenderPipelineType::SkiaGL:
- //TODO: implement SKIA GL
- LOG_ALWAYS_FATAL("skiaGL canvas type not implemented.");
- break;
+ return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
+ std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
case RenderPipelineType::SkiaVulkan:
- //TODO: implement Vulkan
- LOG_ALWAYS_FATAL("Vulkan canvas type not implemented.");
- break;
+ return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
+ std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread));
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
break;
@@ -89,6 +90,10 @@ void CanvasContext::destroyLayer(RenderNode* node) {
case RenderPipelineType::OpenGL:
OpenGLPipeline::destroyLayer(node);
break;
+ case RenderPipelineType::SkiaGL:
+ case RenderPipelineType::SkiaVulkan:
+ skiapipeline::SkiaPipeline::destroyLayer(node);
+ break;
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
break;
@@ -102,6 +107,12 @@ void CanvasContext::invokeFunctor(const RenderThread& thread, Functor* functor)
case RenderPipelineType::OpenGL:
OpenGLPipeline::invokeFunctor(thread, functor);
break;
+ case RenderPipelineType::SkiaGL:
+ skiapipeline::SkiaOpenGLPipeline::invokeFunctor(thread, functor);
+ break;
+ case RenderPipelineType::SkiaVulkan:
+ skiapipeline::SkiaVulkanPipeline::invokeFunctor(thread, functor);
+ break;
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
break;
@@ -114,6 +125,10 @@ void CanvasContext::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
case RenderPipelineType::OpenGL:
OpenGLPipeline::prepareToDraw(thread, bitmap);
break;
+ case RenderPipelineType::SkiaGL:
+ case RenderPipelineType::SkiaVulkan:
+ skiapipeline::SkiaPipeline::prepareToDraw(thread, bitmap);
+ break;
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
break;
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 7349dcc9f041..b12522e6bd41 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -31,6 +31,11 @@ class EglManager;
class Frame {
public:
+ Frame(EGLint width, EGLint height, EGLint bufferAge)
+ : mWidth(width)
+ , mHeight(height)
+ , mBufferAge(bufferAge) { }
+
EGLint width() const { return mWidth; }
EGLint height() const { return mHeight; }
@@ -39,6 +44,7 @@ public:
EGLint bufferAge() const { return mBufferAge; }
private:
+ Frame() {}
friend class EglManager;
EGLSurface mSurface;
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 0be5a3b9d439..0ce598d4dba4 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -22,6 +22,7 @@
#include <Rect.h>
#include <RenderNode.h>
#include <hwui/Bitmap.h>
+#include <pipeline/skia/SkiaRecordingCanvas.h>
#include <renderstate/RenderState.h>
#include <renderthread/RenderThread.h>
#include <Snapshot.h>
@@ -196,6 +197,35 @@ public:
node.setStagingDisplayList(canvas->finishRecording(), nullptr);
}
+ static sp<RenderNode> createSkiaNode(int left, int top, int right, int bottom,
+ std::function<void(RenderProperties& props, skiapipeline::SkiaRecordingCanvas& canvas)> setup,
+ const char* name = nullptr, skiapipeline::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<skiapipeline::SkiaRecordingCanvas> canvas(
+ new skiapipeline::SkiaRecordingCanvas(nullptr,
+ props.getWidth(), props.getHeight()));
+ setup(props, *canvas.get());
+ node->setStagingDisplayList(canvas->finishRecording(), nullptr);
+ }
+ node->setPropertyFieldsDirty(0xFFFFFFFF);
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+ return node;
+ }
+
/**
* Forces a sync of a tree of RenderNode, such that every descendant will have its staging
* properties and DisplayList moved to the render copies.
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 19c311c7e182..c68ca4e74368 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -34,34 +34,6 @@ 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) {
@@ -86,7 +58,7 @@ TEST(RenderNodeDrawable, drawContent) {
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,
+ auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
[](RenderProperties& props, SkiaRecordingCanvas& recorder) {
recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
});
@@ -118,14 +90,14 @@ TEST(RenderNodeDrawable, drawAndReorder) {
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
//-z draws to all 4 pixels (RED)
- auto redNode = createSkiaNode(0, 0, 4, 4,
+ auto redNode = TestUtils::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,
+ auto bottomHalfGreenNode = TestUtils::createSkiaNode(0, 0, 4, 4,
[](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
SkPaint greenPaint;
greenPaint.setColor(SK_ColorGREEN);
@@ -135,7 +107,7 @@ TEST(RenderNodeDrawable, drawAndReorder) {
}, "bottomHalfGreenNode");
//+z draws to right 2 pixels (BLUE)
- auto rightHalfBlueNode = createSkiaNode(0, 0, 4, 4,
+ auto rightHalfBlueNode = TestUtils::createSkiaNode(0, 0, 4, 4,
[](RenderProperties& props, SkiaRecordingCanvas& rightHalfBlueCanvas) {
SkPaint bluePaint;
bluePaint.setColor(SK_ColorBLUE);
@@ -144,7 +116,7 @@ TEST(RenderNodeDrawable, drawAndReorder) {
props.setElevation(10.0f);
}, "rightHalfBlueNode");
- auto rootNode = createSkiaNode(0, 0, 4, 4,
+ auto rootNode = TestUtils::createSkiaNode(0, 0, 4, 4,
[&](RenderProperties& props, SkiaRecordingCanvas& rootRecorder) {
rootRecorder.insertReorderBarrier(true);
//draw in reverse Z order, so Z alters draw order
@@ -167,7 +139,7 @@ TEST(RenderNodeDrawable, composeOnLayer)
canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
- auto rootNode = createSkiaNode(0, 0, 1, 1,
+ auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
[](RenderProperties& props, SkiaRecordingCanvas& recorder) {
recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
});
@@ -176,7 +148,7 @@ TEST(RenderNodeDrawable, composeOnLayer)
auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
auto canvas2 = surfaceLayer->getCanvas();
canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
- rootNode->setLayerSurface( surfaceLayer );
+ rootNode->setLayerSurface(surfaceLayer);
RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
canvas.drawDrawable(&drawable1);
@@ -190,7 +162,7 @@ TEST(RenderNodeDrawable, composeOnLayer)
canvas.drawDrawable(&drawable3);
ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
- rootNode->setLayerSurface( sk_sp<SkSurface>() );
+ rootNode->setLayerSurface(sk_sp<SkSurface>());
}
//TODO: refactor to cover test cases from FrameBuilderTests_projectionReorder
@@ -203,18 +175,18 @@ TEST(RenderNodeDrawable, projectDraw) {
canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
- auto redNode = createSkiaNode(0, 0, 1, 1,
+ auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1,
[](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
}, "redNode");
- auto greenNodeWithRedChild = createSkiaNode(0, 0, 1, 1,
+ auto greenNodeWithRedChild = TestUtils::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,
+ auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
[&](RenderProperties& props, SkiaRecordingCanvas& rootCanvas) {
rootCanvas.drawRenderNode(greenNodeWithRedChild.get());
}, "rootNode");
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index 3f80d6ec6577..f32d97a3d809 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -93,7 +93,7 @@ TEST(SkiaBehavior, porterDuffCreateIsCached) {
}
TEST(SkiaBehavior, srgbColorSpaceIsSingleton) {
- sk_sp<SkColorSpace> sRGB1 = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
- sk_sp<SkColorSpace> sRGB2 = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
+ sk_sp<SkColorSpace> sRGB1 = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
+ sk_sp<SkColorSpace> sRGB2 = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
ASSERT_EQ(sRGB1.get(), sRGB2.get());
}
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
new file mode 100644
index 000000000000..7a2fa5792f67
--- /dev/null
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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 "pipeline/skia/SkiaOpenGLPipeline.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;
+
+RENDERTHREAD_TEST(SkiaPipeline, renderFrame) {
+ auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1,
+ [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+ redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+ });
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeLargest();
+ std::vector<sp<RenderNode>> renderNodes;
+ renderNodes.push_back(redNode);
+ bool opaque = true;
+ android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1);
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+ surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
+}
+
+RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckBounds) {
+ auto backdropRedNode = TestUtils::createSkiaNode(1, 1, 4, 4,
+ [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+ redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+ });
+ auto contentGreenNode = TestUtils::createSkiaNode(2, 2, 5, 5,
+ [](RenderProperties& props, SkiaRecordingCanvas& blueCanvas) {
+ blueCanvas.drawColor(SK_ColorGREEN, SkBlendMode::kSrcOver);
+ });
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeLargest();
+ std::vector<sp<RenderNode>> renderNodes;
+ renderNodes.push_back(backdropRedNode); //first node is backdrop
+ renderNodes.push_back(contentGreenNode); //second node is content drawn on top of the backdrop
+ android::uirenderer::Rect contentDrawBounds(1, 1, 3, 3);
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ auto surface = SkSurface::MakeRasterN32Premul(5, 5);
+ surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ //backdropBounds is (1, 1, 3, 3), content clip is (1, 1, 3, 3), content translate is (0, 0)
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
+ ASSERT_EQ(TestUtils::getColor(surface, 2, 2), SK_ColorGREEN);
+ ASSERT_EQ(TestUtils::getColor(surface, 3, 3), SK_ColorRED);
+ ASSERT_EQ(TestUtils::getColor(surface, 4, 4), SK_ColorBLUE);
+
+ surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ contentDrawBounds.set(0, 0, 5, 5);
+ //backdropBounds is (1, 1, 4, 4), content clip is (0, 0, 3, 3), content translate is (1, 1)
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
+ ASSERT_EQ(TestUtils::getColor(surface, 2, 2), SK_ColorRED);
+ ASSERT_EQ(TestUtils::getColor(surface, 3, 3), SK_ColorGREEN);
+ ASSERT_EQ(TestUtils::getColor(surface, 4, 4), SK_ColorBLUE);
+}
+
+RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckOpaque) {
+ auto halfGreenNode = TestUtils::createSkiaNode(0, 0, 2, 2,
+ [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
+ SkPaint greenPaint;
+ greenPaint.setColor(SK_ColorGREEN);
+ greenPaint.setStyle(SkPaint::kFill_Style);
+ bottomHalfGreenCanvas.drawRect(0, 1, 2, 2, greenPaint);
+ });
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeLargest();
+ std::vector<sp<RenderNode>> renderNodes;
+ renderNodes.push_back(halfGreenNode);
+ android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2);
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ auto surface = SkSurface::MakeRasterN32Premul(2, 2);
+ surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
+}
+
+RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
+ auto redNode = TestUtils::createSkiaNode(0, 0, 2, 2,
+ [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+ redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+ });
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeXYWH(0, 1, 2, 1);
+ std::vector<sp<RenderNode>> renderNodes;
+ renderNodes.push_back(redNode);
+ android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2);
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ auto surface = SkSurface::MakeRasterN32Premul(2, 2);
+ surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorRED);
+ ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
+}
+
+RENDERTHREAD_TEST(SkiaPipeline, renderLayer) {
+ auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1,
+ [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+ redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+ });
+ auto surfaceLayer1 = SkSurface::MakeRasterN32Premul(1, 1);
+ surfaceLayer1->getCanvas()->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorWHITE);
+ redNode->setLayerSurface(surfaceLayer1);
+
+ //create a 2nd 2x2 layer and add it to the queue as well.
+ //make the layer's dirty area one half of the layer and verify only the dirty half is updated.
+ auto blueNode = TestUtils::createSkiaNode(0, 0, 2, 2,
+ [](RenderProperties& props, SkiaRecordingCanvas& blueCanvas) {
+ blueCanvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ });
+ auto surfaceLayer2 = SkSurface::MakeRasterN32Premul(2, 2);
+ surfaceLayer2->getCanvas()->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorWHITE);
+ blueNode->setLayerSurface(surfaceLayer2);
+
+ //attach both layers to the update queue
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeLargest();
+ layerUpdateQueue.enqueueLayerWithDamage(redNode.get(), dirty);
+ layerUpdateQueue.enqueueLayerWithDamage(blueNode.get(), SkRect::MakeWH(2, 1));
+ ASSERT_EQ(layerUpdateQueue.entries().size(), 2UL);
+
+ bool opaque = true;
+ FrameBuilder::LightGeometry lightGeometry;
+ lightGeometry.radius = 1.0f;
+ lightGeometry.center = { 0.0f, 0.0f, 0.0f };
+ BakedOpRenderer::LightInfo lightInfo;
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, lightInfo);
+ ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorRED);
+ ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorBLUE);
+ ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 1), SK_ColorWHITE);
+ ASSERT_TRUE(layerUpdateQueue.entries().empty());
+ redNode->setLayerSurface(sk_sp<SkSurface>());
+ blueNode->setLayerSurface(sk_sp<SkSurface>());
+}
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index 624d20763384..fa3e13dd71ba 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -110,7 +110,7 @@ public:
}
bool capturePixels(SkBitmap* bmp) {
- sk_sp<SkColorSpace> colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
+ sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
SkImageInfo destinationConfig =
SkImageInfo::Make(mSize.width(), mSize.height(),
kRGBA_8888_SkColorType, kPremul_SkAlphaType, colorSpace);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4d59d574adc8..2adb26159622 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -415,6 +415,18 @@
android:launchMode="singleTop"
android:excludeFromRecents="true" />
+ <activity
+ android:name=".pip.phone.PipMenuActivity"
+ android:theme="@style/PipPhoneOverlayControlTheme"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:excludeFromRecents="true"
+ android:exported="false"
+ android:resizeableActivity="true"
+ android:supportsPictureInPicture="true"
+ android:stateNotNeeded="true"
+ android:taskAffinity=""
+ androidprv:alwaysFocusable="true" />
+
<!-- platform logo easter egg activity -->
<activity
android:name=".DessertCase"
diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml
new file mode 100644
index 000000000000..88e6e725c976
--- /dev/null
+++ b/packages/SystemUI/res/layout/pip_menu_activity.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#33000000">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center">
+ <Button
+ android:id="@+id/expand_pip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:textSize="14sp"
+ android:textColor="#ffffffff"
+ android:text="@string/pip_phone_expand"
+ android:fontFamily="sans-serif" />
+ </LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d8c8b82eee4a..37a7e38eb4f9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1675,6 +1675,9 @@
not appear on production builds ever. -->
<string name="tuner_doze_always_on" translatable="false">Always on</string>
+ <!-- Making the PIP fullscreen -->
+ <string name="pip_phone_expand">Expand</string>
+
<!-- PIP section of the tuner. Non-translatable since it should
not appear on production builds ever. -->
<string name="picture_in_picture" translatable="false">Picture-in-Picture</string>
@@ -1695,4 +1698,20 @@
not appear on production builds ever. -->
<string name="pip_drag_to_dismiss_summary" translatable="false">Drag to the dismiss target at the bottom of the screen to close the PIP</string>
+ <!-- PIP tap once to break through to the activity. Non-translatable since it should
+ not appear on production builds ever. -->
+ <string name="pip_tap_through_title" translatable="false">Tap to interact</string>
+
+ <!-- PIP tap once to break through to the activity. Non-translatable since it should
+ not appear on production builds ever. -->
+ <string name="pip_tap_through_summary" translatable="false">Tap once to interact with the activity</string>
+
+ <!-- PIP snap to closest edge. Non-translatable since it should
+ not appear on production builds ever. -->
+ <string name="pip_snap_mode_edge_title" translatable="false">Snap to closest edge</string>
+
+ <!-- PIP snap to closest edge. Non-translatable since it should
+ not appear on production builds ever. -->
+ <string name="pip_snap_mode_edge_summary" translatable="false">Snap to the closest edge</string>
+
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e5bc8b996cea..6661f076d848 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -56,6 +56,24 @@
<item name="android:activityCloseExitAnimation">@anim/forced_resizable_exit</item>
</style>
+ <style name="PipPhoneOverlayControlTheme" parent="@android:style/Theme.Material">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowBackground">@drawable/forced_resizable_background</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:statusBarColor">@color/transparent</item>
+ <item name="android:windowAnimationStyle">@style/Animation.PipPhoneOverlayControl</item>
+ </style>
+
+ <style name="Animation.PipPhoneOverlayControl" parent="@android:style/Animation">
+ <item name="android:activityOpenEnterAnimation">@anim/forced_resizable_enter</item>
+
+ <!-- If the target stack doesn't have focus, we do a task to front animation. -->
+ <item name="android:taskToFrontEnterAnimation">@anim/forced_resizable_enter</item>
+ <item name="android:activityCloseExitAnimation">@anim/forced_resizable_exit</item>
+ </style>
+
<style name="TextAppearance.StatusBar.HeadsUp"
parent="@*android:style/TextAppearance.StatusBar">
</style>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 942f8472b7ef..f09d6e9fe052 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -137,6 +137,18 @@
android:summary="@string/pip_drag_to_dismiss_summary"
sysui:defValue="true" />
+ <com.android.systemui.tuner.TunerSwitch
+ android:key="pip_tap_through"
+ android:title="@string/pip_tap_through_title"
+ android:summary="@string/pip_tap_through_summary"
+ sysui:defValue="false" />
+
+ <com.android.systemui.tuner.TunerSwitch
+ android:key="pip_snap_mode_edge"
+ android:title="@string/pip_snap_mode_edge_title"
+ android:summary="@string/pip_snap_mode_edge_summary"
+ sysui:defValue="false" />
+
</PreferenceScreen>
<PreferenceScreen
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index f9a4f7ced6cc..7b8d27eef8b4 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -34,6 +34,7 @@ public class PipManager {
private IActivityManager mActivityManager;
private IWindowManager mWindowManager;
+ private PipMenuActivityController mMenuController;
private PipTouchHandler mTouchHandler;
private PipManager() {}
@@ -46,7 +47,9 @@ public class PipManager {
mActivityManager = ActivityManagerNative.getDefault();
mWindowManager = WindowManagerGlobal.getWindowManagerService();
- mTouchHandler = new PipTouchHandler(context, mActivityManager, mWindowManager);
+ mMenuController = new PipMenuActivityController(context, mActivityManager, mWindowManager);
+ mTouchHandler = new PipTouchHandler(context, mMenuController, mActivityManager,
+ mWindowManager);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
new file mode 100644
index 000000000000..bfe5cff90a24
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -0,0 +1,137 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.pip.phone;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import com.android.systemui.R;
+
+/**
+ * Translucent activity that gets started on top of a task in PIP to allow the user to control it.
+ */
+public class PipMenuActivity extends Activity {
+
+ private static final String TAG = "PipMenuActivity";
+
+ public static final int MESSAGE_FINISH_SELF = 2;
+
+ private static final long INITIAL_DISMISS_DELAY = 2000;
+ private static final long POST_INTERACTION_DISMISS_DELAY = 1500;
+
+ private Messenger mToControllerMessenger;
+ private Messenger mMessenger = new Messenger(new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_FINISH_SELF:
+ finish();
+ break;
+ }
+ }
+ });
+
+ private final Runnable mFinishRunnable = new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ };
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent startingIntet = getIntent();
+ mToControllerMessenger = startingIntet.getParcelableExtra(
+ PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER);
+
+ setContentView(R.layout.pip_menu_activity);
+ findViewById(R.id.expand_pip).setOnClickListener((view) -> {
+ finish();
+ notifyExpandPip();
+ });
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ notifyActivityVisibility(true);
+ repostDelayedFinish(INITIAL_DISMISS_DELAY);
+ }
+
+ @Override
+ public void onUserInteraction() {
+ repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ finish();
+ }
+
+ @Override
+ public void finish() {
+ View v = getWindow().getDecorView();
+ v.removeCallbacks(mFinishRunnable);
+ notifyActivityVisibility(false);
+ super.finish();
+ overridePendingTransition(0, R.anim.forced_resizable_exit);
+ }
+
+ @Override
+ public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
+ // Do nothing
+ }
+
+ private void notifyActivityVisibility(boolean visible) {
+ Message m = Message.obtain();
+ m.what = PipMenuActivityController.MESSAGE_ACTIVITY_VISIBILITY_CHANGED;
+ m.arg1 = visible ? 1 : 0;
+ m.replyTo = visible ? mMessenger : null;
+ try {
+ mToControllerMessenger.send(m);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not notify controller of PIP menu visibility", e);
+ }
+ }
+
+ private void notifyExpandPip() {
+ Message m = Message.obtain();
+ m.what = PipMenuActivityController.MESSAGE_EXPAND_PIP;
+ try {
+ mToControllerMessenger.send(m);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not notify controller to expand PIP", e);
+ }
+ }
+
+ private void repostDelayedFinish(long delay) {
+ View v = getWindow().getDecorView();
+ v.removeCallbacks(mFinishRunnable);
+ v.postDelayed(mFinishRunnable, delay);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
new file mode 100644
index 000000000000..d1bce0c74ccd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -0,0 +1,126 @@
+package com.android.systemui.pip.phone;
+
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityOptions;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.IWindowManager;
+
+import java.util.ArrayList;
+
+public class PipMenuActivityController {
+
+ private static final String TAG = "PipMenuActivityController";
+
+ public static final String EXTRA_CONTROLLER_MESSENGER = "messenger";
+ public static final int MESSAGE_ACTIVITY_VISIBILITY_CHANGED = 1;
+ public static final int MESSAGE_EXPAND_PIP = 3;
+
+ /**
+ * A listener interface to receive notification on changes in PIP.
+ */
+ public interface Listener {
+ /**
+ * Called when the PIP menu visibility changes.
+ */
+ void onPipMenuVisibilityChanged(boolean visible);
+ }
+
+ private Context mContext;
+ private IActivityManager mActivityManager;
+ private IWindowManager mWindowManager;
+ private ArrayList<Listener> mListeners = new ArrayList<>();
+
+ private Messenger mToActivityMessenger;
+ private Messenger mMessenger = new Messenger(new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_ACTIVITY_VISIBILITY_CHANGED: {
+ boolean visible = msg.arg1 > 0;
+ int listenerCount = mListeners.size();
+ for (int i = 0; i < listenerCount; i++) {
+ mListeners.get(i).onPipMenuVisibilityChanged(visible);
+ }
+ mToActivityMessenger = msg.replyTo;
+ break;
+ }
+ case MESSAGE_EXPAND_PIP: {
+ try {
+ mActivityManager.resizeStack(PINNED_STACK_ID, null, true, true, true, 225);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error showing PIP menu activity", e);
+ }
+ break;
+ }
+ }
+ }
+ });
+
+ public PipMenuActivityController(Context context, IActivityManager activityManager,
+ IWindowManager windowManager) {
+ mContext = context;
+ mActivityManager = activityManager;
+ mWindowManager = windowManager;
+ }
+
+ /**
+ * Adds a new menu activity listener.
+ */
+ public void addListener(Listener listener) {
+ if (!mListeners.contains(listener)) {
+ mListeners.add(listener);
+ }
+ }
+
+ /**
+ * Shows the menu activity.
+ */
+ public void showMenu() {
+ // Start the menu activity on the top task of the pinned stack
+ try {
+ StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
+ pinnedStackInfo.taskIds.length > 0) {
+ Intent intent = new Intent(mContext, PipMenuActivity.class);
+ intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchTaskId(
+ pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
+ options.setTaskOverlay(true);
+ mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
+ } else {
+ Log.e(TAG, "No PIP tasks found");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error showing PIP menu activity", e);
+ }
+ }
+
+ /**
+ * Hides the menu activity.
+ */
+ public void hideMenu() {
+ if (mToActivityMessenger != null) {
+ Message m = Message.obtain();
+ m.what = PipMenuActivity.MESSAGE_FINISH_SELF;
+ try {
+ mToActivityMessenger.send(m);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not notify menu activity to finish", e);
+ }
+ mToActivityMessenger = null;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index e32022abdf38..a3593806fb1f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -32,6 +32,7 @@ import android.app.IActivityManager;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
@@ -61,6 +62,8 @@ public class PipTouchHandler implements TunerService.Tunable {
private static final String TUNER_KEY_SWIPE_TO_DISMISS = "pip_swipe_to_dismiss";
private static final String TUNER_KEY_DRAG_TO_DISMISS = "pip_drag_to_dismiss";
+ private static final String TUNER_KEY_TAP_THROUGH = "pip_tap_through";
+ private static final String TUNER_KEY_SNAP_MODE_EDGE = "pip_snap_mode_edge";
private static final int SNAP_STACK_DURATION = 225;
private static final int DISMISS_STACK_DURATION = 375;
@@ -70,17 +73,20 @@ public class PipTouchHandler implements TunerService.Tunable {
private final IActivityManager mActivityManager;
private final IWindowManager mWindowManager;
private final ViewConfiguration mViewConfig;
- private final InputChannel mInputChannel = new InputChannel();
private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
+ private final PipMenuListener mMenuListener = new PipMenuListener();
private IPinnedStackController mPinnedStackController;
- private final PipInputEventReceiver mInputEventReceiver;
+ private PipInputEventReceiver mInputEventReceiver;
+ private PipMenuActivityController mMenuController;
private PipDismissViewController mDismissViewController;
private final PipSnapAlgorithm mSnapAlgorithm;
private PipMotionHelper mMotionHelper;
private boolean mEnableSwipeToDismiss = true;
private boolean mEnableDragToDismiss = true;
+ private boolean mEnableTapThrough = false;
+ private boolean mEnableSnapToEdge = false;
private final Rect mPinnedStackBounds = new Rect();
private final Rect mBoundedPinnedStackBounds = new Rect();
@@ -97,6 +103,7 @@ public class PipTouchHandler implements TunerService.Tunable {
private final PointF mLastTouch = new PointF();
private boolean mIsDragging;
private boolean mIsSwipingToDismiss;
+ private boolean mIsTappingThrough;
private int mActivePointerId;
private final FlingAnimationUtils mFlingAnimationUtils;
@@ -120,7 +127,7 @@ public class PipTouchHandler implements TunerService.Tunable {
// To be implemented for input handling over Pip windows
if (event instanceof MotionEvent) {
MotionEvent ev = (MotionEvent) event;
- handleTouchEvent(ev);
+ handled = handleTouchEvent(ev);
}
} finally {
finishInputEvent(event, handled);
@@ -144,13 +151,26 @@ public class PipTouchHandler implements TunerService.Tunable {
}
}
- public PipTouchHandler(Context context, IActivityManager activityManager,
- IWindowManager windowManager) {
+ /**
+ * A listener for the PIP menu activity.
+ */
+ private class PipMenuListener implements PipMenuActivityController.Listener {
+ @Override
+ public void onPipMenuVisibilityChanged(boolean visible) {
+ if (!visible) {
+ mIsTappingThrough = false;
+ registerInputConsumer();
+ } else {
+ unregisterInputConsumer();
+ }
+ }
+ }
+
+ public PipTouchHandler(Context context, PipMenuActivityController menuController,
+ IActivityManager activityManager, IWindowManager windowManager) {
// Initialize the Pip input consumer
try {
- windowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
- windowManager.createInputConsumer(INPUT_CONSUMER_PIP, mInputChannel);
windowManager.registerPinnedStackListener(DEFAULT_DISPLAY, mPinnedStackListener);
} catch (RemoteException e) {
Log.e(TAG, "Failed to create PIP input consumer", e);
@@ -159,22 +179,29 @@ public class PipTouchHandler implements TunerService.Tunable {
mActivityManager = activityManager;
mWindowManager = windowManager;
mViewConfig = ViewConfiguration.get(context);
- mInputEventReceiver = new PipInputEventReceiver(mInputChannel, Looper.myLooper());
- if (mEnableDragToDismiss) {
- mDismissViewController = new PipDismissViewController(context);
- }
+ mMenuController = menuController;
+ mMenuController.addListener(mMenuListener);
+ mDismissViewController = new PipDismissViewController(context);
mSnapAlgorithm = new PipSnapAlgorithm(mContext);
mFlingAnimationUtils = new FlingAnimationUtils(context, 2f);
mMotionHelper = new PipMotionHelper(BackgroundThread.getHandler());
+ registerInputConsumer();
// Register any tuner settings changes
TunerService.get(context).addTunable(this, TUNER_KEY_SWIPE_TO_DISMISS,
- TUNER_KEY_DRAG_TO_DISMISS);
+ TUNER_KEY_DRAG_TO_DISMISS, TUNER_KEY_TAP_THROUGH, TUNER_KEY_SNAP_MODE_EDGE);
}
@Override
public void onTuningChanged(String key, String newValue) {
if (newValue == null) {
+ // Reset back to default
+ mEnableSwipeToDismiss = true;
+ mEnableDragToDismiss = true;
+ mEnableTapThrough = false;
+ mIsTappingThrough = false;
+ mEnableSnapToEdge = false;
+ setSnapToEdge(false);
return;
}
switch (key) {
@@ -184,6 +211,14 @@ public class PipTouchHandler implements TunerService.Tunable {
case TUNER_KEY_DRAG_TO_DISMISS:
mEnableDragToDismiss = Integer.parseInt(newValue) != 0;
break;
+ case TUNER_KEY_TAP_THROUGH:
+ mEnableTapThrough = Integer.parseInt(newValue) != 0;
+ mIsTappingThrough = false;
+ break;
+ case TUNER_KEY_SNAP_MODE_EDGE:
+ mEnableSnapToEdge = Integer.parseInt(newValue) != 0;
+ setSnapToEdge(mEnableSnapToEdge);
+ break;
}
}
@@ -192,10 +227,10 @@ public class PipTouchHandler implements TunerService.Tunable {
updateBoundedPinnedStackBounds(false /* updatePinnedStackBounds */);
}
- private void handleTouchEvent(MotionEvent ev) {
+ private boolean handleTouchEvent(MotionEvent ev) {
// Skip touch handling until we are bound to the controller
if (mPinnedStackController == null) {
- return;
+ return true;
}
switch (ev.getAction()) {
@@ -239,6 +274,8 @@ public class PipTouchHandler implements TunerService.Tunable {
float movement = PointF.length(mDownTouch.x - x, mDownTouch.y - y);
if (movement > mViewConfig.getScaledTouchSlop()) {
mIsDragging = true;
+ mIsTappingThrough = false;
+ mMenuController.hideMenu();
if (mEnableSwipeToDismiss) {
// TODO: this check can have some buffer so that we only start swiping
// after a significant move out of bounds
@@ -328,7 +365,14 @@ public class PipTouchHandler implements TunerService.Tunable {
}
}
} else {
- expandPinnedStackToFullscreen();
+ if (mEnableTapThrough) {
+ if (!mIsTappingThrough) {
+ mMenuController.showMenu();
+ mIsTappingThrough = true;
+ }
+ } else {
+ expandPinnedStackToFullscreen();
+ }
}
if (mEnableDragToDismiss) {
mDismissViewController.destroyDismissTarget();
@@ -348,6 +392,7 @@ public class PipTouchHandler implements TunerService.Tunable {
break;
}
}
+ return !mIsTappingThrough;
}
private void initOrResetVelocityTracker() {
@@ -366,6 +411,44 @@ public class PipTouchHandler implements TunerService.Tunable {
}
/**
+ * Registers the input consumer.
+ */
+ private void registerInputConsumer() {
+ final InputChannel inputChannel = new InputChannel();
+ try {
+ mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+ mWindowManager.createInputConsumer(INPUT_CONSUMER_PIP, inputChannel);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to create PIP input consumer", e);
+ }
+ mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
+ }
+
+ /**
+ * Unregisters the input consumer.
+ */
+ private void unregisterInputConsumer() {
+ try {
+ mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to destroy PIP input consumer", e);
+ }
+ mInputEventReceiver.dispose();
+ }
+
+ /**
+ * Sets the snap-to-edge state.
+ */
+ private void setSnapToEdge(boolean snapToEdge) {
+ mSnapAlgorithm.setSnapToEdge(snapToEdge);
+ try {
+ mPinnedStackController.setSnapToEdge(snapToEdge);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not set snap mode to edge", e);
+ }
+ }
+
+ /**
* Flings the PIP to the closest snap target.
*/
private void flingToSnapTarget(float velocity, float velocityX, float velocityY) {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 0cad814fe8b1..a2207b259123 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -585,6 +585,20 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.e(TAG, "Unable to remove tied profile key", e);
}
}
+
+ boolean isWatch = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WATCH);
+ // Wear used to set DISABLE_LOCKSCREEN to 'true', but because Wear now allows accounts
+ // and device management the lockscreen must be re-enabled now for users that upgrade.
+ if (isWatch && getString("migrated_wear_lockscreen_disabled", null, 0) == null) {
+ final int userCount = users.size();
+ for (int i = 0; i < userCount; i++) {
+ int id = users.get(i).id;
+ setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id);
+ }
+ setString("migrated_wear_lockscreen_disabled", "true", 0);
+ Slog.i(TAG, "Migrated lockscreen_disabled for Wear devices");
+ }
} catch (RemoteException re) {
Slog.e(TAG, "Unable to migrate old data", re);
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index faca8db3ab87..1ccf7229cc87 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -99,6 +99,13 @@ class PinnedStackController {
mInInteractiveMode = inInteractiveMode;
});
}
+
+ @Override
+ public void setSnapToEdge(final boolean snapToEdge) {
+ mHandler.post(() -> {
+ mSnapAlgorithm.setSnapToEdge(snapToEdge);
+ });
+ }
}
/**
diff --git a/services/tests/runtests.py b/services/tests/runtests.py
index 35fec90f6ac5..7980dc23e8e2 100755
--- a/services/tests/runtests.py
+++ b/services/tests/runtests.py
@@ -61,8 +61,8 @@ def main():
print 'Running tests...'
if len(sys.argv) != 1:
- run('adb shell am instrument -w "%s" %s' %
- (INSTRUMENTED_PACKAGE_RUNNER, ' '.join(sys.argv[1:])))
+ run('adb shell am instrument -w %s "%s"' %
+ (' '.join(sys.argv[1:]), INSTRUMENTED_PACKAGE_RUNNER))
return 0
# It would be nice if the activity manager accepted a list of packages, but
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 197884dcf85c..1efd2ed6446c 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -194,14 +194,14 @@ include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libaapt2_jni
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-LOCAL_MODULE_HOST_OS := darwin linux
+LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_CFLAGS := $(cFlags)
LOCAL_CFLAGS_darwin := $(cFlags_darwin)
LOCAL_CFLAGS_windows := $(cFlags_windows)
LOCAL_CPPFLAGS := $(cppFlags)
LOCAL_C_INCLUDES := $(protoIncludes)
LOCAL_SRC_FILES := $(toolSources) $(sourcesJni)
-LOCAL_STATIC_LIBRARIES := libaapt2 libnativehelper $(hostStaticLibs)
+LOCAL_STATIC_LIBRARIES := libaapt2 $(hostStaticLibs)
LOCAL_STATIC_LIBRARIES_windows := $(hostStaticLibs_windows)
LOCAL_LDLIBS := $(hostLdLibs)
LOCAL_LDLIBS_darwin := $(hostLdLibs_darwin)
diff --git a/tools/aapt2/jni/Aapt2.java b/tools/aapt2/jni/Aapt2.java
deleted file mode 100644
index aed23de92fba..000000000000
--- a/tools/aapt2/jni/Aapt2.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.android.tools.aapt2;
-
-import java.util.List;
-
-/**
- * {@code aapt2} JNI interface. To use the {@code aapt2} native interface, the
- * shared library must first be loaded and then a new instance of this class can
- * be used to access the library.
- */
-public class Aapt2 {
-
- /**
- * Invokes {@code aapt2} to perform resource compilation.
- *
- * @param arguments arguments for compilation (see {@code Compile.cpp})
- */
- public static void compile(List<String> arguments) {
- nativeCompile(arguments);
- }
-
- /**
- * Invokes {@code aapt2} to perform linking.
- *
- * @param arguments arguments for linking (see {@code Link.cpp})
- */
- public static void link(List<String> arguments) {
- nativeLink(arguments);
- }
-
- /**
- * JNI call.
- *
- * @param arguments arguments for compilation (see {@code Compile.cpp})
- */
- private static native void nativeCompile(List<String> arguments);
-
- /**
- * JNI call.
- *
- * @param arguments arguments for linking (see {@code Link.cpp})
- */
- private static native void nativeLink(List<String> arguments);
-}
-
diff --git a/tools/aapt2/jni/Makefile b/tools/aapt2/jni/Makefile
deleted file mode 100644
index a9e628f45ce5..000000000000
--- a/tools/aapt2/jni/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# This Makefile will generate the JNI headers for the Aapt2 class.
-#
-
-AAPT2_PKG=com.android.tools.aapt2
-AAPT2_DIR=$(shell echo -n com/android/tools/aapt2 | tr . /)
-OUT=out
-OUT_CLASSES=$(OUT)/classes
-OUT_HEADERS=.
-
-AAPT2_JAVA=Aapt2.java
-AAPT2_CLASSES=$(OUT_CLASSES)/$(AAPT2_DIR)/Aapt2.class
-
-AAPT2_HEADERS=$(OUT_HEADERS)/Aapt2.h
-
-all: $(AAPT2_HEADERS)
-
-$(AAPT2_HEADERS): $(AAPT2_JAVA) $(AAPT2_CLASSES)
- mkdir -p $(OUT_HEADERS)
- $(JAVA_HOME)/bin/javah -d $(OUT_HEADERS) -cp $(OUT_CLASSES) $(AAPT2_PKG).Aapt2
-
-$(AAPT2_CLASSES): $(AAPT2_JAVA)
- mkdir -p $(OUT_CLASSES)
- javac -d $(OUT_CLASSES) $(AAPT2_JAVA)
-
diff --git a/tools/aapt2/jni/ScopedUtfChars.h b/tools/aapt2/jni/ScopedUtfChars.h
new file mode 100644
index 000000000000..a8c4b136dcf6
--- /dev/null
+++ b/tools/aapt2/jni/ScopedUtfChars.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef SCOPED_UTF_CHARS_H_included
+#define SCOPED_UTF_CHARS_H_included
+
+#include <string.h>
+#include <jni.h>
+
+#include "android-base/logging.h"
+
+// This file was copied with some minor modifications from libnativehelper.
+// As soon as libnativehelper can be compiled for Windows, this file should be
+// replaced with libnativehelper's implementation.
+class ScopedUtfChars {
+ public:
+ ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
+ CHECK(s != nullptr);
+ utf_chars_ = env->GetStringUTFChars(s, nullptr);
+ }
+
+ ScopedUtfChars(ScopedUtfChars&& rhs) :
+ env_(rhs.env_), string_(rhs.string_), utf_chars_(rhs.utf_chars_) {
+ rhs.env_ = nullptr;
+ rhs.string_ = nullptr;
+ rhs.utf_chars_ = nullptr;
+ }
+
+ ~ScopedUtfChars() {
+ if (utf_chars_) {
+ env_->ReleaseStringUTFChars(string_, utf_chars_);
+ }
+ }
+
+ ScopedUtfChars& operator=(ScopedUtfChars&& rhs) {
+ if (this != &rhs) {
+ // Delete the currently owned UTF chars.
+ this->~ScopedUtfChars();
+
+ // Move the rhs ScopedUtfChars and zero it out.
+ env_ = rhs.env_;
+ string_ = rhs.string_;
+ utf_chars_ = rhs.utf_chars_;
+ rhs.env_ = nullptr;
+ rhs.string_ = nullptr;
+ rhs.utf_chars_ = nullptr;
+ }
+ return *this;
+ }
+
+ const char* c_str() const {
+ return utf_chars_;
+ }
+
+ size_t size() const {
+ return strlen(utf_chars_);
+ }
+
+ const char& operator[](size_t n) const {
+ return utf_chars_[n];
+ }
+
+ private:
+ JNIEnv* env_;
+ jstring string_;
+ const char* utf_chars_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedUtfChars);
+};
+
+#endif // SCOPED_UTF_CHARS_H_included
diff --git a/tools/aapt2/jni/aapt2_jni.cpp b/tools/aapt2/jni/aapt2_jni.cpp
index 211ada8db078..24e83609c35a 100644
--- a/tools/aapt2/jni/aapt2_jni.cpp
+++ b/tools/aapt2/jni/aapt2_jni.cpp
@@ -22,7 +22,7 @@
#include <vector>
#include "android-base/logging.h"
-#include "nativehelper/ScopedUtfChars.h"
+#include "ScopedUtfChars.h"
#include "util/Util.h"
@@ -46,8 +46,8 @@ static std::vector<ScopedUtfChars> list_to_utfchars(JNIEnv *env, jobject obj) {
// Now, iterate all strings in the list
// (note: generic erasure means get() return an Object)
- jmethodID get_method_id =
- env->GetMethodID(list_cls, "get", "()Ljava/lang/Object;");
+ jmethodID get_method_id = env->GetMethodID(list_cls, "get", "(I)Ljava/lang/Object;");
+ CHECK(get_method_id != 0);
for (jint i = 0; i < size; i++) {
// Call get(i) to get the string in the ith position.
jobject string_obj_uncast = env->CallObjectMethod(obj, get_method_id, i);
@@ -92,3 +92,8 @@ JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2_nativeLink(
std::vector<aapt::StringPiece> link_args = extract_pieces(link_args_jni);
aapt::Link(link_args);
}
+
+JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2_ping(
+ JNIEnv *env, jclass aapt_obj) {
+ // This is just a dummy method to see if the library has been loaded.
+}
diff --git a/tools/aapt2/jni/com_android_tools_aapt2_Aapt2.h b/tools/aapt2/jni/com_android_tools_aapt2_Aapt2.h
index 443b98f2590d..26dc52db8695 100644
--- a/tools/aapt2/jni/com_android_tools_aapt2_Aapt2.h
+++ b/tools/aapt2/jni/com_android_tools_aapt2_Aapt2.h
@@ -9,6 +9,14 @@ extern "C" {
#endif
/*
* Class: com_android_tools_aapt2_Aapt2
+ * Method: ping
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2_ping
+ (JNIEnv *, jclass);
+
+/*
+ * Class: com_android_tools_aapt2_Aapt2
* Method: nativeCompile
* Signature: (Ljava/util/List;)V
*/