Move android.graphics JNI & APEX files into HWUI

The graphics JNI code is now separate from libandroid_runtime
and it along with HWUI headers are no longer visible to targets
outside the boundary of what is to become the UI mainline module

The exposed headers to targets outside the module are now restriced
to C APIs contained in the apex header directory.

Bug: 137655431
Test: CtsUiRenderingTestCases
Change-Id: I30d34055b6870dc1039f190a88f4a747cee17300
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 81dedda..954e4da 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -152,9 +152,229 @@
 }
 
 // ------------------------
+// APEX
+// ------------------------
+
+cc_library_headers {
+    name: "android_graphics_apex_headers",
+
+    host_supported: true,
+    export_include_dirs: [
+        "apex/include",
+    ],
+    target: {
+        windows: {
+            enabled: true,
+        },
+    }
+}
+
+cc_defaults {
+    name: "android_graphics_apex",
+    host_supported: true,
+    cflags: [
+        "-Wno-unused-parameter",
+        "-Wno-non-virtual-dtor",
+        "-Wno-maybe-uninitialized",
+        "-Wno-parentheses",
+        "-Wall",
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+
+    cppflags: ["-Wno-conversion-null"],
+
+    srcs: [
+        "apex/android_matrix.cpp",
+        "apex/android_paint.cpp",
+        "apex/android_region.cpp",
+    ],
+
+    header_libs: [ "android_graphics_apex_headers" ],
+
+    target: {
+        android: {
+            srcs: [ // sources that depend on android only libraries
+                "apex/android_bitmap.cpp",
+                "apex/android_canvas.cpp",
+                "apex/jni_runtime.cpp",
+                "apex/renderthread.cpp",
+            ],
+        },
+    },
+}
+
+// ------------------------
+// Android Graphics JNI
+// ------------------------
+
+cc_library_headers {
+    name: "android_graphics_jni_headers",
+
+    host_supported: true,
+    export_include_dirs: [
+        "jni",
+    ],
+    target: {
+        windows: {
+            enabled: true,
+        },
+    }
+}
+
+cc_defaults {
+    name: "android_graphics_jni",
+    host_supported: true,
+    cflags: [
+        "-Wno-unused-parameter",
+        "-Wno-non-virtual-dtor",
+        "-Wno-maybe-uninitialized",
+        "-Wno-parentheses",
+
+        "-DGL_GLEXT_PROTOTYPES",
+        "-DEGL_EGLEXT_PROTOTYPES",
+
+        "-DU_USING_ICU_NAMESPACE=0",
+
+        "-Wall",
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+
+    cppflags: ["-Wno-conversion-null"],
+
+    srcs: [
+        "jni/android_graphics_animation_NativeInterpolatorFactory.cpp",
+        "jni/android_graphics_animation_RenderNodeAnimator.cpp",
+        "jni/android_graphics_Canvas.cpp",
+        "jni/android_graphics_ColorSpace.cpp",
+        "jni/android_graphics_drawable_AnimatedVectorDrawable.cpp",
+        "jni/android_graphics_drawable_VectorDrawable.cpp",
+        "jni/android_graphics_HardwareRendererObserver.cpp",
+        "jni/android_graphics_Matrix.cpp",
+        "jni/android_graphics_Picture.cpp",
+        "jni/android_graphics_DisplayListCanvas.cpp",
+        "jni/android_graphics_RenderNode.cpp",
+        "jni/android_nio_utils.cpp",
+        "jni/android_util_PathParser.cpp",
+
+        "jni/Bitmap.cpp",
+        "jni/BitmapFactory.cpp",
+        "jni/ByteBufferStreamAdaptor.cpp",
+        "jni/Camera.cpp",
+        "jni/CanvasProperty.cpp",
+        "jni/ColorFilter.cpp",
+        "jni/CreateJavaOutputStreamAdaptor.cpp",
+        "jni/FontFamily.cpp",
+        "jni/FontUtils.cpp",
+        "jni/Graphics.cpp",
+        "jni/ImageDecoder.cpp",
+        "jni/Interpolator.cpp",
+        "jni/MaskFilter.cpp",
+        "jni/NinePatch.cpp",
+        "jni/NinePatchPeeker.cpp",
+        "jni/Paint.cpp",
+        "jni/PaintFilter.cpp",
+        "jni/Path.cpp",
+        "jni/PathEffect.cpp",
+        "jni/PathMeasure.cpp",
+        "jni/Picture.cpp",
+        "jni/Region.cpp",
+        "jni/Shader.cpp",
+        "jni/Typeface.cpp",
+        "jni/Utils.cpp",
+        "jni/YuvToJpegEncoder.cpp",
+        "jni/fonts/Font.cpp",
+        "jni/fonts/FontFamily.cpp",
+        "jni/text/LineBreaker.cpp",
+        "jni/text/MeasuredText.cpp",
+    ],
+
+    header_libs: [ "android_graphics_jni_headers" ],
+
+    include_dirs: [
+        "external/skia/include/private",
+        "external/skia/src/codec",
+        "external/skia/src/core",
+        "external/skia/src/effects",
+        "external/skia/src/image",
+        "external/skia/src/images",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libharfbuzz_ng",
+        "liblog",
+        "libminikin",
+        "libnativehelper",
+        "libz",
+        "libziparchive",
+        "libjpeg",
+    ],
+
+    target: {
+        android: {
+            srcs: [ // sources that depend on android only libraries
+                "jni/AnimatedImageDrawable.cpp",
+                "jni/android_graphics_TextureLayer.cpp",
+                "jni/android_graphics_HardwareRenderer.cpp",
+                "jni/BitmapRegionDecoder.cpp",
+                "jni/GIFMovie.cpp",
+                "jni/GraphicsStatsService.cpp",
+                "jni/Movie.cpp",
+                "jni/MovieImpl.cpp",
+                "jni/pdf/PdfDocument.cpp",
+                "jni/pdf/PdfEditor.cpp",
+                "jni/pdf/PdfRenderer.cpp",
+                "jni/pdf/PdfUtils.cpp",
+            ],
+            shared_libs: [
+                "libandroidfw",
+                "libmediandk",
+                "libnativedisplay",
+                "libnativewindow",
+                "libstatspull",
+                "libstatssocket",
+                "libpdfium",
+            ],
+            static_libs: [
+                "libgif",
+                "libstatslog",
+            ],
+        },
+        host: {
+            cflags: [
+                "-Wno-unused-const-variable",
+                "-Wno-unused-function",
+            ],
+            static_libs: [
+                "libandroidfw",
+            ],
+        }
+    },
+}
+
+// ------------------------
 // library
 // ------------------------
 
+cc_library_headers {
+    name: "libhwui_internal_headers",
+
+    host_supported: true,
+    export_include_dirs: [
+        ".",
+    ],
+    header_libs: [ "android_graphics_jni_headers" ],
+    export_header_lib_headers: [ "android_graphics_jni_headers" ],
+}
+
 cc_defaults {
     name: "libhwui_defaults",
     defaults: ["hwui_defaults"],
@@ -205,11 +425,8 @@
         export_proto_headers: true,
     },
 
-    export_include_dirs: ["."],
-
     target: {
         android: {
-
             srcs: [
                 "hwui/AnimatedImageThread.cpp",
                 "pipeline/skia/ATraceMemoryDump.cpp",
@@ -274,7 +491,10 @@
     host_supported: true,
     defaults: [
         "libhwui_defaults",
+        "android_graphics_apex",
+        "android_graphics_jni",
     ],
+    export_header_lib_headers: ["android_graphics_apex_headers"],
 }
 
 cc_library_static {
diff --git a/libs/hwui/apex/TypeCast.h b/libs/hwui/apex/TypeCast.h
new file mode 100644
index 0000000..96721d0
--- /dev/null
+++ b/libs/hwui/apex/TypeCast.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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 ANDROID_GRAPHICS_TYPECAST_H
+#define ANDROID_GRAPHICS_TYPECAST_H
+
+struct ABitmap;
+struct ACanvas;
+struct APaint;
+
+namespace android {
+
+    class Bitmap;
+    class Canvas;
+    class Paint;
+
+    class TypeCast {
+    public:
+        static inline Bitmap& toBitmapRef(const ABitmap* bitmap) {
+            return const_cast<Bitmap&>(reinterpret_cast<const Bitmap&>(*bitmap));
+        }
+
+        static inline Bitmap* toBitmap(ABitmap* bitmap) {
+            return reinterpret_cast<Bitmap*>(bitmap);
+        }
+
+        static inline ABitmap* toABitmap(Bitmap* bitmap) {
+            return reinterpret_cast<ABitmap*>(bitmap);
+        }
+
+        static inline Canvas* toCanvas(ACanvas* canvas) {
+            return reinterpret_cast<Canvas*>(canvas);
+        }
+
+        static inline ACanvas* toACanvas(Canvas* canvas) {
+            return reinterpret_cast<ACanvas *>(canvas);
+        }
+
+        static inline const Paint& toPaintRef(const APaint* paint) {
+            return reinterpret_cast<const Paint&>(*paint);
+        }
+
+        static inline const Paint* toPaint(const APaint* paint) {
+            return reinterpret_cast<const Paint*>(paint);
+        }
+
+        static inline Paint* toPaint(APaint* paint) {
+            return reinterpret_cast<Paint*>(paint);
+        }
+
+        static inline APaint* toAPaint(Paint* paint) {
+            return reinterpret_cast<APaint*>(paint);
+        }
+    };
+}; // namespace android
+
+#endif // ANDROID_GRAPHICS_TYPECAST_H
diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp
new file mode 100644
index 0000000..decd190
--- /dev/null
+++ b/libs/hwui/apex/android_bitmap.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "Bitmap"
+#include <log/log.h>
+
+#include "android/graphics/bitmap.h"
+#include "TypeCast.h"
+#include "GraphicsJNI.h"
+
+#include <GraphicsJNI.h>
+#include <hwui/Bitmap.h>
+#include <utils/Color.h>
+
+using namespace android;
+
+ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj) {
+    Bitmap* bitmap = GraphicsJNI::getNativeBitmap(env, bitmapObj);
+    if (bitmap) {
+        bitmap->ref();
+        return TypeCast::toABitmap(bitmap);
+    }
+    return nullptr;
+}
+
+void ABitmap_acquireRef(ABitmap* bitmap) {
+    SkSafeRef(TypeCast::toBitmap(bitmap));
+}
+
+void ABitmap_releaseRef(ABitmap* bitmap) {
+    SkSafeUnref(TypeCast::toBitmap(bitmap));
+}
+
+static AndroidBitmapFormat getFormat(const SkImageInfo& info) {
+    switch (info.colorType()) {
+        case kN32_SkColorType:
+            return ANDROID_BITMAP_FORMAT_RGBA_8888;
+        case kRGB_565_SkColorType:
+            return ANDROID_BITMAP_FORMAT_RGB_565;
+        case kARGB_4444_SkColorType:
+            return ANDROID_BITMAP_FORMAT_RGBA_4444;
+        case kAlpha_8_SkColorType:
+            return ANDROID_BITMAP_FORMAT_A_8;
+        case kRGBA_F16_SkColorType:
+            return ANDROID_BITMAP_FORMAT_RGBA_F16;
+        default:
+            return ANDROID_BITMAP_FORMAT_NONE;
+    }
+}
+
+static SkColorType getColorType(AndroidBitmapFormat format) {
+    switch (format) {
+        case ANDROID_BITMAP_FORMAT_RGBA_8888:
+            return kN32_SkColorType;
+        case ANDROID_BITMAP_FORMAT_RGB_565:
+            return kRGB_565_SkColorType;
+        case ANDROID_BITMAP_FORMAT_RGBA_4444:
+            return kARGB_4444_SkColorType;
+        case ANDROID_BITMAP_FORMAT_A_8:
+            return kAlpha_8_SkColorType;
+        case ANDROID_BITMAP_FORMAT_RGBA_F16:
+            return kRGBA_F16_SkColorType;
+        default:
+            return kUnknown_SkColorType;
+    }
+}
+
+static uint32_t getAlphaFlags(const SkImageInfo& info) {
+    switch (info.alphaType()) {
+        case kUnknown_SkAlphaType:
+            LOG_ALWAYS_FATAL("Bitmap has no alpha type");
+            break;
+        case kOpaque_SkAlphaType:
+            return ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE;
+        case kPremul_SkAlphaType:
+            return ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
+        case kUnpremul_SkAlphaType:
+            return ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL;
+    }
+}
+
+static uint32_t getInfoFlags(const SkImageInfo& info, bool isHardware) {
+    uint32_t flags = getAlphaFlags(info);
+    if (isHardware) {
+        flags |= ANDROID_BITMAP_FLAGS_IS_HARDWARE;
+    }
+    return flags;
+}
+
+ABitmap* ABitmap_copy(ABitmap* srcBitmapHandle, AndroidBitmapFormat dstFormat) {
+    SkColorType dstColorType = getColorType(dstFormat);
+    if (srcBitmapHandle && dstColorType != kUnknown_SkColorType) {
+        SkBitmap srcBitmap;
+        TypeCast::toBitmap(srcBitmapHandle)->getSkBitmap(&srcBitmap);
+
+        sk_sp<Bitmap> dstBitmap =
+                Bitmap::allocateHeapBitmap(srcBitmap.info().makeColorType(dstColorType));
+        if (dstBitmap && srcBitmap.readPixels(dstBitmap->info(), dstBitmap->pixels(),
+                                              dstBitmap->rowBytes(), 0, 0)) {
+            return TypeCast::toABitmap(dstBitmap.release());
+        }
+    }
+    return nullptr;
+}
+
+static AndroidBitmapInfo getInfo(const SkImageInfo& imageInfo, uint32_t rowBytes, bool isHardware) {
+    AndroidBitmapInfo info;
+    info.width = imageInfo.width();
+    info.height = imageInfo.height();
+    info.stride = rowBytes;
+    info.format = getFormat(imageInfo);
+    info.flags = getInfoFlags(imageInfo, isHardware);
+    return info;
+}
+
+AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmapHandle) {
+    Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
+    return getInfo(bitmap->info(), bitmap->rowBytes(), bitmap->isHardware());
+}
+
+ADataSpace ABitmap_getDataSpace(ABitmap* bitmapHandle) {
+    Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
+    const SkImageInfo& info = bitmap->info();
+    return (ADataSpace)uirenderer::ColorSpaceToADataSpace(info.colorSpace(), info.colorType());
+}
+
+AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj) {
+    uint32_t rowBytes = 0;
+    bool isHardware = false;
+    SkImageInfo imageInfo = GraphicsJNI::getBitmapInfo(env, bitmapObj, &rowBytes, &isHardware);
+    return getInfo(imageInfo, rowBytes, isHardware);
+}
+
+void* ABitmap_getPixels(ABitmap* bitmapHandle) {
+    Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
+    if (bitmap->isHardware()) {
+        return nullptr;
+    }
+    return bitmap->pixels();
+}
+
+AndroidBitmapFormat ABitmapConfig_getFormatFromConfig(JNIEnv* env, jobject bitmapConfigObj) {
+    return GraphicsJNI::getFormatFromConfig(env, bitmapConfigObj);
+}
+
+jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format) {
+    return GraphicsJNI::getConfigFromFormat(env, format);
+}
+
+void ABitmap_notifyPixelsChanged(ABitmap* bitmapHandle) {
+    Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
+    if (bitmap->isImmutable()) {
+        ALOGE("Attempting to modify an immutable Bitmap!");
+    }
+    return bitmap->notifyPixelsChanged();
+}
+
+namespace {
+SkAlphaType getAlphaType(const AndroidBitmapInfo* info) {
+    switch (info->flags & ANDROID_BITMAP_FLAGS_ALPHA_MASK) {
+        case ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE:
+            return kOpaque_SkAlphaType;
+        case ANDROID_BITMAP_FLAGS_ALPHA_PREMUL:
+            return kPremul_SkAlphaType;
+        case ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL:
+            return kUnpremul_SkAlphaType;
+        default:
+            return kUnknown_SkAlphaType;
+    }
+}
+
+class CompressWriter : public SkWStream {
+public:
+    CompressWriter(void* userContext, AndroidBitmap_CompressWriteFunc fn)
+          : mUserContext(userContext), mFn(fn), mBytesWritten(0) {}
+
+    bool write(const void* buffer, size_t size) override {
+        if (mFn(mUserContext, buffer, size)) {
+            mBytesWritten += size;
+            return true;
+        }
+        return false;
+    }
+
+    size_t bytesWritten() const override { return mBytesWritten; }
+
+private:
+    void* mUserContext;
+    AndroidBitmap_CompressWriteFunc mFn;
+    size_t mBytesWritten;
+};
+
+} // anonymous namespace
+
+int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels,
+                     AndroidBitmapCompressFormat inFormat, int32_t quality, void* userContext,
+                     AndroidBitmap_CompressWriteFunc fn) {
+    Bitmap::JavaCompressFormat format;
+    switch (inFormat) {
+        case ANDROID_BITMAP_COMPRESS_FORMAT_JPEG:
+            format = Bitmap::JavaCompressFormat::Jpeg;
+            break;
+        case ANDROID_BITMAP_COMPRESS_FORMAT_PNG:
+            format = Bitmap::JavaCompressFormat::Png;
+            break;
+        case ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY:
+            format = Bitmap::JavaCompressFormat::WebpLossy;
+            break;
+        case ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS:
+            format = Bitmap::JavaCompressFormat::WebpLossless;
+            break;
+        default:
+            // kWEBP_JavaEncodeFormat is a valid parameter for Bitmap::compress,
+            // for the deprecated Bitmap.CompressFormat.WEBP, but it should not
+            // be provided via the NDK. Other integers are likewise invalid.
+            return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+    }
+
+    SkColorType colorType;
+    switch (info->format) {
+        case ANDROID_BITMAP_FORMAT_RGBA_8888:
+            colorType = kN32_SkColorType;
+            break;
+        case ANDROID_BITMAP_FORMAT_RGB_565:
+            colorType = kRGB_565_SkColorType;
+            break;
+        case ANDROID_BITMAP_FORMAT_A_8:
+            // FIXME b/146637821: Should this encode as grayscale? We should
+            // make the same decision as for encoding an android.graphics.Bitmap.
+            // Note that encoding kAlpha_8 as WebP or JPEG will fail. Encoding
+            // it to PNG encodes as GRAY+ALPHA with a secret handshake that we
+            // only care about the alpha. I'm not sure whether Android decoding
+            // APIs respect that handshake.
+            colorType = kAlpha_8_SkColorType;
+            break;
+        case ANDROID_BITMAP_FORMAT_RGBA_F16:
+            colorType = kRGBA_F16_SkColorType;
+            break;
+        default:
+            return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+    }
+
+    auto alphaType = getAlphaType(info);
+    if (alphaType == kUnknown_SkAlphaType) {
+        return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+    }
+
+    sk_sp<SkColorSpace> cs;
+    if (info->format == ANDROID_BITMAP_FORMAT_A_8) {
+        // FIXME: A Java Bitmap with ALPHA_8 never has a ColorSpace. So should
+        // we force that here (as I'm doing now) or should we treat anything
+        // besides ADATASPACE_UNKNOWN as an error?
+        cs = nullptr;
+    } else {
+        cs = uirenderer::DataSpaceToColorSpace((android_dataspace) dataSpace);
+        // DataSpaceToColorSpace treats UNKNOWN as SRGB, but compress forces the
+        // client to specify SRGB if that is what they want.
+        if (!cs || dataSpace == ADATASPACE_UNKNOWN) {
+            return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+        }
+    }
+
+    {
+        size_t size;
+        if (!Bitmap::computeAllocationSize(info->stride, info->height, &size)) {
+            return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+        }
+    }
+
+    auto imageInfo =
+            SkImageInfo::Make(info->width, info->height, colorType, alphaType, std::move(cs));
+    SkBitmap bitmap;
+    // We are not going to modify the pixels, but installPixels expects them to
+    // not be const, since for all it knows we might want to draw to the SkBitmap.
+    if (!bitmap.installPixels(imageInfo, const_cast<void*>(pixels), info->stride)) {
+        return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+    }
+
+    CompressWriter stream(userContext, fn);
+    return Bitmap::compress(bitmap, format, quality, &stream) ? ANDROID_BITMAP_RESULT_SUCCESS
+                                                              : ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
+}
+
+AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmapHandle) {
+    Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
+    AHardwareBuffer* buffer = bitmap->hardwareBuffer();
+    if (buffer) {
+        AHardwareBuffer_acquire(buffer);
+    }
+    return buffer;
+}
diff --git a/libs/hwui/apex/android_canvas.cpp b/libs/hwui/apex/android_canvas.cpp
new file mode 100644
index 0000000..2a939ef
--- /dev/null
+++ b/libs/hwui/apex/android_canvas.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2019 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 "android/graphics/canvas.h"
+
+#include "TypeCast.h"
+#include "GraphicsJNI.h"
+
+#include <hwui/Canvas.h>
+#include <utils/Color.h>
+
+#include <SkBitmap.h>
+#include <SkSurface.h>
+
+using namespace android;
+
+/*
+ * Converts a buffer and dataspace into an SkBitmap only if the resulting bitmap can be treated as a
+ * rendering destination for a Canvas.  If the buffer is null or the format is one that we cannot
+ * render into with a Canvas then false is returned and the outBitmap param is unmodified.
+ */
+static bool convert(const ANativeWindow_Buffer* buffer,
+                    int32_t /*android_dataspace_t*/ dataspace,
+                    SkBitmap* outBitmap) {
+    if (buffer == nullptr) {
+        return false;
+    }
+
+    sk_sp<SkColorSpace> cs(uirenderer::DataSpaceToColorSpace((android_dataspace)dataspace));
+    SkImageInfo imageInfo = uirenderer::ANativeWindowToImageInfo(*buffer, cs);
+    size_t rowBytes = buffer->stride * imageInfo.bytesPerPixel();
+
+    // If SkSurface::MakeRasterDirect fails then we should as well as we will not be able to
+    // draw into the canvas.
+    sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect(imageInfo, buffer->bits, rowBytes);
+    if (surface.get() != nullptr) {
+        if (outBitmap) {
+            outBitmap->setInfo(imageInfo, rowBytes);
+            outBitmap->setPixels(buffer->bits);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat) {
+    char pixels[8];
+    ANativeWindow_Buffer buffer { 1, 1, 1, bufferFormat, pixels, {0} };
+    return convert(&buffer, HAL_DATASPACE_UNKNOWN, nullptr);
+}
+
+ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvasObj) {
+    return TypeCast::toACanvas(GraphicsJNI::getNativeCanvas(env, canvasObj));
+}
+
+ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer,
+                              int32_t /*android_dataspace_t*/ dataspace) {
+    SkBitmap bitmap;
+    bool isValidBuffer = convert(buffer, dataspace, &bitmap);
+    return isValidBuffer ? TypeCast::toACanvas(Canvas::create_canvas(bitmap)) : nullptr;
+}
+
+void ACanvas_destroyCanvas(ACanvas* canvas) {
+    delete TypeCast::toCanvas(canvas);
+}
+
+bool ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
+                       int32_t /*android_dataspace_t*/ dataspace) {
+    SkBitmap bitmap;
+    bool isValidBuffer = (buffer == nullptr) ? false : convert(buffer, dataspace, &bitmap);
+    TypeCast::toCanvas(canvas)->setBitmap(bitmap);
+    return isValidBuffer;
+}
+
+void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool /*doAA*/) {
+    //TODO update Canvas to take antialias param
+    TypeCast::toCanvas(canvas)->clipRect(clipRect->left, clipRect->top, clipRect->right,
+                                         clipRect->bottom, SkClipOp::kIntersect);
+}
+
+void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool /*doAA*/) {
+    //TODO update Canvas to take antialias param
+    TypeCast::toCanvas(canvas)->clipRect(clipRect->left, clipRect->top, clipRect->right,
+                                         clipRect->bottom, SkClipOp::kDifference);
+}
+
+void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint) {
+    TypeCast::toCanvas(canvas)->drawRect(rect->left, rect->top, rect->right, rect->bottom,
+                                         TypeCast::toPaintRef(paint));
+}
+
+void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top,
+                        const APaint* paint) {
+    TypeCast::toCanvas(canvas)->drawBitmap(TypeCast::toBitmapRef(bitmap), left, top,
+                                           TypeCast::toPaint(paint));
+}
diff --git a/libs/hwui/apex/android_matrix.cpp b/libs/hwui/apex/android_matrix.cpp
new file mode 100644
index 0000000..693b22b
--- /dev/null
+++ b/libs/hwui/apex/android_matrix.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 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 "android/graphics/matrix.h"
+#include "android_graphics_Matrix.h"
+
+bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]) {
+    static_assert(SkMatrix::kMScaleX == 0, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMSkewX ==  1, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMTransX == 2, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMSkewY ==  3, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMScaleY == 4, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMTransY == 5, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMPersp0 == 6, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMPersp1 == 7, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMPersp2 == 8, "SkMatrix unexpected index");
+
+    SkMatrix* m = android::android_graphics_Matrix_getSkMatrix(env, matrixObj);
+    if (m != nullptr) {
+        m->get9(values);
+        return true;
+    }
+    return false;
+}
diff --git a/libs/hwui/apex/android_paint.cpp b/libs/hwui/apex/android_paint.cpp
new file mode 100644
index 0000000..70bd085
--- /dev/null
+++ b/libs/hwui/apex/android_paint.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 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 "android/graphics/paint.h"
+
+#include "TypeCast.h"
+
+#include <hwui/Paint.h>
+
+using namespace android;
+
+
+APaint* APaint_createPaint() {
+    return TypeCast::toAPaint(new Paint());
+}
+
+void APaint_destroyPaint(APaint* paint) {
+    delete TypeCast::toPaint(paint);
+}
+
+static SkBlendMode convertBlendMode(ABlendMode blendMode) {
+    switch (blendMode) {
+        case ABLEND_MODE_CLEAR:
+            return SkBlendMode::kClear;
+        case ABLEND_MODE_SRC_OVER:
+            return SkBlendMode::kSrcOver;
+        case ABLEND_MODE_SRC:
+            return SkBlendMode::kSrc;
+    }
+}
+
+void APaint_setBlendMode(APaint* paint, ABlendMode blendMode) {
+    TypeCast::toPaint(paint)->setBlendMode(convertBlendMode(blendMode));
+}
diff --git a/libs/hwui/apex/android_region.cpp b/libs/hwui/apex/android_region.cpp
new file mode 100644
index 0000000..2030e7e
--- /dev/null
+++ b/libs/hwui/apex/android_region.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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 "android/graphics/region.h"
+
+#include "GraphicsJNI.h"
+
+#include <SkRegion.h>
+
+static inline SkRegion::Iterator* ARegionIter_to_SkRegionIter(ARegionIterator* iterator) {
+    return reinterpret_cast<SkRegion::Iterator*>(iterator);
+}
+
+static inline ARegionIterator* SkRegionIter_to_ARegionIter(SkRegion::Iterator* iterator) {
+    return reinterpret_cast<ARegionIterator*>(iterator);
+}
+
+ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject regionObj) {
+    SkRegion* region = GraphicsJNI::getNativeRegion(env, regionObj);
+    return (!region) ? nullptr : SkRegionIter_to_ARegionIter(new SkRegion::Iterator(*region));
+}
+
+void ARegionIterator_releaseIterator(ARegionIterator* iterator) {
+    delete ARegionIter_to_SkRegionIter(iterator);
+}
+
+bool ARegionIterator_isComplex(ARegionIterator* iterator) {
+    return ARegionIter_to_SkRegionIter(iterator)->rgn()->isComplex();
+}
+
+bool ARegionIterator_isDone(ARegionIterator* iterator) {
+    return ARegionIter_to_SkRegionIter(iterator)->done();
+}
+
+void ARegionIterator_next(ARegionIterator* iterator) {
+    ARegionIter_to_SkRegionIter(iterator)->next();
+}
+
+ARect ARegionIterator_getRect(ARegionIterator* iterator) {
+    const SkIRect& rect = ARegionIter_to_SkRegionIter(iterator)->rect();
+    return { rect.fLeft, rect.fTop, rect.fRight, rect.fBottom };
+}
+
+ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator) {
+    const SkIRect& bounds = ARegionIter_to_SkRegionIter(iterator)->rgn()->getBounds();
+    return { bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom };
+}
diff --git a/libs/hwui/apex/include/android/graphics/bitmap.h b/libs/hwui/apex/include/android/graphics/bitmap.h
new file mode 100644
index 0000000..45fec2ab
--- /dev/null
+++ b/libs/hwui/apex/include/android/graphics/bitmap.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 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 ANDROID_GRAPHICS_BITMAP_H
+#define ANDROID_GRAPHICS_BITMAP_H
+
+#include <android/bitmap.h>
+#include <android/data_space.h>
+#include <jni.h>
+#include <sys/cdefs.h>
+
+struct AHardwareBuffer;
+
+__BEGIN_DECLS
+
+/**
+ * Opaque handle for a native graphics bitmap.
+ */
+typedef struct ABitmap ABitmap;
+
+/**
+ * Retrieve bitmapInfo for the provided java bitmap even if it has been recycled.  In the case of a
+ * recycled bitmap the values contained in the bitmap before it was recycled are returned.
+ *
+ * NOTE: This API does not need to remain as an APEX API if/when we pull libjnigraphics into the
+ *       UI module.
+ */
+AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj);
+
+/**
+ *
+ * @return ptr to an opaque handle to the native bitmap or null if the java bitmap has been recycled
+ *         or does not exist.
+ */
+ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj);
+
+ABitmap* ABitmap_copy(ABitmap* srcBitmap, AndroidBitmapFormat dstFormat);
+
+void ABitmap_acquireRef(ABitmap* bitmap);
+void ABitmap_releaseRef(ABitmap* bitmap);
+
+AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmap);
+ADataSpace ABitmap_getDataSpace(ABitmap* bitmap);
+
+void* ABitmap_getPixels(ABitmap* bitmap);
+void ABitmap_notifyPixelsChanged(ABitmap* bitmap);
+
+AndroidBitmapFormat ABitmapConfig_getFormatFromConfig(JNIEnv* env, jobject bitmapConfigObj);
+jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format);
+
+// NDK access
+int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels,
+                     AndroidBitmapCompressFormat format, int32_t quality, void* userContext,
+                     AndroidBitmap_CompressWriteFunc);
+/**
+ *  Retrieve the native object associated with a HARDWARE Bitmap.
+ *
+ *  Client must not modify it while a Bitmap is wrapping it.
+ *
+ *  @param bitmap Handle to an android.graphics.Bitmap.
+ *  @return on success, a pointer to the
+ *         AHardwareBuffer associated with bitmap. This acquires
+ *         a reference on the buffer, and the client must call
+ *         AHardwareBuffer_release when finished with it.
+ */
+AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmap);
+
+__END_DECLS
+
+#ifdef	__cplusplus
+namespace android {
+namespace graphics {
+    class Bitmap {
+    public:
+        Bitmap() : mBitmap(nullptr) {}
+        Bitmap(JNIEnv* env, jobject bitmapObj) :
+                mBitmap(ABitmap_acquireBitmapFromJava(env, bitmapObj)) {}
+        Bitmap(const Bitmap& src) : mBitmap(src.mBitmap) { ABitmap_acquireRef(src.mBitmap); }
+        ~Bitmap() { ABitmap_releaseRef(mBitmap); }
+
+        // copy operator
+        Bitmap& operator=(const Bitmap& other) {
+            if (&other != this) {
+                ABitmap_releaseRef(mBitmap);
+                mBitmap = other.mBitmap;
+                ABitmap_acquireRef(mBitmap);
+            }
+            return *this;
+        }
+
+        // move operator
+        Bitmap& operator=(Bitmap&& other) {
+            if (&other != this) {
+                ABitmap_releaseRef(mBitmap);
+                mBitmap = other.mBitmap;
+                other.mBitmap = nullptr;
+            }
+            return *this;
+        }
+
+        Bitmap copy(AndroidBitmapFormat dstFormat) const {
+            return Bitmap(ABitmap_copy(mBitmap, dstFormat));
+        }
+
+        bool isValid() const { return mBitmap != nullptr; }
+        bool isEmpty() const {
+            AndroidBitmapInfo info = getInfo();
+            return info.width <= 0 || info.height <= 0;
+        }
+        void reset() {
+            ABitmap_releaseRef(mBitmap);
+            mBitmap = nullptr;
+        }
+
+        ABitmap* get() const { return mBitmap; }
+
+        AndroidBitmapInfo getInfo() const { return ABitmap_getInfo(mBitmap); }
+        ADataSpace getDataSpace() const { return ABitmap_getDataSpace(mBitmap); }
+        void* getPixels() const { return ABitmap_getPixels(mBitmap); }
+        void notifyPixelsChanged() const { ABitmap_notifyPixelsChanged(mBitmap); }
+        AHardwareBuffer* getHardwareBuffer() const { return ABitmap_getHardwareBuffer(mBitmap); }
+
+    private:
+        // takes ownership of the provided ABitmap
+        Bitmap(ABitmap* bitmap) : mBitmap(bitmap) {}
+
+        ABitmap* mBitmap;
+    };
+}; // namespace graphics
+}; // namespace android
+#endif // __cplusplus
+
+#endif // ANDROID_GRAPHICS_BITMAP_H
diff --git a/libs/hwui/apex/include/android/graphics/canvas.h b/libs/hwui/apex/include/android/graphics/canvas.h
new file mode 100644
index 0000000..6fd6b06
--- /dev/null
+++ b/libs/hwui/apex/include/android/graphics/canvas.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2019 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 ANDROID_GRAPHICS_CANVAS_H
+#define ANDROID_GRAPHICS_CANVAS_H
+
+#include <android/graphics/bitmap.h>
+#include <android/graphics/paint.h>
+#include <android/native_window.h>
+#include <android/rect.h>
+#include <jni.h>
+
+__BEGIN_DECLS
+
+/**
+ * Opaque handle for a native graphics canvas.
+ */
+typedef struct ACanvas ACanvas;
+
+//  One of AHardwareBuffer_Format.
+bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat);
+
+/**
+ * Returns a native handle to a Java android.graphics.Canvas
+ *
+ * @return ACanvas* that is only valid for the life of the jobject.
+ */
+ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvas);
+
+/**
+ * Creates a canvas that wraps the buffer
+ *
+ * @param buffer is a required param.  If no buffer is provided a nullptr will be returned.
+ */
+ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer,
+                              int32_t /*android_dataspace_t*/ dataspace);
+
+void ACanvas_destroyCanvas(ACanvas* canvas);
+
+/**
+ * Updates the canvas to render into the pixels in the provided buffer
+ *
+ * @param buffer The buffer that will provide the backing store for this canvas.  The buffer must
+ *               remain valid until the this method is called again with either another active
+ *               buffer or nullptr.  If nullptr is given the canvas will release the previous buffer
+ *               and set an empty backing store.
+ * @return A boolean value indicating whether or not the buffer was successfully set. If false the
+ *         method will behave as if nullptr were passed as the input buffer and the previous buffer
+ *         will still be released.
+ */
+bool ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
+                       int32_t /*android_dataspace_t*/ dataspace);
+
+/**
+ * Clips operations on the canvas to the intersection of the current clip and the provided clipRect.
+ *
+ * @param clipRect required
+ */
+void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false);
+
+/**
+ * Clips operations on the canvas to the difference of the current clip and the provided clipRect.
+ *
+ * @param clipRect required
+ */
+void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false);
+
+/**
+ *
+ * @param rect required
+ * @param paint required
+ */
+void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint);
+
+/**
+ *
+ * @param bitmap required
+ * @param left
+ * @param top
+ * @param paint
+ */
+void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top,
+                        const APaint* paint);
+
+__END_DECLS
+
+#ifdef	__cplusplus
+namespace android {
+namespace graphics {
+    class Canvas {
+    public:
+        Canvas(JNIEnv* env, jobject canvasObj) :
+                mCanvas(ACanvas_getNativeHandleFromJava(env, canvasObj)),
+                mOwnedPtr(false) {}
+        Canvas(const ANativeWindow_Buffer& buffer, int32_t /*android_dataspace_t*/ dataspace) :
+                mCanvas(ACanvas_createCanvas(&buffer, dataspace)),
+                mOwnedPtr(true) {}
+        ~Canvas() {
+            if (mOwnedPtr) {
+                ACanvas_destroyCanvas(mCanvas);
+            }
+        }
+
+        bool setBuffer(const ANativeWindow_Buffer* buffer,
+                       int32_t /*android_dataspace_t*/ dataspace) {
+            return ACanvas_setBuffer(mCanvas, buffer, dataspace);
+        }
+
+        void clipRect(const ARect& clipRect, bool doAntiAlias = false) {
+            ACanvas_clipRect(mCanvas, &clipRect, doAntiAlias);
+        }
+
+        void drawRect(const ARect& rect, const Paint& paint) {
+            ACanvas_drawRect(mCanvas, &rect, &paint.get());
+        }
+        void drawBitmap(const Bitmap& bitmap, float left, float top, const Paint* paint) {
+            const APaint* aPaint = (paint) ? &paint->get() : nullptr;
+            ACanvas_drawBitmap(mCanvas, bitmap.get(), left, top, aPaint);
+        }
+
+    private:
+        ACanvas* mCanvas;
+        const bool mOwnedPtr;
+    };
+}; // namespace graphics
+}; // namespace android
+#endif // __cplusplus
+
+#endif // ANDROID_GRAPHICS_CANVAS_H
\ No newline at end of file
diff --git a/libs/hwui/apex/include/android/graphics/jni_runtime.h b/libs/hwui/apex/include/android/graphics/jni_runtime.h
new file mode 100644
index 0000000..872a949
--- /dev/null
+++ b/libs/hwui/apex/include/android/graphics/jni_runtime.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 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 ANDROID_GRAPHICS_JNI_RUNTIME_H
+#define ANDROID_GRAPHICS_JNI_RUNTIME_H
+
+#include <jni.h>
+
+__BEGIN_DECLS
+
+void init_android_graphics();
+
+int register_android_graphics_classes(JNIEnv* env);
+
+void zygote_preload_graphics();
+
+__END_DECLS
+
+
+#endif // ANDROID_GRAPHICS_JNI_RUNTIME_H
\ No newline at end of file
diff --git a/libs/hwui/apex/include/android/graphics/matrix.h b/libs/hwui/apex/include/android/graphics/matrix.h
new file mode 100644
index 0000000..4039cd1
--- /dev/null
+++ b/libs/hwui/apex/include/android/graphics/matrix.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 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 ANDROID_GRAPHICS_MATRIX_H
+#define ANDROID_GRAPHICS_MATRIX_H
+
+#include <jni.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Returns an array of floats that represents the 3x3 matrix of the java object.
+ * @param values The 9 values of the 3x3 matrix in the following order.
+ *               values[0] = scaleX  values[1] = skewX   values[2] = transX
+ *               values[3] = skewY   values[4] = scaleY  values[5] = transY
+ *               values[6] = persp0  values[7] = persp1  values[8] = persp2
+ * @return true if the values param was populated and false otherwise.
+
+ */
+bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]);
+
+__END_DECLS
+
+#endif // ANDROID_GRAPHICS_MATRIX_H
diff --git a/libs/hwui/apex/include/android/graphics/paint.h b/libs/hwui/apex/include/android/graphics/paint.h
new file mode 100644
index 0000000..5895e00
--- /dev/null
+++ b/libs/hwui/apex/include/android/graphics/paint.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 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 ANDROID_GRAPHICS_PAINT_H
+#define ANDROID_GRAPHICS_PAINT_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Opaque handle for a native graphics canvas.
+ */
+typedef struct APaint APaint;
+
+/** Bitmap pixel format. */
+enum ABlendMode {
+    /** replaces destination with zero: fully transparent */
+    ABLEND_MODE_CLEAR    = 0,
+    /** source over destination */
+    ABLEND_MODE_SRC_OVER = 1,
+    /** replaces destination **/
+    ABLEND_MODE_SRC      = 2,
+};
+
+APaint* APaint_createPaint();
+
+void APaint_destroyPaint(APaint* paint);
+
+void APaint_setBlendMode(APaint* paint, ABlendMode blendMode);
+
+__END_DECLS
+
+#ifdef	__cplusplus
+namespace android {
+namespace graphics {
+    class Paint {
+    public:
+        Paint() : mPaint(APaint_createPaint()) {}
+        ~Paint() { APaint_destroyPaint(mPaint); }
+
+        void setBlendMode(ABlendMode blendMode) { APaint_setBlendMode(mPaint, blendMode); }
+
+        const APaint& get() const { return *mPaint; }
+
+    private:
+        APaint* mPaint;
+    };
+}; // namespace graphics
+}; // namespace android
+#endif // __cplusplus
+
+
+#endif // ANDROID_GRAPHICS_PAINT_H
\ No newline at end of file
diff --git a/libs/hwui/apex/include/android/graphics/region.h b/libs/hwui/apex/include/android/graphics/region.h
new file mode 100644
index 0000000..961067a
--- /dev/null
+++ b/libs/hwui/apex/include/android/graphics/region.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 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 ANDROID_GRAPHICS_REGION_H
+#define ANDROID_GRAPHICS_REGION_H
+
+#include <android/rect.h>
+#include <sys/cdefs.h>
+#include <jni.h>
+
+__BEGIN_DECLS
+
+/**
+* Opaque handle for a native graphics region iterator.
+*/
+typedef struct ARegionIterator ARegionIterator;
+
+/**
+ * Returns a iterator for a Java android.graphics.Region
+ *
+ * @param env
+ * @param region
+ * @return ARegionIterator that must be closed and must not live longer than the life
+ *         of the jobject.  It returns nullptr if the region is not a valid object.
+ */
+ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject region);
+
+void ARegionIterator_releaseIterator(ARegionIterator* iterator);
+
+bool ARegionIterator_isComplex(ARegionIterator* iterator);
+
+bool ARegionIterator_isDone(ARegionIterator* iterator);
+
+void ARegionIterator_next(ARegionIterator* iterator);
+
+ARect ARegionIterator_getRect(ARegionIterator* iterator);
+
+ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator);
+
+__END_DECLS
+
+#ifdef	__cplusplus
+namespace android {
+namespace graphics {
+    class RegionIterator {
+    public:
+        RegionIterator(JNIEnv* env, jobject region)
+                : mIterator(ARegionIterator_acquireIterator(env, region)) {}
+        ~RegionIterator() { ARegionIterator_releaseIterator(mIterator); }
+
+        bool isValid() const { return mIterator != nullptr; }
+        bool isComplex() { return ARegionIterator_isComplex(mIterator); }
+        bool isDone() { return ARegionIterator_isDone(mIterator); }
+        void next() { ARegionIterator_next(mIterator); }
+        ARect getRect() { return ARegionIterator_getRect(mIterator); }
+        ARect getTotalBounds() const { return ARegionIterator_getTotalBounds(mIterator); }
+    private:
+        ARegionIterator* mIterator;
+    };
+}; // namespace graphics
+}; // namespace android
+
+#endif // __cplusplus
+#endif // ANDROID_GRAPHICS_REGION_H
\ No newline at end of file
diff --git a/libs/hwui/apex/include/android/graphics/renderthread.h b/libs/hwui/apex/include/android/graphics/renderthread.h
new file mode 100644
index 0000000..0a790af
--- /dev/null
+++ b/libs/hwui/apex/include/android/graphics/renderthread.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 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 ANDROID_GRAPHICS_RENDERTHREAD_H
+#define ANDROID_GRAPHICS_RENDERTHREAD_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Dumps a textual representation of the graphics stats for this process.
+ * @param fd The file descriptor that the available graphics stats will be appended to.  The
+ *           function requires a valid fd, but does not persist or assume ownership of the fd
+ *           outside the scope of this function.
+ */
+void ARenderThread_dumpGraphicsMemory(int fd);
+
+__END_DECLS
+
+#endif // ANDROID_GRAPHICS_RENDERTHREAD_H
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
new file mode 100644
index 0000000..506c578
--- /dev/null
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2019 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 "android/graphics/jni_runtime.h"
+
+#include <android/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <sys/cdefs.h>
+
+#include <EGL/egl.h>
+#include <Properties.h>
+#include <SkGraphics.h>
+
+#define LOG_TAG "AndroidGraphicsJNI"
+
+extern int register_android_graphics_Bitmap(JNIEnv*);
+extern int register_android_graphics_BitmapFactory(JNIEnv*);
+extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
+extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env);
+extern int register_android_graphics_Camera(JNIEnv* env);
+extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env);
+extern int register_android_graphics_Graphics(JNIEnv* env);
+extern int register_android_graphics_ImageDecoder(JNIEnv*);
+extern int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv*);
+extern int register_android_graphics_Interpolator(JNIEnv* env);
+extern int register_android_graphics_MaskFilter(JNIEnv* env);
+extern int register_android_graphics_Movie(JNIEnv* env);
+extern int register_android_graphics_NinePatch(JNIEnv*);
+extern int register_android_graphics_PathEffect(JNIEnv* env);
+extern int register_android_graphics_Shader(JNIEnv* env);
+extern int register_android_graphics_Typeface(JNIEnv* env);
+extern int register_android_graphics_YuvImage(JNIEnv* env);
+
+namespace android {
+
+extern int register_android_graphics_Canvas(JNIEnv* env);
+extern int register_android_graphics_CanvasProperty(JNIEnv* env);
+extern int register_android_graphics_ColorFilter(JNIEnv* env);
+extern int register_android_graphics_ColorSpace(JNIEnv* env);
+extern int register_android_graphics_DrawFilter(JNIEnv* env);
+extern int register_android_graphics_FontFamily(JNIEnv* env);
+extern int register_android_graphics_HardwareRendererObserver(JNIEnv* env);
+extern int register_android_graphics_Matrix(JNIEnv* env);
+extern int register_android_graphics_Paint(JNIEnv* env);
+extern int register_android_graphics_Path(JNIEnv* env);
+extern int register_android_graphics_PathMeasure(JNIEnv* env);
+extern int register_android_graphics_Picture(JNIEnv*);
+extern int register_android_graphics_Region(JNIEnv* env);
+extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env);
+extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env);
+extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
+extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
+extern int register_android_graphics_fonts_Font(JNIEnv* env);
+extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
+extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
+extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
+extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
+extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
+extern int register_android_graphics_text_LineBreaker(JNIEnv *env);
+
+extern int register_android_util_PathParser(JNIEnv* env);
+extern int register_android_view_DisplayListCanvas(JNIEnv* env);
+extern int register_android_view_RenderNode(JNIEnv* env);
+extern int register_android_view_TextureLayer(JNIEnv* env);
+extern int register_android_view_ThreadedRenderer(JNIEnv* env);
+
+#ifdef NDEBUG
+    #define REG_JNI(name)      { name }
+    struct RegJNIRec {
+        int (*mProc)(JNIEnv*);
+    };
+#else
+    #define REG_JNI(name)      { name, #name }
+    struct RegJNIRec {
+        int (*mProc)(JNIEnv*);
+        const char* mName;
+    };
+#endif
+
+static const RegJNIRec gRegJNI[] = {
+    REG_JNI(register_android_graphics_Canvas),
+    // This needs to be before register_android_graphics_Graphics, or the latter
+    // will not be able to find the jmethodID for ColorSpace.get().
+    REG_JNI(register_android_graphics_ColorSpace),
+    REG_JNI(register_android_graphics_Graphics),
+    REG_JNI(register_android_graphics_Bitmap),
+    REG_JNI(register_android_graphics_BitmapFactory),
+    REG_JNI(register_android_graphics_BitmapRegionDecoder),
+    REG_JNI(register_android_graphics_ByteBufferStreamAdaptor),
+    REG_JNI(register_android_graphics_Camera),
+    REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
+    REG_JNI(register_android_graphics_CanvasProperty),
+    REG_JNI(register_android_graphics_ColorFilter),
+    REG_JNI(register_android_graphics_DrawFilter),
+    REG_JNI(register_android_graphics_FontFamily),
+    REG_JNI(register_android_graphics_HardwareRendererObserver),
+    REG_JNI(register_android_graphics_ImageDecoder),
+    REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable),
+    REG_JNI(register_android_graphics_Interpolator),
+    REG_JNI(register_android_graphics_MaskFilter),
+    REG_JNI(register_android_graphics_Matrix),
+    REG_JNI(register_android_graphics_Movie),
+    REG_JNI(register_android_graphics_NinePatch),
+    REG_JNI(register_android_graphics_Paint),
+    REG_JNI(register_android_graphics_Path),
+    REG_JNI(register_android_graphics_PathMeasure),
+    REG_JNI(register_android_graphics_PathEffect),
+    REG_JNI(register_android_graphics_Picture),
+    REG_JNI(register_android_graphics_Region),
+    REG_JNI(register_android_graphics_Shader),
+    REG_JNI(register_android_graphics_Typeface),
+    REG_JNI(register_android_graphics_YuvImage),
+    REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory),
+    REG_JNI(register_android_graphics_animation_RenderNodeAnimator),
+    REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
+    REG_JNI(register_android_graphics_drawable_VectorDrawable),
+    REG_JNI(register_android_graphics_fonts_Font),
+    REG_JNI(register_android_graphics_fonts_FontFamily),
+    REG_JNI(register_android_graphics_pdf_PdfDocument),
+    REG_JNI(register_android_graphics_pdf_PdfEditor),
+    REG_JNI(register_android_graphics_pdf_PdfRenderer),
+    REG_JNI(register_android_graphics_text_MeasuredText),
+    REG_JNI(register_android_graphics_text_LineBreaker),
+
+    REG_JNI(register_android_util_PathParser),
+    REG_JNI(register_android_view_RenderNode),
+    REG_JNI(register_android_view_DisplayListCanvas),
+    REG_JNI(register_android_view_TextureLayer),
+    REG_JNI(register_android_view_ThreadedRenderer),
+};
+
+} // namespace android
+
+void init_android_graphics() {
+    SkGraphics::Init();
+}
+
+int register_android_graphics_classes(JNIEnv *env) {
+    for (size_t i = 0; i < NELEM(android::gRegJNI); i++) {
+        if (android::gRegJNI[i].mProc(env) < 0) {
+#ifndef NDEBUG
+            __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, "JNI Error!!! %s failed to load\n",
+                                android::gRegJNI[i].mName);
+#endif
+            return -1;
+        }
+    }
+    return 0;
+}
+
+using android::uirenderer::Properties;
+using android::uirenderer::RenderPipelineType;
+
+void zygote_preload_graphics() {
+    if (Properties::peekRenderPipelineType() == RenderPipelineType::SkiaGL) {
+        eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    }
+}
\ No newline at end of file
diff --git a/libs/hwui/apex/renderthread.cpp b/libs/hwui/apex/renderthread.cpp
new file mode 100644
index 0000000..5d26afe
--- /dev/null
+++ b/libs/hwui/apex/renderthread.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 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 "android/graphics/renderthread.h"
+
+#include <renderthread/RenderProxy.h>
+
+using namespace android;
+
+void ARenderThread_dumpGraphicsMemory(int fd) {
+    uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);
+}
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
new file mode 100644
index 0000000..6c2a5a3
--- /dev/null
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2018 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 "GraphicsJNI.h"
+#include "ImageDecoder.h"
+#include "Utils.h"
+#include "core_jni_helpers.h"
+
+#include <SkAndroidCodec.h>
+#include <SkAnimatedImage.h>
+#include <SkColorFilter.h>
+#include <SkPicture.h>
+#include <SkPictureRecorder.h>
+#include <hwui/AnimatedImageDrawable.h>
+#include <hwui/ImageDecoder.h>
+#include <hwui/Canvas.h>
+#include <utils/Looper.h>
+
+using namespace android;
+
+static jmethodID gAnimatedImageDrawable_onAnimationEndMethodID;
+
+// Note: jpostProcess holds a handle to the ImageDecoder.
+static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
+                                           jlong nativeImageDecoder, jobject jpostProcess,
+                                           jint width, jint height, jlong colorSpaceHandle,
+                                           jboolean extended, jobject jsubset) {
+    if (nativeImageDecoder == 0) {
+        doThrowIOE(env, "Cannot create AnimatedImageDrawable from null!");
+        return 0;
+    }
+
+    auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder);
+    SkIRect subset;
+    if (jsubset) {
+        GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
+    } else {
+        subset = SkIRect::MakeWH(width, height);
+    }
+
+    bool hasRestoreFrame = false;
+    if (imageDecoder->mCodec->getEncodedFormat() != SkEncodedImageFormat::kWEBP) {
+        const int frameCount = imageDecoder->mCodec->codec()->getFrameCount();
+        for (int i = 0; i < frameCount; ++i) {
+            SkCodec::FrameInfo frameInfo;
+            if (!imageDecoder->mCodec->codec()->getFrameInfo(i, &frameInfo)) {
+                doThrowIOE(env, "Failed to read frame info!");
+                return 0;
+            }
+            if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious) {
+                hasRestoreFrame = true;
+                break;
+            }
+        }
+    }
+
+    auto info = imageDecoder->mCodec->getInfo().makeWH(width, height)
+        .makeColorSpace(GraphicsJNI::getNativeColorSpace(colorSpaceHandle));
+    if (extended) {
+        info = info.makeColorType(kRGBA_F16_SkColorType);
+    }
+
+    size_t bytesUsed = info.computeMinByteSize();
+    // SkAnimatedImage has one SkBitmap for decoding, plus an extra one if there is a
+    // kRestorePrevious frame. AnimatedImageDrawable has two SkPictures storing the current
+    // frame and the next frame. (The former assumes that the image is animated, and the
+    // latter assumes that it is drawn to a hardware canvas.)
+    bytesUsed *= hasRestoreFrame ? 4 : 3;
+    sk_sp<SkPicture> picture;
+    if (jpostProcess) {
+        SkRect bounds = SkRect::MakeWH(subset.width(), subset.height());
+
+        SkPictureRecorder recorder;
+        SkCanvas* skcanvas = recorder.beginRecording(bounds);
+        std::unique_ptr<Canvas> canvas(Canvas::create_canvas(skcanvas));
+        postProcessAndRelease(env, jpostProcess, std::move(canvas));
+        if (env->ExceptionCheck()) {
+            return 0;
+        }
+        picture = recorder.finishRecordingAsPicture();
+        bytesUsed += picture->approximateBytesUsed();
+    }
+
+
+    sk_sp<SkAnimatedImage> animatedImg = SkAnimatedImage::Make(std::move(imageDecoder->mCodec),
+                                                               info, subset,
+                                                               std::move(picture));
+    if (!animatedImg) {
+        doThrowIOE(env, "Failed to create drawable");
+        return 0;
+    }
+
+    bytesUsed += sizeof(animatedImg.get());
+
+    sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(std::move(animatedImg),
+                                                                    bytesUsed));
+    return reinterpret_cast<jlong>(drawable.release());
+}
+
+static void AnimatedImageDrawable_destruct(AnimatedImageDrawable* drawable) {
+    SkSafeUnref(drawable);
+}
+
+static jlong AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&AnimatedImageDrawable_destruct));
+}
+
+// Java's FINISHED relies on this being -1
+static_assert(SkAnimatedImage::kFinished == -1);
+
+static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+                                         jlong canvasPtr) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    auto* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    return (jlong) canvas->drawAnimatedImage(drawable);
+}
+
+static void AnimatedImageDrawable_nSetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+                                            jint alpha) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    drawable->setStagingAlpha(alpha);
+}
+
+static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    return drawable->getStagingAlpha();
+}
+
+static void AnimatedImageDrawable_nSetColorFilter(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+                                                  jlong nativeFilter) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    auto* filter = reinterpret_cast<SkColorFilter*>(nativeFilter);
+    drawable->setStagingColorFilter(sk_ref_sp(filter));
+}
+
+static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    return drawable->isRunning();
+}
+
+static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    return drawable->start();
+}
+
+static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    return drawable->stop();
+}
+
+// Java's LOOP_INFINITE relies on this being the same.
+static_assert(SkCodec::kRepetitionCountInfinite == -1);
+
+static jint AnimatedImageDrawable_nGetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    return drawable->getRepetitionCount();
+}
+
+static void AnimatedImageDrawable_nSetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+                                                  jint loopCount) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    drawable->setRepetitionCount(loopCount);
+}
+
+class InvokeListener : public MessageHandler {
+public:
+    InvokeListener(JNIEnv* env, jobject javaObject) {
+        LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK);
+        // Hold a weak reference to break a cycle that would prevent GC.
+        mWeakRef = env->NewWeakGlobalRef(javaObject);
+    }
+
+    ~InvokeListener() override {
+        auto* env = get_env_or_die(mJvm);
+        env->DeleteWeakGlobalRef(mWeakRef);
+    }
+
+    virtual void handleMessage(const Message&) override {
+        auto* env = get_env_or_die(mJvm);
+        jobject localRef = env->NewLocalRef(mWeakRef);
+        if (localRef) {
+            env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID);
+        }
+    }
+
+private:
+    JavaVM* mJvm;
+    jweak mWeakRef;
+};
+
+class JniAnimationEndListener : public OnAnimationEndListener {
+public:
+    JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) {
+        mListener = new InvokeListener(env, javaObject);
+        mLooper = std::move(looper);
+    }
+
+    void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); }
+
+private:
+    sp<InvokeListener> mListener;
+    sp<Looper> mLooper;
+};
+
+static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/,
+                                                             jlong nativePtr, jobject jdrawable) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    if (!jdrawable) {
+        drawable->setOnAnimationEndListener(nullptr);
+    } else {
+        sp<Looper> looper = Looper::getForThread();
+        if (!looper.get()) {
+            doThrowISE(env,
+                       "Must set AnimatedImageDrawable's AnimationCallback on a thread with a "
+                       "looper!");
+            return;
+        }
+
+        drawable->setOnAnimationEndListener(
+                std::make_unique<JniAnimationEndListener>(std::move(looper), env, jdrawable));
+    }
+}
+
+static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    return drawable->byteSize();
+}
+
+static void AnimatedImageDrawable_nSetMirrored(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+                                               jboolean mirrored) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    drawable->setStagingMirrored(mirrored);
+}
+
+static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
+    { "nCreate",             "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",(void*) AnimatedImageDrawable_nCreate },
+    { "nGetNativeFinalizer", "()J",                                                          (void*) AnimatedImageDrawable_nGetNativeFinalizer },
+    { "nDraw",               "(JJ)J",                                                        (void*) AnimatedImageDrawable_nDraw },
+    { "nSetAlpha",           "(JI)V",                                                        (void*) AnimatedImageDrawable_nSetAlpha },
+    { "nGetAlpha",           "(J)I",                                                         (void*) AnimatedImageDrawable_nGetAlpha },
+    { "nSetColorFilter",     "(JJ)V",                                                        (void*) AnimatedImageDrawable_nSetColorFilter },
+    { "nIsRunning",          "(J)Z",                                                         (void*) AnimatedImageDrawable_nIsRunning },
+    { "nStart",              "(J)Z",                                                         (void*) AnimatedImageDrawable_nStart },
+    { "nStop",               "(J)Z",                                                         (void*) AnimatedImageDrawable_nStop },
+    { "nGetRepeatCount",     "(J)I",                                                         (void*) AnimatedImageDrawable_nGetRepeatCount },
+    { "nSetRepeatCount",     "(JI)V",                                                        (void*) AnimatedImageDrawable_nSetRepeatCount },
+    { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
+    { "nNativeByteSize",     "(J)J",                                                         (void*) AnimatedImageDrawable_nNativeByteSize },
+    { "nSetMirrored",        "(JZ)V",                                                        (void*) AnimatedImageDrawable_nSetMirrored },
+};
+
+int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
+    jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable");
+    gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V");
+
+    return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
+            gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
+}
+
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
new file mode 100755
index 0000000..130322a
--- /dev/null
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -0,0 +1,1178 @@
+#define LOG_TAG "Bitmap"
+#include "Bitmap.h"
+
+#include "SkBitmap.h"
+#include "SkPixelRef.h"
+#include "SkImageEncoder.h"
+#include "SkImageInfo.h"
+#include "SkColor.h"
+#include "SkColorSpace.h"
+#include "GraphicsJNI.h"
+#include "SkStream.h"
+#include "SkWebpEncoder.h"
+
+#include "android_os_Parcel.h"
+#include "android_nio_utils.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+#include <hwui/Paint.h>
+#include <hwui/Bitmap.h>
+#include <utils/Color.h>
+
+#ifdef __ANDROID__ // Layoutlib does not support graphic buffer, parcel or render thread
+#include <android_runtime/android_graphics_GraphicBuffer.h>
+#include <binder/Parcel.h>
+#include <dlfcn.h>
+#include <renderthread/RenderProxy.h>
+#endif
+
+#include "core_jni_helpers.h"
+
+#include <jni.h>
+#include <string.h>
+#include <memory>
+#include <string>
+
+#define DEBUG_PARCEL 0
+
+static jclass   gBitmap_class;
+static jfieldID gBitmap_nativePtr;
+static jmethodID gBitmap_constructorMethodID;
+static jmethodID gBitmap_reinitMethodID;
+
+namespace android {
+
+class BitmapWrapper {
+public:
+    explicit BitmapWrapper(Bitmap* bitmap)
+        : mBitmap(bitmap) { }
+
+    void freePixels() {
+        mInfo = mBitmap->info();
+        mHasHardwareMipMap = mBitmap->hasHardwareMipMap();
+        mAllocationSize = mBitmap->getAllocationByteCount();
+        mRowBytes = mBitmap->rowBytes();
+        mGenerationId = mBitmap->getGenerationID();
+        mIsHardware = mBitmap->isHardware();
+        mBitmap.reset();
+    }
+
+    bool valid() {
+        return mBitmap != nullptr;
+    }
+
+    Bitmap& bitmap() {
+        assertValid();
+        return *mBitmap;
+    }
+
+    void assertValid() {
+        LOG_ALWAYS_FATAL_IF(!valid(), "Error, cannot access an invalid/free'd bitmap here!");
+    }
+
+    void getSkBitmap(SkBitmap* outBitmap) {
+        assertValid();
+        mBitmap->getSkBitmap(outBitmap);
+    }
+
+    bool hasHardwareMipMap() {
+        if (mBitmap) {
+            return mBitmap->hasHardwareMipMap();
+        }
+        return mHasHardwareMipMap;
+    }
+
+    void setHasHardwareMipMap(bool hasMipMap) {
+        assertValid();
+        mBitmap->setHasHardwareMipMap(hasMipMap);
+    }
+
+    void setAlphaType(SkAlphaType alphaType) {
+        assertValid();
+        mBitmap->setAlphaType(alphaType);
+    }
+
+    void setColorSpace(sk_sp<SkColorSpace> colorSpace) {
+        assertValid();
+        mBitmap->setColorSpace(colorSpace);
+    }
+
+    const SkImageInfo& info() {
+        if (mBitmap) {
+            return mBitmap->info();
+        }
+        return mInfo;
+    }
+
+    size_t getAllocationByteCount() const {
+        if (mBitmap) {
+            return mBitmap->getAllocationByteCount();
+        }
+        return mAllocationSize;
+    }
+
+    size_t rowBytes() const {
+        if (mBitmap) {
+            return mBitmap->rowBytes();
+        }
+        return mRowBytes;
+    }
+
+    uint32_t getGenerationID() const {
+        if (mBitmap) {
+            return mBitmap->getGenerationID();
+        }
+        return mGenerationId;
+    }
+
+    bool isHardware() {
+        if (mBitmap) {
+            return mBitmap->isHardware();
+        }
+        return mIsHardware;
+    }
+
+    ~BitmapWrapper() { }
+
+private:
+    sk_sp<Bitmap> mBitmap;
+    SkImageInfo mInfo;
+    bool mHasHardwareMipMap;
+    size_t mAllocationSize;
+    size_t mRowBytes;
+    uint32_t mGenerationId;
+    bool mIsHardware;
+};
+
+// Convenience class that does not take a global ref on the pixels, relying
+// on the caller already having a local JNI ref
+class LocalScopedBitmap {
+public:
+    explicit LocalScopedBitmap(jlong bitmapHandle)
+            : mBitmapWrapper(reinterpret_cast<BitmapWrapper*>(bitmapHandle)) {}
+
+    BitmapWrapper* operator->() {
+        return mBitmapWrapper;
+    }
+
+    void* pixels() {
+        return mBitmapWrapper->bitmap().pixels();
+    }
+
+    bool valid() {
+        return mBitmapWrapper && mBitmapWrapper->valid();
+    }
+
+private:
+    BitmapWrapper* mBitmapWrapper;
+};
+
+namespace bitmap {
+
+// Assert that bitmap's SkAlphaType is consistent with isPremultiplied.
+static void assert_premultiplied(const SkImageInfo& info, bool isPremultiplied) {
+    // kOpaque_SkAlphaType and kIgnore_SkAlphaType mean that isPremultiplied is
+    // irrelevant. This just tests to ensure that the SkAlphaType is not
+    // opposite of isPremultiplied.
+    if (isPremultiplied) {
+        SkASSERT(info.alphaType() != kUnpremul_SkAlphaType);
+    } else {
+        SkASSERT(info.alphaType() != kPremul_SkAlphaType);
+    }
+}
+
+void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info,
+        bool isPremultiplied)
+{
+    // The caller needs to have already set the alpha type properly, so the
+    // native SkBitmap stays in sync with the Java Bitmap.
+    assert_premultiplied(info, isPremultiplied);
+
+    env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID,
+            info.width(), info.height(), isPremultiplied);
+}
+
+jobject createBitmap(JNIEnv* env, Bitmap* bitmap,
+        int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets,
+        int density) {
+    bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable;
+    bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied;
+    // The caller needs to have already set the alpha type properly, so the
+    // native SkBitmap stays in sync with the Java Bitmap.
+    assert_premultiplied(bitmap->info(), isPremultiplied);
+    bool fromMalloc = bitmap->pixelStorageType() == PixelStorageType::Heap;
+    BitmapWrapper* bitmapWrapper = new BitmapWrapper(bitmap);
+    if (!isMutable) {
+        bitmapWrapper->bitmap().setImmutable();
+    }
+    jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
+            reinterpret_cast<jlong>(bitmapWrapper), bitmap->width(), bitmap->height(), density,
+            isPremultiplied, ninePatchChunk, ninePatchInsets, fromMalloc);
+
+    if (env->ExceptionCheck() != 0) {
+        ALOGE("*** Uncaught exception returned from Java call!\n");
+        env->ExceptionDescribe();
+    }
+    return obj;
+}
+
+void toSkBitmap(jlong bitmapHandle, SkBitmap* outBitmap) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    bitmap->getSkBitmap(outBitmap);
+}
+
+Bitmap& toBitmap(jlong bitmapHandle) {
+    LocalScopedBitmap localBitmap(bitmapHandle);
+    return localBitmap->bitmap();
+}
+
+} // namespace bitmap
+
+} // namespace android
+
+using namespace android;
+using namespace android::bitmap;
+
+Bitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) {
+    SkASSERT(env);
+    SkASSERT(bitmap);
+    SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
+    jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
+    LocalScopedBitmap localBitmap(bitmapHandle);
+    return localBitmap.valid() ? &localBitmap->bitmap() : nullptr;
+}
+
+SkImageInfo GraphicsJNI::getBitmapInfo(JNIEnv* env, jobject bitmap, uint32_t* outRowBytes,
+                                       bool* isHardware) {
+    SkASSERT(env);
+    SkASSERT(bitmap);
+    SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
+    jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
+    LocalScopedBitmap localBitmap(bitmapHandle);
+    if (outRowBytes) {
+        *outRowBytes = localBitmap->rowBytes();
+    }
+    if (isHardware) {
+        *isHardware = localBitmap->isHardware();
+    }
+    return localBitmap->info();
+}
+
+bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,
+        int x, int y, int width, int height, SkBitmap* dstBitmap) {
+    const jint* array = env->GetIntArrayElements(srcColors, NULL);
+    const SkColor* src = (const SkColor*)array + srcOffset;
+
+    auto sRGB = SkColorSpace::MakeSRGB();
+    SkImageInfo srcInfo = SkImageInfo::Make(
+            width, height, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, sRGB);
+    SkPixmap srcPM(srcInfo, src, srcStride * 4);
+
+    dstBitmap->writePixels(srcPM, x, y);
+
+    env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array), JNI_ABORT);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int getPremulBitmapCreateFlags(bool isMutable) {
+    int flags = android::bitmap::kBitmapCreateFlag_Premultiplied;
+    if (isMutable) flags |= android::bitmap::kBitmapCreateFlag_Mutable;
+    return flags;
+}
+
+static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
+                              jint offset, jint stride, jint width, jint height,
+                              jint configHandle, jboolean isMutable,
+                              jlong colorSpacePtr) {
+    SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle);
+    if (NULL != jColors) {
+        size_t n = env->GetArrayLength(jColors);
+        if (n < SkAbs32(stride) * (size_t)height) {
+            doThrowAIOOBE(env);
+            return NULL;
+        }
+    }
+
+    // ARGB_4444 is a deprecated format, convert automatically to 8888
+    if (colorType == kARGB_4444_SkColorType) {
+        colorType = kN32_SkColorType;
+    }
+
+    sk_sp<SkColorSpace> colorSpace;
+    if (colorType == kAlpha_8_SkColorType) {
+        colorSpace = nullptr;
+    } else {
+        colorSpace = GraphicsJNI::getNativeColorSpace(colorSpacePtr);
+    }
+
+    SkBitmap bitmap;
+    bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType,
+                colorSpace));
+
+    sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap);
+    if (!nativeBitmap) {
+        ALOGE("OOM allocating Bitmap with dimensions %i x %i", width, height);
+        doThrowOOME(env);
+        return NULL;
+    }
+
+    if (jColors != NULL) {
+        GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, &bitmap);
+    }
+
+    return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable));
+}
+
+static bool bitmapCopyTo(SkBitmap* dst, SkColorType dstCT, const SkBitmap& src,
+        SkBitmap::Allocator* alloc) {
+    SkPixmap srcPM;
+    if (!src.peekPixels(&srcPM)) {
+        return false;
+    }
+
+    SkImageInfo dstInfo = srcPM.info().makeColorType(dstCT);
+    switch (dstCT) {
+        case kRGB_565_SkColorType:
+            dstInfo = dstInfo.makeAlphaType(kOpaque_SkAlphaType);
+            break;
+        case kAlpha_8_SkColorType:
+            dstInfo = dstInfo.makeColorSpace(nullptr);
+            break;
+        default:
+            break;
+    }
+
+    if (!dstInfo.colorSpace() && dstCT != kAlpha_8_SkColorType) {
+        dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGB());
+    }
+
+    if (!dst->setInfo(dstInfo)) {
+        return false;
+    }
+    if (!dst->tryAllocPixels(alloc)) {
+        return false;
+    }
+
+    SkPixmap dstPM;
+    if (!dst->peekPixels(&dstPM)) {
+        return false;
+    }
+
+    return srcPM.readPixels(dstPM);
+}
+
+static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
+                           jint dstConfigHandle, jboolean isMutable) {
+    SkBitmap src;
+    reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src);
+    if (dstConfigHandle == GraphicsJNI::hardwareLegacyBitmapConfig()) {
+        sk_sp<Bitmap> bitmap(Bitmap::allocateHardwareBitmap(src));
+        if (!bitmap.get()) {
+            return NULL;
+        }
+        return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(isMutable));
+    }
+
+    SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle);
+    SkBitmap result;
+    HeapAllocator allocator;
+
+    if (!bitmapCopyTo(&result, dstCT, src, &allocator)) {
+        return NULL;
+    }
+    auto bitmap = allocator.getStorageObjAndReset();
+    return createBitmap(env, bitmap, getPremulBitmapCreateFlags(isMutable));
+}
+
+static Bitmap* Bitmap_copyAshmemImpl(JNIEnv* env, SkBitmap& src, SkColorType& dstCT) {
+    SkBitmap result;
+
+    AshmemPixelAllocator allocator(env);
+    if (!bitmapCopyTo(&result, dstCT, src, &allocator)) {
+        return NULL;
+    }
+    auto bitmap = allocator.getStorageObjAndReset();
+    bitmap->setImmutable();
+    return bitmap;
+}
+
+static jobject Bitmap_copyAshmem(JNIEnv* env, jobject, jlong srcHandle) {
+    SkBitmap src;
+    reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src);
+    SkColorType dstCT = src.colorType();
+    auto bitmap = Bitmap_copyAshmemImpl(env, src, dstCT);
+    jobject ret = createBitmap(env, bitmap, getPremulBitmapCreateFlags(false));
+    return ret;
+}
+
+static jobject Bitmap_copyAshmemConfig(JNIEnv* env, jobject, jlong srcHandle, jint dstConfigHandle) {
+    SkBitmap src;
+    reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src);
+    SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle);
+    auto bitmap = Bitmap_copyAshmemImpl(env, src, dstCT);
+    jobject ret = createBitmap(env, bitmap, getPremulBitmapCreateFlags(false));
+    return ret;
+}
+
+static void Bitmap_destruct(BitmapWrapper* bitmap) {
+    delete bitmap;
+}
+
+static jlong Bitmap_getNativeFinalizer(JNIEnv*, jobject) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Bitmap_destruct));
+}
+
+static void Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    bitmap->freePixels();
+}
+
+static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
+        jint width, jint height, jint configHandle, jboolean requestPremul) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    bitmap->assertValid();
+    SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle);
+
+    // ARGB_4444 is a deprecated format, convert automatically to 8888
+    if (colorType == kARGB_4444_SkColorType) {
+        colorType = kN32_SkColorType;
+    }
+    size_t requestedSize = width * height * SkColorTypeBytesPerPixel(colorType);
+    if (requestedSize > bitmap->getAllocationByteCount()) {
+        // done in native as there's no way to get BytesPerPixel in Java
+        doThrowIAE(env, "Bitmap not large enough to support new configuration");
+        return;
+    }
+    SkAlphaType alphaType;
+    if (bitmap->info().colorType() != kRGB_565_SkColorType
+            && bitmap->info().alphaType() == kOpaque_SkAlphaType) {
+        // If the original bitmap was set to opaque, keep that setting, unless it
+        // was 565, which is required to be opaque.
+        alphaType = kOpaque_SkAlphaType;
+    } else {
+        // Otherwise respect the premultiplied request.
+        alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
+    }
+    bitmap->bitmap().reconfigure(SkImageInfo::Make(width, height, colorType, alphaType,
+            sk_ref_sp(bitmap->info().colorSpace())));
+}
+
+static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
+                                jint format, jint quality,
+                                jobject jstream, jbyteArray jstorage) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    if (!bitmap.valid()) {
+        return JNI_FALSE;
+    }
+
+    std::unique_ptr<SkWStream> strm(CreateJavaOutputStreamAdaptor(env, jstream, jstorage));
+    if (!strm.get()) {
+        return JNI_FALSE;
+    }
+
+    auto fm = static_cast<Bitmap::JavaCompressFormat>(format);
+    return bitmap->bitmap().compress(fm, quality, strm.get()) ? JNI_TRUE : JNI_FALSE;
+}
+
+static inline void bitmapErase(SkBitmap bitmap, const SkColor4f& color,
+        const sk_sp<SkColorSpace>& colorSpace) {
+    SkPaint p;
+    p.setColor4f(color, colorSpace.get());
+    p.setBlendMode(SkBlendMode::kSrc);
+    SkCanvas canvas(bitmap);
+    canvas.drawPaint(p);
+}
+
+static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    SkBitmap skBitmap;
+    bitmap->getSkBitmap(&skBitmap);
+    bitmapErase(skBitmap, SkColor4f::FromColor(color), SkColorSpace::MakeSRGB());
+}
+
+static void Bitmap_eraseLong(JNIEnv* env, jobject, jlong bitmapHandle,
+        jlong colorSpaceHandle, jlong colorLong) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    SkBitmap skBitmap;
+    bitmap->getSkBitmap(&skBitmap);
+
+    SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
+    sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+    bitmapErase(skBitmap, color, cs);
+}
+
+static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    return static_cast<jint>(bitmap->rowBytes());
+}
+
+static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    if (bitmap->isHardware()) {
+        return GraphicsJNI::hardwareLegacyBitmapConfig();
+    }
+    return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->info().colorType());
+}
+
+static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    return static_cast<jint>(bitmap->getGenerationID());
+}
+
+static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    if (bitmap->info().alphaType() == kPremul_SkAlphaType) {
+        return JNI_TRUE;
+    }
+    return JNI_FALSE;
+}
+
+static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    return !bitmap->info().isOpaque() ? JNI_TRUE : JNI_FALSE;
+}
+
+static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle,
+        jboolean hasAlpha, jboolean requestPremul) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    if (hasAlpha) {
+        bitmap->setAlphaType(
+                requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
+    } else {
+        bitmap->setAlphaType(kOpaque_SkAlphaType);
+    }
+}
+
+static void Bitmap_setPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
+        jboolean isPremul) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    if (!bitmap->info().isOpaque()) {
+        if (isPremul) {
+            bitmap->setAlphaType(kPremul_SkAlphaType);
+        } else {
+            bitmap->setAlphaType(kUnpremul_SkAlphaType);
+        }
+    }
+}
+
+static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE;
+}
+
+static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle,
+                                jboolean hasMipMap) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    bitmap->setHasHardwareMipMap(hasMipMap);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// This is the maximum possible size because the SkColorSpace must be
+// representable (and therefore serializable) using a matrix and numerical
+// transfer function.  If we allow more color space representations in the
+// framework, we may need to update this maximum size.
+static constexpr uint32_t kMaxColorSpaceSerializedBytes = 80;
+
+static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
+#ifdef __ANDROID__ // Layoutlib does not support parcel
+    if (parcel == NULL) {
+        SkDebugf("-------- unparcel parcel is NULL\n");
+        return NULL;
+    }
+
+    android::Parcel* p = android::parcelForJavaObject(env, parcel);
+
+    const SkColorType colorType = (SkColorType)p->readInt32();
+    const SkAlphaType alphaType = (SkAlphaType)p->readInt32();
+    const uint32_t    colorSpaceSize = p->readUint32();
+    sk_sp<SkColorSpace> colorSpace;
+    if (colorSpaceSize > 0) {
+        if (colorSpaceSize > kMaxColorSpaceSerializedBytes) {
+            ALOGD("Bitmap_createFromParcel: Serialized SkColorSpace is larger than expected: "
+                    "%d bytes\n", colorSpaceSize);
+        }
+
+        const void* data = p->readInplace(colorSpaceSize);
+        if (data) {
+            colorSpace = SkColorSpace::Deserialize(data, colorSpaceSize);
+        } else {
+            ALOGD("Bitmap_createFromParcel: Unable to read serialized SkColorSpace data\n");
+        }
+    }
+    const int         width = p->readInt32();
+    const int         height = p->readInt32();
+    const int         rowBytes = p->readInt32();
+    const int         density = p->readInt32();
+
+    if (kN32_SkColorType != colorType &&
+            kRGBA_F16_SkColorType != colorType &&
+            kRGB_565_SkColorType != colorType &&
+            kARGB_4444_SkColorType != colorType &&
+            kAlpha_8_SkColorType != colorType) {
+        SkDebugf("Bitmap_createFromParcel unknown colortype: %d\n", colorType);
+        return NULL;
+    }
+
+    std::unique_ptr<SkBitmap> bitmap(new SkBitmap);
+    if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType, colorSpace),
+            rowBytes)) {
+        return NULL;
+    }
+
+    // Read the bitmap blob.
+    size_t size = bitmap->computeByteSize();
+    android::Parcel::ReadableBlob blob;
+    android::status_t status = p->readBlob(size, &blob);
+    if (status) {
+        doThrowRE(env, "Could not read bitmap blob.");
+        return NULL;
+    }
+
+    // Map the bitmap in place from the ashmem region if possible otherwise copy.
+    sk_sp<Bitmap> nativeBitmap;
+    if (blob.fd() >= 0 && !blob.isMutable()) {
+#if DEBUG_PARCEL
+        ALOGD("Bitmap.createFromParcel: mapped contents of bitmap from %s blob "
+                "(fds %s)",
+                blob.isMutable() ? "mutable" : "immutable",
+                p->allowFds() ? "allowed" : "forbidden");
+#endif
+        // Dup the file descriptor so we can keep a reference to it after the Parcel
+        // is disposed.
+        int dupFd = fcntl(blob.fd(), F_DUPFD_CLOEXEC, 0);
+        if (dupFd < 0) {
+            ALOGE("Error allocating dup fd. Error:%d", errno);
+            blob.release();
+            doThrowRE(env, "Could not allocate dup blob fd.");
+            return NULL;
+        }
+
+        // Map the pixels in place and take ownership of the ashmem region. We must also respect the
+        // rowBytes value already set on the bitmap instead of attempting to compute our own.
+        nativeBitmap = Bitmap::createFrom(bitmap->info(), bitmap->rowBytes(), dupFd,
+                                          const_cast<void*>(blob.data()), size, true);
+        if (!nativeBitmap) {
+            close(dupFd);
+            blob.release();
+            doThrowRE(env, "Could not allocate ashmem pixel ref.");
+            return NULL;
+        }
+
+        // Clear the blob handle, don't release it.
+        blob.clear();
+    } else {
+#if DEBUG_PARCEL
+        if (blob.fd() >= 0) {
+            ALOGD("Bitmap.createFromParcel: copied contents of mutable bitmap "
+                    "from immutable blob (fds %s)",
+                    p->allowFds() ? "allowed" : "forbidden");
+        } else {
+            ALOGD("Bitmap.createFromParcel: copied contents from %s blob "
+                    "(fds %s)",
+                    blob.isMutable() ? "mutable" : "immutable",
+                    p->allowFds() ? "allowed" : "forbidden");
+        }
+#endif
+
+        // Copy the pixels into a new buffer.
+        nativeBitmap = Bitmap::allocateHeapBitmap(bitmap.get());
+        if (!nativeBitmap) {
+            blob.release();
+            doThrowRE(env, "Could not allocate java pixel ref.");
+            return NULL;
+        }
+        memcpy(bitmap->getPixels(), blob.data(), size);
+
+        // Release the blob handle.
+        blob.release();
+    }
+
+    return createBitmap(env, nativeBitmap.release(),
+            getPremulBitmapCreateFlags(false), NULL, NULL, density);
+#else
+    doThrowRE(env, "Cannot use parcels outside of Android");
+    return NULL;
+#endif
+}
+
+static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
+                                     jlong bitmapHandle, jint density, jobject parcel) {
+#ifdef __ANDROID__ // Layoutlib does not support parcel
+    if (parcel == NULL) {
+        SkDebugf("------- writeToParcel null parcel\n");
+        return JNI_FALSE;
+    }
+
+    android::Parcel* p = android::parcelForJavaObject(env, parcel);
+    SkBitmap bitmap;
+
+    auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle);
+    bitmapWrapper->getSkBitmap(&bitmap);
+
+    p->writeInt32(bitmap.colorType());
+    p->writeInt32(bitmap.alphaType());
+    SkColorSpace* colorSpace = bitmap.colorSpace();
+    if (colorSpace != nullptr) {
+        sk_sp<SkData> data = colorSpace->serialize();
+        size_t size = data->size();
+        p->writeUint32(size);
+        if (size > 0) {
+            if (size > kMaxColorSpaceSerializedBytes) {
+                ALOGD("Bitmap_writeToParcel: Serialized SkColorSpace is larger than expected: "
+                        "%zu bytes\n", size);
+            }
+
+            p->write(data->data(), size);
+        }
+    } else {
+        p->writeUint32(0);
+    }
+    p->writeInt32(bitmap.width());
+    p->writeInt32(bitmap.height());
+    p->writeInt32(bitmap.rowBytes());
+    p->writeInt32(density);
+
+    // Transfer the underlying ashmem region if we have one and it's immutable.
+    android::status_t status;
+    int fd = bitmapWrapper->bitmap().getAshmemFd();
+    if (fd >= 0 && p->allowFds()) {
+#if DEBUG_PARCEL
+        ALOGD("Bitmap.writeToParcel: transferring immutable bitmap's ashmem fd as "
+                "immutable blob (fds %s)",
+                p->allowFds() ? "allowed" : "forbidden");
+#endif
+
+        status = p->writeDupImmutableBlobFileDescriptor(fd);
+        if (status) {
+            doThrowRE(env, "Could not write bitmap blob file descriptor.");
+            return JNI_FALSE;
+        }
+        return JNI_TRUE;
+    }
+
+    // Copy the bitmap to a new blob.
+#if DEBUG_PARCEL
+    ALOGD("Bitmap.writeToParcel: copying bitmap into new blob (fds %s)",
+            p->allowFds() ? "allowed" : "forbidden");
+#endif
+
+    size_t size = bitmap.computeByteSize();
+    android::Parcel::WritableBlob blob;
+    status = p->writeBlob(size, false, &blob);
+    if (status) {
+        doThrowRE(env, "Could not copy bitmap to parcel blob.");
+        return JNI_FALSE;
+    }
+
+    const void* pSrc =  bitmap.getPixels();
+    if (pSrc == NULL) {
+        memset(blob.data(), 0, size);
+    } else {
+        memcpy(blob.data(), pSrc, size);
+    }
+
+    blob.release();
+    return JNI_TRUE;
+#else
+    doThrowRE(env, "Cannot use parcels outside of Android");
+    return JNI_FALSE;
+#endif
+}
+
+static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
+                                   jlong srcHandle, jlong paintHandle,
+                                   jintArray offsetXY) {
+    SkBitmap src;
+    reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src);
+    const android::Paint* paint = reinterpret_cast<android::Paint*>(paintHandle);
+    SkIPoint  offset;
+    SkBitmap dst;
+    HeapAllocator allocator;
+
+    src.extractAlpha(&dst, paint, &allocator, &offset);
+    // If Skia can't allocate pixels for destination bitmap, it resets
+    // it, that is set its pixels buffer to NULL, and zero width and height.
+    if (dst.getPixels() == NULL && src.getPixels() != NULL) {
+        doThrowOOME(env, "failed to allocate pixels for alpha");
+        return NULL;
+    }
+    if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
+        int* array = env->GetIntArrayElements(offsetXY, NULL);
+        array[0] = offset.fX;
+        array[1] = offset.fY;
+        env->ReleaseIntArrayElements(offsetXY, array, 0);
+    }
+
+    return createBitmap(env, allocator.getStorageObjAndReset(),
+            getPremulBitmapCreateFlags(true));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jboolean Bitmap_isSRGB(JNIEnv* env, jobject, jlong bitmapHandle) {
+    LocalScopedBitmap bitmapHolder(bitmapHandle);
+    if (!bitmapHolder.valid()) return JNI_TRUE;
+
+    SkColorSpace* colorSpace = bitmapHolder->info().colorSpace();
+    return colorSpace == nullptr || colorSpace->isSRGB();
+}
+
+static jboolean Bitmap_isSRGBLinear(JNIEnv* env, jobject, jlong bitmapHandle) {
+    LocalScopedBitmap bitmapHolder(bitmapHandle);
+    if (!bitmapHolder.valid()) return JNI_FALSE;
+
+    SkColorSpace* colorSpace = bitmapHolder->info().colorSpace();
+    sk_sp<SkColorSpace> srgbLinear = SkColorSpace::MakeSRGBLinear();
+    return colorSpace == srgbLinear.get() ? JNI_TRUE : JNI_FALSE;
+}
+
+static jobject Bitmap_computeColorSpace(JNIEnv* env, jobject, jlong bitmapHandle) {
+    LocalScopedBitmap bitmapHolder(bitmapHandle);
+    if (!bitmapHolder.valid()) return nullptr;
+
+    SkColorSpace* colorSpace = bitmapHolder->info().colorSpace();
+    if (colorSpace == nullptr) return nullptr;
+
+    return GraphicsJNI::getColorSpace(env, colorSpace, bitmapHolder->info().colorType());
+}
+
+static void Bitmap_setColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, jlong colorSpacePtr) {
+    LocalScopedBitmap bitmapHolder(bitmapHandle);
+    sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpacePtr);
+    bitmapHolder->setColorSpace(cs);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
+        jint x, jint y) {
+    SkBitmap bitmap;
+    reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap);
+
+    auto sRGB = SkColorSpace::MakeSRGB();
+    SkImageInfo dstInfo = SkImageInfo::Make(
+            1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, sRGB);
+
+    SkColor dst;
+    bitmap.readPixels(dstInfo, &dst, dstInfo.minRowBytes(), x, y);
+    return static_cast<jint>(dst);
+}
+
+static jlong Bitmap_getColor(JNIEnv* env, jobject, jlong bitmapHandle,
+        jint x, jint y) {
+    SkBitmap bitmap;
+    reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap);
+
+    SkImageInfo dstInfo = SkImageInfo::Make(
+            1, 1, kRGBA_F16_SkColorType, kUnpremul_SkAlphaType, bitmap.refColorSpace());
+
+    uint64_t dst;
+    bitmap.readPixels(dstInfo, &dst, dstInfo.minRowBytes(), x, y);
+    return static_cast<jlong>(dst);
+}
+
+static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,
+        jintArray pixelArray, jint offset, jint stride,
+        jint x, jint y, jint width, jint height) {
+    SkBitmap bitmap;
+    reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap);
+
+    auto sRGB = SkColorSpace::MakeSRGB();
+    SkImageInfo dstInfo = SkImageInfo::Make(
+            width, height, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, sRGB);
+
+    jint* dst = env->GetIntArrayElements(pixelArray, NULL);
+    bitmap.readPixels(dstInfo, dst + offset, stride * 4, x, y);
+    env->ReleaseIntArrayElements(pixelArray, dst, 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
+        jint x, jint y, jint colorHandle) {
+    SkBitmap bitmap;
+    reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap);
+    SkColor color = static_cast<SkColor>(colorHandle);
+
+    auto sRGB = SkColorSpace::MakeSRGB();
+    SkImageInfo srcInfo = SkImageInfo::Make(
+            1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, sRGB);
+    SkPixmap srcPM(srcInfo, &color, srcInfo.minRowBytes());
+
+    bitmap.writePixels(srcPM, x, y);
+}
+
+static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle,
+        jintArray pixelArray, jint offset, jint stride,
+        jint x, jint y, jint width, jint height) {
+    SkBitmap bitmap;
+    reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap);
+    GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
+            x, y, width, height, &bitmap);
+}
+
+static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
+                                      jlong bitmapHandle, jobject jbuffer) {
+    SkBitmap bitmap;
+    reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap);
+    const void* src = bitmap.getPixels();
+
+    if (NULL != src) {
+        android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
+
+        // the java side has already checked that buffer is large enough
+        memcpy(abp.pointer(), src, bitmap.computeByteSize());
+    }
+}
+
+static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
+                                        jlong bitmapHandle, jobject jbuffer) {
+    SkBitmap bitmap;
+    reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap);
+    void* dst = bitmap.getPixels();
+
+    if (NULL != dst) {
+        android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
+        // the java side has already checked that buffer is large enough
+        memcpy(dst, abp.pointer(), bitmap.computeByteSize());
+        bitmap.notifyPixelsChanged();
+    }
+}
+
+static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, jlong bm1Handle) {
+    SkBitmap bm0;
+    SkBitmap bm1;
+
+    LocalScopedBitmap bitmap0(bm0Handle);
+    LocalScopedBitmap bitmap1(bm1Handle);
+
+    // Paying the price for making Hardware Bitmap as Config:
+    // later check for colorType will pass successfully,
+    // because Hardware Config internally may be RGBA8888 or smth like that.
+    if (bitmap0->isHardware() != bitmap1->isHardware()) {
+        return JNI_FALSE;
+    }
+
+    bitmap0->bitmap().getSkBitmap(&bm0);
+    bitmap1->bitmap().getSkBitmap(&bm1);
+    if (bm0.width() != bm1.width()
+            || bm0.height() != bm1.height()
+            || bm0.colorType() != bm1.colorType()
+            || bm0.alphaType() != bm1.alphaType()
+            || !SkColorSpace::Equals(bm0.colorSpace(), bm1.colorSpace())) {
+        return JNI_FALSE;
+    }
+
+    // if we can't load the pixels, return false
+    if (NULL == bm0.getPixels() || NULL == bm1.getPixels()) {
+        return JNI_FALSE;
+    }
+
+    // now compare each scanline. We can't do the entire buffer at once,
+    // since we don't care about the pixel values that might extend beyond
+    // the width (since the scanline might be larger than the logical width)
+    const int h = bm0.height();
+    const size_t size = bm0.width() * bm0.bytesPerPixel();
+    for (int y = 0; y < h; y++) {
+        // SkBitmap::getAddr(int, int) may return NULL due to unrecognized config
+        // (ex: kRLE_Index8_Config). This will cause memcmp method to crash. Since bm0
+        // and bm1 both have pixel data() (have passed NULL == getPixels() check),
+        // those 2 bitmaps should be valid (only unrecognized), we return JNI_FALSE
+        // to warn user those 2 unrecognized config bitmaps may be different.
+        void *bm0Addr = bm0.getAddr(0, y);
+        void *bm1Addr = bm1.getAddr(0, y);
+
+        if(bm0Addr == NULL || bm1Addr == NULL) {
+            return JNI_FALSE;
+        }
+
+        if (memcmp(bm0Addr, bm1Addr, size) != 0) {
+            return JNI_FALSE;
+        }
+    }
+    return JNI_TRUE;
+}
+
+static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapPtr) {
+#ifdef __ANDROID__ // Layoutlib does not support render thread
+    LocalScopedBitmap bitmapHandle(bitmapPtr);
+    if (!bitmapHandle.valid()) return;
+    android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmapHandle->bitmap());
+#endif
+}
+
+static jint Bitmap_getAllocationByteCount(JNIEnv* env, jobject, jlong bitmapPtr) {
+    LocalScopedBitmap bitmapHandle(bitmapPtr);
+    return static_cast<jint>(bitmapHandle->getAllocationByteCount());
+}
+
+static jobject Bitmap_copyPreserveInternalConfig(JNIEnv* env, jobject, jlong bitmapPtr) {
+    LocalScopedBitmap bitmapHandle(bitmapPtr);
+    LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(),
+            "Hardware config is only supported config in Bitmap_nativeCopyPreserveInternalConfig");
+    Bitmap& hwuiBitmap = bitmapHandle->bitmap();
+    SkBitmap src;
+    hwuiBitmap.getSkBitmap(&src);
+
+    if (src.pixelRef() == nullptr) {
+        doThrowRE(env, "Could not copy a hardware bitmap.");
+        return NULL;
+    }
+
+    sk_sp<Bitmap> bitmap = Bitmap::createFrom(src.info(), *src.pixelRef());
+    return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false));
+}
+
+#ifdef __ANDROID__ // Layoutlib does not support graphic buffer
+typedef AHardwareBuffer* (*AHB_from_HB)(JNIEnv*, jobject);
+AHB_from_HB AHardwareBuffer_fromHardwareBuffer;
+
+typedef jobject (*AHB_to_HB)(JNIEnv*, AHardwareBuffer*);
+AHB_to_HB AHardwareBuffer_toHardwareBuffer;
+#endif
+
+static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer,
+                                               jlong colorSpacePtr) {
+#ifdef __ANDROID__ // Layoutlib does not support graphic buffer
+    AHardwareBuffer* buffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBuffer);
+    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer,
+                                              GraphicsJNI::getNativeColorSpace(colorSpacePtr));
+    if (!bitmap.get()) {
+        ALOGW("failed to create hardware bitmap from hardware buffer");
+        return NULL;
+    }
+    return bitmap::createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false));
+#else
+    return NULL;
+#endif
+}
+
+static jobject Bitmap_createGraphicBufferHandle(JNIEnv* env, jobject, jlong bitmapPtr) {
+#ifdef __ANDROID__ // Layoutlib does not support graphic buffer
+    LocalScopedBitmap bitmapHandle(bitmapPtr);
+    LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(),
+            "Hardware config is only supported config in Bitmap_getGraphicBuffer");
+
+    Bitmap& bitmap = bitmapHandle->bitmap();
+    return android_graphics_GraphicBuffer_createFromAHardwareBuffer(env, bitmap.hardwareBuffer());
+#else
+    return NULL;
+#endif
+}
+
+static jobject Bitmap_getHardwareBuffer(JNIEnv* env, jobject, jlong bitmapPtr) {
+#ifdef __ANDROID__ // Layoutlib does not support graphic buffer
+    LocalScopedBitmap bitmapHandle(bitmapPtr);
+    LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(),
+            "Hardware config is only supported config in Bitmap_getHardwareBuffer");
+
+    Bitmap& bitmap = bitmapHandle->bitmap();
+    return AHardwareBuffer_toHardwareBuffer(env, bitmap.hardwareBuffer());
+#else
+    return NULL;
+#endif
+}
+
+static jboolean Bitmap_isImmutable(CRITICAL_JNI_PARAMS_COMMA jlong bitmapHandle) {
+    LocalScopedBitmap bitmapHolder(bitmapHandle);
+    if (!bitmapHolder.valid()) return JNI_FALSE;
+
+    return bitmapHolder->bitmap().isImmutable() ? JNI_TRUE : JNI_FALSE;
+}
+
+static void Bitmap_setImmutable(JNIEnv* env, jobject, jlong bitmapHandle) {
+    LocalScopedBitmap bitmapHolder(bitmapHandle);
+    if (!bitmapHolder.valid()) return;
+
+    return bitmapHolder->bitmap().setImmutable();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gBitmapMethods[] = {
+    {   "nativeCreate",             "([IIIIIIZJ)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_creator },
+    {   "nativeCopy",               "(JIZ)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_copy },
+    {   "nativeCopyAshmem",         "(J)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_copyAshmem },
+    {   "nativeCopyAshmemConfig",   "(JI)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_copyAshmemConfig },
+    {   "nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer },
+    {   "nativeRecycle",            "(J)V", (void*)Bitmap_recycle },
+    {   "nativeReconfigure",        "(JIIIZ)V", (void*)Bitmap_reconfigure },
+    {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",
+        (void*)Bitmap_compress },
+    {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },
+    {   "nativeErase",              "(JJJ)V", (void*)Bitmap_eraseLong },
+    {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },
+    {   "nativeConfig",             "(J)I", (void*)Bitmap_config },
+    {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },
+    {   "nativeIsPremultiplied",    "(J)Z", (void*)Bitmap_isPremultiplied},
+    {   "nativeSetHasAlpha",        "(JZZ)V", (void*)Bitmap_setHasAlpha},
+    {   "nativeSetPremultiplied",   "(JZ)V", (void*)Bitmap_setPremultiplied},
+    {   "nativeHasMipMap",          "(J)Z", (void*)Bitmap_hasMipMap },
+    {   "nativeSetHasMipMap",       "(JZ)V", (void*)Bitmap_setHasMipMap },
+    {   "nativeCreateFromParcel",
+        "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_createFromParcel },
+    {   "nativeWriteToParcel",      "(JILandroid/os/Parcel;)Z",
+        (void*)Bitmap_writeToParcel },
+    {   "nativeExtractAlpha",       "(JJ[I)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_extractAlpha },
+    {   "nativeGenerationId",       "(J)I", (void*)Bitmap_getGenerationId },
+    {   "nativeGetPixel",           "(JII)I", (void*)Bitmap_getPixel },
+    {   "nativeGetColor",           "(JII)J", (void*)Bitmap_getColor },
+    {   "nativeGetPixels",          "(J[IIIIIII)V", (void*)Bitmap_getPixels },
+    {   "nativeSetPixel",           "(JIII)V", (void*)Bitmap_setPixel },
+    {   "nativeSetPixels",          "(J[IIIIIII)V", (void*)Bitmap_setPixels },
+    {   "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",
+                                            (void*)Bitmap_copyPixelsToBuffer },
+    {   "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
+                                            (void*)Bitmap_copyPixelsFromBuffer },
+    {   "nativeSameAs",             "(JJ)Z", (void*)Bitmap_sameAs },
+    {   "nativePrepareToDraw",      "(J)V", (void*)Bitmap_prepareToDraw },
+    {   "nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount },
+    {   "nativeCopyPreserveInternalConfig", "(J)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_copyPreserveInternalConfig },
+    {   "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;",
+        (void*) Bitmap_wrapHardwareBufferBitmap },
+    {   "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;",
+        (void*) Bitmap_createGraphicBufferHandle },
+    {   "nativeGetHardwareBuffer", "(J)Landroid/hardware/HardwareBuffer;",
+        (void*) Bitmap_getHardwareBuffer },
+    {   "nativeComputeColorSpace",  "(J)Landroid/graphics/ColorSpace;", (void*)Bitmap_computeColorSpace },
+    {   "nativeSetColorSpace",      "(JJ)V", (void*)Bitmap_setColorSpace },
+    {   "nativeIsSRGB",             "(J)Z", (void*)Bitmap_isSRGB },
+    {   "nativeIsSRGBLinear",       "(J)Z", (void*)Bitmap_isSRGBLinear},
+    {   "nativeSetImmutable",       "(J)V", (void*)Bitmap_setImmutable},
+
+    // ------------ @CriticalNative ----------------
+    {   "nativeIsImmutable",        "(J)Z", (void*)Bitmap_isImmutable}
+
+};
+
+int register_android_graphics_Bitmap(JNIEnv* env)
+{
+    gBitmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap"));
+    gBitmap_nativePtr = GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J");
+    gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "<init>", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V");
+    gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V");
+
+#ifdef __ANDROID__ // Layoutlib does not support graphic buffer
+    void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+    AHardwareBuffer_fromHardwareBuffer =
+            (AHB_from_HB)dlsym(handle_, "AHardwareBuffer_fromHardwareBuffer");
+    LOG_ALWAYS_FATAL_IF(AHardwareBuffer_fromHardwareBuffer == nullptr,
+                        "Failed to find required symbol AHardwareBuffer_fromHardwareBuffer!");
+
+    AHardwareBuffer_toHardwareBuffer = (AHB_to_HB)dlsym(handle_, "AHardwareBuffer_toHardwareBuffer");
+    LOG_ALWAYS_FATAL_IF(AHardwareBuffer_toHardwareBuffer == nullptr,
+                        " Failed to find required symbol AHardwareBuffer_toHardwareBuffer!");
+#endif
+    return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods,
+                                         NELEM(gBitmapMethods));
+}
diff --git a/libs/hwui/jni/Bitmap.h b/libs/hwui/jni/Bitmap.h
new file mode 100644
index 0000000..73eca3a
--- /dev/null
+++ b/libs/hwui/jni/Bitmap.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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 BITMAP_H_
+#define BITMAP_H_
+
+#include <jni.h>
+#include <android/bitmap.h>
+
+class SkBitmap;
+struct SkImageInfo;
+
+namespace android {
+
+class Bitmap;
+
+namespace bitmap {
+
+enum BitmapCreateFlags {
+    kBitmapCreateFlag_None = 0x0,
+    kBitmapCreateFlag_Mutable = 0x1,
+    kBitmapCreateFlag_Premultiplied = 0x2,
+};
+
+jobject createBitmap(JNIEnv* env, Bitmap* bitmap,
+            int bitmapCreateFlags, jbyteArray ninePatchChunk = nullptr,
+            jobject ninePatchInsets = nullptr, int density = -1);
+
+Bitmap& toBitmap(jlong bitmapHandle);
+
+/** Reinitialize a bitmap. bitmap must already have its SkAlphaType set in
+    sync with isPremultiplied
+*/
+void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info,
+        bool isPremultiplied);
+
+} // namespace bitmap
+
+} // namespace android
+
+#endif /* BITMAP_H_ */
diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp
new file mode 100644
index 0000000..adedffd
--- /dev/null
+++ b/libs/hwui/jni/BitmapFactory.cpp
@@ -0,0 +1,664 @@
+#define LOG_TAG "BitmapFactory"
+
+#include "BitmapFactory.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "GraphicsJNI.h"
+#include "NinePatchPeeker.h"
+#include "SkAndroidCodec.h"
+#include "SkBRDAllocator.h"
+#include "SkFrontBufferedStream.h"
+#include "SkMath.h"
+#include "SkPixelRef.h"
+#include "SkStream.h"
+#include "SkUtils.h"
+#include "Utils.h"
+#include "core_jni_helpers.h"
+
+#include <HardwareBitmapUploader.h>
+#include <nativehelper/JNIHelp.h>
+#include <androidfw/Asset.h>
+#include <androidfw/ResourceTypes.h>
+#include <cutils/compiler.h>
+#include <memory>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+jfieldID gOptions_justBoundsFieldID;
+jfieldID gOptions_sampleSizeFieldID;
+jfieldID gOptions_configFieldID;
+jfieldID gOptions_colorSpaceFieldID;
+jfieldID gOptions_premultipliedFieldID;
+jfieldID gOptions_mutableFieldID;
+jfieldID gOptions_ditherFieldID;
+jfieldID gOptions_preferQualityOverSpeedFieldID;
+jfieldID gOptions_scaledFieldID;
+jfieldID gOptions_densityFieldID;
+jfieldID gOptions_screenDensityFieldID;
+jfieldID gOptions_targetDensityFieldID;
+jfieldID gOptions_widthFieldID;
+jfieldID gOptions_heightFieldID;
+jfieldID gOptions_mimeFieldID;
+jfieldID gOptions_outConfigFieldID;
+jfieldID gOptions_outColorSpaceFieldID;
+jfieldID gOptions_mCancelID;
+jfieldID gOptions_bitmapFieldID;
+
+jfieldID gBitmap_ninePatchInsetsFieldID;
+
+jclass gBitmapConfig_class;
+jmethodID gBitmapConfig_nativeToConfigMethodID;
+
+using namespace android;
+
+const char* getMimeType(SkEncodedImageFormat format) {
+    switch (format) {
+        case SkEncodedImageFormat::kBMP:
+            return "image/bmp";
+        case SkEncodedImageFormat::kGIF:
+            return "image/gif";
+        case SkEncodedImageFormat::kICO:
+            return "image/x-ico";
+        case SkEncodedImageFormat::kJPEG:
+            return "image/jpeg";
+        case SkEncodedImageFormat::kPNG:
+            return "image/png";
+        case SkEncodedImageFormat::kWEBP:
+            return "image/webp";
+        case SkEncodedImageFormat::kHEIF:
+            return "image/heif";
+        case SkEncodedImageFormat::kWBMP:
+            return "image/vnd.wap.wbmp";
+        case SkEncodedImageFormat::kDNG:
+            return "image/x-adobe-dng";
+        default:
+            return nullptr;
+    }
+}
+
+jstring getMimeTypeAsJavaString(JNIEnv* env, SkEncodedImageFormat format) {
+    jstring jstr = nullptr;
+    const char* mimeType = getMimeType(format);
+    if (mimeType) {
+        // NOTE: Caller should env->ExceptionCheck() for OOM
+        // (can't check for nullptr as it's a valid return value)
+        jstr = env->NewStringUTF(mimeType);
+    }
+    return jstr;
+}
+
+class ScaleCheckingAllocator : public SkBitmap::HeapAllocator {
+public:
+    ScaleCheckingAllocator(float scale, int size)
+            : mScale(scale), mSize(size) {
+    }
+
+    virtual bool allocPixelRef(SkBitmap* bitmap) {
+        // accounts for scale in final allocation, using eventual size and config
+        const int bytesPerPixel = SkColorTypeBytesPerPixel(bitmap->colorType());
+        const int requestedSize = bytesPerPixel *
+                int(bitmap->width() * mScale + 0.5f) *
+                int(bitmap->height() * mScale + 0.5f);
+        if (requestedSize > mSize) {
+            ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)",
+                    mSize, requestedSize);
+            return false;
+        }
+        return SkBitmap::HeapAllocator::allocPixelRef(bitmap);
+    }
+private:
+    const float mScale;
+    const int mSize;
+};
+
+class RecyclingPixelAllocator : public SkBitmap::Allocator {
+public:
+    RecyclingPixelAllocator(android::Bitmap* bitmap, unsigned int size)
+            : mBitmap(bitmap), mSize(size) {
+    }
+
+    ~RecyclingPixelAllocator() {
+    }
+
+    virtual bool allocPixelRef(SkBitmap* bitmap) {
+        const SkImageInfo& info = bitmap->info();
+        if (info.colorType() == kUnknown_SkColorType) {
+            ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration");
+            return false;
+        }
+
+        const size_t size = info.computeByteSize(bitmap->rowBytes());
+        if (size > SK_MaxS32) {
+            ALOGW("bitmap is too large");
+            return false;
+        }
+
+        if (size > mSize) {
+            ALOGW("bitmap marked for reuse (%u bytes) can't fit new bitmap "
+                  "(%zu bytes)", mSize, size);
+            return false;
+        }
+
+        mBitmap->reconfigure(info, bitmap->rowBytes());
+        bitmap->setPixelRef(sk_ref_sp(mBitmap), 0, 0);
+        return true;
+    }
+
+private:
+    android::Bitmap* const mBitmap;
+    const unsigned int mSize;
+};
+
+// Necessary for decodes when the native decoder cannot scale to appropriately match the sampleSize
+// (for example, RAW). If the sampleSize divides evenly into the dimension, we require that the
+// scale matches exactly. If sampleSize does not divide evenly, we allow the decoder to choose how
+// best to round.
+static bool needsFineScale(const int fullSize, const int decodedSize, const int sampleSize) {
+    if (fullSize % sampleSize == 0 && fullSize / sampleSize != decodedSize) {
+        return true;
+    } else if ((fullSize / sampleSize + 1) != decodedSize &&
+               (fullSize / sampleSize) != decodedSize) {
+        return true;
+    }
+    return false;
+}
+
+static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize,
+                           const int sampleSize) {
+    return needsFineScale(fullSize.width(), decodedSize.width(), sampleSize) ||
+           needsFineScale(fullSize.height(), decodedSize.height(), sampleSize);
+}
+
+static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
+                        jobject padding, jobject options, jlong inBitmapHandle,
+                        jlong colorSpaceHandle) {
+    // Set default values for the options parameters.
+    int sampleSize = 1;
+    bool onlyDecodeSize = false;
+    SkColorType prefColorType = kN32_SkColorType;
+    bool isHardware = false;
+    bool isMutable = false;
+    float scale = 1.0f;
+    bool requireUnpremultiplied = false;
+    jobject javaBitmap = NULL;
+    sk_sp<SkColorSpace> prefColorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+
+    // Update with options supplied by the client.
+    if (options != NULL) {
+        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
+        // Correct a non-positive sampleSize.  sampleSize defaults to zero within the
+        // options object, which is strange.
+        if (sampleSize <= 0) {
+            sampleSize = 1;
+        }
+
+        if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
+            onlyDecodeSize = true;
+        }
+
+        // initialize these, in case we fail later on
+        env->SetIntField(options, gOptions_widthFieldID, -1);
+        env->SetIntField(options, gOptions_heightFieldID, -1);
+        env->SetObjectField(options, gOptions_mimeFieldID, 0);
+        env->SetObjectField(options, gOptions_outConfigFieldID, 0);
+        env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0);
+
+        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
+        prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
+        isHardware = GraphicsJNI::isHardwareConfig(env, jconfig);
+        isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
+        requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
+        javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
+
+        if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
+            const int density = env->GetIntField(options, gOptions_densityFieldID);
+            const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
+            const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
+            if (density != 0 && targetDensity != 0 && density != screenDensity) {
+                scale = (float) targetDensity / density;
+            }
+        }
+    }
+
+    if (isMutable && isHardware) {
+        doThrowIAE(env, "Bitmaps with Config.HARDWARE are always immutable");
+        return nullObjectReturn("Cannot create mutable hardware bitmap");
+    }
+
+    // Create the codec.
+    NinePatchPeeker peeker;
+    std::unique_ptr<SkAndroidCodec> codec;
+    {
+        SkCodec::Result result;
+        std::unique_ptr<SkCodec> c = SkCodec::MakeFromStream(std::move(stream), &result,
+                                                             &peeker);
+        if (!c) {
+            SkString msg;
+            msg.printf("Failed to create image decoder with message '%s'",
+                       SkCodec::ResultToString(result));
+            return nullObjectReturn(msg.c_str());
+        }
+
+        codec = SkAndroidCodec::MakeFromCodec(std::move(c));
+        if (!codec) {
+            return nullObjectReturn("SkAndroidCodec::MakeFromCodec returned null");
+        }
+    }
+
+    // Do not allow ninepatch decodes to 565.  In the past, decodes to 565
+    // would dither, and we do not want to pre-dither ninepatches, since we
+    // know that they will be stretched.  We no longer dither 565 decodes,
+    // but we continue to prevent ninepatches from decoding to 565, in order
+    // to maintain the old behavior.
+    if (peeker.mPatch && kRGB_565_SkColorType == prefColorType) {
+        prefColorType = kN32_SkColorType;
+    }
+
+    // Determine the output size.
+    SkISize size = codec->getSampledDimensions(sampleSize);
+
+    int scaledWidth = size.width();
+    int scaledHeight = size.height();
+    bool willScale = false;
+
+    // Apply a fine scaling step if necessary.
+    if (needsFineScale(codec->getInfo().dimensions(), size, sampleSize)) {
+        willScale = true;
+        scaledWidth = codec->getInfo().width() / sampleSize;
+        scaledHeight = codec->getInfo().height() / sampleSize;
+    }
+
+    // Set the decode colorType
+    SkColorType decodeColorType = codec->computeOutputColorType(prefColorType);
+    if (decodeColorType == kRGBA_F16_SkColorType && isHardware &&
+            !uirenderer::HardwareBitmapUploader::hasFP16Support()) {
+        decodeColorType = kN32_SkColorType;
+    }
+
+    sk_sp<SkColorSpace> decodeColorSpace = codec->computeOutputColorSpace(
+            decodeColorType, prefColorSpace);
+
+    // Set the options and return if the client only wants the size.
+    if (options != NULL) {
+        jstring mimeType = getMimeTypeAsJavaString(env, codec->getEncodedFormat());
+        if (env->ExceptionCheck()) {
+            return nullObjectReturn("OOM in getMimeTypeAsJavaString()");
+        }
+        env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
+        env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
+        env->SetObjectField(options, gOptions_mimeFieldID, mimeType);
+
+        jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType);
+        if (isHardware) {
+            configID = GraphicsJNI::kHardware_LegacyBitmapConfig;
+        }
+        jobject config = env->CallStaticObjectMethod(gBitmapConfig_class,
+                gBitmapConfig_nativeToConfigMethodID, configID);
+        env->SetObjectField(options, gOptions_outConfigFieldID, config);
+
+        env->SetObjectField(options, gOptions_outColorSpaceFieldID,
+                GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType));
+
+        if (onlyDecodeSize) {
+            return nullptr;
+        }
+    }
+
+    // Scale is necessary due to density differences.
+    if (scale != 1.0f) {
+        willScale = true;
+        scaledWidth = static_cast<int>(scaledWidth * scale + 0.5f);
+        scaledHeight = static_cast<int>(scaledHeight * scale + 0.5f);
+    }
+
+    android::Bitmap* reuseBitmap = nullptr;
+    unsigned int existingBufferSize = 0;
+    if (javaBitmap != nullptr) {
+        reuseBitmap = &bitmap::toBitmap(inBitmapHandle);
+        if (reuseBitmap->isImmutable()) {
+            ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
+            javaBitmap = nullptr;
+            reuseBitmap = nullptr;
+        } else {
+            existingBufferSize = reuseBitmap->getAllocationByteCount();
+        }
+    }
+
+    HeapAllocator defaultAllocator;
+    RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize);
+    ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
+    SkBitmap::HeapAllocator heapAllocator;
+    SkBitmap::Allocator* decodeAllocator;
+    if (javaBitmap != nullptr && willScale) {
+        // This will allocate pixels using a HeapAllocator, since there will be an extra
+        // scaling step that copies these pixels into Java memory.  This allocator
+        // also checks that the recycled javaBitmap is large enough.
+        decodeAllocator = &scaleCheckingAllocator;
+    } else if (javaBitmap != nullptr) {
+        decodeAllocator = &recyclingAllocator;
+    } else if (willScale || isHardware) {
+        // This will allocate pixels using a HeapAllocator,
+        // for scale case: there will be an extra scaling step.
+        // for hardware case: there will be extra swizzling & upload to gralloc step.
+        decodeAllocator = &heapAllocator;
+    } else {
+        decodeAllocator = &defaultAllocator;
+    }
+
+    SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied);
+
+    const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(),
+            decodeColorType, alphaType, decodeColorSpace);
+
+    SkImageInfo bitmapInfo = decodeInfo;
+    if (decodeColorType == kGray_8_SkColorType) {
+        // The legacy implementation of BitmapFactory used kAlpha8 for
+        // grayscale images (before kGray8 existed).  While the codec
+        // recognizes kGray8, we need to decode into a kAlpha8 bitmap
+        // in order to avoid a behavior change.
+        bitmapInfo =
+                bitmapInfo.makeColorType(kAlpha_8_SkColorType).makeAlphaType(kPremul_SkAlphaType);
+    }
+    SkBitmap decodingBitmap;
+    if (!decodingBitmap.setInfo(bitmapInfo) ||
+            !decodingBitmap.tryAllocPixels(decodeAllocator)) {
+        // SkAndroidCodec should recommend a valid SkImageInfo, so setInfo()
+        // should only only fail if the calculated value for rowBytes is too
+        // large.
+        // tryAllocPixels() can fail due to OOM on the Java heap, OOM on the
+        // native heap, or the recycled javaBitmap being too small to reuse.
+        return nullptr;
+    }
+
+    // Use SkAndroidCodec to perform the decode.
+    SkAndroidCodec::AndroidOptions codecOptions;
+    codecOptions.fZeroInitialized = decodeAllocator == &defaultAllocator ?
+            SkCodec::kYes_ZeroInitialized : SkCodec::kNo_ZeroInitialized;
+    codecOptions.fSampleSize = sampleSize;
+    SkCodec::Result result = codec->getAndroidPixels(decodeInfo, decodingBitmap.getPixels(),
+            decodingBitmap.rowBytes(), &codecOptions);
+    switch (result) {
+        case SkCodec::kSuccess:
+        case SkCodec::kIncompleteInput:
+            break;
+        default:
+            return nullObjectReturn("codec->getAndroidPixels() failed.");
+    }
+
+    // This is weird so let me explain: we could use the scale parameter
+    // directly, but for historical reasons this is how the corresponding
+    // Dalvik code has always behaved. We simply recreate the behavior here.
+    // The result is slightly different from simply using scale because of
+    // the 0.5f rounding bias applied when computing the target image size
+    const float scaleX = scaledWidth / float(decodingBitmap.width());
+    const float scaleY = scaledHeight / float(decodingBitmap.height());
+
+    jbyteArray ninePatchChunk = NULL;
+    if (peeker.mPatch != NULL) {
+        if (willScale) {
+            peeker.scale(scaleX, scaleY, scaledWidth, scaledHeight);
+        }
+
+        size_t ninePatchArraySize = peeker.mPatch->serializedSize();
+        ninePatchChunk = env->NewByteArray(ninePatchArraySize);
+        if (ninePatchChunk == NULL) {
+            return nullObjectReturn("ninePatchChunk == null");
+        }
+
+        jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL);
+        if (array == NULL) {
+            return nullObjectReturn("primitive array == null");
+        }
+
+        memcpy(array, peeker.mPatch, peeker.mPatchSize);
+        env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
+    }
+
+    jobject ninePatchInsets = NULL;
+    if (peeker.mHasInsets) {
+        ninePatchInsets = peeker.createNinePatchInsets(env, scale);
+        if (ninePatchInsets == NULL) {
+            return nullObjectReturn("nine patch insets == null");
+        }
+        if (javaBitmap != NULL) {
+            env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets);
+        }
+    }
+
+    SkBitmap outputBitmap;
+    if (willScale) {
+        // Set the allocator for the outputBitmap.
+        SkBitmap::Allocator* outputAllocator;
+        if (javaBitmap != nullptr) {
+            outputAllocator = &recyclingAllocator;
+        } else {
+            outputAllocator = &defaultAllocator;
+        }
+
+        SkColorType scaledColorType = decodingBitmap.colorType();
+        // FIXME: If the alphaType is kUnpremul and the image has alpha, the
+        // colors may not be correct, since Skia does not yet support drawing
+        // to/from unpremultiplied bitmaps.
+        outputBitmap.setInfo(
+                bitmapInfo.makeWH(scaledWidth, scaledHeight).makeColorType(scaledColorType));
+        if (!outputBitmap.tryAllocPixels(outputAllocator)) {
+            // This should only fail on OOM.  The recyclingAllocator should have
+            // enough memory since we check this before decoding using the
+            // scaleCheckingAllocator.
+            return nullObjectReturn("allocation failed for scaled bitmap");
+        }
+
+        SkPaint paint;
+        // kSrc_Mode instructs us to overwrite the uninitialized pixels in
+        // outputBitmap.  Otherwise we would blend by default, which is not
+        // what we want.
+        paint.setBlendMode(SkBlendMode::kSrc);
+        paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering
+
+        SkCanvas canvas(outputBitmap, SkCanvas::ColorBehavior::kLegacy);
+        canvas.scale(scaleX, scaleY);
+        canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
+    } else {
+        outputBitmap.swap(decodingBitmap);
+    }
+
+    if (padding) {
+        peeker.getPadding(env, padding);
+    }
+
+    // If we get here, the outputBitmap should have an installed pixelref.
+    if (outputBitmap.pixelRef() == NULL) {
+        return nullObjectReturn("Got null SkPixelRef");
+    }
+
+    if (!isMutable && javaBitmap == NULL) {
+        // promise we will never change our pixels (great for sharing and pictures)
+        outputBitmap.setImmutable();
+    }
+
+    bool isPremultiplied = !requireUnpremultiplied;
+    if (javaBitmap != nullptr) {
+        bitmap::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied);
+        outputBitmap.notifyPixelsChanged();
+        // If a java bitmap was passed in for reuse, pass it back
+        return javaBitmap;
+    }
+
+    int bitmapCreateFlags = 0x0;
+    if (isMutable) bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Mutable;
+    if (isPremultiplied) bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied;
+
+    if (isHardware) {
+        sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(outputBitmap);
+        if (!hardwareBitmap.get()) {
+            return nullObjectReturn("Failed to allocate a hardware bitmap");
+        }
+        return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags,
+                ninePatchChunk, ninePatchInsets, -1);
+    }
+
+    // now create the java bitmap
+    return bitmap::createBitmap(env, defaultAllocator.getStorageObjAndReset(),
+            bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
+}
+
+static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
+        jobject padding, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) {
+
+    jobject bitmap = NULL;
+    std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));
+
+    if (stream.get()) {
+        std::unique_ptr<SkStreamRewindable> bufferedStream(
+                SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded()));
+        SkASSERT(bufferedStream.get() != NULL);
+        bitmap = doDecode(env, std::move(bufferedStream), padding, options, inBitmapHandle,
+                          colorSpaceHandle);
+    }
+    return bitmap;
+}
+
+static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
+        jobject padding, jobject bitmapFactoryOptions, jlong inBitmapHandle, jlong colorSpaceHandle) {
+
+    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
+
+    int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
+
+    struct stat fdStat;
+    if (fstat(descriptor, &fdStat) == -1) {
+        doThrowIOE(env, "broken file descriptor");
+        return nullObjectReturn("fstat return -1");
+    }
+
+    // Restore the descriptor's offset on exiting this function. Even though
+    // we dup the descriptor, both the original and dup refer to the same open
+    // file description and changes to the file offset in one impact the other.
+    AutoFDSeek autoRestore(descriptor);
+
+    // Duplicate the descriptor here to prevent leaking memory. A leak occurs
+    // if we only close the file descriptor and not the file object it is used to
+    // create.  If we don't explicitly clean up the file (which in turn closes the
+    // descriptor) the buffers allocated internally by fseek will be leaked.
+    int dupDescriptor = fcntl(descriptor, F_DUPFD_CLOEXEC, 0);
+
+    FILE* file = fdopen(dupDescriptor, "r");
+    if (file == NULL) {
+        // cleanup the duplicated descriptor since it will not be closed when the
+        // file is cleaned up (fclose).
+        close(dupDescriptor);
+        return nullObjectReturn("Could not open file");
+    }
+
+    std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file));
+
+    // If there is no offset for the file descriptor, we use SkFILEStream directly.
+    if (::lseek(descriptor, 0, SEEK_CUR) == 0) {
+        assert(isSeekable(dupDescriptor));
+        return doDecode(env, std::move(fileStream), padding, bitmapFactoryOptions,
+                        inBitmapHandle, colorSpaceHandle);
+    }
+
+    // Use a buffered stream. Although an SkFILEStream can be rewound, this
+    // ensures that SkImageDecoder::Factory never rewinds beyond the
+    // current position of the file descriptor.
+    std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Make(std::move(fileStream),
+            SkCodec::MinBufferedBytesNeeded()));
+
+    return doDecode(env, std::move(stream), padding, bitmapFactoryOptions, inBitmapHandle,
+                    colorSpaceHandle);
+}
+
+static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset,
+        jobject padding, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) {
+
+    Asset* asset = reinterpret_cast<Asset*>(native_asset);
+    // since we know we'll be done with the asset when we return, we can
+    // just use a simple wrapper
+    return doDecode(env, std::make_unique<AssetStreamAdaptor>(asset), padding, options,
+                    inBitmapHandle, colorSpaceHandle);
+}
+
+static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
+        jint offset, jint length, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) {
+
+    AutoJavaByteArray ar(env, byteArray);
+    return doDecode(env, std::make_unique<SkMemoryStream>(ar.ptr() + offset, length, false),
+                    nullptr, options, inBitmapHandle, colorSpaceHandle);
+}
+
+static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
+    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
+    return isSeekable(descriptor) ? JNI_TRUE : JNI_FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gMethods[] = {
+    {   "nativeDecodeStream",
+        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;",
+        (void*)nativeDecodeStream
+    },
+
+    {   "nativeDecodeFileDescriptor",
+        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;",
+        (void*)nativeDecodeFileDescriptor
+    },
+
+    {   "nativeDecodeAsset",
+        "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;",
+        (void*)nativeDecodeAsset
+    },
+
+    {   "nativeDecodeByteArray",
+        "([BIILandroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;",
+        (void*)nativeDecodeByteArray
+    },
+
+    {   "nativeIsSeekable",
+        "(Ljava/io/FileDescriptor;)Z",
+        (void*)nativeIsSeekable
+    },
+};
+
+int register_android_graphics_BitmapFactory(JNIEnv* env) {
+    jclass options_class = FindClassOrDie(env, "android/graphics/BitmapFactory$Options");
+    gOptions_bitmapFieldID = GetFieldIDOrDie(env, options_class, "inBitmap",
+            "Landroid/graphics/Bitmap;");
+    gOptions_justBoundsFieldID = GetFieldIDOrDie(env, options_class, "inJustDecodeBounds", "Z");
+    gOptions_sampleSizeFieldID = GetFieldIDOrDie(env, options_class, "inSampleSize", "I");
+    gOptions_configFieldID = GetFieldIDOrDie(env, options_class, "inPreferredConfig",
+            "Landroid/graphics/Bitmap$Config;");
+    gOptions_colorSpaceFieldID = GetFieldIDOrDie(env, options_class, "inPreferredColorSpace",
+            "Landroid/graphics/ColorSpace;");
+    gOptions_premultipliedFieldID = GetFieldIDOrDie(env, options_class, "inPremultiplied", "Z");
+    gOptions_mutableFieldID = GetFieldIDOrDie(env, options_class, "inMutable", "Z");
+    gOptions_ditherFieldID = GetFieldIDOrDie(env, options_class, "inDither", "Z");
+    gOptions_preferQualityOverSpeedFieldID = GetFieldIDOrDie(env, options_class,
+            "inPreferQualityOverSpeed", "Z");
+    gOptions_scaledFieldID = GetFieldIDOrDie(env, options_class, "inScaled", "Z");
+    gOptions_densityFieldID = GetFieldIDOrDie(env, options_class, "inDensity", "I");
+    gOptions_screenDensityFieldID = GetFieldIDOrDie(env, options_class, "inScreenDensity", "I");
+    gOptions_targetDensityFieldID = GetFieldIDOrDie(env, options_class, "inTargetDensity", "I");
+    gOptions_widthFieldID = GetFieldIDOrDie(env, options_class, "outWidth", "I");
+    gOptions_heightFieldID = GetFieldIDOrDie(env, options_class, "outHeight", "I");
+    gOptions_mimeFieldID = GetFieldIDOrDie(env, options_class, "outMimeType", "Ljava/lang/String;");
+    gOptions_outConfigFieldID = GetFieldIDOrDie(env, options_class, "outConfig",
+             "Landroid/graphics/Bitmap$Config;");
+    gOptions_outColorSpaceFieldID = GetFieldIDOrDie(env, options_class, "outColorSpace",
+             "Landroid/graphics/ColorSpace;");
+    gOptions_mCancelID = GetFieldIDOrDie(env, options_class, "mCancel", "Z");
+
+    jclass bitmap_class = FindClassOrDie(env, "android/graphics/Bitmap");
+    gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets",
+            "Landroid/graphics/NinePatch$InsetStruct;");
+
+    gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
+            "android/graphics/Bitmap$Config"));
+    gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class,
+            "nativeToConfig", "(I)Landroid/graphics/Bitmap$Config;");
+
+    return android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory",
+                                         gMethods, NELEM(gMethods));
+}
diff --git a/libs/hwui/jni/BitmapFactory.h b/libs/hwui/jni/BitmapFactory.h
new file mode 100644
index 0000000..45bffc4
--- /dev/null
+++ b/libs/hwui/jni/BitmapFactory.h
@@ -0,0 +1,31 @@
+#ifndef _ANDROID_GRAPHICS_BITMAP_FACTORY_H_
+#define _ANDROID_GRAPHICS_BITMAP_FACTORY_H_
+
+#include "GraphicsJNI.h"
+#include "SkEncodedImageFormat.h"
+
+extern jclass gOptions_class;
+extern jfieldID gOptions_justBoundsFieldID;
+extern jfieldID gOptions_sampleSizeFieldID;
+extern jfieldID gOptions_configFieldID;
+extern jfieldID gOptions_colorSpaceFieldID;
+extern jfieldID gOptions_premultipliedFieldID;
+extern jfieldID gOptions_ditherFieldID;
+extern jfieldID gOptions_purgeableFieldID;
+extern jfieldID gOptions_shareableFieldID;
+extern jfieldID gOptions_nativeAllocFieldID;
+extern jfieldID gOptions_preferQualityOverSpeedFieldID;
+extern jfieldID gOptions_widthFieldID;
+extern jfieldID gOptions_heightFieldID;
+extern jfieldID gOptions_mimeFieldID;
+extern jfieldID gOptions_outConfigFieldID;
+extern jfieldID gOptions_outColorSpaceFieldID;
+extern jfieldID gOptions_mCancelID;
+extern jfieldID gOptions_bitmapFieldID;
+
+extern jclass gBitmapConfig_class;
+extern jmethodID gBitmapConfig_nativeToConfigMethodID;
+
+jstring getMimeTypeAsJavaString(JNIEnv*, SkEncodedImageFormat);
+
+#endif  // _ANDROID_GRAPHICS_BITMAP_FACTORY_H_
diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp
new file mode 100644
index 0000000..06b4ff8
--- /dev/null
+++ b/libs/hwui/jni/BitmapRegionDecoder.cpp
@@ -0,0 +1,291 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "BitmapRegionDecoder"
+
+#include "BitmapFactory.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "GraphicsJNI.h"
+#include "Utils.h"
+
+#include "SkBitmap.h"
+#include "SkBitmapRegionDecoder.h"
+#include "SkCodec.h"
+#include "SkData.h"
+#include "SkStream.h"
+
+#include "core_jni_helpers.h"
+
+#include <HardwareBitmapUploader.h>
+#include <nativehelper/JNIHelp.h>
+#include <androidfw/Asset.h>
+#include <jni.h>
+#include <sys/stat.h>
+
+#include <memory>
+
+using namespace android;
+
+static jobject createBitmapRegionDecoder(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream) {
+  std::unique_ptr<SkBitmapRegionDecoder> brd(
+            SkBitmapRegionDecoder::Create(stream.release(),
+                                          SkBitmapRegionDecoder::kAndroidCodec_Strategy));
+    if (!brd) {
+        doThrowIOE(env, "Image format not supported");
+        return nullObjectReturn("CreateBitmapRegionDecoder returned null");
+    }
+
+    return GraphicsJNI::createBitmapRegionDecoder(env, brd.release());
+}
+
+static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
+                                     jint offset, jint length, jboolean isShareable) {
+    /*  If isShareable we could decide to just wrap the java array and
+        share it, but that means adding a globalref to the java array object
+        For now we just always copy the array's data if isShareable.
+     */
+    AutoJavaByteArray ar(env, byteArray);
+    std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(ar.ptr() + offset, length, true));
+
+    // the decoder owns the stream.
+    jobject brd = createBitmapRegionDecoder(env, std::move(stream));
+    return brd;
+}
+
+static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
+                                          jobject fileDescriptor, jboolean isShareable) {
+    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
+
+    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
+
+    struct stat fdStat;
+    if (fstat(descriptor, &fdStat) == -1) {
+        doThrowIOE(env, "broken file descriptor");
+        return nullObjectReturn("fstat return -1");
+    }
+
+    sk_sp<SkData> data(SkData::MakeFromFD(descriptor));
+    std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(std::move(data)));
+
+    // the decoder owns the stream.
+    jobject brd = createBitmapRegionDecoder(env, std::move(stream));
+    return brd;
+}
+
+static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
+                                  jobject is,       // InputStream
+                                  jbyteArray storage, // byte[]
+                                  jboolean isShareable) {
+    jobject brd = NULL;
+    // for now we don't allow shareable with java inputstreams
+    std::unique_ptr<SkStreamRewindable> stream(CopyJavaInputStream(env, is, storage));
+
+    if (stream) {
+        // the decoder owns the stream.
+        brd = createBitmapRegionDecoder(env, std::move(stream));
+    }
+    return brd;
+}
+
+static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
+                                 jlong native_asset, // Asset
+                                 jboolean isShareable) {
+    Asset* asset = reinterpret_cast<Asset*>(native_asset);
+    std::unique_ptr<SkMemoryStream> stream(CopyAssetToStream(asset));
+    if (NULL == stream) {
+        return NULL;
+    }
+
+    // the decoder owns the stream.
+    jobject brd = createBitmapRegionDecoder(env, std::move(stream));
+    return brd;
+}
+
+/*
+ * nine patch not supported
+ * purgeable not supported
+ * reportSizeToVM not supported
+ */
+static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX,
+        jint inputY, jint inputWidth, jint inputHeight, jobject options, jlong inBitmapHandle,
+        jlong colorSpaceHandle) {
+
+    // Set default options.
+    int sampleSize = 1;
+    SkColorType colorType = kN32_SkColorType;
+    bool requireUnpremul = false;
+    jobject javaBitmap = nullptr;
+    bool isHardware = false;
+    sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+    // Update the default options with any options supplied by the client.
+    if (NULL != options) {
+        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
+        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
+        colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
+        isHardware = GraphicsJNI::isHardwareConfig(env, jconfig);
+        requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
+        javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
+        // The Java options of ditherMode and preferQualityOverSpeed are deprecated.  We will
+        // ignore the values of these fields.
+
+        // Initialize these fields to indicate a failure.  If the decode succeeds, we
+        // will update them later on.
+        env->SetIntField(options, gOptions_widthFieldID, -1);
+        env->SetIntField(options, gOptions_heightFieldID, -1);
+        env->SetObjectField(options, gOptions_mimeFieldID, 0);
+        env->SetObjectField(options, gOptions_outConfigFieldID, 0);
+        env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0);
+    }
+
+    // Recycle a bitmap if possible.
+    android::Bitmap* recycledBitmap = nullptr;
+    size_t recycledBytes = 0;
+    if (javaBitmap) {
+        recycledBitmap = &bitmap::toBitmap(inBitmapHandle);
+        if (recycledBitmap->isImmutable()) {
+            ALOGW("Warning: Reusing an immutable bitmap as an image decoder target.");
+        }
+        recycledBytes = recycledBitmap->getAllocationByteCount();
+    }
+
+    SkBitmapRegionDecoder* brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+    SkColorType decodeColorType = brd->computeOutputColorType(colorType);
+    if (decodeColorType == kRGBA_F16_SkColorType && isHardware &&
+            !uirenderer::HardwareBitmapUploader::hasFP16Support()) {
+        decodeColorType = kN32_SkColorType;
+    }
+
+    // Set up the pixel allocator
+    SkBRDAllocator* allocator = nullptr;
+    RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes);
+    HeapAllocator heapAlloc;
+    if (javaBitmap) {
+        allocator = &recycleAlloc;
+        // We are required to match the color type of the recycled bitmap.
+        decodeColorType = recycledBitmap->info().colorType();
+    } else {
+        allocator = &heapAlloc;
+    }
+
+    sk_sp<SkColorSpace> decodeColorSpace = brd->computeOutputColorSpace(
+            decodeColorType, colorSpace);
+
+    // Decode the region.
+    SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight);
+    SkBitmap bitmap;
+    if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize,
+            decodeColorType, requireUnpremul, decodeColorSpace)) {
+        return nullObjectReturn("Failed to decode region.");
+    }
+
+    // If the client provided options, indicate that the decode was successful.
+    if (NULL != options) {
+        env->SetIntField(options, gOptions_widthFieldID, bitmap.width());
+        env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
+
+        env->SetObjectField(options, gOptions_mimeFieldID,
+                getMimeTypeAsJavaString(env, brd->getEncodedFormat()));
+        if (env->ExceptionCheck()) {
+            return nullObjectReturn("OOM in encodedFormatToString()");
+        }
+
+        jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType);
+        if (isHardware) {
+            configID = GraphicsJNI::kHardware_LegacyBitmapConfig;
+        }
+        jobject config = env->CallStaticObjectMethod(gBitmapConfig_class,
+                gBitmapConfig_nativeToConfigMethodID, configID);
+        env->SetObjectField(options, gOptions_outConfigFieldID, config);
+
+        env->SetObjectField(options, gOptions_outColorSpaceFieldID,
+                GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType));
+    }
+
+    // If we may have reused a bitmap, we need to indicate that the pixels have changed.
+    if (javaBitmap) {
+        recycleAlloc.copyIfNecessary();
+        bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul);
+        return javaBitmap;
+    }
+
+    int bitmapCreateFlags = 0;
+    if (!requireUnpremul) {
+        bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied;
+    }
+    if (isHardware) {
+        sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(bitmap);
+        return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags);
+    }
+    return android::bitmap::createBitmap(env, heapAlloc.getStorageObjAndReset(), bitmapCreateFlags);
+}
+
+static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
+    SkBitmapRegionDecoder* brd =
+            reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+    return static_cast<jint>(brd->height());
+}
+
+static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
+    SkBitmapRegionDecoder* brd =
+            reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+    return static_cast<jint>(brd->width());
+}
+
+static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
+    SkBitmapRegionDecoder* brd =
+            reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+    delete brd;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gBitmapRegionDecoderMethods[] = {
+    {   "nativeDecodeRegion",
+        "(JIIIILandroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;",
+        (void*)nativeDecodeRegion},
+
+    {   "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
+
+    {   "nativeGetWidth", "(J)I", (void*)nativeGetWidth},
+
+    {   "nativeClean", "(J)V", (void*)nativeClean},
+
+    {   "nativeNewInstance",
+        "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromByteArray
+    },
+
+    {   "nativeNewInstance",
+        "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromStream
+    },
+
+    {   "nativeNewInstance",
+        "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromFileDescriptor
+    },
+
+    {   "nativeNewInstance",
+        "(JZ)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromAsset
+    },
+};
+
+int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
+{
+    return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder",
+            gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods));
+}
diff --git a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp
new file mode 100644
index 0000000..d443fd8
--- /dev/null
+++ b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp
@@ -0,0 +1,335 @@
+#include "ByteBufferStreamAdaptor.h"
+#include "core_jni_helpers.h"
+#include "Utils.h"
+#include <jni.h>
+
+#include <SkStream.h>
+
+using namespace android;
+
+static jmethodID gByteBuffer_getMethodID;
+static jmethodID gByteBuffer_setPositionMethodID;
+
+/**
+ * Helper method for accessing the JNI interface pointer.
+ *
+ * Image decoding (which this supports) is started on a thread that is already
+ * attached to the Java VM. But an AnimatedImageDrawable continues decoding on
+ * the AnimatedImageThread, which is not attached. This will attach if
+ * necessary.
+ */
+static JNIEnv* requireEnv(JavaVM* jvm) {
+    JNIEnv* env;
+    if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+            LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
+        }
+    }
+    return env;
+}
+
+class ByteBufferStream : public SkStreamAsset {
+private:
+    ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length,
+                     jbyteArray storage)
+            : mJvm(jvm)
+            , mByteBuffer(jbyteBuffer)
+            , mPosition(0)
+            , mInitialPosition(initialPosition)
+            , mLength(length)
+            , mStorage(storage) {}
+
+public:
+    static ByteBufferStream* Create(JavaVM* jvm, JNIEnv* env, jobject jbyteBuffer,
+                                    size_t position, size_t length) {
+        // This object outlives its native method call.
+        jbyteBuffer = env->NewGlobalRef(jbyteBuffer);
+        if (!jbyteBuffer) {
+            return nullptr;
+        }
+
+        jbyteArray storage = env->NewByteArray(kStorageSize);
+        if (!storage) {
+            env->DeleteGlobalRef(jbyteBuffer);
+            return nullptr;
+        }
+
+        // This object outlives its native method call.
+        storage = static_cast<jbyteArray>(env->NewGlobalRef(storage));
+        if (!storage) {
+            env->DeleteGlobalRef(jbyteBuffer);
+            return nullptr;
+        }
+
+        return new ByteBufferStream(jvm, jbyteBuffer, position, length, storage);
+    }
+
+    ~ByteBufferStream() override {
+        auto* env = requireEnv(mJvm);
+        env->DeleteGlobalRef(mByteBuffer);
+        env->DeleteGlobalRef(mStorage);
+    }
+
+    size_t read(void* buffer, size_t size) override {
+        if (size > mLength - mPosition) {
+            size = mLength - mPosition;
+        }
+        if (!size) {
+            return 0;
+        }
+
+        if (!buffer) {
+            return this->setPosition(mPosition + size) ? size : 0;
+        }
+
+        auto* env = requireEnv(mJvm);
+        size_t bytesRead = 0;
+        do {
+            const size_t requested = (size > kStorageSize) ? kStorageSize : size;
+            const jint jrequested = static_cast<jint>(requested);
+            env->CallObjectMethod(mByteBuffer, gByteBuffer_getMethodID, mStorage, 0, jrequested);
+            if (env->ExceptionCheck()) {
+                ALOGE("Error in ByteBufferStream::read - was the ByteBuffer modified externally?");
+                env->ExceptionDescribe();
+                env->ExceptionClear();
+                mPosition = mLength;
+                return bytesRead;
+            }
+
+            env->GetByteArrayRegion(mStorage, 0, requested, reinterpret_cast<jbyte*>(buffer));
+            if (env->ExceptionCheck()) {
+                ALOGE("Internal error in ByteBufferStream::read");
+                env->ExceptionDescribe();
+                env->ExceptionClear();
+                mPosition = mLength;
+                return bytesRead;
+            }
+
+            mPosition += requested;
+            buffer = reinterpret_cast<void*>(reinterpret_cast<char*>(buffer) + requested);
+            bytesRead += requested;
+            size -= requested;
+        } while (size);
+        return bytesRead;
+    }
+
+    bool isAtEnd() const override { return mLength == mPosition; }
+
+    // SkStreamRewindable overrides
+    bool rewind() override { return this->setPosition(0); }
+
+    SkStreamAsset* onDuplicate() const override {
+        // SkStreamRewindable requires overriding this, but it is not called by
+        // decoders, so does not need a true implementation. A proper
+        // implementation would require duplicating the ByteBuffer, which has
+        // its own internal position state.
+        return nullptr;
+    }
+
+    // SkStreamSeekable overrides
+    size_t getPosition() const override { return mPosition; }
+
+    bool seek(size_t position) override {
+        return this->setPosition(position > mLength ? mLength : position);
+    }
+
+    bool move(long offset) override {
+        long newPosition = mPosition + offset;
+        if (newPosition < 0) {
+            return this->setPosition(0);
+        }
+        return this->seek(static_cast<size_t>(newPosition));
+    }
+
+    SkStreamAsset* onFork() const override {
+        // SkStreamSeekable requires overriding this, but it is not called by
+        // decoders, so does not need a true implementation. A proper
+        // implementation would require duplicating the ByteBuffer, which has
+        // its own internal position state.
+        return nullptr;
+    }
+
+    // SkStreamAsset overrides
+    size_t getLength() const override { return mLength; }
+
+private:
+    JavaVM*          mJvm;
+    jobject          mByteBuffer;
+    // Logical position of the SkStream, between 0 and mLength.
+    size_t           mPosition;
+    // Initial position of mByteBuffer, treated as mPosition 0.
+    const size_t     mInitialPosition;
+    // Logical length of the SkStream, from mInitialPosition to
+    // mByteBuffer.limit().
+    const size_t     mLength;
+
+    // Range has already been checked by the caller.
+    bool setPosition(size_t newPosition) {
+        auto* env = requireEnv(mJvm);
+        env->CallObjectMethod(mByteBuffer, gByteBuffer_setPositionMethodID,
+                              newPosition + mInitialPosition);
+        if (env->ExceptionCheck()) {
+            ALOGE("Internal error in ByteBufferStream::setPosition");
+            env->ExceptionDescribe();
+            env->ExceptionClear();
+            mPosition = mLength;
+            return false;
+        }
+        mPosition = newPosition;
+        return true;
+    }
+
+    // FIXME: This is an arbitrary storage size, which should be plenty for
+    // some formats (png, gif, many bmps). But for jpeg, the more we can supply
+    // in one call the better, and webp really wants all of the data. How to
+    // best choose the amount of storage used?
+    static constexpr size_t kStorageSize = 4096;
+    jbyteArray mStorage;
+};
+
+class ByteArrayStream : public SkStreamAsset {
+private:
+    ByteArrayStream(JavaVM* jvm, jbyteArray jarray, size_t offset, size_t length)
+            : mJvm(jvm), mByteArray(jarray), mOffset(offset), mPosition(0), mLength(length) {}
+
+public:
+    static ByteArrayStream* Create(JavaVM* jvm, JNIEnv* env, jbyteArray jarray, size_t offset,
+                                   size_t length) {
+        // This object outlives its native method call.
+        jarray = static_cast<jbyteArray>(env->NewGlobalRef(jarray));
+        if (!jarray) {
+            return nullptr;
+        }
+        return new ByteArrayStream(jvm, jarray, offset, length);
+    }
+
+    ~ByteArrayStream() override {
+        auto* env = requireEnv(mJvm);
+        env->DeleteGlobalRef(mByteArray);
+    }
+
+    size_t read(void* buffer, size_t size) override {
+        if (size > mLength - mPosition) {
+            size = mLength - mPosition;
+        }
+        if (!size) {
+            return 0;
+        }
+
+        auto* env = requireEnv(mJvm);
+        if (buffer) {
+            env->GetByteArrayRegion(mByteArray, mPosition + mOffset, size,
+                                    reinterpret_cast<jbyte*>(buffer));
+            if (env->ExceptionCheck()) {
+                ALOGE("Internal error in ByteArrayStream::read");
+                env->ExceptionDescribe();
+                env->ExceptionClear();
+                mPosition = mLength;
+                return 0;
+            }
+        }
+
+        mPosition += size;
+        return size;
+    }
+
+    bool isAtEnd() const override { return mLength == mPosition; }
+
+    // SkStreamRewindable overrides
+    bool rewind() override {
+        mPosition = 0;
+        return true;
+    }
+    SkStreamAsset* onDuplicate() const override {
+        // SkStreamRewindable requires overriding this, but it is not called by
+        // decoders, so does not need a true implementation. Note that a proper
+        // implementation is fairly straightforward
+        return nullptr;
+    }
+
+    // SkStreamSeekable overrides
+    size_t getPosition() const override { return mPosition; }
+
+    bool seek(size_t position) override {
+        mPosition = (position > mLength) ? mLength : position;
+        return true;
+    }
+
+    bool move(long offset) override {
+        long newPosition = mPosition + offset;
+        if (newPosition < 0) {
+            return this->seek(0);
+        }
+        return this->seek(static_cast<size_t>(newPosition));
+    }
+
+    SkStreamAsset* onFork() const override {
+        // SkStreamSeekable requires overriding this, but it is not called by
+        // decoders, so does not need a true implementation. Note that a proper
+        // implementation is fairly straightforward
+        return nullptr;
+    }
+
+    // SkStreamAsset overrides
+    size_t getLength() const override { return mLength; }
+
+private:
+    JavaVM*      mJvm;
+    jbyteArray   mByteArray;
+    // Offset in mByteArray. Only used when communicating with Java.
+    const size_t mOffset;
+    // Logical position of the SkStream, between 0 and mLength.
+    size_t       mPosition;
+    const size_t mLength;
+};
+
+struct release_proc_context {
+    JavaVM* jvm;
+    jobject jbyteBuffer;
+};
+
+std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv* env, jobject jbyteBuffer,
+                                                        size_t position, size_t limit) {
+    JavaVM* jvm;
+    LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK);
+
+    const size_t length = limit - position;
+    void* addr = env->GetDirectBufferAddress(jbyteBuffer);
+    if (addr) {
+        addr = reinterpret_cast<void*>(reinterpret_cast<char*>(addr) + position);
+        jbyteBuffer = env->NewGlobalRef(jbyteBuffer);
+        if (!jbyteBuffer) {
+            return nullptr;
+        }
+
+        auto* context = new release_proc_context{jvm, jbyteBuffer};
+        auto releaseProc = [](const void*, void* context) {
+            auto* c = reinterpret_cast<release_proc_context*>(context);
+            JNIEnv* env = get_env_or_die(c->jvm);
+            env->DeleteGlobalRef(c->jbyteBuffer);
+            delete c;
+        };
+        auto data = SkData::MakeWithProc(addr, length, releaseProc, context);
+        // The new SkMemoryStream will read directly from addr.
+        return std::unique_ptr<SkStream>(new SkMemoryStream(std::move(data)));
+    }
+
+    // Non-direct, or direct access is not supported.
+    return std::unique_ptr<SkStream>(ByteBufferStream::Create(jvm, env, jbyteBuffer, position,
+                                                              length));
+}
+
+std::unique_ptr<SkStream> CreateByteArrayStreamAdaptor(JNIEnv* env, jbyteArray array, size_t offset,
+                                                       size_t length) {
+    JavaVM* jvm;
+    LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK);
+
+    return std::unique_ptr<SkStream>(ByteArrayStream::Create(jvm, env, array, offset, length));
+}
+
+int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env) {
+    jclass byteBuffer_class = FindClassOrDie(env, "java/nio/ByteBuffer");
+    gByteBuffer_getMethodID         = GetMethodIDOrDie(env, byteBuffer_class, "get", "([BII)Ljava/nio/ByteBuffer;");
+    gByteBuffer_setPositionMethodID = GetMethodIDOrDie(env, byteBuffer_class, "position", "(I)Ljava/nio/Buffer;");
+    return true;
+}
diff --git a/libs/hwui/jni/ByteBufferStreamAdaptor.h b/libs/hwui/jni/ByteBufferStreamAdaptor.h
new file mode 100644
index 0000000..367a48f
--- /dev/null
+++ b/libs/hwui/jni/ByteBufferStreamAdaptor.h
@@ -0,0 +1,37 @@
+#ifndef _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_
+#define _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_
+
+#include <jni.h>
+#include <memory>
+
+class SkStream;
+
+/**
+ * Create an adaptor for treating a java.nio.ByteBuffer as an SkStream.
+ *
+ * This will special case direct ByteBuffers, but not the case where a byte[]
+ * can be used directly. For that, use CreateByteArrayStreamAdaptor.
+ *
+ * @param jbyteBuffer corresponding to the java ByteBuffer. This method will
+ *      add a global ref.
+ * @param initialPosition returned by ByteBuffer.position(). Decoding starts
+ *      from here.
+ * @param limit returned by ByteBuffer.limit().
+ *
+ * Returns null on failure.
+ */
+std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv*, jobject jbyteBuffer,
+                                                        size_t initialPosition, size_t limit);
+
+/**
+ * Create an adaptor for treating a Java byte[] as an SkStream.
+ *
+ * @param offset into the byte[] of the beginning of the data to use.
+ * @param length of data to use, starting from offset.
+ *
+ * Returns null on failure.
+ */
+std::unique_ptr<SkStream> CreateByteArrayStreamAdaptor(JNIEnv*, jbyteArray array, size_t offset,
+                                                       size_t length);
+
+#endif  // _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_
diff --git a/libs/hwui/jni/Camera.cpp b/libs/hwui/jni/Camera.cpp
new file mode 100644
index 0000000..da95497
--- /dev/null
+++ b/libs/hwui/jni/Camera.cpp
@@ -0,0 +1,146 @@
+#include "jni.h"
+#include "core_jni_helpers.h"
+
+#include "SkCamera.h"
+
+#include "GraphicsJNI.h"
+#include <hwui/Canvas.h>
+
+static jfieldID gNativeInstanceFieldID;
+
+static void Camera_constructor(JNIEnv* env, jobject obj) {
+    Sk3DView* view = new Sk3DView;
+    env->SetLongField(obj, gNativeInstanceFieldID, reinterpret_cast<jlong>(view));
+}
+
+static void Camera_destructor(JNIEnv* env, jobject obj) {
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* view = reinterpret_cast<Sk3DView*>(viewHandle);
+    delete view;
+}
+
+static void Camera_save(JNIEnv* env, jobject obj) {
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    v->save();
+}
+
+static void Camera_restore(JNIEnv* env, jobject obj) {
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    v->restore();
+}
+
+static void Camera_translate(JNIEnv* env, jobject obj,
+                             jfloat dx, jfloat dy, jfloat dz) {
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    v->translate(dx, dy, dz);
+}
+
+static void Camera_rotateX(JNIEnv* env, jobject obj, jfloat degrees) {
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    v->rotateX(degrees);
+}
+
+static void Camera_rotateY(JNIEnv* env, jobject obj, jfloat degrees) {
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    v->rotateY(degrees);
+}
+
+static void Camera_rotateZ(JNIEnv* env, jobject obj, jfloat degrees) {
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    v->rotateZ(degrees);
+}
+
+static void Camera_rotate(JNIEnv* env, jobject obj, jfloat x, jfloat y, jfloat z) {
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    v->rotateX(x);
+    v->rotateY(y);
+    v->rotateZ(z);
+}
+
+static void Camera_setLocation(JNIEnv* env, jobject obj, jfloat x, jfloat y, jfloat z) {
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    v->setCameraLocation(x, y, z);
+}
+
+static jfloat Camera_getLocationX(JNIEnv* env, jobject obj) {
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    return SkScalarToFloat(v->getCameraLocationX());
+}
+
+static jfloat Camera_getLocationY(JNIEnv* env, jobject obj) {
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    return SkScalarToFloat(v->getCameraLocationY());
+}
+
+static jfloat Camera_getLocationZ(JNIEnv* env, jobject obj) {
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    return SkScalarToFloat(v->getCameraLocationZ());
+}
+
+static void Camera_getMatrix(JNIEnv* env, jobject obj, jlong matrixHandle) {
+    SkMatrix* native_matrix =  reinterpret_cast<SkMatrix*>(matrixHandle);
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    v->getMatrix(native_matrix);
+}
+
+static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) {
+    android::Canvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle);
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    SkMatrix matrix;
+    v->getMatrix(&matrix);
+    canvas->concat(matrix);
+}
+
+static jfloat Camera_dotWithNormal(JNIEnv* env, jobject obj,
+                                  jfloat x, jfloat y, jfloat z) {
+    jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
+    Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
+    SkScalar dot = v->dotWithNormal(x, y, z);
+    return SkScalarToFloat(dot);
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gCameraMethods[] = {
+    /* name, signature, funcPtr */
+
+    { "nativeConstructor",   "()V",    (void*)Camera_constructor   },
+    { "nativeDestructor",    "()V",    (void*)Camera_destructor    },
+    { "save",                "()V",    (void*)Camera_save          },
+    { "restore",             "()V",    (void*)Camera_restore       },
+    { "translate",           "(FFF)V", (void*)Camera_translate     },
+    { "rotateX",             "(F)V",   (void*)Camera_rotateX       },
+    { "rotateY",             "(F)V",   (void*)Camera_rotateY       },
+    { "rotateZ",             "(F)V",   (void*)Camera_rotateZ       },
+    { "rotate",              "(FFF)V", (void*)Camera_rotate        },
+    { "setLocation",         "(FFF)V", (void*)Camera_setLocation   },
+    { "getLocationX",        "()F",    (void*)Camera_getLocationX  },
+    { "getLocationY",        "()F",    (void*)Camera_getLocationY  },
+    { "getLocationZ",        "()F",    (void*)Camera_getLocationZ  },
+    { "nativeGetMatrix",     "(J)V",   (void*)Camera_getMatrix     },
+    { "nativeApplyToCanvas", "(J)V",   (void*)Camera_applyToCanvas },
+    { "dotWithNormal",       "(FFF)F", (void*)Camera_dotWithNormal }
+};
+
+int register_android_graphics_Camera(JNIEnv* env) {
+    jclass clazz = android::FindClassOrDie(env, "android/graphics/Camera");
+    gNativeInstanceFieldID = android::GetFieldIDOrDie(env, clazz, "native_instance", "J");
+    return android::RegisterMethodsOrDie(env, "android/graphics/Camera", gCameraMethods,
+                                         NELEM(gCameraMethods));
+}
diff --git a/libs/hwui/jni/CanvasProperty.cpp b/libs/hwui/jni/CanvasProperty.cpp
new file mode 100644
index 0000000..c841d6a
--- /dev/null
+++ b/libs/hwui/jni/CanvasProperty.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 20014 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 "jni.h"
+#include "GraphicsJNI.h"
+#include <core_jni_helpers.h>
+
+#include <hwui/Paint.h>
+#include <utils/RefBase.h>
+#include <CanvasProperty.h>
+
+namespace android {
+
+using namespace uirenderer;
+
+static jlong createFloat(JNIEnv* env, jobject clazz, jfloat initialValue) {
+    return reinterpret_cast<jlong>(new CanvasPropertyPrimitive(initialValue));
+}
+
+static jlong createPaint(JNIEnv* env, jobject clazz, jlong paintPtr) {
+    const Paint* paint = reinterpret_cast<const Paint*>(paintPtr);
+    return reinterpret_cast<jlong>(new CanvasPropertyPaint(*paint));
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod gMethods[] = {
+    { "nCreateFloat", "(F)J", (void*) createFloat },
+    { "nCreatePaint", "(J)J", (void*) createPaint },
+};
+
+int register_android_graphics_CanvasProperty(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/CanvasProperty", gMethods,
+                                NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp
new file mode 100644
index 0000000..164d35f
--- /dev/null
+++ b/libs/hwui/jni/ColorFilter.cpp
@@ -0,0 +1,91 @@
+/* libs/android_runtime/android/graphics/ColorFilter.cpp
+**
+** Copyright 2006, 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 "jni.h"
+#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
+
+#include "SkColorFilter.h"
+#include "SkColorMatrixFilter.h"
+
+namespace android {
+
+using namespace uirenderer;
+
+class SkColorFilterGlue {
+public:
+    static void SafeUnref(SkColorFilter* filter) {
+        SkSafeUnref(filter);
+    }
+
+    static jlong GetNativeFinalizer(JNIEnv*, jobject) {
+        return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SafeUnref));
+    }
+
+    static jlong CreateBlendModeFilter(JNIEnv* env, jobject, jint srcColor, jint modeHandle) {
+        SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
+        return reinterpret_cast<jlong>(SkColorFilters::Blend(srcColor, mode).release());
+    }
+
+    static jlong CreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) {
+        return reinterpret_cast<jlong>(SkColorMatrixFilter::MakeLightingFilter(mul, add).release());
+    }
+
+    static jlong CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) {
+        float matrix[20];
+        env->GetFloatArrayRegion(jarray, 0, 20, matrix);
+        // java biases the translates by 255, so undo that before calling skia
+        matrix[ 4] *= (1.0f/255);
+        matrix[ 9] *= (1.0f/255);
+        matrix[14] *= (1.0f/255);
+        matrix[19] *= (1.0f/255);
+        return reinterpret_cast<jlong>(SkColorFilters::Matrix(matrix).release());
+    }
+};
+
+static const JNINativeMethod colorfilter_methods[] = {
+    {"nativeGetFinalizer", "()J", (void*) SkColorFilterGlue::GetNativeFinalizer }
+};
+
+static const JNINativeMethod blendmode_methods[] = {
+    { "native_CreateBlendModeFilter", "(II)J", (void*) SkColorFilterGlue::CreateBlendModeFilter },
+};
+
+static const JNINativeMethod lighting_methods[] = {
+    { "native_CreateLightingFilter", "(II)J", (void*) SkColorFilterGlue::CreateLightingFilter },
+};
+
+static const JNINativeMethod colormatrix_methods[] = {
+    { "nativeColorMatrixFilter", "([F)J", (void*) SkColorFilterGlue::CreateColorMatrixFilter },
+};
+
+int register_android_graphics_ColorFilter(JNIEnv* env) {
+    android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods,
+                                  NELEM(colorfilter_methods));
+    android::RegisterMethodsOrDie(env, "android/graphics/PorterDuffColorFilter", blendmode_methods,
+                                  NELEM(blendmode_methods));
+    android::RegisterMethodsOrDie(env, "android/graphics/BlendModeColorFilter", blendmode_methods,
+                                  NELEM(blendmode_methods));
+    android::RegisterMethodsOrDie(env, "android/graphics/LightingColorFilter", lighting_methods,
+                                  NELEM(lighting_methods));
+    android::RegisterMethodsOrDie(env, "android/graphics/ColorMatrixColorFilter",
+                                  colormatrix_methods, NELEM(colormatrix_methods));
+    
+    return 0;
+}
+
+}
diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
new file mode 100644
index 0000000..39483b5
--- /dev/null
+++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
@@ -0,0 +1,306 @@
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "SkData.h"
+#include "SkMalloc.h"
+#include "SkRefCnt.h"
+#include "SkStream.h"
+#include "SkTypes.h"
+#include "Utils.h"
+
+#include <nativehelper/JNIHelp.h>
+#include <log/log.h>
+#include <memory>
+
+static jmethodID    gInputStream_readMethodID;
+static jmethodID    gInputStream_skipMethodID;
+
+/**
+ *  Wrapper for a Java InputStream.
+ */
+class JavaInputStreamAdaptor : public SkStream {
+    JavaInputStreamAdaptor(JavaVM* jvm, jobject js, jbyteArray ar, jint capacity,
+                           bool swallowExceptions)
+            : fJvm(jvm)
+            , fJavaInputStream(js)
+            , fJavaByteArray(ar)
+            , fCapacity(capacity)
+            , fBytesRead(0)
+            , fIsAtEnd(false)
+            , fSwallowExceptions(swallowExceptions) {}
+
+public:
+    static JavaInputStreamAdaptor* Create(JNIEnv* env, jobject js, jbyteArray ar,
+                                          bool swallowExceptions) {
+        JavaVM* jvm;
+        LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK);
+
+        js = env->NewGlobalRef(js);
+        if (!js) {
+            return nullptr;
+        }
+
+        ar = (jbyteArray) env->NewGlobalRef(ar);
+        if (!ar) {
+            env->DeleteGlobalRef(js);
+            return nullptr;
+        }
+
+        jint capacity = env->GetArrayLength(ar);
+        return new JavaInputStreamAdaptor(jvm, js, ar, capacity, swallowExceptions);
+    }
+
+    ~JavaInputStreamAdaptor() override {
+        auto* env = android::get_env_or_die(fJvm);
+        env->DeleteGlobalRef(fJavaInputStream);
+        env->DeleteGlobalRef(fJavaByteArray);
+    }
+
+    size_t read(void* buffer, size_t size) override {
+        auto* env = android::get_env_or_die(fJvm);
+        if (!fSwallowExceptions && checkException(env)) {
+            // Just in case the caller did not clear from a previous exception.
+            return 0;
+        }
+        if (NULL == buffer) {
+            if (0 == size) {
+                return 0;
+            } else {
+                /*  InputStream.skip(n) can return <=0 but still not be at EOF
+                    If we see that value, we need to call read(), which will
+                    block if waiting for more data, or return -1 at EOF
+                 */
+                size_t amountSkipped = 0;
+                do {
+                    size_t amount = this->doSkip(size - amountSkipped, env);
+                    if (0 == amount) {
+                        char tmp;
+                        amount = this->doRead(&tmp, 1, env);
+                        if (0 == amount) {
+                            // if read returned 0, we're at EOF
+                            fIsAtEnd = true;
+                            break;
+                        }
+                    }
+                    amountSkipped += amount;
+                } while (amountSkipped < size);
+                return amountSkipped;
+            }
+        }
+        return this->doRead(buffer, size, env);
+    }
+
+    bool isAtEnd() const override { return fIsAtEnd; }
+
+private:
+    size_t doRead(void* buffer, size_t size, JNIEnv* env) {
+        size_t bytesRead = 0;
+        // read the bytes
+        do {
+            jint requested = 0;
+            if (size > static_cast<size_t>(fCapacity)) {
+                requested = fCapacity;
+            } else {
+                // This is safe because requested is clamped to (jint)
+                // fCapacity.
+                requested = static_cast<jint>(size);
+            }
+
+            jint n = env->CallIntMethod(fJavaInputStream,
+                                        gInputStream_readMethodID, fJavaByteArray, 0, requested);
+            if (checkException(env)) {
+                SkDebugf("---- read threw an exception\n");
+                return bytesRead;
+            }
+
+            if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications.
+                fIsAtEnd = true;
+                break;  // eof
+            }
+
+            env->GetByteArrayRegion(fJavaByteArray, 0, n,
+                                    reinterpret_cast<jbyte*>(buffer));
+            if (checkException(env)) {
+                SkDebugf("---- read:GetByteArrayRegion threw an exception\n");
+                return bytesRead;
+            }
+
+            buffer = (void*)((char*)buffer + n);
+            bytesRead += n;
+            size -= n;
+            fBytesRead += n;
+        } while (size != 0);
+
+        return bytesRead;
+    }
+
+    size_t doSkip(size_t size, JNIEnv* env) {
+        jlong skipped = env->CallLongMethod(fJavaInputStream,
+                                            gInputStream_skipMethodID, (jlong)size);
+        if (checkException(env)) {
+            SkDebugf("------- skip threw an exception\n");
+            return 0;
+        }
+        if (skipped < 0) {
+            skipped = 0;
+        }
+
+        return (size_t)skipped;
+    }
+
+    bool checkException(JNIEnv* env) {
+        if (!env->ExceptionCheck()) {
+            return false;
+        }
+
+        env->ExceptionDescribe();
+        if (fSwallowExceptions) {
+            env->ExceptionClear();
+        }
+
+        // There is no way to recover from the error, so consider the stream
+        // to be at the end.
+        fIsAtEnd = true;
+
+        return true;
+    }
+
+    JavaVM*     fJvm;
+    jobject     fJavaInputStream;
+    jbyteArray  fJavaByteArray;
+    const jint  fCapacity;
+    size_t      fBytesRead;
+    bool        fIsAtEnd;
+    const bool  fSwallowExceptions;
+};
+
+SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage,
+                                       bool swallowExceptions) {
+    return JavaInputStreamAdaptor::Create(env, stream, storage, swallowExceptions);
+}
+
+static SkMemoryStream* adaptor_to_mem_stream(SkStream* stream) {
+    SkASSERT(stream != NULL);
+    size_t bufferSize = 4096;
+    size_t streamLen = 0;
+    size_t len;
+    char* data = (char*)sk_malloc_throw(bufferSize);
+
+    while ((len = stream->read(data + streamLen,
+                               bufferSize - streamLen)) != 0) {
+        streamLen += len;
+        if (streamLen == bufferSize) {
+            bufferSize *= 2;
+            data = (char*)sk_realloc_throw(data, bufferSize);
+        }
+    }
+    data = (char*)sk_realloc_throw(data, streamLen);
+
+    SkMemoryStream* streamMem = new SkMemoryStream();
+    streamMem->setMemoryOwned(data, streamLen);
+    return streamMem;
+}
+
+SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream,
+                                        jbyteArray storage) {
+    std::unique_ptr<SkStream> adaptor(CreateJavaInputStreamAdaptor(env, stream, storage));
+    if (NULL == adaptor.get()) {
+        return NULL;
+    }
+    return adaptor_to_mem_stream(adaptor.get());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jmethodID    gOutputStream_writeMethodID;
+static jmethodID    gOutputStream_flushMethodID;
+
+class SkJavaOutputStream : public SkWStream {
+public:
+    SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage)
+        : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage), fBytesWritten(0) {
+        fCapacity = env->GetArrayLength(storage);
+    }
+
+    virtual size_t bytesWritten() const {
+        return fBytesWritten;
+    }
+
+    virtual bool write(const void* buffer, size_t size) {
+        JNIEnv* env = fEnv;
+        jbyteArray storage = fJavaByteArray;
+
+        while (size > 0) {
+            jint requested = 0;
+            if (size > static_cast<size_t>(fCapacity)) {
+                requested = fCapacity;
+            } else {
+                // This is safe because requested is clamped to (jint)
+                // fCapacity.
+                requested = static_cast<jint>(size);
+            }
+
+            env->SetByteArrayRegion(storage, 0, requested,
+                                    reinterpret_cast<const jbyte*>(buffer));
+            if (env->ExceptionCheck()) {
+                env->ExceptionDescribe();
+                env->ExceptionClear();
+                SkDebugf("--- write:SetByteArrayElements threw an exception\n");
+                return false;
+            }
+
+            fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID,
+                                 storage, 0, requested);
+            if (env->ExceptionCheck()) {
+                env->ExceptionDescribe();
+                env->ExceptionClear();
+                SkDebugf("------- write threw an exception\n");
+                return false;
+            }
+
+            buffer = (void*)((char*)buffer + requested);
+            size -= requested;
+            fBytesWritten += requested;
+        }
+        return true;
+    }
+
+    virtual void flush() {
+        fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID);
+    }
+
+private:
+    JNIEnv*     fEnv;
+    jobject     fJavaOutputStream;  // the caller owns this object
+    jbyteArray  fJavaByteArray;     // the caller owns this object
+    jint        fCapacity;
+    size_t      fBytesWritten;
+};
+
+SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
+                                         jbyteArray storage) {
+    return new SkJavaOutputStream(env, stream, storage);
+}
+
+static jclass findClassCheck(JNIEnv* env, const char classname[]) {
+    jclass clazz = env->FindClass(classname);
+    SkASSERT(!env->ExceptionCheck());
+    return clazz;
+}
+
+static jmethodID getMethodIDCheck(JNIEnv* env, jclass clazz,
+                                  const char methodname[], const char type[]) {
+    jmethodID id = env->GetMethodID(clazz, methodname, type);
+    SkASSERT(!env->ExceptionCheck());
+    return id;
+}
+
+int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env) {
+    jclass inputStream_Clazz = findClassCheck(env, "java/io/InputStream");
+    gInputStream_readMethodID = getMethodIDCheck(env, inputStream_Clazz, "read", "([BII)I");
+    gInputStream_skipMethodID = getMethodIDCheck(env, inputStream_Clazz, "skip", "(J)J");
+
+    jclass outputStream_Clazz = findClassCheck(env, "java/io/OutputStream");
+    gOutputStream_writeMethodID = getMethodIDCheck(env, outputStream_Clazz, "write", "([BII)V");
+    gOutputStream_flushMethodID = getMethodIDCheck(env, outputStream_Clazz, "flush", "()V");
+
+    return 0;
+}
diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h
new file mode 100644
index 0000000..fccd471
--- /dev/null
+++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h
@@ -0,0 +1,43 @@
+#ifndef _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_
+#define _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_
+
+//#include <android_runtime/AndroidRuntime.h>
+#include "jni.h"
+
+class SkMemoryStream;
+class SkStream;
+class SkStreamRewindable;
+class SkWStream;
+
+/**
+ *  Return an adaptor from a Java InputStream to an SkStream.
+ *  Does not support rewind.
+ *  @param env JNIEnv object.
+ *  @param stream Pointer to Java InputStream.
+ *  @param storage Java byte array for retrieving data from the
+ *      Java InputStream.
+ *  @param swallowExceptions Whether to call ExceptionClear() after
+ *      an Exception is thrown. If false, it is up to the client to
+ *      clear or propagate the exception.
+ *  @return SkStream Simple subclass of SkStream which supports its
+ *      basic methods like reading. Only valid until the calling
+ *      function returns, since the Java InputStream is not managed
+ *      by the SkStream.
+ */
+SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage,
+                                       bool swallowExceptions = true);
+
+/**
+ *  Copy a Java InputStream. The result will be rewindable.
+ *  @param env JNIEnv object.
+ *  @param stream Pointer to Java InputStream.
+ *  @param storage Java byte array for retrieving data from the
+ *      Java InputStream.
+ *  @return SkStreamRewindable The data in stream will be copied
+ *      to a new SkStreamRewindable.
+ */
+SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, jbyteArray storage);
+
+SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage);
+
+#endif  // _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_
diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp
new file mode 100644
index 0000000..0fd9cc7
--- /dev/null
+++ b/libs/hwui/jni/FontFamily.cpp
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Minikin"
+
+#include <nativehelper/JNIHelp.h>
+#include <core_jni_helpers.h>
+
+#include "SkData.h"
+#include "SkFontMgr.h"
+#include "SkRefCnt.h"
+#include "SkTypeface.h"
+#include "GraphicsJNI.h"
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <android_runtime/AndroidRuntime.h>
+#include "Utils.h"
+#include "FontUtils.h"
+
+#include <hwui/MinikinSkia.h>
+#include <hwui/Typeface.h>
+#include <minikin/FontFamily.h>
+#include <minikin/LocaleList.h>
+#include <ui/FatVector.h>
+
+#include <memory>
+
+namespace android {
+
+struct NativeFamilyBuilder {
+    NativeFamilyBuilder(uint32_t langId, int variant)
+        : langId(langId), variant(static_cast<minikin::FamilyVariant>(variant)) {}
+    uint32_t langId;
+    minikin::FamilyVariant variant;
+    std::vector<minikin::Font> fonts;
+    std::vector<minikin::FontVariation> axes;
+};
+
+static inline NativeFamilyBuilder* toNativeBuilder(jlong ptr) {
+    return reinterpret_cast<NativeFamilyBuilder*>(ptr);
+}
+
+static inline FontFamilyWrapper* toFamily(jlong ptr) {
+    return reinterpret_cast<FontFamilyWrapper*>(ptr);
+}
+
+template<typename Ptr> static inline jlong toJLong(Ptr ptr) {
+    return reinterpret_cast<jlong>(ptr);
+}
+
+static jlong FontFamily_initBuilder(JNIEnv* env, jobject clazz, jstring langs, jint variant) {
+    NativeFamilyBuilder* builder;
+    if (langs != nullptr) {
+        ScopedUtfChars str(env, langs);
+        builder = new NativeFamilyBuilder(minikin::registerLocaleList(str.c_str()), variant);
+    } else {
+        builder = new NativeFamilyBuilder(minikin::registerLocaleList(""), variant);
+    }
+    return toJLong(builder);
+}
+
+static jlong FontFamily_create(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr) {
+    if (builderPtr == 0) {
+        return 0;
+    }
+    NativeFamilyBuilder* builder = toNativeBuilder(builderPtr);
+    if (builder->fonts.empty()) {
+        return 0;
+    }
+    std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
+            builder->langId, builder->variant, std::move(builder->fonts),
+            true /* isCustomFallback */);
+    if (family->getCoverage().length() == 0) {
+        return 0;
+    }
+    return toJLong(new FontFamilyWrapper(std::move(family)));
+}
+
+static void releaseBuilder(jlong builderPtr) {
+    delete toNativeBuilder(builderPtr);
+}
+
+static jlong FontFamily_getBuilderReleaseFunc(CRITICAL_JNI_PARAMS) {
+    return toJLong(&releaseBuilder);
+}
+
+static void releaseFamily(jlong familyPtr) {
+    delete toFamily(familyPtr);
+}
+
+static jlong FontFamily_getFamilyReleaseFunc(CRITICAL_JNI_PARAMS) {
+    return toJLong(&releaseFamily);
+}
+
+static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex,
+        jint weight, jint italic) {
+    FatVector<SkFontArguments::Axis, 2> skiaAxes;
+    for (const auto& axis : builder->axes) {
+        skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
+    }
+
+    const size_t fontSize = data->size();
+    const void* fontPtr = data->data();
+    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));
+
+    SkFontArguments params;
+    params.setCollectionIndex(ttcIndex);
+    params.setAxes(skiaAxes.data(), skiaAxes.size());
+
+    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+    sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params));
+    if (face == NULL) {
+        ALOGE("addFont failed to create font, invalid request");
+        builder->axes.clear();
+        return false;
+    }
+    std::shared_ptr<minikin::MinikinFont> minikinFont =
+            std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, "", ttcIndex,
+                    builder->axes);
+    minikin::Font::Builder fontBuilder(minikinFont);
+
+    if (weight != RESOLVE_BY_FONT_TABLE) {
+        fontBuilder.setWeight(weight);
+    }
+    if (italic != RESOLVE_BY_FONT_TABLE) {
+        fontBuilder.setSlant(static_cast<minikin::FontStyle::Slant>(italic != 0));
+    }
+    builder->fonts.push_back(fontBuilder.build());
+    builder->axes.clear();
+    return true;
+}
+
+static void release_global_ref(const void* /*data*/, void* context) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    bool needToAttach = (env == NULL);
+    if (needToAttach) {
+        JavaVMAttachArgs args;
+        args.version = JNI_VERSION_1_4;
+        args.name = "release_font_data";
+        args.group = NULL;
+        jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args);
+        if (result != JNI_OK) {
+            ALOGE("failed to attach to thread to release global ref.");
+            return;
+        }
+    }
+
+    jobject obj = reinterpret_cast<jobject>(context);
+    env->DeleteGlobalRef(obj);
+
+    if (needToAttach) {
+       AndroidRuntime::getJavaVM()->DetachCurrentThread();
+    }
+}
+
+static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong builderPtr, jobject bytebuf,
+        jint ttcIndex, jint weight, jint isItalic) {
+    NPE_CHECK_RETURN_ZERO(env, bytebuf);
+    NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
+    const void* fontPtr = env->GetDirectBufferAddress(bytebuf);
+    if (fontPtr == NULL) {
+        ALOGE("addFont failed to create font, buffer invalid");
+        builder->axes.clear();
+        return false;
+    }
+    jlong fontSize = env->GetDirectBufferCapacity(bytebuf);
+    if (fontSize < 0) {
+        ALOGE("addFont failed to create font, buffer size invalid");
+        builder->axes.clear();
+        return false;
+    }
+    jobject fontRef = MakeGlobalRefOrDie(env, bytebuf);
+    sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
+            release_global_ref, reinterpret_cast<void*>(fontRef)));
+    return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
+}
+
+static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong builderPtr,
+        jobject font, jint ttcIndex, jint weight, jint isItalic) {
+    NPE_CHECK_RETURN_ZERO(env, font);
+    NativeFamilyBuilder* builder = toNativeBuilder(builderPtr);
+    const void* fontPtr = env->GetDirectBufferAddress(font);
+    if (fontPtr == NULL) {
+        ALOGE("addFont failed to create font, buffer invalid");
+        builder->axes.clear();
+        return false;
+    }
+    jlong fontSize = env->GetDirectBufferCapacity(font);
+    if (fontSize < 0) {
+        ALOGE("addFont failed to create font, buffer size invalid");
+        builder->axes.clear();
+        return false;
+    }
+    jobject fontRef = MakeGlobalRefOrDie(env, font);
+    sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
+            release_global_ref, reinterpret_cast<void*>(fontRef)));
+    return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
+}
+
+static void FontFamily_addAxisValue(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jint tag, jfloat value) {
+    NativeFamilyBuilder* builder = toNativeBuilder(builderPtr);
+    builder->axes.push_back({static_cast<minikin::AxisTag>(tag), value});
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gFontFamilyMethods[] = {
+    { "nInitBuilder",           "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder },
+    { "nCreateFamily",          "(J)J", (void*)FontFamily_create },
+    { "nGetBuilderReleaseFunc", "()J", (void*)FontFamily_getBuilderReleaseFunc },
+    { "nGetFamilyReleaseFunc",  "()J", (void*)FontFamily_getFamilyReleaseFunc },
+    { "nAddFont",               "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont },
+    { "nAddFontWeightStyle",    "(JLjava/nio/ByteBuffer;III)Z",
+            (void*)FontFamily_addFontWeightStyle },
+    { "nAddAxisValue",         "(JIF)V", (void*)FontFamily_addAxisValue },
+};
+
+int register_android_graphics_FontFamily(JNIEnv* env)
+{
+    int err = RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods,
+            NELEM(gFontFamilyMethods));
+
+    init_FontUtils(env);
+    return err;
+}
+
+}
diff --git a/libs/hwui/jni/FontUtils.cpp b/libs/hwui/jni/FontUtils.cpp
new file mode 100644
index 0000000..0cf61b9
--- /dev/null
+++ b/libs/hwui/jni/FontUtils.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 "FontUtils.h"
+
+#include <nativehelper/JNIHelp.h>
+#include <core_jni_helpers.h>
+
+namespace android {
+namespace {
+
+static struct {
+    jmethodID mGet;
+    jmethodID mSize;
+} gListClassInfo;
+
+static struct {
+    jfieldID mTag;
+    jfieldID mStyleValue;
+} gAxisClassInfo;
+
+}  // namespace
+
+jint ListHelper::size() const {
+    return mEnv->CallIntMethod(mList, gListClassInfo.mSize);
+}
+
+jobject ListHelper::get(jint index) const {
+    return mEnv->CallObjectMethod(mList, gListClassInfo.mGet, index);
+}
+
+jint AxisHelper::getTag() const {
+    return mEnv->GetIntField(mAxis, gAxisClassInfo.mTag);
+}
+
+jfloat AxisHelper::getStyleValue() const {
+    return mEnv->GetFloatField(mAxis, gAxisClassInfo.mStyleValue);
+}
+
+void init_FontUtils(JNIEnv* env) {
+    jclass listClass = FindClassOrDie(env, "java/util/List");
+    gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;");
+    gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I");
+
+    jclass axisClass = FindClassOrDie(env, "android/graphics/fonts/FontVariationAxis");
+    gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "mTag", "I");
+    gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "mStyleValue", "F");
+}
+
+}  // namespace android
diff --git a/libs/hwui/jni/FontUtils.h b/libs/hwui/jni/FontUtils.h
new file mode 100644
index 0000000..b36b4e6
--- /dev/null
+++ b/libs/hwui/jni/FontUtils.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_GRAPHICS_FONT_UTILS_H_
+#define _ANDROID_GRAPHICS_FONT_UTILS_H_
+
+#include <jni.h>
+#include <memory>
+
+#include <minikin/Font.h>
+
+namespace minikin {
+class FontFamily;
+}  // namespace minikin
+
+namespace android {
+
+struct FontFamilyWrapper {
+  explicit FontFamilyWrapper(std::shared_ptr<minikin::FontFamily>&& family) : family(family) {}
+  std::shared_ptr<minikin::FontFamily> family;
+};
+
+struct FontWrapper {
+  FontWrapper(minikin::Font&& font) : font(std::move(font)) {}
+  minikin::Font font;
+};
+
+// Utility wrapper for java.util.List
+class ListHelper {
+public:
+  ListHelper(JNIEnv* env, jobject list) : mEnv(env), mList(list) {}
+
+  jint size() const;
+  jobject get(jint index) const;
+
+private:
+  JNIEnv* mEnv;
+  jobject mList;
+};
+
+// Utility wrapper for android.graphics.FontConfig$Axis
+class AxisHelper {
+public:
+  AxisHelper(JNIEnv* env, jobject axis) : mEnv(env), mAxis(axis) {}
+
+  jint getTag() const;
+  jfloat getStyleValue() const;
+
+private:
+  JNIEnv* mEnv;
+  jobject mAxis;
+};
+
+void init_FontUtils(JNIEnv* env);
+
+}; // namespace android
+
+#endif  // _ANDROID_GRAPHICS_FONT_UTILS_H_
diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp
new file mode 100644
index 0000000..f84a4bd
--- /dev/null
+++ b/libs/hwui/jni/GIFMovie.cpp
@@ -0,0 +1,447 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "Movie.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+
+#include "gif_lib.h"
+
+#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
+#define DGifCloseFile(a, b) DGifCloseFile(a)
+#endif
+
+class GIFMovie : public Movie {
+public:
+    explicit GIFMovie(SkStream* stream);
+    virtual ~GIFMovie();
+
+protected:
+    virtual bool onGetInfo(Info*);
+    virtual bool onSetTime(SkMSec);
+    virtual bool onGetBitmap(SkBitmap*);
+
+private:
+    GifFileType* fGIF;
+    int fCurrIndex;
+    int fLastDrawIndex;
+    SkBitmap fBackup;
+    SkColor fPaintingColor;
+};
+
+static int Decode(GifFileType* fileType, GifByteType* out, int size) {
+    SkStream* stream = (SkStream*) fileType->UserData;
+    return (int) stream->read(out, size);
+}
+
+GIFMovie::GIFMovie(SkStream* stream)
+{
+#if GIFLIB_MAJOR < 5
+    fGIF = DGifOpen( stream, Decode );
+#else
+    fGIF = DGifOpen( stream, Decode, nullptr );
+#endif
+    if (nullptr == fGIF)
+        return;
+
+    if (DGifSlurp(fGIF) != GIF_OK)
+    {
+        DGifCloseFile(fGIF, nullptr);
+        fGIF = nullptr;
+    }
+    fCurrIndex = -1;
+    fLastDrawIndex = -1;
+    fPaintingColor = SkPackARGB32(0, 0, 0, 0);
+}
+
+GIFMovie::~GIFMovie()
+{
+    if (fGIF)
+        DGifCloseFile(fGIF, nullptr);
+}
+
+static SkMSec savedimage_duration(const SavedImage* image)
+{
+    for (int j = 0; j < image->ExtensionBlockCount; j++)
+    {
+        if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
+        {
+            SkASSERT(image->ExtensionBlocks[j].ByteCount >= 4);
+            const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
+            return ((b[2] << 8) | b[1]) * 10;
+        }
+    }
+    return 0;
+}
+
+bool GIFMovie::onGetInfo(Info* info)
+{
+    if (nullptr == fGIF)
+        return false;
+
+    SkMSec dur = 0;
+    for (int i = 0; i < fGIF->ImageCount; i++)
+        dur += savedimage_duration(&fGIF->SavedImages[i]);
+
+    info->fDuration = dur;
+    info->fWidth = fGIF->SWidth;
+    info->fHeight = fGIF->SHeight;
+    info->fIsOpaque = false;    // how to compute?
+    return true;
+}
+
+bool GIFMovie::onSetTime(SkMSec time)
+{
+    if (nullptr == fGIF)
+        return false;
+
+    SkMSec dur = 0;
+    for (int i = 0; i < fGIF->ImageCount; i++)
+    {
+        dur += savedimage_duration(&fGIF->SavedImages[i]);
+        if (dur >= time)
+        {
+            fCurrIndex = i;
+            return fLastDrawIndex != fCurrIndex;
+        }
+    }
+    fCurrIndex = fGIF->ImageCount - 1;
+    return true;
+}
+
+static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap,
+                     int transparent, int width)
+{
+    for (; width > 0; width--, src++, dst++) {
+        if (*src != transparent && *src < cmap->ColorCount) {
+            const GifColorType& col = cmap->Colors[*src];
+            *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
+        }
+    }
+}
+
+#if GIFLIB_MAJOR < 5
+static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src,
+                               const ColorMapObject* cmap, int transparent, int copyWidth,
+                               int copyHeight, const GifImageDesc& imageDesc, int rowStep,
+                               int startRow)
+{
+    int row;
+    // every 'rowStep'th row, starting with row 'startRow'
+    for (row = startRow; row < copyHeight; row += rowStep) {
+        uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row);
+        copyLine(dst, src, cmap, transparent, copyWidth);
+        src += imageDesc.Width;
+    }
+
+    // pad for rest height
+    src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep);
+}
+
+static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
+                          int transparent)
+{
+    int width = bm->width();
+    int height = bm->height();
+    GifWord copyWidth = frame->ImageDesc.Width;
+    if (frame->ImageDesc.Left + copyWidth > width) {
+        copyWidth = width - frame->ImageDesc.Left;
+    }
+
+    GifWord copyHeight = frame->ImageDesc.Height;
+    if (frame->ImageDesc.Top + copyHeight > height) {
+        copyHeight = height - frame->ImageDesc.Top;
+    }
+
+    // deinterlace
+    const unsigned char* src = (unsigned char*)frame->RasterBits;
+
+    // group 1 - every 8th row, starting with row 0
+    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0);
+
+    // group 2 - every 8th row, starting with row 4
+    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4);
+
+    // group 3 - every 4th row, starting with row 2
+    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2);
+
+    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1);
+}
+#endif
+
+static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
+                       int transparent)
+{
+    int width = bm->width();
+    int height = bm->height();
+    const unsigned char* src = (unsigned char*)frame->RasterBits;
+    uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top);
+    GifWord copyWidth = frame->ImageDesc.Width;
+    if (frame->ImageDesc.Left + copyWidth > width) {
+        copyWidth = width - frame->ImageDesc.Left;
+    }
+
+    GifWord copyHeight = frame->ImageDesc.Height;
+    if (frame->ImageDesc.Top + copyHeight > height) {
+        copyHeight = height - frame->ImageDesc.Top;
+    }
+
+    for (; copyHeight > 0; copyHeight--) {
+        copyLine(dst, src, cmap, transparent, copyWidth);
+        src += frame->ImageDesc.Width;
+        dst += width;
+    }
+}
+
+static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height,
+                     uint32_t col)
+{
+    int bmWidth = bm->width();
+    int bmHeight = bm->height();
+    uint32_t* dst = bm->getAddr32(left, top);
+    GifWord copyWidth = width;
+    if (left + copyWidth > bmWidth) {
+        copyWidth = bmWidth - left;
+    }
+
+    GifWord copyHeight = height;
+    if (top + copyHeight > bmHeight) {
+        copyHeight = bmHeight - top;
+    }
+
+    for (; copyHeight > 0; copyHeight--) {
+        sk_memset32(dst, col, copyWidth);
+        dst += bmWidth;
+    }
+}
+
+static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap)
+{
+    int transparent = -1;
+
+    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
+        ExtensionBlock* eb = frame->ExtensionBlocks + i;
+        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
+            eb->ByteCount == 4) {
+            bool has_transparency = ((eb->Bytes[0] & 1) == 1);
+            if (has_transparency) {
+                transparent = (unsigned char)eb->Bytes[3];
+            }
+        }
+    }
+
+    if (frame->ImageDesc.ColorMap != nullptr) {
+        // use local color table
+        cmap = frame->ImageDesc.ColorMap;
+    }
+
+    if (cmap == nullptr || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
+        SkDEBUGFAIL("bad colortable setup");
+        return;
+    }
+
+#if GIFLIB_MAJOR < 5
+    // before GIFLIB 5, de-interlacing wasn't done by library at load time
+    if (frame->ImageDesc.Interlace) {
+        blitInterlace(bm, frame, cmap, transparent);
+        return;
+    }
+#endif
+
+    blitNormal(bm, frame, cmap, transparent);
+}
+
+static bool checkIfWillBeCleared(const SavedImage* frame)
+{
+    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
+        ExtensionBlock* eb = frame->ExtensionBlocks + i;
+        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
+            eb->ByteCount == 4) {
+            // check disposal method
+            int disposal = ((eb->Bytes[0] >> 2) & 7);
+            if (disposal == 2 || disposal == 3) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal)
+{
+    *trans = false;
+    *disposal = 0;
+    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
+        ExtensionBlock* eb = frame->ExtensionBlocks + i;
+        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
+            eb->ByteCount == 4) {
+            *trans = ((eb->Bytes[0] & 1) == 1);
+            *disposal = ((eb->Bytes[0] >> 2) & 7);
+        }
+    }
+}
+
+// return true if area of 'target' is completely covers area of 'covered'
+static bool checkIfCover(const SavedImage* target, const SavedImage* covered)
+{
+    if (target->ImageDesc.Left <= covered->ImageDesc.Left
+        && covered->ImageDesc.Left + covered->ImageDesc.Width <=
+               target->ImageDesc.Left + target->ImageDesc.Width
+        && target->ImageDesc.Top <= covered->ImageDesc.Top
+        && covered->ImageDesc.Top + covered->ImageDesc.Height <=
+               target->ImageDesc.Top + target->ImageDesc.Height) {
+        return true;
+    }
+    return false;
+}
+
+static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next,
+                                 SkBitmap* backup, SkColor color)
+{
+    // We can skip disposal process if next frame is not transparent
+    // and completely covers current area
+    bool curTrans;
+    int curDisposal;
+    getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal);
+    bool nextTrans;
+    int nextDisposal;
+    getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal);
+    if ((curDisposal == 2 || curDisposal == 3)
+        && (nextTrans || !checkIfCover(next, cur))) {
+        switch (curDisposal) {
+        // restore to background color
+        // -> 'background' means background under this image.
+        case 2:
+            fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top,
+                     cur->ImageDesc.Width, cur->ImageDesc.Height,
+                     color);
+            break;
+
+        // restore to previous
+        case 3:
+            bm->swap(*backup);
+            break;
+        }
+    }
+
+    // Save current image if next frame's disposal method == 3
+    if (nextDisposal == 3) {
+        const uint32_t* src = bm->getAddr32(0, 0);
+        uint32_t* dst = backup->getAddr32(0, 0);
+        int cnt = bm->width() * bm->height();
+        memcpy(dst, src, cnt*sizeof(uint32_t));
+    }
+}
+
+bool GIFMovie::onGetBitmap(SkBitmap* bm)
+{
+    const GifFileType* gif = fGIF;
+    if (nullptr == gif)
+        return false;
+
+    if (gif->ImageCount < 1) {
+        return false;
+    }
+
+    const int width = gif->SWidth;
+    const int height = gif->SHeight;
+    if (width <= 0 || height <= 0) {
+        return false;
+    }
+
+    // no need to draw
+    if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) {
+        return true;
+    }
+
+    int startIndex = fLastDrawIndex + 1;
+    if (fLastDrawIndex < 0 || !bm->readyToDraw()) {
+        // first time
+
+        startIndex = 0;
+
+        // create bitmap
+        if (!bm->tryAllocN32Pixels(width, height)) {
+            return false;
+        }
+        // create bitmap for backup
+        if (!fBackup.tryAllocN32Pixels(width, height)) {
+            return false;
+        }
+    } else if (startIndex > fCurrIndex) {
+        // rewind to 1st frame for repeat
+        startIndex = 0;
+    }
+
+    int lastIndex = fCurrIndex;
+    if (lastIndex < 0) {
+        // first time
+        lastIndex = 0;
+    } else if (lastIndex > fGIF->ImageCount - 1) {
+        // this block must not be reached.
+        lastIndex = fGIF->ImageCount - 1;
+    }
+
+    SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
+    if (gif->SColorMap != nullptr && gif->SBackGroundColor < gif->SColorMap->ColorCount) {
+        const GifColorType& col = gif->SColorMap->Colors[gif->SBackGroundColor];
+        bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
+    }
+
+    // draw each frames - not intelligent way
+    for (int i = startIndex; i <= lastIndex; i++) {
+        const SavedImage* cur = &fGIF->SavedImages[i];
+        if (i == 0) {
+            bool trans;
+            int disposal;
+            getTransparencyAndDisposalMethod(cur, &trans, &disposal);
+            if (!trans && gif->SColorMap != nullptr) {
+                fPaintingColor = bgColor;
+            } else {
+                fPaintingColor = SkColorSetARGB(0, 0, 0, 0);
+            }
+
+            bm->eraseColor(fPaintingColor);
+            fBackup.eraseColor(fPaintingColor);
+        } else {
+            // Dispose previous frame before move to next frame.
+            const SavedImage* prev = &fGIF->SavedImages[i-1];
+            disposeFrameIfNeeded(bm, prev, cur, &fBackup, fPaintingColor);
+        }
+
+        // Draw frame
+        // We can skip this process if this index is not last and disposal
+        // method == 2 or method == 3
+        if (i == lastIndex || !checkIfWillBeCleared(cur)) {
+            drawFrame(bm, cur, gif->SColorMap);
+        }
+    }
+
+    // save index
+    fLastDrawIndex = lastIndex;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Movie* Movie::DecodeStream(SkStreamRewindable* stream) {
+    char buf[GIF_STAMP_LEN];
+    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
+        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
+                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
+                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
+            // must rewind here, since our construct wants to re-read the data
+            stream->rewind();
+            return new GIFMovie(stream);
+        }
+    }
+    return nullptr;
+}
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
new file mode 100644
index 0000000..38fb8bd
--- /dev/null
+++ b/libs/hwui/jni/Graphics.cpp
@@ -0,0 +1,735 @@
+#define LOG_TAG "GraphicsJNI"
+
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
+
+#include "SkCanvas.h"
+#include "SkMath.h"
+#include "SkRegion.h"
+#include <android_runtime/AndroidRuntime.h>
+#include <cutils/ashmem.h>
+#include <hwui/Canvas.h>
+
+using namespace android;
+
+void doThrowNPE(JNIEnv* env) {
+    jniThrowNullPointerException(env, NULL);
+}
+
+void doThrowAIOOBE(JNIEnv* env) {
+    jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
+}
+
+void doThrowRE(JNIEnv* env, const char* msg) {
+    jniThrowRuntimeException(env, msg);
+}
+
+void doThrowIAE(JNIEnv* env, const char* msg) {
+    jniThrowException(env, "java/lang/IllegalArgumentException", msg);
+}
+
+void doThrowISE(JNIEnv* env, const char* msg) {
+    jniThrowException(env, "java/lang/IllegalStateException", msg);
+}
+
+void doThrowOOME(JNIEnv* env, const char* msg) {
+    jniThrowException(env, "java/lang/OutOfMemoryError", msg);
+}
+
+void doThrowIOE(JNIEnv* env, const char* msg) {
+    jniThrowException(env, "java/io/IOException", msg);
+}
+
+bool GraphicsJNI::hasException(JNIEnv *env) {
+    if (env->ExceptionCheck() != 0) {
+        ALOGE("*** Uncaught exception returned from Java call!\n");
+        env->ExceptionDescribe();
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AutoJavaFloatArray::AutoJavaFloatArray(JNIEnv* env, jfloatArray array,
+                                       int minLength, JNIAccess access)
+: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
+    ALOG_ASSERT(env);
+    if (array) {
+        fLen = env->GetArrayLength(array);
+        if (fLen < minLength) {
+            LOG_ALWAYS_FATAL("bad length");
+        }
+        fPtr = env->GetFloatArrayElements(array, NULL);
+    }
+    fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0;
+}
+
+AutoJavaFloatArray::~AutoJavaFloatArray() {
+    if (fPtr) {
+        fEnv->ReleaseFloatArrayElements(fArray, fPtr, fReleaseMode);
+    }
+}
+
+AutoJavaIntArray::AutoJavaIntArray(JNIEnv* env, jintArray array,
+                                       int minLength)
+: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
+    ALOG_ASSERT(env);
+    if (array) {
+        fLen = env->GetArrayLength(array);
+        if (fLen < minLength) {
+            LOG_ALWAYS_FATAL("bad length");
+        }
+        fPtr = env->GetIntArrayElements(array, NULL);
+    }
+}
+
+AutoJavaIntArray::~AutoJavaIntArray() {
+    if (fPtr) {
+        fEnv->ReleaseIntArrayElements(fArray, fPtr, 0);
+    }
+}
+
+AutoJavaShortArray::AutoJavaShortArray(JNIEnv* env, jshortArray array,
+                                       int minLength, JNIAccess access)
+: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
+    ALOG_ASSERT(env);
+    if (array) {
+        fLen = env->GetArrayLength(array);
+        if (fLen < minLength) {
+            LOG_ALWAYS_FATAL("bad length");
+        }
+        fPtr = env->GetShortArrayElements(array, NULL);
+    }
+    fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0;
+}
+
+AutoJavaShortArray::~AutoJavaShortArray() {
+    if (fPtr) {
+        fEnv->ReleaseShortArrayElements(fArray, fPtr, fReleaseMode);
+    }
+}
+
+AutoJavaByteArray::AutoJavaByteArray(JNIEnv* env, jbyteArray array,
+                                       int minLength)
+: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
+    ALOG_ASSERT(env);
+    if (array) {
+        fLen = env->GetArrayLength(array);
+        if (fLen < minLength) {
+            LOG_ALWAYS_FATAL("bad length");
+        }
+        fPtr = env->GetByteArrayElements(array, NULL);
+    }
+}
+
+AutoJavaByteArray::~AutoJavaByteArray() {
+    if (fPtr) {
+        fEnv->ReleaseByteArrayElements(fArray, fPtr, 0);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jclass   gRect_class;
+static jfieldID gRect_leftFieldID;
+static jfieldID gRect_topFieldID;
+static jfieldID gRect_rightFieldID;
+static jfieldID gRect_bottomFieldID;
+
+static jclass   gRectF_class;
+static jfieldID gRectF_leftFieldID;
+static jfieldID gRectF_topFieldID;
+static jfieldID gRectF_rightFieldID;
+static jfieldID gRectF_bottomFieldID;
+
+static jclass   gPoint_class;
+static jfieldID gPoint_xFieldID;
+static jfieldID gPoint_yFieldID;
+
+static jclass   gPointF_class;
+static jfieldID gPointF_xFieldID;
+static jfieldID gPointF_yFieldID;
+
+static jclass   gBitmapConfig_class;
+static jfieldID gBitmapConfig_nativeInstanceID;
+static jmethodID gBitmapConfig_nativeToConfigMethodID;
+
+static jclass   gBitmapRegionDecoder_class;
+static jmethodID gBitmapRegionDecoder_constructorMethodID;
+
+static jclass   gCanvas_class;
+static jfieldID gCanvas_nativeInstanceID;
+
+static jclass   gPicture_class;
+static jfieldID gPicture_nativeInstanceID;
+
+static jclass   gRegion_class;
+static jfieldID gRegion_nativeInstanceID;
+static jmethodID gRegion_constructorMethodID;
+
+static jclass    gByte_class;
+static jobject   gVMRuntime;
+static jclass    gVMRuntime_class;
+static jmethodID gVMRuntime_newNonMovableArray;
+static jmethodID gVMRuntime_addressOf;
+
+static jclass gColorSpace_class;
+static jmethodID gColorSpace_getMethodID;
+static jmethodID gColorSpace_matchMethodID;
+
+static jclass gColorSpaceRGB_class;
+static jmethodID gColorSpaceRGB_constructorMethodID;
+
+static jclass gColorSpace_Named_class;
+static jfieldID gColorSpace_Named_sRGBFieldID;
+static jfieldID gColorSpace_Named_ExtendedSRGBFieldID;
+static jfieldID gColorSpace_Named_LinearSRGBFieldID;
+static jfieldID gColorSpace_Named_LinearExtendedSRGBFieldID;
+
+static jclass gTransferParameters_class;
+static jmethodID gTransferParameters_constructorMethodID;
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B)
+{
+    ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class));
+
+    *L = env->GetIntField(obj, gRect_leftFieldID);
+    *T = env->GetIntField(obj, gRect_topFieldID);
+    *R = env->GetIntField(obj, gRect_rightFieldID);
+    *B = env->GetIntField(obj, gRect_bottomFieldID);
+}
+
+void GraphicsJNI::set_jrect(JNIEnv* env, jobject obj, int L, int T, int R, int B)
+{
+    ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class));
+
+    env->SetIntField(obj, gRect_leftFieldID, L);
+    env->SetIntField(obj, gRect_topFieldID, T);
+    env->SetIntField(obj, gRect_rightFieldID, R);
+    env->SetIntField(obj, gRect_bottomFieldID, B);
+}
+
+SkIRect* GraphicsJNI::jrect_to_irect(JNIEnv* env, jobject obj, SkIRect* ir)
+{
+    ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class));
+
+    ir->setLTRB(env->GetIntField(obj, gRect_leftFieldID),
+                env->GetIntField(obj, gRect_topFieldID),
+                env->GetIntField(obj, gRect_rightFieldID),
+                env->GetIntField(obj, gRect_bottomFieldID));
+    return ir;
+}
+
+void GraphicsJNI::irect_to_jrect(const SkIRect& ir, JNIEnv* env, jobject obj)
+{
+    ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class));
+
+    env->SetIntField(obj, gRect_leftFieldID, ir.fLeft);
+    env->SetIntField(obj, gRect_topFieldID, ir.fTop);
+    env->SetIntField(obj, gRect_rightFieldID, ir.fRight);
+    env->SetIntField(obj, gRect_bottomFieldID, ir.fBottom);
+}
+
+SkRect* GraphicsJNI::jrectf_to_rect(JNIEnv* env, jobject obj, SkRect* r)
+{
+    ALOG_ASSERT(env->IsInstanceOf(obj, gRectF_class));
+
+    r->setLTRB(env->GetFloatField(obj, gRectF_leftFieldID),
+               env->GetFloatField(obj, gRectF_topFieldID),
+               env->GetFloatField(obj, gRectF_rightFieldID),
+               env->GetFloatField(obj, gRectF_bottomFieldID));
+    return r;
+}
+
+SkRect* GraphicsJNI::jrect_to_rect(JNIEnv* env, jobject obj, SkRect* r)
+{
+    ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class));
+
+    r->setLTRB(SkIntToScalar(env->GetIntField(obj, gRect_leftFieldID)),
+               SkIntToScalar(env->GetIntField(obj, gRect_topFieldID)),
+               SkIntToScalar(env->GetIntField(obj, gRect_rightFieldID)),
+               SkIntToScalar(env->GetIntField(obj, gRect_bottomFieldID)));
+    return r;
+}
+
+void GraphicsJNI::rect_to_jrectf(const SkRect& r, JNIEnv* env, jobject obj)
+{
+    ALOG_ASSERT(env->IsInstanceOf(obj, gRectF_class));
+
+    env->SetFloatField(obj, gRectF_leftFieldID, SkScalarToFloat(r.fLeft));
+    env->SetFloatField(obj, gRectF_topFieldID, SkScalarToFloat(r.fTop));
+    env->SetFloatField(obj, gRectF_rightFieldID, SkScalarToFloat(r.fRight));
+    env->SetFloatField(obj, gRectF_bottomFieldID, SkScalarToFloat(r.fBottom));
+}
+
+SkIPoint* GraphicsJNI::jpoint_to_ipoint(JNIEnv* env, jobject obj, SkIPoint* point)
+{
+    ALOG_ASSERT(env->IsInstanceOf(obj, gPoint_class));
+
+    point->set(env->GetIntField(obj, gPoint_xFieldID),
+               env->GetIntField(obj, gPoint_yFieldID));
+    return point;
+}
+
+void GraphicsJNI::ipoint_to_jpoint(const SkIPoint& ir, JNIEnv* env, jobject obj)
+{
+    ALOG_ASSERT(env->IsInstanceOf(obj, gPoint_class));
+
+    env->SetIntField(obj, gPoint_xFieldID, ir.fX);
+    env->SetIntField(obj, gPoint_yFieldID, ir.fY);
+}
+
+SkPoint* GraphicsJNI::jpointf_to_point(JNIEnv* env, jobject obj, SkPoint* point)
+{
+    ALOG_ASSERT(env->IsInstanceOf(obj, gPointF_class));
+
+    point->set(env->GetIntField(obj, gPointF_xFieldID),
+               env->GetIntField(obj, gPointF_yFieldID));
+    return point;
+}
+
+void GraphicsJNI::point_to_jpointf(const SkPoint& r, JNIEnv* env, jobject obj)
+{
+    ALOG_ASSERT(env->IsInstanceOf(obj, gPointF_class));
+
+    env->SetFloatField(obj, gPointF_xFieldID, SkScalarToFloat(r.fX));
+    env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY));
+}
+
+// See enum values in GraphicsJNI.h
+jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) {
+    switch (colorType) {
+        case kRGBA_F16_SkColorType:
+            return kRGBA_16F_LegacyBitmapConfig;
+        case kN32_SkColorType:
+            return kARGB_8888_LegacyBitmapConfig;
+        case kARGB_4444_SkColorType:
+            return kARGB_4444_LegacyBitmapConfig;
+        case kRGB_565_SkColorType:
+            return kRGB_565_LegacyBitmapConfig;
+        case kAlpha_8_SkColorType:
+            return kA8_LegacyBitmapConfig;
+        case kUnknown_SkColorType:
+        default:
+            break;
+    }
+    return kNo_LegacyBitmapConfig;
+}
+
+SkColorType GraphicsJNI::legacyBitmapConfigToColorType(jint legacyConfig) {
+    const uint8_t gConfig2ColorType[] = {
+        kUnknown_SkColorType,
+        kAlpha_8_SkColorType,
+        kUnknown_SkColorType, // Previously kIndex_8_SkColorType,
+        kRGB_565_SkColorType,
+        kARGB_4444_SkColorType,
+        kN32_SkColorType,
+        kRGBA_F16_SkColorType,
+        kN32_SkColorType
+    };
+
+    if (legacyConfig < 0 || legacyConfig > kLastEnum_LegacyBitmapConfig) {
+        legacyConfig = kNo_LegacyBitmapConfig;
+    }
+    return static_cast<SkColorType>(gConfig2ColorType[legacyConfig]);
+}
+
+AndroidBitmapFormat GraphicsJNI::getFormatFromConfig(JNIEnv* env, jobject jconfig) {
+    ALOG_ASSERT(env);
+    if (NULL == jconfig) {
+        return ANDROID_BITMAP_FORMAT_NONE;
+    }
+    ALOG_ASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class));
+    jint javaConfigId = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID);
+
+    const AndroidBitmapFormat config2BitmapFormat[] = {
+        ANDROID_BITMAP_FORMAT_NONE,
+        ANDROID_BITMAP_FORMAT_A_8,
+        ANDROID_BITMAP_FORMAT_NONE, // Previously Config.Index_8
+        ANDROID_BITMAP_FORMAT_RGB_565,
+        ANDROID_BITMAP_FORMAT_RGBA_4444,
+        ANDROID_BITMAP_FORMAT_RGBA_8888,
+        ANDROID_BITMAP_FORMAT_RGBA_F16,
+        ANDROID_BITMAP_FORMAT_NONE // Congfig.HARDWARE
+    };
+    return config2BitmapFormat[javaConfigId];
+}
+
+jobject GraphicsJNI::getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format) {
+    ALOG_ASSERT(env);
+    jint configId = kNo_LegacyBitmapConfig;
+    switch (format) {
+      case ANDROID_BITMAP_FORMAT_A_8:
+        configId = kA8_LegacyBitmapConfig;
+        break;
+      case ANDROID_BITMAP_FORMAT_RGB_565:
+        configId = kRGB_565_LegacyBitmapConfig;
+        break;
+      case ANDROID_BITMAP_FORMAT_RGBA_4444:
+        configId = kARGB_4444_LegacyBitmapConfig;
+        break;
+      case ANDROID_BITMAP_FORMAT_RGBA_8888:
+        configId = kARGB_8888_LegacyBitmapConfig;
+        break;
+      case ANDROID_BITMAP_FORMAT_RGBA_F16:
+        configId = kRGBA_16F_LegacyBitmapConfig;
+        break;
+      default:
+        break;
+    }
+
+    return env->CallStaticObjectMethod(gBitmapConfig_class,
+                                       gBitmapConfig_nativeToConfigMethodID, configId);
+}
+
+SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) {
+    ALOG_ASSERT(env);
+    if (NULL == jconfig) {
+        return kUnknown_SkColorType;
+    }
+    ALOG_ASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class));
+    int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID);
+    return legacyBitmapConfigToColorType(c);
+}
+
+bool GraphicsJNI::isHardwareConfig(JNIEnv* env, jobject jconfig) {
+    ALOG_ASSERT(env);
+    if (NULL == jconfig) {
+        return false;
+    }
+    int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID);
+    return c == kHardware_LegacyBitmapConfig;
+}
+
+jint GraphicsJNI::hardwareLegacyBitmapConfig() {
+    return kHardware_LegacyBitmapConfig;
+}
+
+android::Canvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
+    ALOG_ASSERT(env);
+    ALOG_ASSERT(canvas);
+    ALOG_ASSERT(env->IsInstanceOf(canvas, gCanvas_class));
+    jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID);
+    if (!canvasHandle) {
+        return NULL;
+    }
+    return reinterpret_cast<android::Canvas*>(canvasHandle);
+}
+
+SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region)
+{
+    ALOG_ASSERT(env);
+    ALOG_ASSERT(region);
+    ALOG_ASSERT(env->IsInstanceOf(region, gRegion_class));
+    jlong regionHandle = env->GetLongField(region, gRegion_nativeInstanceID);
+    SkRegion* r = reinterpret_cast<SkRegion*>(regionHandle);
+    ALOG_ASSERT(r);
+    return r;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
+{
+    ALOG_ASSERT(bitmap != NULL);
+
+    jobject obj = env->NewObject(gBitmapRegionDecoder_class,
+            gBitmapRegionDecoder_constructorMethodID,
+            reinterpret_cast<jlong>(bitmap));
+    hasException(env); // For the side effect of logging.
+    return obj;
+}
+
+jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
+{
+    ALOG_ASSERT(region != NULL);
+    jobject obj = env->NewObject(gRegion_class, gRegion_constructorMethodID,
+                                 reinterpret_cast<jlong>(region), 0);
+    hasException(env); // For the side effect of logging.
+    return obj;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+jobject GraphicsJNI::getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace,
+        SkColorType decodeColorType) {
+    if (!decodeColorSpace || decodeColorType == kAlpha_8_SkColorType) {
+        return nullptr;
+    }
+
+    // Special checks for the common sRGB cases and their extended variants.
+    jobject namedCS = nullptr;
+    sk_sp<SkColorSpace> srgbLinear = SkColorSpace::MakeSRGBLinear();
+    if (decodeColorType == kRGBA_F16_SkColorType) {
+        // An F16 Bitmap will always report that it is EXTENDED if
+        // it matches a ColorSpace that has an EXTENDED variant.
+        if (decodeColorSpace->isSRGB()) {
+            namedCS = env->GetStaticObjectField(gColorSpace_Named_class,
+                                                gColorSpace_Named_ExtendedSRGBFieldID);
+        } else if (decodeColorSpace == srgbLinear.get()) {
+            namedCS = env->GetStaticObjectField(gColorSpace_Named_class,
+                                                gColorSpace_Named_LinearExtendedSRGBFieldID);
+        }
+    } else if (decodeColorSpace->isSRGB()) {
+        namedCS = env->GetStaticObjectField(gColorSpace_Named_class,
+                                            gColorSpace_Named_sRGBFieldID);
+    } else if (decodeColorSpace == srgbLinear.get()) {
+        namedCS = env->GetStaticObjectField(gColorSpace_Named_class,
+                                            gColorSpace_Named_LinearSRGBFieldID);
+    }
+
+    if (namedCS) {
+        return env->CallStaticObjectMethod(gColorSpace_class, gColorSpace_getMethodID, namedCS);
+    }
+
+    // Try to match against known RGB color spaces using the CIE XYZ D50
+    // conversion matrix and numerical transfer function parameters
+    skcms_Matrix3x3 xyzMatrix;
+    LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix));
+
+    skcms_TransferFunction transferParams;
+    // We can only handle numerical transfer functions at the moment
+    LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams));
+
+    jobject params = env->NewObject(gTransferParameters_class,
+            gTransferParameters_constructorMethodID,
+            transferParams.a, transferParams.b, transferParams.c,
+            transferParams.d, transferParams.e, transferParams.f,
+            transferParams.g);
+
+    jfloatArray xyzArray = env->NewFloatArray(9);
+    jfloat xyz[9] = {
+            xyzMatrix.vals[0][0],
+            xyzMatrix.vals[1][0],
+            xyzMatrix.vals[2][0],
+            xyzMatrix.vals[0][1],
+            xyzMatrix.vals[1][1],
+            xyzMatrix.vals[2][1],
+            xyzMatrix.vals[0][2],
+            xyzMatrix.vals[1][2],
+            xyzMatrix.vals[2][2]
+    };
+    env->SetFloatArrayRegion(xyzArray, 0, 9, xyz);
+
+    jobject colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
+            gColorSpace_matchMethodID, xyzArray, params);
+
+    if (colorSpace == nullptr) {
+        // We couldn't find an exact match, let's create a new color space
+        // instance with the 3x3 conversion matrix and transfer function
+        colorSpace = env->NewObject(gColorSpaceRGB_class,
+                gColorSpaceRGB_constructorMethodID,
+                env->NewStringUTF("Unknown"), xyzArray, params);
+    }
+
+    env->DeleteLocalRef(xyzArray);
+    return colorSpace;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool HeapAllocator::allocPixelRef(SkBitmap* bitmap) {
+    mStorage = android::Bitmap::allocateHeapBitmap(bitmap);
+    return !!mStorage;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+RecyclingClippingPixelAllocator::RecyclingClippingPixelAllocator(
+        android::Bitmap* recycledBitmap, size_t recycledBytes)
+    : mRecycledBitmap(recycledBitmap)
+    , mRecycledBytes(recycledBytes)
+    , mSkiaBitmap(nullptr)
+    , mNeedsCopy(false)
+{}
+
+RecyclingClippingPixelAllocator::~RecyclingClippingPixelAllocator() {}
+
+bool RecyclingClippingPixelAllocator::allocPixelRef(SkBitmap* bitmap) {
+    // Ensure that the caller did not pass in a NULL bitmap to the constructor or this
+    // function.
+    LOG_ALWAYS_FATAL_IF(!mRecycledBitmap);
+    LOG_ALWAYS_FATAL_IF(!bitmap);
+    mSkiaBitmap = bitmap;
+
+    // This behaves differently than the RecyclingPixelAllocator.  For backwards
+    // compatibility, the original color type of the recycled bitmap must be maintained.
+    if (mRecycledBitmap->info().colorType() != bitmap->colorType()) {
+        return false;
+    }
+
+    // The Skia bitmap specifies the width and height needed by the decoder.
+    // mRecycledBitmap specifies the width and height of the bitmap that we
+    // want to reuse.  Neither can be changed.  We will try to find a way
+    // to reuse the memory.
+    const int maxWidth = std::max(bitmap->width(), mRecycledBitmap->info().width());
+    const int maxHeight = std::max(bitmap->height(), mRecycledBitmap->info().height());
+    const SkImageInfo maxInfo = bitmap->info().makeWH(maxWidth, maxHeight);
+    const size_t rowBytes = maxInfo.minRowBytes();
+    const size_t bytesNeeded = maxInfo.computeByteSize(rowBytes);
+    if (bytesNeeded <= mRecycledBytes) {
+        // Here we take advantage of reconfigure() to reset the rowBytes
+        // of mRecycledBitmap.  It is very important that we pass in
+        // mRecycledBitmap->info() for the SkImageInfo.  According to the
+        // specification for BitmapRegionDecoder, we are not allowed to change
+        // the SkImageInfo.
+        // We can (must) preserve the color space since it doesn't affect the
+        // storage needs
+        mRecycledBitmap->reconfigure(
+                mRecycledBitmap->info().makeColorSpace(bitmap->refColorSpace()),
+                rowBytes);
+
+        // Give the bitmap the same pixelRef as mRecycledBitmap.
+        // skbug.com/4538: We also need to make sure that the rowBytes on the pixel ref
+        //                 match the rowBytes on the bitmap.
+        bitmap->setInfo(bitmap->info(), rowBytes);
+        bitmap->setPixelRef(sk_ref_sp(mRecycledBitmap), 0, 0);
+
+        // Make sure that the recycled bitmap has the correct alpha type.
+        mRecycledBitmap->setAlphaType(bitmap->alphaType());
+
+        bitmap->notifyPixelsChanged();
+        mNeedsCopy = false;
+
+        // TODO: If the dimensions of the SkBitmap are smaller than those of
+        // mRecycledBitmap, should we zero the memory in mRecycledBitmap?
+        return true;
+    }
+
+    // In the event that mRecycledBitmap is not large enough, allocate new memory
+    // on the heap.
+    SkBitmap::HeapAllocator heapAllocator;
+
+    // We will need to copy from heap memory to mRecycledBitmap's memory after the
+    // decode is complete.
+    mNeedsCopy = true;
+
+    return heapAllocator.allocPixelRef(bitmap);
+}
+
+void RecyclingClippingPixelAllocator::copyIfNecessary() {
+    if (mNeedsCopy) {
+        mRecycledBitmap->ref();
+        SkPixelRef* recycledPixels = mRecycledBitmap;
+        void* dst = recycledPixels->pixels();
+        const size_t dstRowBytes = mRecycledBitmap->rowBytes();
+        const size_t bytesToCopy = std::min(mRecycledBitmap->info().minRowBytes(),
+                mSkiaBitmap->info().minRowBytes());
+        const int rowsToCopy = std::min(mRecycledBitmap->info().height(),
+                mSkiaBitmap->info().height());
+        for (int y = 0; y < rowsToCopy; y++) {
+            memcpy(dst, mSkiaBitmap->getAddr(0, y), bytesToCopy);
+            dst = SkTAddOffset<void>(dst, dstRowBytes);
+        }
+        recycledPixels->notifyPixelsChanged();
+        recycledPixels->unref();
+    }
+    mRecycledBitmap = nullptr;
+    mSkiaBitmap = nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+AshmemPixelAllocator::AshmemPixelAllocator(JNIEnv *env) {
+    LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJavaVM) != JNI_OK,
+            "env->GetJavaVM failed");
+}
+
+bool AshmemPixelAllocator::allocPixelRef(SkBitmap* bitmap) {
+    mStorage = android::Bitmap::allocateAshmemBitmap(bitmap);
+    return !!mStorage;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+int register_android_graphics_Graphics(JNIEnv* env)
+{
+    jmethodID m;
+    jclass c;
+
+    gRect_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Rect"));
+    gRect_leftFieldID = GetFieldIDOrDie(env, gRect_class, "left", "I");
+    gRect_topFieldID = GetFieldIDOrDie(env, gRect_class, "top", "I");
+    gRect_rightFieldID = GetFieldIDOrDie(env, gRect_class, "right", "I");
+    gRect_bottomFieldID = GetFieldIDOrDie(env, gRect_class, "bottom", "I");
+
+    gRectF_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/RectF"));
+    gRectF_leftFieldID = GetFieldIDOrDie(env, gRectF_class, "left", "F");
+    gRectF_topFieldID = GetFieldIDOrDie(env, gRectF_class, "top", "F");
+    gRectF_rightFieldID = GetFieldIDOrDie(env, gRectF_class, "right", "F");
+    gRectF_bottomFieldID = GetFieldIDOrDie(env, gRectF_class, "bottom", "F");
+
+    gPoint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Point"));
+    gPoint_xFieldID = GetFieldIDOrDie(env, gPoint_class, "x", "I");
+    gPoint_yFieldID = GetFieldIDOrDie(env, gPoint_class, "y", "I");
+
+    gPointF_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/PointF"));
+    gPointF_xFieldID = GetFieldIDOrDie(env, gPointF_class, "x", "F");
+    gPointF_yFieldID = GetFieldIDOrDie(env, gPointF_class, "y", "F");
+
+    gBitmapRegionDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/BitmapRegionDecoder"));
+    gBitmapRegionDecoder_constructorMethodID = GetMethodIDOrDie(env, gBitmapRegionDecoder_class, "<init>", "(J)V");
+
+    gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap$Config"));
+    gBitmapConfig_nativeInstanceID = GetFieldIDOrDie(env, gBitmapConfig_class, "nativeInt", "I");
+    gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class,
+                                                                  "nativeToConfig",
+                                                                  "(I)Landroid/graphics/Bitmap$Config;");
+
+    gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas"));
+    gCanvas_nativeInstanceID = GetFieldIDOrDie(env, gCanvas_class, "mNativeCanvasWrapper", "J");
+
+    gPicture_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Picture"));
+    gPicture_nativeInstanceID = GetFieldIDOrDie(env, gPicture_class, "mNativePicture", "J");
+
+    gRegion_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Region"));
+    gRegion_nativeInstanceID = GetFieldIDOrDie(env, gRegion_class, "mNativeRegion", "J");
+    gRegion_constructorMethodID = GetMethodIDOrDie(env, gRegion_class, "<init>", "(JI)V");
+
+    c = env->FindClass("java/lang/Byte");
+    gByte_class = (jclass) env->NewGlobalRef(
+        env->GetStaticObjectField(c, env->GetStaticFieldID(c, "TYPE", "Ljava/lang/Class;")));
+
+    gVMRuntime_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "dalvik/system/VMRuntime"));
+    m = env->GetStaticMethodID(gVMRuntime_class, "getRuntime", "()Ldalvik/system/VMRuntime;");
+    gVMRuntime = env->NewGlobalRef(env->CallStaticObjectMethod(gVMRuntime_class, m));
+    gVMRuntime_newNonMovableArray = GetMethodIDOrDie(env, gVMRuntime_class, "newNonMovableArray",
+                                                     "(Ljava/lang/Class;I)Ljava/lang/Object;");
+    gVMRuntime_addressOf = GetMethodIDOrDie(env, gVMRuntime_class, "addressOf", "(Ljava/lang/Object;)J");
+
+    gColorSpace_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ColorSpace"));
+    gColorSpace_getMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class,
+            "get", "(Landroid/graphics/ColorSpace$Named;)Landroid/graphics/ColorSpace;");
+    gColorSpace_matchMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "match",
+            "([FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/ColorSpace;");
+
+    gColorSpaceRGB_class = MakeGlobalRefOrDie(env,
+            FindClassOrDie(env, "android/graphics/ColorSpace$Rgb"));
+    gColorSpaceRGB_constructorMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class,
+            "<init>", "(Ljava/lang/String;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)V");
+
+    gColorSpace_Named_class = MakeGlobalRefOrDie(env,
+            FindClassOrDie(env, "android/graphics/ColorSpace$Named"));
+    gColorSpace_Named_sRGBFieldID = GetStaticFieldIDOrDie(env,
+            gColorSpace_Named_class, "SRGB", "Landroid/graphics/ColorSpace$Named;");
+    gColorSpace_Named_ExtendedSRGBFieldID = GetStaticFieldIDOrDie(env,
+            gColorSpace_Named_class, "EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;");
+    gColorSpace_Named_LinearSRGBFieldID = GetStaticFieldIDOrDie(env,
+            gColorSpace_Named_class, "LINEAR_SRGB", "Landroid/graphics/ColorSpace$Named;");
+    gColorSpace_Named_LinearExtendedSRGBFieldID = GetStaticFieldIDOrDie(env,
+            gColorSpace_Named_class, "LINEAR_EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;");
+
+    gTransferParameters_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
+            "android/graphics/ColorSpace$Rgb$TransferParameters"));
+    gTransferParameters_constructorMethodID = GetMethodIDOrDie(env, gTransferParameters_class,
+            "<init>", "(DDDDDDD)V");
+
+    return 0;
+}
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
new file mode 100644
index 0000000..1e49765
--- /dev/null
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -0,0 +1,314 @@
+#ifndef _ANDROID_GRAPHICS_GRAPHICS_JNI_H_
+#define _ANDROID_GRAPHICS_GRAPHICS_JNI_H_
+
+#include "Bitmap.h"
+#include "SkBitmap.h"
+#include "SkBRDAllocator.h"
+#include "SkCodec.h"
+#include "SkPixelRef.h"
+#include "SkMallocPixelRef.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+#include "SkColorSpace.h"
+#include <jni.h>
+#include <hwui/Canvas.h>
+#include <hwui/Bitmap.h>
+
+class SkBitmapRegionDecoder;
+class SkCanvas;
+
+namespace android {
+class Paint;
+struct Typeface;
+}
+
+class GraphicsJNI {
+public:
+    // This enum must keep these int values, to match the int values
+    // in the java Bitmap.Config enum.
+    enum LegacyBitmapConfig {
+        kNo_LegacyBitmapConfig          = 0,
+        kA8_LegacyBitmapConfig          = 1,
+        kIndex8_LegacyBitmapConfig      = 2,
+        kRGB_565_LegacyBitmapConfig     = 3,
+        kARGB_4444_LegacyBitmapConfig   = 4,
+        kARGB_8888_LegacyBitmapConfig   = 5,
+        kRGBA_16F_LegacyBitmapConfig    = 6,
+        kHardware_LegacyBitmapConfig    = 7,
+
+        kLastEnum_LegacyBitmapConfig = kHardware_LegacyBitmapConfig
+    };
+
+    // returns true if an exception is set (and dumps it out to the Log)
+    static bool hasException(JNIEnv*);
+
+    static void get_jrect(JNIEnv*, jobject jrect, int* L, int* T, int* R, int* B);
+    static void set_jrect(JNIEnv*, jobject jrect, int L, int T, int R, int B);
+
+    static SkIRect* jrect_to_irect(JNIEnv*, jobject jrect, SkIRect*);
+    static void irect_to_jrect(const SkIRect&, JNIEnv*, jobject jrect);
+
+    static SkRect* jrectf_to_rect(JNIEnv*, jobject jrectf, SkRect*);
+    static SkRect* jrect_to_rect(JNIEnv*, jobject jrect, SkRect*);
+    static void rect_to_jrectf(const SkRect&, JNIEnv*, jobject jrectf);
+
+    static void set_jpoint(JNIEnv*, jobject jrect, int x, int y);
+
+    static SkIPoint* jpoint_to_ipoint(JNIEnv*, jobject jpoint, SkIPoint* point);
+    static void ipoint_to_jpoint(const SkIPoint& point, JNIEnv*, jobject jpoint);
+
+    static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point);
+    static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf);
+
+    static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas);
+    static android::Bitmap* getNativeBitmap(JNIEnv*, jobject bitmap);
+    static SkImageInfo getBitmapInfo(JNIEnv*, jobject bitmap, uint32_t* outRowBytes,
+                                     bool* isHardware);
+    static SkRegion* getNativeRegion(JNIEnv*, jobject region);
+
+    /*
+     *  LegacyBitmapConfig is the old enum in Skia that matched the enum int values
+     *  in Bitmap.Config. Skia no longer supports this config, but has replaced it
+     *  with SkColorType. These routines convert between the two.
+     */
+    static SkColorType legacyBitmapConfigToColorType(jint legacyConfig);
+    static jint colorTypeToLegacyBitmapConfig(SkColorType colorType);
+
+    /** Return the corresponding native colorType from the java Config enum,
+        or kUnknown_SkColorType if the java object is null.
+    */
+    static SkColorType getNativeBitmapColorType(JNIEnv*, jobject jconfig);
+    static AndroidBitmapFormat getFormatFromConfig(JNIEnv* env, jobject jconfig);
+    static jobject getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format);
+
+    static bool isHardwareConfig(JNIEnv* env, jobject jconfig);
+    static jint hardwareLegacyBitmapConfig();
+
+    static jobject createRegion(JNIEnv* env, SkRegion* region);
+
+    static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
+
+    /**
+     * Given a bitmap we natively allocate a memory block to store the contents
+     * of that bitmap.  The memory is then attached to the bitmap via an
+     * SkPixelRef, which ensures that upon deletion the appropriate caches
+     * are notified.
+     */
+    static bool allocatePixels(JNIEnv* env, SkBitmap* bitmap);
+
+    /** Copy the colors in colors[] to the bitmap, convert to the correct
+        format along the way.
+        Whether to use premultiplied pixels is determined by dstBitmap's alphaType.
+    */
+    static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset,
+            int srcStride, int x, int y, int width, int height,
+            SkBitmap* dstBitmap);
+
+    /**
+     * Convert the native SkColorSpace retrieved from ColorSpace.Rgb.getNativeInstance().
+     *
+     * This will never throw an Exception. If the ColorSpace is one that Skia cannot
+     * use, ColorSpace.Rgb.getNativeInstance() would have thrown an Exception. It may,
+     * however, be nullptr, which may be acceptable.
+     */
+    static sk_sp<SkColorSpace> getNativeColorSpace(jlong colorSpaceHandle);
+
+    /**
+     * Return the android.graphics.ColorSpace Java object that corresponds to decodeColorSpace
+     * and decodeColorType.
+     *
+     * This may create a new object if none of the Named ColorSpaces match.
+     */
+    static jobject getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace,
+            SkColorType decodeColorType);
+
+    /**
+     * Convert from a Java @ColorLong to an SkColor4f that Skia can use directly.
+     *
+     * This ignores the encoded ColorSpace, besides checking to see if it is sRGB,
+     * which is encoded differently. The color space should be passed down separately
+     * via ColorSpace#getNativeInstance(), and converted with getNativeColorSpace(),
+     * above.
+     */
+    static SkColor4f convertColorLong(jlong color);
+};
+
+class HeapAllocator : public SkBRDAllocator {
+public:
+   HeapAllocator() { };
+    ~HeapAllocator() { };
+
+    virtual bool allocPixelRef(SkBitmap* bitmap) override;
+
+    /**
+     * Fetches the backing allocation object. Must be called!
+     */
+    android::Bitmap* getStorageObjAndReset() {
+        return mStorage.release();
+    };
+
+    SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kYes_ZeroInitialized; }
+private:
+    sk_sp<android::Bitmap> mStorage;
+};
+
+/**
+ *  Allocator to handle reusing bitmaps for BitmapRegionDecoder.
+ *
+ *  The BitmapRegionDecoder documentation states that, if it is
+ *  provided, the recycled bitmap will always be reused, clipping
+ *  the decoded output to fit in the recycled bitmap if necessary.
+ *  This allocator implements that behavior.
+ *
+ *  Skia's SkBitmapRegionDecoder expects the memory that
+ *  is allocated to be large enough to decode the entire region
+ *  that is requested.  It will decode directly into the memory
+ *  that is provided.
+ *
+ *  FIXME: BUG:25465958
+ *  If the recycled bitmap is not large enough for the decode
+ *  requested, meaning that a clip is required, we will allocate
+ *  enough memory for Skia to perform the decode, and then copy
+ *  from the decoded output into the recycled bitmap.
+ *
+ *  If the recycled bitmap is large enough for the decode requested,
+ *  we will provide that memory for Skia to decode directly into.
+ *
+ *  This allocator should only be used for a single allocation.
+ *  After we reuse the recycledBitmap once, it is dangerous to
+ *  reuse it again, given that it still may be in use from our
+ *  first allocation.
+ */
+class RecyclingClippingPixelAllocator : public SkBRDAllocator {
+public:
+
+    RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap,
+            size_t recycledBytes);
+
+    ~RecyclingClippingPixelAllocator();
+
+    virtual bool allocPixelRef(SkBitmap* bitmap) override;
+
+    /**
+     *  Must be called!
+     *
+     *  In the event that the recycled bitmap is not large enough for
+     *  the allocation requested, we will allocate memory on the heap
+     *  instead.  As a final step, once we are done using this memory,
+     *  we will copy the contents of the heap memory into the recycled
+     *  bitmap's memory, clipping as necessary.
+     */
+    void copyIfNecessary();
+
+    /**
+     *  Indicates that this allocator does not allocate zero initialized
+     *  memory.
+     */
+    SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kNo_ZeroInitialized; }
+
+private:
+    android::Bitmap* mRecycledBitmap;
+    const size_t     mRecycledBytes;
+    SkBitmap*        mSkiaBitmap;
+    bool             mNeedsCopy;
+};
+
+class AshmemPixelAllocator : public SkBitmap::Allocator {
+public:
+    explicit AshmemPixelAllocator(JNIEnv* env);
+    ~AshmemPixelAllocator() { };
+    virtual bool allocPixelRef(SkBitmap* bitmap);
+    android::Bitmap* getStorageObjAndReset() {
+        return mStorage.release();
+    };
+
+private:
+    JavaVM* mJavaVM;
+    sk_sp<android::Bitmap> mStorage;
+};
+
+
+enum JNIAccess {
+    kRO_JNIAccess,
+    kRW_JNIAccess
+};
+
+class AutoJavaFloatArray {
+public:
+    AutoJavaFloatArray(JNIEnv* env, jfloatArray array,
+                       int minLength = 0, JNIAccess = kRW_JNIAccess);
+    ~AutoJavaFloatArray();
+
+    float* ptr() const { return fPtr; }
+    int    length() const { return fLen; }
+
+private:
+    JNIEnv*     fEnv;
+    jfloatArray fArray;
+    float*      fPtr;
+    int         fLen;
+    int         fReleaseMode;
+};
+
+class AutoJavaIntArray {
+public:
+    AutoJavaIntArray(JNIEnv* env, jintArray array, int minLength = 0);
+    ~AutoJavaIntArray();
+
+    jint* ptr() const { return fPtr; }
+    int    length() const { return fLen; }
+
+private:
+    JNIEnv*     fEnv;
+    jintArray fArray;
+    jint*      fPtr;
+    int         fLen;
+};
+
+class AutoJavaShortArray {
+public:
+    AutoJavaShortArray(JNIEnv* env, jshortArray array,
+                       int minLength = 0, JNIAccess = kRW_JNIAccess);
+    ~AutoJavaShortArray();
+
+    jshort* ptr() const { return fPtr; }
+    int    length() const { return fLen; }
+
+private:
+    JNIEnv*     fEnv;
+    jshortArray fArray;
+    jshort*      fPtr;
+    int         fLen;
+    int         fReleaseMode;
+};
+
+class AutoJavaByteArray {
+public:
+    AutoJavaByteArray(JNIEnv* env, jbyteArray array, int minLength = 0);
+    ~AutoJavaByteArray();
+
+    jbyte* ptr() const { return fPtr; }
+    int    length() const { return fLen; }
+
+private:
+    JNIEnv*     fEnv;
+    jbyteArray fArray;
+    jbyte*      fPtr;
+    int         fLen;
+};
+
+void doThrowNPE(JNIEnv* env);
+void doThrowAIOOBE(JNIEnv* env); // Array Index Out Of Bounds Exception
+void doThrowIAE(JNIEnv* env, const char* msg = NULL);   // Illegal Argument
+void doThrowRE(JNIEnv* env, const char* msg = NULL);   // Runtime
+void doThrowISE(JNIEnv* env, const char* msg = NULL);   // Illegal State
+void doThrowOOME(JNIEnv* env, const char* msg = NULL);   // Out of memory
+void doThrowIOE(JNIEnv* env, const char* msg = NULL);   // IO Exception
+
+#define NPE_CHECK_RETURN_ZERO(env, object)    \
+    do { if (NULL == (object)) { doThrowNPE(env); return 0; } } while (0)
+
+#define NPE_CHECK_RETURN_VOID(env, object)    \
+    do { if (NULL == (object)) { doThrowNPE(env); return; } } while (0)
+
+#endif  // _ANDROID_GRAPHICS_GRAPHICS_JNI_H_
diff --git a/libs/hwui/jni/GraphicsStatsService.cpp b/libs/hwui/jni/GraphicsStatsService.cpp
new file mode 100644
index 0000000..ef0aacc
--- /dev/null
+++ b/libs/hwui/jni/GraphicsStatsService.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GraphicsStatsService"
+
+#include <JankTracker.h>
+#include <jni.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <service/GraphicsStatsService.h>
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
+#include "core_jni_helpers.h"
+
+namespace android {
+
+using namespace android::uirenderer;
+
+static jint getAshmemSize(JNIEnv*, jobject) {
+    return sizeof(ProfileData);
+}
+
+static jlong createDump(JNIEnv*, jobject, jint fd, jboolean isProto) {
+    GraphicsStatsService::Dump* dump =
+            GraphicsStatsService::createDump(fd,
+                                             isProto ? GraphicsStatsService::DumpType::Protobuf
+                                                     : GraphicsStatsService::DumpType::Text);
+    return reinterpret_cast<jlong>(dump);
+}
+
+static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage,
+                      jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
+    std::string path;
+    const ProfileData* data = nullptr;
+    LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null");
+    ScopedByteArrayRO buffer{env};
+    if (jdata != nullptr) {
+        buffer.reset(jdata);
+        LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
+                            "Buffer size %zu doesn't match expected %zu!", buffer.size(),
+                            sizeof(ProfileData));
+        data = reinterpret_cast<const ProfileData*>(buffer.get());
+    }
+    if (jpath != nullptr) {
+        ScopedUtfChars pathChars(env, jpath);
+        LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(),
+                            "Failed to get path chars");
+        path.assign(pathChars.c_str(), pathChars.size());
+    }
+    ScopedUtfChars packageChars(env, jpackage);
+    LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(),
+                        "Failed to get path chars");
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    LOG_ALWAYS_FATAL_IF(!dump, "null passed for dump pointer");
+
+    const std::string package(packageChars.c_str(), packageChars.size());
+    GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime, data);
+}
+
+static void addFileToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath) {
+    ScopedUtfChars pathChars(env, jpath);
+    LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
+    const std::string path(pathChars.c_str(), pathChars.size());
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    GraphicsStatsService::addToDump(dump, path);
+}
+
+static void finishDump(JNIEnv*, jobject, jlong dumpPtr) {
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    GraphicsStatsService::finishDump(dump);
+}
+
+static void finishDumpInMemory(JNIEnv* env, jobject, jlong dumpPtr, jlong pulledData,
+                               jboolean lastFullDay) {
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    AStatsEventList* data = reinterpret_cast<AStatsEventList*>(pulledData);
+    GraphicsStatsService::finishDumpInMemory(dump, data, lastFullDay == JNI_TRUE);
+}
+
+static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage,
+                       jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
+    ScopedByteArrayRO buffer(env, jdata);
+    LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
+                        "Buffer size %zu doesn't match expected %zu!", buffer.size(),
+                        sizeof(ProfileData));
+    ScopedUtfChars pathChars(env, jpath);
+    LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
+    ScopedUtfChars packageChars(env, jpackage);
+    LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(),
+                        "Failed to get path chars");
+
+    const std::string path(pathChars.c_str(), pathChars.size());
+    const std::string package(packageChars.c_str(), packageChars.size());
+    const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer.get());
+    GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data);
+}
+
+static jobject gGraphicsStatsServiceObject = nullptr;
+static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID;
+
+static JNIEnv* getJNIEnv() {
+    JavaVM* vm = AndroidRuntime::getJavaVM();
+    JNIEnv* env = nullptr;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+            LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
+        }
+    }
+    return env;
+}
+
+// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
+static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t atom_tag,
+                                                                      AStatsEventList* data,
+                                                                      void* cookie) {
+    JNIEnv* env = getJNIEnv();
+    if (!env) {
+        return false;
+    }
+    if (gGraphicsStatsServiceObject == nullptr) {
+        ALOGE("Failed to get graphicsstats service");
+        return AStatsManager_PULL_SKIP;
+    }
+
+    for (bool lastFullDay : {true, false}) {
+        env->CallVoidMethod(gGraphicsStatsServiceObject,
+                            gGraphicsStatsService_pullGraphicsStatsMethodID,
+                            (jboolean)(lastFullDay ? JNI_TRUE : JNI_FALSE),
+                            reinterpret_cast<jlong>(data));
+        if (env->ExceptionCheck()) {
+            env->ExceptionDescribe();
+            env->ExceptionClear();
+            ALOGE("Failed to invoke graphicsstats service");
+            return AStatsManager_PULL_SKIP;
+        }
+    }
+    return AStatsManager_PULL_SUCCESS;
+}
+
+// Register a puller for GRAPHICS_STATS atom with the statsd service.
+static void nativeInit(JNIEnv* env, jobject javaObject) {
+    gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject);
+    AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
+    AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, 10 * 1000000);  // 10 milliseconds
+    AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, 2 * NS_PER_SEC); // 2 seconds
+
+    AStatsManager_registerPullAtomCallback(android::util::GRAPHICS_STATS,
+                                           &graphicsStatsPullCallback, metadata, nullptr);
+
+    AStatsManager_PullAtomMetadata_release(metadata);
+}
+
+static void nativeDestructor(JNIEnv* env, jobject javaObject) {
+    AStatsManager_unregisterPullAtomCallback(android::util::GRAPHICS_STATS);
+    env->DeleteGlobalRef(gGraphicsStatsServiceObject);
+    gGraphicsStatsServiceObject = nullptr;
+}
+
+static const JNINativeMethod sMethods[] =
+        {{"nGetAshmemSize", "()I", (void*)getAshmemSize},
+         {"nCreateDump", "(IZ)J", (void*)createDump},
+         {"nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)addToDump},
+         {"nAddToDump", "(JLjava/lang/String;)V", (void*)addFileToDump},
+         {"nFinishDump", "(J)V", (void*)finishDump},
+         {"nFinishDumpInMemory", "(JJZ)V", (void*)finishDumpInMemory},
+         {"nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)saveBuffer},
+         {"nativeInit", "()V", (void*)nativeInit},
+         {"nativeDestructor", "()V", (void*)nativeDestructor}};
+
+int register_android_graphics_GraphicsStatsService(JNIEnv* env) {
+    jclass graphicsStatsService_class =
+            FindClassOrDie(env, "android/graphics/GraphicsStatsService");
+    gGraphicsStatsService_pullGraphicsStatsMethodID =
+            GetMethodIDOrDie(env, graphicsStatsService_class, "pullGraphicsStats", "(ZJ)V");
+    return jniRegisterNativeMethods(env, "android/graphics/GraphicsStatsService", sMethods,
+                                    NELEM(sMethods));
+}
+
+} // namespace android
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
new file mode 100644
index 0000000..e17e057
--- /dev/null
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Bitmap.h"
+#include "BitmapFactory.h"
+#include "ByteBufferStreamAdaptor.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "GraphicsJNI.h"
+#include "ImageDecoder.h"
+#include "NinePatchPeeker.h"
+#include "Utils.h"
+#include "core_jni_helpers.h"
+
+#include <hwui/Bitmap.h>
+#include <hwui/ImageDecoder.h>
+#include <HardwareBitmapUploader.h>
+
+#include <SkAndroidCodec.h>
+#include <SkEncodedImageFormat.h>
+#include <SkFrontBufferedStream.h>
+#include <SkStream.h>
+
+#include <androidfw/Asset.h>
+#include <jni.h>
+#include <sys/stat.h>
+
+using namespace android;
+
+static jclass    gImageDecoder_class;
+static jclass    gSize_class;
+static jclass    gDecodeException_class;
+static jclass    gCanvas_class;
+static jmethodID gImageDecoder_constructorMethodID;
+static jmethodID gImageDecoder_postProcessMethodID;
+static jmethodID gSize_constructorMethodID;
+static jmethodID gDecodeException_constructorMethodID;
+static jmethodID gCallback_onPartialImageMethodID;
+static jmethodID gCanvas_constructorMethodID;
+static jmethodID gCanvas_releaseMethodID;
+
+// These need to stay in sync with ImageDecoder.java's Allocator constants.
+enum Allocator {
+    kDefault_Allocator      = 0,
+    kSoftware_Allocator     = 1,
+    kSharedMemory_Allocator = 2,
+    kHardware_Allocator     = 3,
+};
+
+// These need to stay in sync with ImageDecoder.java's Error constants.
+enum Error {
+    kSourceException     = 1,
+    kSourceIncomplete    = 2,
+    kSourceMalformedData = 3,
+};
+
+// These need to stay in sync with PixelFormat.java's Format constants.
+enum PixelFormat {
+    kUnknown     =  0,
+    kTranslucent = -3,
+    kOpaque      = -1,
+};
+
+// Clear and return any pending exception for handling other than throwing directly.
+static jthrowable get_and_clear_exception(JNIEnv* env) {
+    jthrowable jexception = env->ExceptionOccurred();
+    if (jexception) {
+        env->ExceptionClear();
+    }
+    return jexception;
+}
+
+// Throw a new ImageDecoder.DecodeException. Returns null for convenience.
+static jobject throw_exception(JNIEnv* env, Error error, const char* msg,
+                               jthrowable cause, jobject source) {
+    jstring jstr = nullptr;
+    if (msg) {
+        jstr = env->NewStringUTF(msg);
+        if (!jstr) {
+            // Out of memory.
+            return nullptr;
+        }
+    }
+    jthrowable exception = (jthrowable) env->NewObject(gDecodeException_class,
+            gDecodeException_constructorMethodID, error, jstr, cause, source);
+    // Only throw if not out of memory.
+    if (exception) {
+        env->Throw(exception);
+    }
+    return nullptr;
+}
+
+static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream,
+        jobject source, jboolean preferAnimation) {
+    if (!stream.get()) {
+        return throw_exception(env, kSourceMalformedData, "Failed to create a stream",
+                               nullptr, source);
+    }
+    sk_sp<NinePatchPeeker> peeker(new NinePatchPeeker);
+    SkCodec::Result result;
+    auto codec = SkCodec::MakeFromStream(
+            std::move(stream), &result, peeker.get(),
+            preferAnimation ? SkCodec::SelectionPolicy::kPreferAnimation
+                            : SkCodec::SelectionPolicy::kPreferStillImage);
+    if (jthrowable jexception = get_and_clear_exception(env)) {
+        return throw_exception(env, kSourceException, "", jexception, source);
+    }
+    if (!codec) {
+        switch (result) {
+            case SkCodec::kIncompleteInput:
+                return throw_exception(env, kSourceIncomplete, "", nullptr, source);
+            default:
+                SkString msg;
+                msg.printf("Failed to create image decoder with message '%s'",
+                           SkCodec::ResultToString(result));
+                return throw_exception(env, kSourceMalformedData,  msg.c_str(),
+                                       nullptr, source);
+
+        }
+    }
+
+    const bool animated = codec->getFrameCount() > 1;
+    if (jthrowable jexception = get_and_clear_exception(env)) {
+        return throw_exception(env, kSourceException, "", jexception, source);
+    }
+
+    auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
+            SkAndroidCodec::ExifOrientationBehavior::kRespect);
+    if (!androidCodec.get()) {
+        return throw_exception(env, kSourceMalformedData, "", nullptr, source);
+    }
+
+    const auto& info = androidCodec->getInfo();
+    const int width = info.width();
+    const int height = info.height();
+    const bool isNinePatch = peeker->mPatch != nullptr;
+    ImageDecoder* decoder = new ImageDecoder(std::move(androidCodec), std::move(peeker));
+    return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID,
+                          reinterpret_cast<jlong>(decoder), width, height,
+                          animated, isNinePatch);
+}
+
+static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
+        jobject fileDescriptor, jboolean preferAnimation, jobject source) {
+    int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
+
+    struct stat fdStat;
+    if (fstat(descriptor, &fdStat) == -1) {
+        return throw_exception(env, kSourceMalformedData,
+                               "broken file descriptor; fstat returned -1", nullptr, source);
+    }
+
+    int dupDescriptor = fcntl(descriptor, F_DUPFD_CLOEXEC, 0);
+    FILE* file = fdopen(dupDescriptor, "r");
+    if (file == NULL) {
+        close(dupDescriptor);
+        return throw_exception(env, kSourceMalformedData, "Could not open file",
+                               nullptr, source);
+    }
+
+    std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file));
+    return native_create(env, std::move(fileStream), source, preferAnimation);
+}
+
+static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/,
+        jobject is, jbyteArray storage, jboolean preferAnimation, jobject source) {
+    std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false));
+
+    if (!stream.get()) {
+        return throw_exception(env, kSourceMalformedData, "Failed to create a stream",
+                               nullptr, source);
+    }
+
+    std::unique_ptr<SkStream> bufferedStream(
+        SkFrontBufferedStream::Make(std::move(stream),
+        SkCodec::MinBufferedBytesNeeded()));
+    return native_create(env, std::move(bufferedStream), source, preferAnimation);
+}
+
+static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/,
+        jlong assetPtr, jboolean preferAnimation, jobject source) {
+    Asset* asset = reinterpret_cast<Asset*>(assetPtr);
+    std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset));
+    return native_create(env, std::move(stream), source, preferAnimation);
+}
+
+static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/,
+        jobject jbyteBuffer, jint initialPosition, jint limit,
+        jboolean preferAnimation, jobject source) {
+    std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer,
+                                                                     initialPosition, limit);
+    if (!stream) {
+        return throw_exception(env, kSourceMalformedData, "Failed to read ByteBuffer",
+                               nullptr, source);
+    }
+    return native_create(env, std::move(stream), source, preferAnimation);
+}
+
+static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/,
+        jbyteArray byteArray, jint offset, jint length,
+        jboolean preferAnimation, jobject source) {
+    std::unique_ptr<SkStream> stream(CreateByteArrayStreamAdaptor(env, byteArray, offset, length));
+    return native_create(env, std::move(stream), source, preferAnimation);
+}
+
+jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) {
+    jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID,
+                                     reinterpret_cast<jlong>(canvas.get()));
+    if (!jcanvas) {
+        doThrowOOME(env, "Failed to create Java Canvas for PostProcess!");
+        return kUnknown;
+    }
+
+    // jcanvas now owns canvas.
+    canvas.release();
+
+    return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID, jcanvas);
+}
+
+static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+                                          jobject jdecoder, jboolean jpostProcess,
+                                          jint targetWidth, jint targetHeight, jobject jsubset,
+                                          jboolean requireMutable, jint allocator,
+                                          jboolean requireUnpremul, jboolean preferRamOverQuality,
+                                          jboolean asAlphaMask, jlong colorSpaceHandle,
+                                          jboolean extended) {
+    auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
+    if (!decoder->setTargetSize(targetWidth, targetHeight)) {
+        doThrowISE(env, "Could not scale to target size!");
+        return nullptr;
+    }
+    if (requireUnpremul && !decoder->setUnpremultipliedRequired(true)) {
+        doThrowISE(env, "Cannot scale unpremultiplied pixels!");
+        return nullptr;
+    }
+
+    SkColorType colorType = kN32_SkColorType;
+    if (asAlphaMask && decoder->gray()) {
+        // We have to trick Skia to decode this to a single channel.
+        colorType = kGray_8_SkColorType;
+    } else if (preferRamOverQuality) {
+        // FIXME: The post-process might add alpha, which would make a 565
+        // result incorrect. If we call the postProcess before now and record
+        // to a picture, we can know whether alpha was added, and if not, we
+        // can still use 565.
+        if (decoder->opaque() && !jpostProcess) {
+            // If the final result will be hardware, decoding to 565 and then
+            // uploading to the gpu as 8888 will not save memory. This still
+            // may save us from using F16, but do not go down to 565.
+            if (allocator != kHardware_Allocator &&
+               (allocator != kDefault_Allocator || requireMutable)) {
+                colorType = kRGB_565_SkColorType;
+            }
+        }
+        // Otherwise, stick with N32
+    } else if (extended) {
+        colorType = kRGBA_F16_SkColorType;
+    } else {
+        colorType = decoder->mCodec->computeOutputColorType(colorType);
+    }
+
+    const bool isHardware = !requireMutable
+        && (allocator == kDefault_Allocator ||
+            allocator == kHardware_Allocator)
+        && colorType != kGray_8_SkColorType;
+
+    if (colorType == kRGBA_F16_SkColorType && isHardware &&
+            !uirenderer::HardwareBitmapUploader::hasFP16Support()) {
+        colorType = kN32_SkColorType;
+    }
+
+    if (!decoder->setOutColorType(colorType)) {
+        doThrowISE(env, "Failed to set out color type!");
+        return nullptr;
+    }
+
+    {
+        sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+        colorSpace = decoder->mCodec->computeOutputColorSpace(colorType, colorSpace);
+        decoder->setOutColorSpace(std::move(colorSpace));
+    }
+
+    if (jsubset) {
+        SkIRect subset;
+        GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
+        if (!decoder->setCropRect(&subset)) {
+            doThrowISE(env, "Invalid crop rect!");
+            return nullptr;
+        }
+    }
+
+    SkImageInfo bitmapInfo = decoder->getOutputInfo();
+    if (decoder->opaque()) {
+        bitmapInfo = bitmapInfo.makeAlphaType(kOpaque_SkAlphaType);
+    }
+    if (asAlphaMask && colorType == kGray_8_SkColorType) {
+        bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType);
+    }
+
+    SkBitmap bm;
+    if (!bm.setInfo(bitmapInfo)) {
+        doThrowIOE(env, "Failed to setInfo properly");
+        return nullptr;
+    }
+
+    sk_sp<Bitmap> nativeBitmap;
+    if (allocator == kSharedMemory_Allocator) {
+        nativeBitmap = Bitmap::allocateAshmemBitmap(&bm);
+    } else {
+        nativeBitmap = Bitmap::allocateHeapBitmap(&bm);
+    }
+    if (!nativeBitmap) {
+        SkString msg;
+        msg.printf("OOM allocating Bitmap with dimensions %i x %i",
+                bitmapInfo.width(), bitmapInfo.height());
+        doThrowOOME(env, msg.c_str());
+        return nullptr;
+    }
+
+    SkCodec::Result result = decoder->decode(bm.getPixels(), bm.rowBytes());
+    jthrowable jexception = get_and_clear_exception(env);
+    int onPartialImageError = jexception ? kSourceException
+                                         : 0; // No error.
+    switch (result) {
+        case SkCodec::kSuccess:
+            // Ignore the exception, since the decode was successful anyway.
+            jexception = nullptr;
+            onPartialImageError = 0;
+            break;
+        case SkCodec::kIncompleteInput:
+            if (!jexception) {
+                onPartialImageError = kSourceIncomplete;
+            }
+            break;
+        case SkCodec::kErrorInInput:
+            if (!jexception) {
+                onPartialImageError = kSourceMalformedData;
+            }
+            break;
+        default:
+            SkString msg;
+            msg.printf("getPixels failed with error %s", SkCodec::ResultToString(result));
+            doThrowIOE(env, msg.c_str());
+            return nullptr;
+    }
+
+    if (onPartialImageError) {
+        env->CallVoidMethod(jdecoder, gCallback_onPartialImageMethodID, onPartialImageError,
+                jexception);
+        if (env->ExceptionCheck()) {
+            return nullptr;
+        }
+    }
+
+    jbyteArray ninePatchChunk = nullptr;
+    jobject ninePatchInsets = nullptr;
+
+    // Ignore ninepatch when post-processing.
+    if (!jpostProcess) {
+        // FIXME: Share more code with BitmapFactory.cpp.
+        auto* peeker = reinterpret_cast<NinePatchPeeker*>(decoder->mPeeker.get());
+        if (peeker->mPatch != nullptr) {
+            size_t ninePatchArraySize = peeker->mPatch->serializedSize();
+            ninePatchChunk = env->NewByteArray(ninePatchArraySize);
+            if (ninePatchChunk == nullptr) {
+                doThrowOOME(env, "Failed to allocate nine patch chunk.");
+                return nullptr;
+            }
+
+            env->SetByteArrayRegion(ninePatchChunk, 0, peeker->mPatchSize,
+                                    reinterpret_cast<jbyte*>(peeker->mPatch));
+        }
+
+        if (peeker->mHasInsets) {
+            ninePatchInsets = peeker->createNinePatchInsets(env, 1.0f);
+            if (ninePatchInsets == nullptr) {
+                doThrowOOME(env, "Failed to allocate nine patch insets.");
+                return nullptr;
+            }
+        }
+    }
+
+    if (jpostProcess) {
+        std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm));
+
+        jint pixelFormat = postProcessAndRelease(env, jdecoder, std::move(canvas));
+        if (env->ExceptionCheck()) {
+            return nullptr;
+        }
+
+        SkAlphaType newAlphaType = bm.alphaType();
+        switch (pixelFormat) {
+            case kUnknown:
+                break;
+            case kTranslucent:
+                newAlphaType = kPremul_SkAlphaType;
+                break;
+            case kOpaque:
+                newAlphaType = kOpaque_SkAlphaType;
+                break;
+            default:
+                SkString msg;
+                msg.printf("invalid return from postProcess: %i", pixelFormat);
+                doThrowIAE(env, msg.c_str());
+                return nullptr;
+        }
+
+        if (newAlphaType != bm.alphaType()) {
+            if (!bm.setAlphaType(newAlphaType)) {
+                SkString msg;
+                msg.printf("incompatible return from postProcess: %i", pixelFormat);
+                doThrowIAE(env, msg.c_str());
+                return nullptr;
+            }
+            nativeBitmap->setAlphaType(newAlphaType);
+        }
+    }
+
+    int bitmapCreateFlags = 0x0;
+    if (!requireUnpremul) {
+        // Even if the image is opaque, setting this flag means that
+        // if alpha is added (e.g. by PostProcess), it will be marked as
+        // premultiplied.
+        bitmapCreateFlags |= bitmap::kBitmapCreateFlag_Premultiplied;
+    }
+
+    if (requireMutable) {
+        bitmapCreateFlags |= bitmap::kBitmapCreateFlag_Mutable;
+    } else {
+        if (isHardware) {
+            sk_sp<Bitmap> hwBitmap = Bitmap::allocateHardwareBitmap(bm);
+            if (hwBitmap) {
+                hwBitmap->setImmutable();
+                return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags,
+                                            ninePatchChunk, ninePatchInsets);
+            }
+            if (allocator == kHardware_Allocator) {
+                doThrowOOME(env, "failed to allocate hardware Bitmap!");
+                return nullptr;
+            }
+            // If we failed to create a hardware bitmap, go ahead and create a
+            // software one.
+        }
+
+        nativeBitmap->setImmutable();
+    }
+    return bitmap::createBitmap(env, nativeBitmap.release(), bitmapCreateFlags, ninePatchChunk,
+                                ninePatchInsets);
+}
+
+static jobject ImageDecoder_nGetSampledSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+                                            jint sampleSize) {
+    auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
+    SkISize size = decoder->mCodec->getSampledDimensions(sampleSize);
+    return env->NewObject(gSize_class, gSize_constructorMethodID, size.width(), size.height());
+}
+
+static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+                                     jobject outPadding) {
+    auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
+    reinterpret_cast<NinePatchPeeker*>(decoder->mPeeker.get())->getPadding(env, outPadding);
+}
+
+static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) {
+    delete reinterpret_cast<ImageDecoder*>(nativePtr);
+}
+
+static jstring ImageDecoder_nGetMimeType(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
+    return getMimeTypeAsJavaString(env, decoder->mCodec->getEncodedFormat());
+}
+
+static jobject ImageDecoder_nGetColorSpace(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* codec = reinterpret_cast<ImageDecoder*>(nativePtr)->mCodec.get();
+    auto colorType = codec->computeOutputColorType(kN32_SkColorType);
+    sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType);
+    return GraphicsJNI::getColorSpace(env, colorSpace.get(), colorType);
+}
+
+static const JNINativeMethod gImageDecoderMethods[] = {
+    { "nCreate",        "(JZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;",    (void*) ImageDecoder_nCreateAsset },
+    { "nCreate",        "(Ljava/nio/ByteBuffer;IIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
+    { "nCreate",        "([BIIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
+    { "nCreate",        "(Ljava/io/InputStream;[BZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
+    { "nCreate",        "(Ljava/io/FileDescriptor;ZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
+    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZJZ)Landroid/graphics/Bitmap;",
+                                                                 (void*) ImageDecoder_nDecodeBitmap },
+    { "nGetSampledSize","(JI)Landroid/util/Size;",               (void*) ImageDecoder_nGetSampledSize },
+    { "nGetPadding",    "(JLandroid/graphics/Rect;)V",           (void*) ImageDecoder_nGetPadding },
+    { "nClose",         "(J)V",                                  (void*) ImageDecoder_nClose},
+    { "nGetMimeType",   "(J)Ljava/lang/String;",                 (void*) ImageDecoder_nGetMimeType },
+    { "nGetColorSpace", "(J)Landroid/graphics/ColorSpace;",      (void*) ImageDecoder_nGetColorSpace },
+};
+
+int register_android_graphics_ImageDecoder(JNIEnv* env) {
+    gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder"));
+    gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JIIZZ)V");
+    gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;)I");
+
+    gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size"));
+    gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V");
+
+    gDecodeException_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$DecodeException"));
+    gDecodeException_constructorMethodID = GetMethodIDOrDie(env, gDecodeException_class, "<init>", "(ILjava/lang/String;Ljava/lang/Throwable;Landroid/graphics/ImageDecoder$Source;)V");
+
+    gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "onPartialImage", "(ILjava/lang/Throwable;)V");
+
+    gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas"));
+    gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V");
+    gCanvas_releaseMethodID = GetMethodIDOrDie(env, gCanvas_class, "release", "()V");
+
+    return android::RegisterMethodsOrDie(env, "android/graphics/ImageDecoder", gImageDecoderMethods,
+                                         NELEM(gImageDecoderMethods));
+}
diff --git a/libs/hwui/jni/ImageDecoder.h b/libs/hwui/jni/ImageDecoder.h
new file mode 100644
index 0000000..8a7fa79
--- /dev/null
+++ b/libs/hwui/jni/ImageDecoder.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 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 <hwui/Canvas.h>
+
+#include <jni.h>
+
+// Creates a Java Canvas object from canvas, calls jimageDecoder's PostProcess on it, and then
+// releases the Canvas.
+// Caller needs to check for exceptions.
+jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder,
+                           std::unique_ptr<android::Canvas> canvas);
diff --git a/libs/hwui/jni/Interpolator.cpp b/libs/hwui/jni/Interpolator.cpp
new file mode 100644
index 0000000..fa28359
--- /dev/null
+++ b/libs/hwui/jni/Interpolator.cpp
@@ -0,0 +1,87 @@
+#include "GraphicsJNI.h"
+#include "SkInterpolator.h"
+#include "core_jni_helpers.h"
+
+#include <jni.h>
+
+static jlong Interpolator_constructor(JNIEnv* env, jobject clazz, jint valueCount, jint frameCount)
+{
+    return reinterpret_cast<jlong>(new SkInterpolator(valueCount, frameCount));
+}
+
+static void Interpolator_destructor(JNIEnv* env, jobject clazz, jlong interpHandle)
+{
+    SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle);
+    delete interp;
+}
+
+static void Interpolator_reset(JNIEnv* env, jobject clazz, jlong interpHandle, jint valueCount, jint frameCount)
+{
+    SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle);
+    interp->reset(valueCount, frameCount);
+}
+
+static void Interpolator_setKeyFrame(JNIEnv* env, jobject clazz, jlong interpHandle, jint index, jint msec, jfloatArray valueArray, jfloatArray blendArray)
+{
+    SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle);
+
+    AutoJavaFloatArray autoValues(env, valueArray);
+    AutoJavaFloatArray autoBlend(env, blendArray, 4);
+#ifdef SK_SCALAR_IS_FLOAT
+    SkScalar* scalars = autoValues.ptr();
+    SkScalar* blend = autoBlend.ptr();
+#else
+    #error Need to convert float array to SkScalar array before calling the following function.
+#endif
+
+    interp->setKeyFrame(index, msec, scalars, blend);
+}
+
+static void Interpolator_setRepeatMirror(JNIEnv* env, jobject clazz, jlong interpHandle, jfloat repeatCount, jboolean mirror)
+{
+    SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle);
+    if (repeatCount > 32000)
+        repeatCount = 32000;
+
+    interp->setRepeatCount(repeatCount);
+    interp->setMirror(mirror != 0);
+}
+
+static jint Interpolator_timeToValues(JNIEnv* env, jobject clazz, jlong interpHandle, jint msec, jfloatArray valueArray)
+{
+    SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle);
+    SkInterpolatorBase::Result result;
+
+    float* values = valueArray ? env->GetFloatArrayElements(valueArray, NULL) : NULL;
+    result = interp->timeToValues(msec, (SkScalar*)values);
+
+    if (valueArray) {
+        int n = env->GetArrayLength(valueArray);
+        for (int i = 0; i < n; i++) {
+            values[i] = SkScalarToFloat(*(SkScalar*)&values[i]);
+        }
+        env->ReleaseFloatArrayElements(valueArray, values, 0);
+    }
+
+    return static_cast<jint>(result);
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gInterpolatorMethods[] = {
+    { "nativeConstructor",      "(II)J",        (void*)Interpolator_constructor     },
+    { "nativeDestructor",       "(J)V",         (void*)Interpolator_destructor      },
+    { "nativeReset",            "(JII)V",       (void*)Interpolator_reset           },
+    { "nativeSetKeyFrame",      "(JII[F[F)V",   (void*)Interpolator_setKeyFrame     },
+    { "nativeSetRepeatMirror",  "(JFZ)V",       (void*)Interpolator_setRepeatMirror },
+    { "nativeTimeToValues",     "(JI[F)I",      (void*)Interpolator_timeToValues    }
+};
+
+int register_android_graphics_Interpolator(JNIEnv* env)
+{
+    return android::RegisterMethodsOrDie(env, "android/graphics/Interpolator",
+                                         gInterpolatorMethods, NELEM(gInterpolatorMethods));
+}
diff --git a/libs/hwui/jni/MaskFilter.cpp b/libs/hwui/jni/MaskFilter.cpp
new file mode 100644
index 0000000..33d346f
--- /dev/null
+++ b/libs/hwui/jni/MaskFilter.cpp
@@ -0,0 +1,94 @@
+#include "GraphicsJNI.h"
+#include "SkMaskFilter.h"
+#include "SkBlurMask.h"
+#include "SkBlurMaskFilter.h"
+#include "SkTableMaskFilter.h"
+
+#include "core_jni_helpers.h"
+
+#include <jni.h>
+
+static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) {
+    if (NULL == ptr) {
+        doThrowIAE(env);
+    }
+}
+
+class SkMaskFilterGlue {
+public:
+    static void destructor(JNIEnv* env, jobject, jlong filterHandle) {
+        SkMaskFilter* filter = reinterpret_cast<SkMaskFilter *>(filterHandle);
+        SkSafeUnref(filter);
+    }
+
+    static jlong createBlur(JNIEnv* env, jobject, jfloat radius, jint blurStyle) {
+        SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
+        SkMaskFilter* filter = SkMaskFilter::MakeBlur((SkBlurStyle)blurStyle, sigma).release();
+        ThrowIAE_IfNull(env, filter);
+        return reinterpret_cast<jlong>(filter);
+    }
+
+    static jlong createEmboss(JNIEnv* env, jobject, jfloatArray dirArray, jfloat ambient, jfloat specular, jfloat radius) {
+        SkScalar direction[3];
+
+        AutoJavaFloatArray autoDir(env, dirArray, 3);
+        float* values = autoDir.ptr();
+        for (int i = 0; i < 3; i++) {
+            direction[i] = values[i];
+        }
+
+        SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
+        SkMaskFilter* filter =  SkBlurMaskFilter::MakeEmboss(sigma,
+                direction, ambient, specular).release();
+        ThrowIAE_IfNull(env, filter);
+        return reinterpret_cast<jlong>(filter);
+    }
+
+    static jlong createTable(JNIEnv* env, jobject, jbyteArray jtable) {
+        AutoJavaByteArray autoTable(env, jtable, 256);
+        SkMaskFilter* filter = SkTableMaskFilter::Create((const uint8_t*)autoTable.ptr());
+        return reinterpret_cast<jlong>(filter);
+    }
+
+    static jlong createClipTable(JNIEnv* env, jobject, jint min, jint max) {
+        SkMaskFilter* filter = SkTableMaskFilter::CreateClip(min, max);
+        return reinterpret_cast<jlong>(filter);
+    }
+
+    static jlong createGammaTable(JNIEnv* env, jobject, jfloat gamma) {
+        SkMaskFilter* filter = SkTableMaskFilter::CreateGamma(gamma);
+        return reinterpret_cast<jlong>(filter);
+    }
+};
+
+static const JNINativeMethod gMaskFilterMethods[] = {
+    { "nativeDestructor",   "(J)V",     (void*)SkMaskFilterGlue::destructor      }
+};
+
+static const JNINativeMethod gBlurMaskFilterMethods[] = {
+    { "nativeConstructor",  "(FI)J",    (void*)SkMaskFilterGlue::createBlur      }
+};
+
+static const JNINativeMethod gEmbossMaskFilterMethods[] = {
+    { "nativeConstructor",  "([FFFF)J", (void*)SkMaskFilterGlue::createEmboss    }
+};
+
+static const JNINativeMethod gTableMaskFilterMethods[] = {
+    { "nativeNewTable", "([B)J", (void*)SkMaskFilterGlue::createTable    },
+    { "nativeNewClip",  "(II)J", (void*)SkMaskFilterGlue::createClipTable    },
+    { "nativeNewGamma", "(F)J", (void*)SkMaskFilterGlue::createGammaTable    }
+};
+
+int register_android_graphics_MaskFilter(JNIEnv* env)
+{
+    android::RegisterMethodsOrDie(env, "android/graphics/MaskFilter", gMaskFilterMethods,
+                                  NELEM(gMaskFilterMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/BlurMaskFilter", gBlurMaskFilterMethods,
+                                  NELEM(gBlurMaskFilterMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/EmbossMaskFilter",
+                                  gEmbossMaskFilterMethods, NELEM(gEmbossMaskFilterMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/TableMaskFilter", gTableMaskFilterMethods,
+                                  NELEM(gTableMaskFilterMethods));
+
+    return 0;
+}
diff --git a/libs/hwui/jni/Movie.cpp b/libs/hwui/jni/Movie.cpp
new file mode 100644
index 0000000..4c10a85
--- /dev/null
+++ b/libs/hwui/jni/Movie.cpp
@@ -0,0 +1,166 @@
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "GraphicsJNI.h"
+#include <nativehelper/ScopedLocalRef.h>
+#include "SkFrontBufferedStream.h"
+#include "Movie.h"
+#include "SkStream.h"
+#include "SkUtils.h"
+#include "Utils.h"
+#include "core_jni_helpers.h"
+
+#include <androidfw/Asset.h>
+#include <androidfw/ResourceTypes.h>
+#include <hwui/Canvas.h>
+#include <hwui/Paint.h>
+#include <jni.h>
+#include <netinet/in.h>
+
+static jclass       gMovie_class;
+static jmethodID    gMovie_constructorMethodID;
+static jfieldID     gMovie_nativeInstanceID;
+
+jobject create_jmovie(JNIEnv* env, Movie* moov) {
+    if (NULL == moov) {
+        return NULL;
+    }
+    return env->NewObject(gMovie_class, gMovie_constructorMethodID,
+            static_cast<jlong>(reinterpret_cast<uintptr_t>(moov)));
+}
+
+static Movie* J2Movie(JNIEnv* env, jobject movie) {
+    SkASSERT(env);
+    SkASSERT(movie);
+    SkASSERT(env->IsInstanceOf(movie, gMovie_class));
+    Movie* m = (Movie*)env->GetLongField(movie, gMovie_nativeInstanceID);
+    SkASSERT(m);
+    return m;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jint movie_width(JNIEnv* env, jobject movie) {
+    NPE_CHECK_RETURN_ZERO(env, movie);
+    return static_cast<jint>(J2Movie(env, movie)->width());
+}
+
+static jint movie_height(JNIEnv* env, jobject movie) {
+    NPE_CHECK_RETURN_ZERO(env, movie);
+    return static_cast<jint>(J2Movie(env, movie)->height());
+}
+
+static jboolean movie_isOpaque(JNIEnv* env, jobject movie) {
+    NPE_CHECK_RETURN_ZERO(env, movie);
+    return J2Movie(env, movie)->isOpaque() ? JNI_TRUE : JNI_FALSE;
+}
+
+static jint movie_duration(JNIEnv* env, jobject movie) {
+    NPE_CHECK_RETURN_ZERO(env, movie);
+    return static_cast<jint>(J2Movie(env, movie)->duration());
+}
+
+static jboolean movie_setTime(JNIEnv* env, jobject movie, jint ms) {
+    NPE_CHECK_RETURN_ZERO(env, movie);
+    return J2Movie(env, movie)->setTime(ms) ? JNI_TRUE : JNI_FALSE;
+}
+
+static void movie_draw(JNIEnv* env, jobject movie, jlong canvasHandle,
+                       jfloat fx, jfloat fy, jlong paintHandle) {
+    NPE_CHECK_RETURN_VOID(env, movie);
+
+    android::Canvas* c = reinterpret_cast<android::Canvas*>(canvasHandle);
+    const android::Paint* p = reinterpret_cast<android::Paint*>(paintHandle);
+
+    // Canvas should never be NULL. However paint is an optional parameter and
+    // therefore may be NULL.
+    SkASSERT(c != NULL);
+
+    Movie* m = J2Movie(env, movie);
+    const SkBitmap& b = m->bitmap();
+    sk_sp<android::Bitmap> wrapper = android::Bitmap::createFrom(b.info(), *b.pixelRef());
+    c->drawBitmap(*wrapper, fx, fy, p);
+}
+
+static jobject movie_decodeAsset(JNIEnv* env, jobject clazz, jlong native_asset) {
+    android::Asset* asset = reinterpret_cast<android::Asset*>(native_asset);
+    if (asset == NULL) return NULL;
+    android::AssetStreamAdaptor stream(asset);
+    Movie* moov = Movie::DecodeStream(&stream);
+    return create_jmovie(env, moov);
+}
+
+static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) {
+
+    NPE_CHECK_RETURN_ZERO(env, istream);
+
+    jbyteArray byteArray = env->NewByteArray(16*1024);
+    ScopedLocalRef<jbyteArray> scoper(env, byteArray);
+    SkStream* strm = CreateJavaInputStreamAdaptor(env, istream, byteArray);
+    if (NULL == strm) {
+        return 0;
+    }
+
+    // Need to buffer enough input to be able to rewind as much as might be read by a decoder
+    // trying to determine the stream's format. The only decoder for movies is GIF, which
+    // will only read 6.
+    // FIXME: Get this number from SkImageDecoder
+    // bufferedStream takes ownership of strm
+    std::unique_ptr<SkStreamRewindable> bufferedStream(SkFrontBufferedStream::Make(
+        std::unique_ptr<SkStream>(strm), 6));
+    SkASSERT(bufferedStream.get() != NULL);
+
+    Movie* moov = Movie::DecodeStream(bufferedStream.get());
+    return create_jmovie(env, moov);
+}
+
+static jobject movie_decodeByteArray(JNIEnv* env, jobject clazz,
+                                     jbyteArray byteArray,
+                                     jint offset, jint length) {
+
+    NPE_CHECK_RETURN_ZERO(env, byteArray);
+
+    int totalLength = env->GetArrayLength(byteArray);
+    if ((offset | length) < 0 || offset + length > totalLength) {
+        doThrowAIOOBE(env);
+        return 0;
+    }
+
+    AutoJavaByteArray   ar(env, byteArray);
+    Movie* moov = Movie::DecodeMemory(ar.ptr() + offset, length);
+    return create_jmovie(env, moov);
+}
+
+static void movie_destructor(JNIEnv* env, jobject, jlong movieHandle) {
+    Movie* movie = (Movie*) movieHandle;
+    delete movie;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gMethods[] = {
+    {   "width",    "()I",  (void*)movie_width  },
+    {   "height",   "()I",  (void*)movie_height  },
+    {   "isOpaque", "()Z",  (void*)movie_isOpaque  },
+    {   "duration", "()I",  (void*)movie_duration  },
+    {   "setTime",  "(I)Z", (void*)movie_setTime  },
+    {   "nDraw",    "(JFFJ)V",
+                            (void*)movie_draw  },
+    { "nativeDecodeAsset", "(J)Landroid/graphics/Movie;",
+                            (void*)movie_decodeAsset },
+    { "nativeDecodeStream", "(Ljava/io/InputStream;)Landroid/graphics/Movie;",
+                            (void*)movie_decodeStream },
+    { "nativeDestructor","(J)V", (void*)movie_destructor },
+    { "decodeByteArray", "([BII)Landroid/graphics/Movie;",
+                            (void*)movie_decodeByteArray },
+};
+
+int register_android_graphics_Movie(JNIEnv* env)
+{
+    gMovie_class = android::FindClassOrDie(env, "android/graphics/Movie");
+    gMovie_class = android::MakeGlobalRefOrDie(env, gMovie_class);
+
+    gMovie_constructorMethodID = android::GetMethodIDOrDie(env, gMovie_class, "<init>", "(J)V");
+
+    gMovie_nativeInstanceID = android::GetFieldIDOrDie(env, gMovie_class, "mNativeMovie", "J");
+
+    return android::RegisterMethodsOrDie(env, "android/graphics/Movie", gMethods, NELEM(gMethods));
+}
diff --git a/libs/hwui/jni/Movie.h b/libs/hwui/jni/Movie.h
new file mode 100644
index 0000000..736890d
--- /dev/null
+++ b/libs/hwui/jni/Movie.h
@@ -0,0 +1,79 @@
+
+/*
+ * Copyright 2008 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef Movie_DEFINED
+#define Movie_DEFINED
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkRefCnt.h"
+
+class SkStreamRewindable;
+
+class Movie : public SkRefCnt {
+public:
+    /** Try to create a movie from the stream. If the stream format is not
+        supported, return NULL.
+    */
+    static Movie* DecodeStream(SkStreamRewindable*);
+    /** Try to create a movie from the specified file path. If the file is not
+        found, or the format is not supported, return NULL. If a movie is
+        returned, the stream may be retained by the movie (via ref()) until
+        the movie is finished with it (by calling unref()).
+    */
+    static Movie* DecodeFile(const char path[]);
+    /** Try to create a movie from the specified memory.
+        If the format is not supported, return NULL. If a movie is returned,
+        the data will have been read or copied, and so the caller may free
+        it.
+    */
+    static Movie* DecodeMemory(const void* data, size_t length);
+
+    SkMSec  duration();
+    int     width();
+    int     height();
+    int     isOpaque();
+
+    /** Specify the time code (between 0...duration) to sample a bitmap
+        from the movie. Returns true if this time code generated a different
+        bitmap/frame from the previous state (i.e. true means you need to
+        redraw).
+    */
+    bool setTime(SkMSec);
+
+    // return the right bitmap for the current time code
+    const SkBitmap& bitmap();
+
+protected:
+    struct Info {
+        SkMSec  fDuration;
+        int     fWidth;
+        int     fHeight;
+        bool    fIsOpaque;
+    };
+
+    virtual bool onGetInfo(Info*) = 0;
+    virtual bool onSetTime(SkMSec) = 0;
+    virtual bool onGetBitmap(SkBitmap*) = 0;
+
+    // visible for subclasses
+    Movie();
+
+private:
+    Info        fInfo;
+    SkMSec      fCurrTime;
+    SkBitmap    fBitmap;
+    bool        fNeedBitmap;
+
+    void ensureInfo();
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/libs/hwui/jni/MovieImpl.cpp b/libs/hwui/jni/MovieImpl.cpp
new file mode 100644
index 0000000..ae9e04e
--- /dev/null
+++ b/libs/hwui/jni/MovieImpl.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Movie.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+// We should never see this in normal operation since our time values are
+// 0-based. So we use it as a sentinal.
+#define UNINITIALIZED_MSEC ((SkMSec)-1)
+
+Movie::Movie()
+{
+    fInfo.fDuration = UNINITIALIZED_MSEC;  // uninitialized
+    fCurrTime = UNINITIALIZED_MSEC; // uninitialized
+    fNeedBitmap = true;
+}
+
+void Movie::ensureInfo()
+{
+    if (fInfo.fDuration == UNINITIALIZED_MSEC && !this->onGetInfo(&fInfo))
+        memset(&fInfo, 0, sizeof(fInfo));   // failure
+}
+
+SkMSec Movie::duration()
+{
+    this->ensureInfo();
+    return fInfo.fDuration;
+}
+
+int Movie::width()
+{
+    this->ensureInfo();
+    return fInfo.fWidth;
+}
+
+int Movie::height()
+{
+    this->ensureInfo();
+    return fInfo.fHeight;
+}
+
+int Movie::isOpaque()
+{
+    this->ensureInfo();
+    return fInfo.fIsOpaque;
+}
+
+bool Movie::setTime(SkMSec time)
+{
+    SkMSec dur = this->duration();
+    if (time > dur)
+        time = dur;
+
+    bool changed = false;
+    if (time != fCurrTime)
+    {
+        fCurrTime = time;
+        changed = this->onSetTime(time);
+        fNeedBitmap |= changed;
+    }
+    return changed;
+}
+
+const SkBitmap& Movie::bitmap()
+{
+    if (fCurrTime == UNINITIALIZED_MSEC)    // uninitialized
+        this->setTime(0);
+
+    if (fNeedBitmap)
+    {
+        if (!this->onGetBitmap(&fBitmap))   // failure
+            fBitmap.reset();
+        fNeedBitmap = false;
+    }
+    return fBitmap;
+}
+
+////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+Movie* Movie::DecodeMemory(const void* data, size_t length) {
+    SkMemoryStream stream(data, length, false);
+    return Movie::DecodeStream(&stream);
+}
+
+Movie* Movie::DecodeFile(const char path[]) {
+    std::unique_ptr<SkStreamRewindable> stream = SkStream::MakeFromFile(path);
+    return stream ? Movie::DecodeStream(stream.get()) : nullptr;
+}
diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp
new file mode 100644
index 0000000..15f9516
--- /dev/null
+++ b/libs/hwui/jni/NinePatch.cpp
@@ -0,0 +1,170 @@
+/*
+**
+** Copyright 2006, 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.
+*/
+
+#define LOG_TAG "9patch"
+#define LOG_NDEBUG 1
+
+#include <androidfw/ResourceTypes.h>
+#include <hwui/Canvas.h>
+#include <hwui/Paint.h>
+#include <utils/Log.h>
+
+#include "SkCanvas.h"
+#include "SkLatticeIter.h"
+#include "SkRegion.h"
+#include "GraphicsJNI.h"
+#include "NinePatchPeeker.h"
+#include "NinePatchUtils.h"
+
+#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
+
+jclass      gInsetStruct_class;
+jmethodID   gInsetStruct_constructorMethodID;
+
+using namespace android;
+
+/**
+ * IMPORTANT NOTE: 9patch chunks can be manipuated either as an array of bytes
+ * or as a Res_png_9patch instance. It is important to note that the size of the
+ * array required to hold a 9patch chunk is greater than sizeof(Res_png_9patch).
+ * The code below manipulates chunks as Res_png_9patch* types to draw and as
+ * int8_t* to allocate and free the backing storage.
+ */
+
+class SkNinePatchGlue {
+public:
+    static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) {
+        if (NULL == obj) {
+            return JNI_FALSE;
+        }
+        if (env->GetArrayLength(obj) < (int)sizeof(Res_png_9patch)) {
+            return JNI_FALSE;
+        }
+        const jbyte* array = env->GetByteArrayElements(obj, 0);
+        if (array != NULL) {
+            const Res_png_9patch* chunk = reinterpret_cast<const Res_png_9patch*>(array);
+            int8_t wasDeserialized = chunk->wasDeserialized;
+            env->ReleaseByteArrayElements(obj, const_cast<jbyte*>(array), JNI_ABORT);
+            return (wasDeserialized != -1) ? JNI_TRUE : JNI_FALSE;
+        }
+        return JNI_FALSE;
+    }
+
+    static jlong validateNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) {
+        size_t chunkSize = env->GetArrayLength(obj);
+        if (chunkSize < (int) (sizeof(Res_png_9patch))) {
+            jniThrowRuntimeException(env, "Array too small for chunk.");
+            return NULL;
+        }
+
+        int8_t* storage = new int8_t[chunkSize];
+        // This call copies the content of the jbyteArray
+        env->GetByteArrayRegion(obj, 0, chunkSize, reinterpret_cast<jbyte*>(storage));
+        // Deserialize in place, return the array we just allocated
+        return reinterpret_cast<jlong>(Res_png_9patch::deserialize(storage));
+    }
+
+    static void finalize(JNIEnv* env, jobject, jlong patchHandle) {
+        int8_t* patch = reinterpret_cast<int8_t*>(patchHandle);
+        delete[] patch;
+    }
+
+    static jlong getTransparentRegion(JNIEnv* env, jobject, jlong bitmapPtr,
+            jlong chunkHandle, jobject dstRect) {
+        Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle);
+        SkASSERT(chunk);
+
+        SkBitmap bitmap;
+        bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
+        SkRect dst;
+        GraphicsJNI::jrect_to_rect(env, dstRect, &dst);
+
+        SkCanvas::Lattice lattice;
+        SkIRect src = SkIRect::MakeWH(bitmap.width(), bitmap.height());
+        lattice.fBounds = &src;
+        NinePatchUtils::SetLatticeDivs(&lattice, *chunk, bitmap.width(), bitmap.height());
+        lattice.fRectTypes = nullptr;
+        lattice.fColors = nullptr;
+
+        SkRegion* region = nullptr;
+        if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), lattice)) {
+            SkLatticeIter iter(lattice, dst);
+            if (iter.numRectsToDraw() == chunk->numColors) {
+                SkRect dummy;
+                SkRect iterDst;
+                int index = 0;
+                while (iter.next(&dummy, &iterDst)) {
+                    if (0 == chunk->getColors()[index++] && !iterDst.isEmpty()) {
+                        if (!region) {
+                            region = new SkRegion();
+                        }
+
+                        region->op(iterDst.round(), SkRegion::kUnion_Op);
+                    }
+                }
+            }
+        }
+
+        return reinterpret_cast<jlong>(region);
+    }
+
+};
+
+jobject NinePatchPeeker::createNinePatchInsets(JNIEnv* env, float scale) const {
+    if (!mHasInsets) {
+        return nullptr;
+    }
+
+    return env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID,
+            mOpticalInsets[0], mOpticalInsets[1],
+            mOpticalInsets[2], mOpticalInsets[3],
+            mOutlineInsets[0], mOutlineInsets[1],
+            mOutlineInsets[2], mOutlineInsets[3],
+            mOutlineRadius, mOutlineAlpha, scale);
+}
+
+void NinePatchPeeker::getPadding(JNIEnv* env, jobject outPadding) const {
+    if (mPatch) {
+        GraphicsJNI::set_jrect(env, outPadding,
+                mPatch->paddingLeft, mPatch->paddingTop,
+                mPatch->paddingRight, mPatch->paddingBottom);
+
+    } else {
+        GraphicsJNI::set_jrect(env, outPadding, -1, -1, -1, -1);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gNinePatchMethods[] = {
+    { "isNinePatchChunk", "([B)Z", (void*) SkNinePatchGlue::isNinePatchChunk },
+    { "validateNinePatchChunk", "([B)J",
+            (void*) SkNinePatchGlue::validateNinePatchChunk },
+    { "nativeFinalize", "(J)V", (void*) SkNinePatchGlue::finalize },
+    { "nativeGetTransparentRegion", "(JJLandroid/graphics/Rect;)J",
+            (void*) SkNinePatchGlue::getTransparentRegion }
+};
+
+int register_android_graphics_NinePatch(JNIEnv* env) {
+    gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
+            "android/graphics/NinePatch$InsetStruct"));
+    gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>",
+            "(IIIIIIIIFIF)V");
+    return android::RegisterMethodsOrDie(env,
+            "android/graphics/NinePatch", gNinePatchMethods, NELEM(gNinePatchMethods));
+}
diff --git a/libs/hwui/jni/NinePatchPeeker.cpp b/libs/hwui/jni/NinePatchPeeker.cpp
new file mode 100644
index 0000000..9171fc6
--- /dev/null
+++ b/libs/hwui/jni/NinePatchPeeker.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 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 "NinePatchPeeker.h"
+
+#include <SkBitmap.h>
+#include <cutils/compiler.h>
+
+using namespace android;
+
+bool NinePatchPeeker::readChunk(const char tag[], const void* data, size_t length) {
+    if (!strcmp("npTc", tag) && length >= sizeof(Res_png_9patch)) {
+        Res_png_9patch* patch = (Res_png_9patch*) data;
+        size_t patchSize = patch->serializedSize();
+        if (length != patchSize) {
+            return false;
+        }
+        // You have to copy the data because it is owned by the png reader
+        Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
+        memcpy(patchNew, patch, patchSize);
+        Res_png_9patch::deserialize(patchNew);
+        patchNew->fileToDevice();
+        free(mPatch);
+        mPatch = patchNew;
+        mPatchSize = patchSize;
+    } else if (!strcmp("npLb", tag) && length == sizeof(int32_t) * 4) {
+        mHasInsets = true;
+        memcpy(&mOpticalInsets, data, sizeof(int32_t) * 4);
+    } else if (!strcmp("npOl", tag) && length == 24) { // 4 int32_ts, 1 float, 1 int32_t sized byte
+        mHasInsets = true;
+        memcpy(&mOutlineInsets, data, sizeof(int32_t) * 4);
+        mOutlineRadius = ((const float*)data)[4];
+        mOutlineAlpha = ((const int32_t*)data)[5] & 0xff;
+    }
+    return true;    // keep on decoding
+}
+
+static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) {
+    for (int i = 0; i < count; i++) {
+        divs[i] = int32_t(divs[i] * scale + 0.5f);
+        if (i > 0 && divs[i] == divs[i - 1]) {
+            divs[i]++; // avoid collisions
+        }
+    }
+
+    if (CC_UNLIKELY(divs[count - 1] > maxValue)) {
+        // if the collision avoidance above put some divs outside the bounds of the bitmap,
+        // slide outer stretchable divs inward to stay within bounds
+        int highestAvailable = maxValue;
+        for (int i = count - 1; i >= 0; i--) {
+            divs[i] = highestAvailable;
+            if (i > 0 && divs[i] <= divs[i-1]) {
+                // keep shifting
+                highestAvailable = divs[i] - 1;
+            } else {
+                break;
+            }
+        }
+    }
+}
+
+void NinePatchPeeker::scale(float scaleX, float scaleY, int scaledWidth, int scaledHeight) {
+    if (!mPatch) {
+        return;
+    }
+
+    // The max value for the divRange is one pixel less than the actual max to ensure that the size
+    // of the last div is not zero. A div of size 0 is considered invalid input and will not render.
+    if (!SkScalarNearlyEqual(scaleX, 1.0f)) {
+        mPatch->paddingLeft   = int(mPatch->paddingLeft   * scaleX + 0.5f);
+        mPatch->paddingRight  = int(mPatch->paddingRight  * scaleX + 0.5f);
+        scaleDivRange(mPatch->getXDivs(), mPatch->numXDivs, scaleX, scaledWidth - 1);
+    }
+
+    if (!SkScalarNearlyEqual(scaleY, 1.0f)) {
+        mPatch->paddingTop    = int(mPatch->paddingTop    * scaleY + 0.5f);
+        mPatch->paddingBottom = int(mPatch->paddingBottom * scaleY + 0.5f);
+        scaleDivRange(mPatch->getYDivs(), mPatch->numYDivs, scaleY, scaledHeight - 1);
+    }
+}
diff --git a/libs/hwui/jni/NinePatchPeeker.h b/libs/hwui/jni/NinePatchPeeker.h
new file mode 100644
index 0000000..e4e58dd
--- /dev/null
+++ b/libs/hwui/jni/NinePatchPeeker.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 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 _ANDROID_GRAPHICS_NINE_PATCH_PEEKER_H_
+#define _ANDROID_GRAPHICS_NINE_PATCH_PEEKER_H_
+
+#include "SkPngChunkReader.h"
+#include <androidfw/ResourceTypes.h>
+
+#include <jni.h>
+
+using namespace android;
+
+class NinePatchPeeker : public SkPngChunkReader {
+public:
+    NinePatchPeeker()
+            : mPatch(NULL)
+            , mPatchSize(0)
+            , mHasInsets(false)
+            , mOutlineRadius(0)
+            , mOutlineAlpha(0) {
+        memset(mOpticalInsets, 0, 4 * sizeof(int32_t));
+        memset(mOutlineInsets, 0, 4 * sizeof(int32_t));
+    }
+
+    ~NinePatchPeeker() {
+        free(mPatch);
+    }
+
+    bool readChunk(const char tag[], const void* data, size_t length) override;
+
+    jobject createNinePatchInsets(JNIEnv*, float scale) const;
+    void getPadding(JNIEnv*, jobject outPadding) const;
+    void scale(float scaleX, float scaleY, int scaledWidth, int scaledHeight);
+
+    Res_png_9patch* mPatch;
+    size_t mPatchSize;
+    bool mHasInsets;
+private:
+    int32_t mOpticalInsets[4];
+    int32_t mOutlineInsets[4];
+    float mOutlineRadius;
+    uint8_t mOutlineAlpha;
+};
+
+#endif  // _ANDROID_GRAPHICS_NINE_PATCH_PEEKER_H_
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
new file mode 100644
index 0000000..8e1bc84
--- /dev/null
+++ b/libs/hwui/jni/Paint.cpp
@@ -0,0 +1,1160 @@
+/* libs/android_runtime/android/graphics/Paint.cpp
+**
+** Copyright 2006, 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.
+*/
+
+#define LOG_TAG "Paint"
+
+#include <utils/Log.h>
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
+#include <nativehelper/ScopedStringChars.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+
+#include "SkBlurDrawLooper.h"
+#include "SkColorFilter.h"
+#include "SkFont.h"
+#include "SkFontMetrics.h"
+#include "SkFontTypes.h"
+#include "SkMaskFilter.h"
+#include "SkPath.h"
+#include "SkPathEffect.h"
+#include "SkShader.h"
+#include "SkBlendMode.h"
+#include "unicode/uloc.h"
+#include "unicode/ushape.h"
+#include "utils/Blur.h"
+
+#include <hwui/MinikinSkia.h>
+#include <hwui/MinikinUtils.h>
+#include <hwui/Paint.h>
+#include <hwui/Typeface.h>
+#include <minikin/GraphemeBreak.h>
+#include <minikin/LocaleList.h>
+#include <minikin/Measurement.h>
+#include <minikin/MinikinPaint.h>
+#include <unicode/utf16.h>
+
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include <vector>
+
+namespace android {
+
+struct JMetricsID {
+    jfieldID    top;
+    jfieldID    ascent;
+    jfieldID    descent;
+    jfieldID    bottom;
+    jfieldID    leading;
+};
+
+static jclass   gFontMetrics_class;
+static JMetricsID gFontMetrics_fieldID;
+
+static jclass   gFontMetricsInt_class;
+static JMetricsID gFontMetricsInt_fieldID;
+
+static void getPosTextPath(const SkFont& font, const uint16_t glyphs[], int count,
+                           const SkPoint pos[], SkPath* dst) {
+    dst->reset();
+    struct Rec {
+        SkPath* fDst;
+        const SkPoint* fPos;
+    } rec = { dst, pos };
+    font.getPaths(glyphs, count, [](const SkPath* src, const SkMatrix& mx, void* ctx) {
+        Rec* rec = (Rec*)ctx;
+        if (src) {
+            SkMatrix tmp(mx);
+            tmp.postTranslate(rec->fPos->fX, rec->fPos->fY);
+            rec->fDst->addPath(*src, tmp);
+        }
+        rec->fPos += 1;
+    }, &rec);
+}
+
+namespace PaintGlue {
+    enum MoveOpt {
+        AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT
+    };
+
+    static void deletePaint(Paint* paint) {
+        delete paint;
+    }
+
+    static jlong getNativeFinalizer(JNIEnv*, jobject) {
+        return static_cast<jlong>(reinterpret_cast<uintptr_t>(&deletePaint));
+    }
+
+    static jlong init(JNIEnv* env, jobject) {
+        return reinterpret_cast<jlong>(new Paint);
+    }
+
+    static jlong initWithPaint(JNIEnv* env, jobject clazz, jlong paintHandle) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        Paint* obj = new Paint(*paint);
+        return reinterpret_cast<jlong>(obj);
+    }
+
+    static int breakText(JNIEnv* env, const Paint& paint, const Typeface* typeface,
+            const jchar text[], int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
+            const bool forwardScan) {
+        size_t measuredCount = 0;
+        float measured = 0;
+
+        std::unique_ptr<float[]> advancesArray(new float[count]);
+        MinikinUtils::measureText(&paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text,
+                0, count, count, advancesArray.get());
+
+        for (int i = 0; i < count; i++) {
+            // traverse in the given direction
+            int index = forwardScan ? i : (count - i - 1);
+            float width = advancesArray[index];
+            if (measured + width > maxWidth) {
+                break;
+            }
+            // properly handle clusters when scanning backwards
+            if (forwardScan || width != 0.0f) {
+                measuredCount = i + 1;
+            }
+            measured += width;
+        }
+
+        if (jmeasured && env->GetArrayLength(jmeasured) > 0) {
+            AutoJavaFloatArray autoMeasured(env, jmeasured, 1);
+            jfloat* array = autoMeasured.ptr();
+            array[0] = measured;
+        }
+        return measuredCount;
+    }
+
+    static jint breakTextC(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray jtext,
+            jint index, jint count, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
+        NPE_CHECK_RETURN_ZERO(env, jtext);
+
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+
+        bool forwardTextDirection;
+        if (count < 0) {
+            forwardTextDirection = false;
+            count = -count;
+        }
+        else {
+            forwardTextDirection = true;
+        }
+
+        if ((index < 0) || (index + count > env->GetArrayLength(jtext))) {
+            doThrowAIOOBE(env);
+            return 0;
+        }
+
+        const jchar* text = env->GetCharArrayElements(jtext, nullptr);
+        count = breakText(env, *paint, typeface, text + index, count, maxWidth,
+                          bidiFlags, jmeasuredWidth, forwardTextDirection);
+        env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text),
+                                      JNI_ABORT);
+        return count;
+    }
+
+    static jint breakTextS(JNIEnv* env, jobject clazz, jlong paintHandle, jstring jtext,
+            jboolean forwards, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
+        NPE_CHECK_RETURN_ZERO(env, jtext);
+
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+
+        int count = env->GetStringLength(jtext);
+        const jchar* text = env->GetStringChars(jtext, nullptr);
+        count = breakText(env, *paint, typeface, text, count, maxWidth, bidiFlags, jmeasuredWidth, forwards);
+        env->ReleaseStringChars(jtext, text);
+        return count;
+    }
+
+    static jfloat doTextAdvances(JNIEnv *env, Paint *paint, const Typeface* typeface,
+            const jchar *text, jint start, jint count, jint contextCount, jint bidiFlags,
+            jfloatArray advances, jint advancesIndex) {
+        NPE_CHECK_RETURN_ZERO(env, text);
+
+        if ((start | count | contextCount | advancesIndex) < 0 || contextCount < count) {
+            doThrowAIOOBE(env);
+            return 0;
+        }
+        if (count == 0) {
+            return 0;
+        }
+        if (advances) {
+            size_t advancesLength = env->GetArrayLength(advances);
+            if ((size_t)(count  + advancesIndex) > advancesLength) {
+                doThrowAIOOBE(env);
+                return 0;
+            }
+        }
+        std::unique_ptr<jfloat[]> advancesArray;
+        if (advances) {
+            advancesArray.reset(new jfloat[count]);
+        }
+        const float advance = MinikinUtils::measureText(paint,
+                static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count, contextCount,
+                advancesArray.get());
+        if (advances) {
+            env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get());
+        }
+        return advance;
+    }
+
+    static jfloat getTextAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
+            jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
+            jint bidiFlags, jfloatArray advances, jint advancesIndex) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+        jchar* textArray = env->GetCharArrayElements(text, nullptr);
+        jfloat result = doTextAdvances(env, paint, typeface, textArray + contextIndex,
+                index - contextIndex, count, contextCount, bidiFlags, advances, advancesIndex);
+        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+        return result;
+    }
+
+    static jfloat getTextAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
+            jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint bidiFlags,
+            jfloatArray advances, jint advancesIndex) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+        const jchar* textArray = env->GetStringChars(text, nullptr);
+        jfloat result = doTextAdvances(env, paint, typeface, textArray + contextStart,
+                start - contextStart, end - start, contextEnd - contextStart, bidiFlags,
+                advances, advancesIndex);
+        env->ReleaseStringChars(text, textArray);
+        return result;
+    }
+
+    static jint doTextRunCursor(JNIEnv *env, Paint* paint, const Typeface* typeface,
+            const jchar *text, jint start, jint count, jint dir, jint offset, jint opt) {
+        minikin::GraphemeBreak::MoveOpt moveOpt = minikin::GraphemeBreak::MoveOpt(opt);
+        minikin::Bidi bidiFlags = dir == 1 ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+        std::unique_ptr<float[]> advancesArray(new float[count]);
+        MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count,
+                advancesArray.get());
+        size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text,
+                start, count, offset, moveOpt);
+        return static_cast<jint>(result);
+    }
+
+    static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text,
+            jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+        jchar* textArray = env->GetCharArrayElements(text, nullptr);
+        jint result = doTextRunCursor(env, paint, typeface, textArray,
+                contextStart, contextCount, dir, offset, cursorOpt);
+        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+        return result;
+    }
+
+    static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle,
+            jstring text, jint contextStart, jint contextEnd, jint dir, jint offset,
+            jint cursorOpt) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+        const jchar* textArray = env->GetStringChars(text, nullptr);
+        jint result = doTextRunCursor(env, paint, typeface, textArray,
+                contextStart, contextEnd - contextStart, dir, offset, cursorOpt);
+        env->ReleaseStringChars(text, textArray);
+        return result;
+    }
+
+    class GetTextFunctor {
+    public:
+        GetTextFunctor(const minikin::Layout& layout, SkPath* path, jfloat x, jfloat y,
+                    Paint* paint, uint16_t* glyphs, SkPoint* pos)
+                : layout(layout), path(path), x(x), y(y), paint(paint), glyphs(glyphs), pos(pos) {
+        }
+
+        void operator()(size_t start, size_t end) {
+            for (size_t i = start; i < end; i++) {
+                glyphs[i] = layout.getGlyphId(i);
+                pos[i].fX = x + layout.getX(i);
+                pos[i].fY = y + layout.getY(i);
+            }
+            const SkFont& font = paint->getSkFont();
+            if (start == 0) {
+                getPosTextPath(font, glyphs, end, pos, path);
+            } else {
+                getPosTextPath(font, glyphs + start, end - start, pos + start, &tmpPath);
+                path->addPath(tmpPath);
+            }
+        }
+    private:
+        const minikin::Layout& layout;
+        SkPath* path;
+        jfloat x;
+        jfloat y;
+        Paint* paint;
+        uint16_t* glyphs;
+        SkPoint* pos;
+        SkPath tmpPath;
+    };
+
+    static void getTextPath(JNIEnv* env, Paint* paint, const Typeface* typeface, const jchar* text,
+            jint count, jint bidiFlags, jfloat x, jfloat y, SkPath* path) {
+        minikin::Layout layout = MinikinUtils::doLayout(
+                paint, static_cast<minikin::Bidi>(bidiFlags), typeface,
+                text, count,  // text buffer
+                0, count,  // draw range
+                0, count,  // context range
+                nullptr);
+        size_t nGlyphs = layout.nGlyphs();
+        uint16_t* glyphs = new uint16_t[nGlyphs];
+        SkPoint* pos = new SkPoint[nGlyphs];
+
+        x += MinikinUtils::xOffsetForTextAlign(paint, layout);
+        Paint::Align align = paint->getTextAlign();
+        paint->setTextAlign(Paint::kLeft_Align);
+        GetTextFunctor f(layout, path, x, y, paint, glyphs, pos);
+        MinikinUtils::forFontRun(layout, paint, f);
+        paint->setTextAlign(align);
+        delete[] glyphs;
+        delete[] pos;
+    }
+
+    static void getTextPath___C(JNIEnv* env, jobject clazz, jlong paintHandle, jint bidiFlags,
+            jcharArray text, jint index, jint count, jfloat x, jfloat y, jlong pathHandle) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+        const jchar* textArray = env->GetCharArrayElements(text, nullptr);
+        getTextPath(env, paint, typeface, textArray + index, count, bidiFlags, x, y, path);
+        env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
+    }
+
+    static void getTextPath__String(JNIEnv* env, jobject clazz, jlong paintHandle, jint bidiFlags,
+            jstring text, jint start, jint end, jfloat x, jfloat y, jlong pathHandle) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+        const jchar* textArray = env->GetStringChars(text, nullptr);
+        getTextPath(env, paint, typeface, textArray + start, end - start, bidiFlags, x, y, path);
+        env->ReleaseStringChars(text, textArray);
+    }
+
+    static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds,
+            const Paint& paint, const Typeface* typeface, jint bidiFlags) {
+        SkRect  r;
+        SkIRect ir;
+
+        minikin::Layout layout = MinikinUtils::doLayout(&paint,
+                static_cast<minikin::Bidi>(bidiFlags), typeface,
+                text, count,  // text buffer
+                0, count,  // draw range
+                0, count,  // context range
+                nullptr);
+        minikin::MinikinRect rect;
+        layout.getBounds(&rect);
+        r.fLeft = rect.mLeft;
+        r.fTop = rect.mTop;
+        r.fRight = rect.mRight;
+        r.fBottom = rect.mBottom;
+        r.roundOut(&ir);
+        GraphicsJNI::irect_to_jrect(ir, env, bounds);
+    }
+
+    static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle, jstring text, jint start,
+            jint end, jint bidiFlags, jobject bounds) {
+        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+        const jchar* textArray = env->GetStringChars(text, nullptr);
+        doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags);
+        env->ReleaseStringChars(text, textArray);
+    }
+
+    static void getCharArrayBounds(JNIEnv* env, jobject, jlong paintHandle, jcharArray text,
+            jint index, jint count, jint bidiFlags, jobject bounds) {
+        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+        const jchar* textArray = env->GetCharArrayElements(text, nullptr);
+        doTextBounds(env, textArray + index, count, bounds, *paint, typeface, bidiFlags);
+        env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
+                                      JNI_ABORT);
+    }
+
+    // Returns true if the given string is exact one pair of regional indicators.
+    static bool isFlag(const jchar* str, size_t length) {
+        const jchar RI_LEAD_SURROGATE = 0xD83C;
+        const jchar RI_TRAIL_SURROGATE_MIN = 0xDDE6;
+        const jchar RI_TRAIL_SURROGATE_MAX = 0xDDFF;
+
+        if (length != 4) {
+            return false;
+        }
+        if (str[0] != RI_LEAD_SURROGATE || str[2] != RI_LEAD_SURROGATE) {
+            return false;
+        }
+        return RI_TRAIL_SURROGATE_MIN <= str[1] && str[1] <= RI_TRAIL_SURROGATE_MAX &&
+            RI_TRAIL_SURROGATE_MIN <= str[3] && str[3] <= RI_TRAIL_SURROGATE_MAX;
+    }
+
+    static jboolean layoutContainsNotdef(const minikin::Layout& layout) {
+        for (size_t i = 0; i < layout.nGlyphs(); i++) {
+            if (layout.getGlyphId(i) == 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // Don't count glyphs that are the recommended "space" glyph and are zero width.
+    // This logic makes assumptions about HarfBuzz layout, but does correctly handle
+    // cases where ligatures form and zero width space glyphs are left in as
+    // placeholders.
+    static size_t countNonSpaceGlyphs(const minikin::Layout& layout) {
+        size_t count = 0;
+        static unsigned int kSpaceGlyphId = 3;
+        for (size_t i = 0; i < layout.nGlyphs(); i++) {
+            if (layout.getGlyphId(i) != kSpaceGlyphId || layout.getCharAdvance(i) != 0.0) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jint bidiFlags,
+            jstring string) {
+        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+        ScopedStringChars str(env, string);
+
+        /* Start by rejecting unsupported base code point and variation selector pairs. */
+        size_t nChars = 0;
+        const uint32_t kStartOfString = 0xFFFFFFFF;
+        uint32_t prevCp = kStartOfString;
+        for (size_t i = 0; i < str.size(); i++) {
+            jchar cu = str[i];
+            uint32_t cp = cu;
+            if (U16_IS_TRAIL(cu)) {
+                // invalid UTF-16, unpaired trailing surrogate
+                return false;
+            } else if (U16_IS_LEAD(cu)) {
+                if (i + 1 == str.size()) {
+                    // invalid UTF-16, unpaired leading surrogate at end of string
+                    return false;
+                }
+                i++;
+                jchar cu2 = str[i];
+                if (!U16_IS_TRAIL(cu2)) {
+                    // invalid UTF-16, unpaired leading surrogate
+                    return false;
+                }
+                cp = U16_GET_SUPPLEMENTARY(cu, cu2);
+            }
+
+            if (prevCp != kStartOfString &&
+                ((0xFE00 <= cp && cp <= 0xFE0F) || (0xE0100 <= cp && cp <= 0xE01EF))) {
+                bool hasVS = MinikinUtils::hasVariationSelector(typeface, prevCp, cp);
+                if (!hasVS) {
+                    // No font has a glyph for the code point and variation selector pair.
+                    return false;
+                } else if (nChars == 1 && i + 1 == str.size()) {
+                    // The string is just a codepoint and a VS, we have an authoritative answer
+                    return true;
+                }
+            }
+            nChars++;
+            prevCp = cp;
+        }
+        minikin::Layout layout = MinikinUtils::doLayout(paint,
+                static_cast<minikin::Bidi>(bidiFlags), typeface,
+                str.get(), str.size(),  // text buffer
+                0, str.size(),  // draw range
+                0, str.size(),  // context range
+                nullptr);
+        size_t nGlyphs = countNonSpaceGlyphs(layout);
+        if (nGlyphs != 1 && nChars > 1) {
+            // multiple-character input, and was not a ligature
+            // TODO: handle ZWJ/ZWNJ characters specially so we can detect certain ligatures
+            // in joining scripts, such as Arabic and Mongolian.
+            return false;
+        }
+
+        if (nGlyphs == 0 || layoutContainsNotdef(layout)) {
+            return false;  // The collection doesn't have a glyph.
+        }
+
+        if (nChars == 2 && isFlag(str.get(), str.size())) {
+            // Some font may have a special glyph for unsupported regional indicator pairs.
+            // To return false for this case, need to compare the glyph id with the one of ZZ
+            // since ZZ is reserved for unknown or invalid territory.
+            // U+1F1FF (REGIONAL INDICATOR SYMBOL LETTER Z) is \uD83C\uDDFF in UTF16.
+            static const jchar ZZ_FLAG_STR[] = { 0xD83C, 0xDDFF, 0xD83C, 0xDDFF };
+            minikin::Layout zzLayout = MinikinUtils::doLayout(paint,
+                    static_cast<minikin::Bidi>(bidiFlags), typeface,
+                    ZZ_FLAG_STR, 4,  // text buffer
+                    0, 4,  // draw range
+                    0, 4,  // context range
+                    nullptr);
+            if (zzLayout.nGlyphs() != 1 || layoutContainsNotdef(zzLayout)) {
+                // The font collection doesn't have a glyph for unknown flag. Just return true.
+                return true;
+            }
+            return zzLayout.getGlyphId(0) != layout.getGlyphId(0);
+        }
+        return true;
+    }
+
+    static jfloat doRunAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[],
+            jint start, jint count, jint bufSize, jboolean isRtl, jint offset) {
+        minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+        if (offset == start + count) {
+            return MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count,
+                    bufSize, nullptr);
+        }
+        std::unique_ptr<float[]> advancesArray(new float[count]);
+        MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
+                advancesArray.get());
+        return minikin::getRunAdvance(advancesArray.get(), buf, start, count, offset);
+    }
+
+    static jfloat getRunAdvance___CIIIIZI_F(JNIEnv *env, jclass, jlong paintHandle, jcharArray text,
+            jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl, jint offset) {
+        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+        ScopedCharArrayRO textArray(env, text);
+        jfloat result = doRunAdvance(paint, typeface, textArray.get() + contextStart,
+                start - contextStart, end - start, contextEnd - contextStart, isRtl,
+                offset - contextStart);
+        return result;
+    }
+
+    static jint doOffsetForAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[],
+            jint start, jint count, jint bufSize, jboolean isRtl, jfloat advance) {
+        minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+        std::unique_ptr<float[]> advancesArray(new float[count]);
+        MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
+                advancesArray.get());
+        return minikin::getOffsetForAdvance(advancesArray.get(), buf, start, count, advance);
+    }
+
+    static jint getOffsetForAdvance___CIIIIZF_I(JNIEnv *env, jclass, jlong paintHandle,
+            jcharArray text, jint start, jint end, jint contextStart, jint contextEnd,
+            jboolean isRtl, jfloat advance) {
+        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        const Typeface* typeface = paint->getAndroidTypeface();
+        ScopedCharArrayRO textArray(env, text);
+        jint result = doOffsetForAdvance(paint, typeface, textArray.get() + contextStart,
+                start - contextStart, end - start, contextEnd - contextStart, isRtl, advance);
+        result += contextStart;
+        return result;
+    }
+
+    // ------------------ @FastNative ---------------------------
+
+    static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        ScopedUtfChars localesChars(env, locales);
+        jint minikinLocaleListId = minikin::registerLocaleList(localesChars.c_str());
+        obj->setMinikinLocaleListId(minikinLocaleListId);
+        return minikinLocaleListId;
+    }
+
+    static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        if (!settings) {
+            paint->setFontFeatureSettings(std::string());
+        } else {
+            ScopedUtfChars settingsChars(env, settings);
+            paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
+        }
+    }
+
+    static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics *metrics) {
+        const int kElegantTop = 2500;
+        const int kElegantBottom = -1000;
+        const int kElegantAscent = 1900;
+        const int kElegantDescent = -500;
+        const int kElegantLeading = 0;
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        SkFont* font = &paint->getSkFont();
+        const Typeface* typeface = paint->getAndroidTypeface();
+        typeface = Typeface::resolveDefault(typeface);
+        minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
+        float saveSkewX = font->getSkewX();
+        bool savefakeBold = font->isEmbolden();
+        MinikinFontSkia::populateSkFont(font, baseFont.font->typeface().get(), baseFont.fakery);
+        SkScalar spacing = font->getMetrics(metrics);
+        // The populateSkPaint call may have changed fake bold / text skew
+        // because we want to measure with those effects applied, so now
+        // restore the original settings.
+        font->setSkewX(saveSkewX);
+        font->setEmbolden(savefakeBold);
+        if (paint->getFamilyVariant() == minikin::FamilyVariant::ELEGANT) {
+            SkScalar size = font->getSize();
+            metrics->fTop = -size * kElegantTop / 2048;
+            metrics->fBottom = -size * kElegantBottom / 2048;
+            metrics->fAscent = -size * kElegantAscent / 2048;
+            metrics->fDescent = -size * kElegantDescent / 2048;
+            metrics->fLeading = size * kElegantLeading / 2048;
+            spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading;
+        }
+        return spacing;
+    }
+
+    static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
+        SkFontMetrics metrics;
+        SkScalar spacing = getMetricsInternal(paintHandle, &metrics);
+
+        if (metricsObj) {
+            SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
+            env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop));
+            env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent));
+            env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent));
+            env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom));
+            env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading));
+        }
+        return SkScalarToFloat(spacing);
+    }
+
+    static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
+        SkFontMetrics metrics;
+
+        getMetricsInternal(paintHandle, &metrics);
+        int ascent = SkScalarRoundToInt(metrics.fAscent);
+        int descent = SkScalarRoundToInt(metrics.fDescent);
+        int leading = SkScalarRoundToInt(metrics.fLeading);
+
+        if (metricsObj) {
+            SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class));
+            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloorToInt(metrics.fTop));
+            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent);
+            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent);
+            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeilToInt(metrics.fBottom));
+            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading);
+        }
+        return descent - ascent + leading;
+    }
+
+
+    // ------------------ @CriticalNative ---------------------------
+
+    static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        reinterpret_cast<Paint*>(objHandle)->reset();
+    }
+
+    static void assign(CRITICAL_JNI_PARAMS_COMMA jlong dstPaintHandle, jlong srcPaintHandle) {
+        Paint* dst = reinterpret_cast<Paint*>(dstPaintHandle);
+        const Paint* src = reinterpret_cast<Paint*>(srcPaintHandle);
+        *dst = *src;
+    }
+
+    static jint getFlags(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        uint32_t flags = reinterpret_cast<Paint*>(paintHandle)->getJavaFlags();
+        return static_cast<jint>(flags);
+    }
+
+    static void setFlags(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint flags) {
+        reinterpret_cast<Paint*>(paintHandle)->setJavaFlags(flags);
+    }
+
+    static jint getHinting(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        return (SkFontHinting)reinterpret_cast<Paint*>(paintHandle)->getSkFont().getHinting()
+                == SkFontHinting::kNone ? 0 : 1;
+    }
+
+    static void setHinting(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint mode) {
+        reinterpret_cast<Paint*>(paintHandle)->getSkFont().setHinting(
+                mode == 0 ? SkFontHinting::kNone : SkFontHinting::kNormal);
+    }
+
+    static void setAntiAlias(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean aa) {
+        reinterpret_cast<Paint*>(paintHandle)->setAntiAlias(aa);
+    }
+
+    static void setLinearText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean linearText) {
+        reinterpret_cast<Paint*>(paintHandle)->getSkFont().setLinearMetrics(linearText);
+    }
+
+    static void setSubpixelText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean subpixelText) {
+        reinterpret_cast<Paint*>(paintHandle)->getSkFont().setSubpixel(subpixelText);
+    }
+
+    static void setUnderlineText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean underlineText) {
+        reinterpret_cast<Paint*>(paintHandle)->setUnderline(underlineText);
+    }
+
+    static void setStrikeThruText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean strikeThruText) {
+        reinterpret_cast<Paint*>(paintHandle)->setStrikeThru(strikeThruText);
+    }
+
+    static void setFakeBoldText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean fakeBoldText) {
+        reinterpret_cast<Paint*>(paintHandle)->getSkFont().setEmbolden(fakeBoldText);
+    }
+
+    static void setFilterBitmap(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean filterBitmap) {
+        reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
+                filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
+    }
+
+    static void setDither(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean dither) {
+        reinterpret_cast<Paint*>(paintHandle)->setDither(dither);
+    }
+
+    static jint getStyle(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        return static_cast<jint>(obj->getStyle());
+    }
+
+    static void setStyle(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint styleHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        Paint::Style style = static_cast<Paint::Style>(styleHandle);
+        obj->setStyle(style);
+    }
+
+    static void setColorLong(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jlong colorSpaceHandle,
+            jlong colorLong) {
+        SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
+        sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+        reinterpret_cast<Paint*>(paintHandle)->setColor4f(color, cs.get());
+    }
+
+    static void setColor(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint color) {
+        reinterpret_cast<Paint*>(paintHandle)->setColor(color);
+    }
+
+    static void setAlpha(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint a) {
+        reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
+    }
+
+    static jfloat getStrokeWidth(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeWidth());
+    }
+
+    static void setStrokeWidth(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat width) {
+        reinterpret_cast<Paint*>(paintHandle)->setStrokeWidth(width);
+    }
+
+    static jfloat getStrokeMiter(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeMiter());
+    }
+
+    static void setStrokeMiter(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat miter) {
+        reinterpret_cast<Paint*>(paintHandle)->setStrokeMiter(miter);
+    }
+
+    static jint getStrokeCap(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        return static_cast<jint>(obj->getStrokeCap());
+    }
+
+    static void setStrokeCap(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint capHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        Paint::Cap cap = static_cast<Paint::Cap>(capHandle);
+        obj->setStrokeCap(cap);
+    }
+
+    static jint getStrokeJoin(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        return static_cast<jint>(obj->getStrokeJoin());
+    }
+
+    static void setStrokeJoin(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint joinHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        Paint::Join join = (Paint::Join) joinHandle;
+        obj->setStrokeJoin(join);
+    }
+
+    static jboolean getFillPath(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong srcHandle, jlong dstHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
+        SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
+        return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static jlong setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
+        obj->setShader(sk_ref_sp(shader));
+        return reinterpret_cast<jlong>(obj->getShader());
+    }
+
+    static jlong setColorFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong filterHandle) {
+        Paint* obj = reinterpret_cast<Paint *>(objHandle);
+        SkColorFilter* filter  = reinterpret_cast<SkColorFilter *>(filterHandle);
+        obj->setColorFilter(sk_ref_sp(filter));
+        return reinterpret_cast<jlong>(obj->getColorFilter());
+    }
+
+    static void setXfermode(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint xfermodeHandle) {
+        // validate that the Java enum values match our expectations
+        static_assert(0 == static_cast<int>(SkBlendMode::kClear), "xfermode_mismatch");
+        static_assert(1 == static_cast<int>(SkBlendMode::kSrc), "xfermode_mismatch");
+        static_assert(2 == static_cast<int>(SkBlendMode::kDst), "xfermode_mismatch");
+        static_assert(3 == static_cast<int>(SkBlendMode::kSrcOver), "xfermode_mismatch");
+        static_assert(4 == static_cast<int>(SkBlendMode::kDstOver), "xfermode_mismatch");
+        static_assert(5 == static_cast<int>(SkBlendMode::kSrcIn), "xfermode_mismatch");
+        static_assert(6 == static_cast<int>(SkBlendMode::kDstIn), "xfermode_mismatch");
+        static_assert(7 == static_cast<int>(SkBlendMode::kSrcOut), "xfermode_mismatch");
+        static_assert(8 == static_cast<int>(SkBlendMode::kDstOut), "xfermode_mismatch");
+        static_assert(9 == static_cast<int>(SkBlendMode::kSrcATop), "xfermode_mismatch");
+        static_assert(10 == static_cast<int>(SkBlendMode::kDstATop), "xfermode_mismatch");
+        static_assert(11 == static_cast<int>(SkBlendMode::kXor), "xfermode_mismatch");
+        static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "xfermode_mismatch");
+        static_assert(13 == static_cast<int>(SkBlendMode::kModulate), "xfermode_mismatch");
+        static_assert(14 == static_cast<int>(SkBlendMode::kScreen), "xfermode_mismatch");
+        static_assert(15 == static_cast<int>(SkBlendMode::kOverlay), "xfermode_mismatch");
+        static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "xfermode_mismatch");
+        static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "xfermode_mismatch");
+        static_assert(18 == static_cast<int>(SkBlendMode::kColorDodge), "xfermode mismatch");
+        static_assert(19 == static_cast<int>(SkBlendMode::kColorBurn), "xfermode mismatch");
+        static_assert(20 == static_cast<int>(SkBlendMode::kHardLight), "xfermode mismatch");
+        static_assert(21 == static_cast<int>(SkBlendMode::kSoftLight), "xfermode mismatch");
+        static_assert(22 == static_cast<int>(SkBlendMode::kDifference), "xfermode mismatch");
+        static_assert(23 == static_cast<int>(SkBlendMode::kExclusion), "xfermode mismatch");
+        static_assert(24 == static_cast<int>(SkBlendMode::kMultiply), "xfermode mismatch");
+        static_assert(25 == static_cast<int>(SkBlendMode::kHue), "xfermode mismatch");
+        static_assert(26 == static_cast<int>(SkBlendMode::kSaturation), "xfermode mismatch");
+        static_assert(27 == static_cast<int>(SkBlendMode::kColor), "xfermode mismatch");
+        static_assert(28 == static_cast<int>(SkBlendMode::kLuminosity), "xfermode mismatch");
+
+        SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        paint->setBlendMode(mode);
+    }
+
+    static jlong setPathEffect(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong effectHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        SkPathEffect* effect  = reinterpret_cast<SkPathEffect*>(effectHandle);
+        obj->setPathEffect(sk_ref_sp(effect));
+        return reinterpret_cast<jlong>(obj->getPathEffect());
+    }
+
+    static jlong setMaskFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong maskfilterHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        SkMaskFilter* maskfilter  = reinterpret_cast<SkMaskFilter*>(maskfilterHandle);
+        obj->setMaskFilter(sk_ref_sp(maskfilter));
+        return reinterpret_cast<jlong>(obj->getMaskFilter());
+    }
+
+    static void setTypeface(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong typefaceHandle) {
+        Paint* paint = reinterpret_cast<Paint*>(objHandle);
+        paint->setAndroidTypeface(reinterpret_cast<Typeface*>(typefaceHandle));
+    }
+
+    static jint getTextAlign(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        return static_cast<jint>(obj->getTextAlign());
+    }
+
+    static void setTextAlign(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint alignHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        Paint::Align align = static_cast<Paint::Align>(alignHandle);
+        obj->setTextAlign(align);
+    }
+
+    static void setTextLocalesByMinikinLocaleListId(CRITICAL_JNI_PARAMS_COMMA jlong objHandle,
+            jint minikinLocaleListId) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        obj->setMinikinLocaleListId(minikinLocaleListId);
+    }
+
+    static jboolean isElegantTextHeight(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(paintHandle);
+        return obj->getFamilyVariant() == minikin::FamilyVariant::ELEGANT;
+    }
+
+    static void setElegantTextHeight(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean aa) {
+        Paint* obj = reinterpret_cast<Paint*>(paintHandle);
+        obj->setFamilyVariant(
+                aa ? minikin::FamilyVariant::ELEGANT : minikin::FamilyVariant::DEFAULT);
+    }
+
+    static jfloat getTextSize(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize());
+    }
+
+    static void setTextSize(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat textSize) {
+        if (textSize >= 0) {
+            reinterpret_cast<Paint*>(paintHandle)->getSkFont().setSize(textSize);
+        }
+    }
+
+    static jfloat getTextScaleX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getSkFont().getScaleX());
+    }
+
+    static void setTextScaleX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat scaleX) {
+        reinterpret_cast<Paint*>(paintHandle)->getSkFont().setScaleX(scaleX);
+    }
+
+    static jfloat getTextSkewX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSkewX());
+    }
+
+    static void setTextSkewX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat skewX) {
+        reinterpret_cast<Paint*>(paintHandle)->getSkFont().setSkewX(skewX);
+    }
+
+    static jfloat getLetterSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        return paint->getLetterSpacing();
+    }
+
+    static void setLetterSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat letterSpacing) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        paint->setLetterSpacing(letterSpacing);
+    }
+
+    static jfloat getWordSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        return paint->getWordSpacing();
+    }
+
+    static void setWordSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat wordSpacing) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        paint->setWordSpacing(wordSpacing);
+    }
+
+    static jint getStartHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        return static_cast<jint>(paint->getStartHyphenEdit());
+    }
+
+    static jint getEndHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        return static_cast<jint>(paint->getEndHyphenEdit());
+    }
+
+    static void setStartHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        paint->setStartHyphenEdit((uint32_t)hyphen);
+    }
+
+    static void setEndHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        paint->setEndHyphenEdit((uint32_t)hyphen);
+    }
+
+    static jfloat ascent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        SkFontMetrics metrics;
+        getMetricsInternal(paintHandle, &metrics);
+        return SkScalarToFloat(metrics.fAscent);
+    }
+
+    static jfloat descent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        SkFontMetrics metrics;
+        getMetricsInternal(paintHandle, &metrics);
+        return SkScalarToFloat(metrics.fDescent);
+    }
+
+    static jfloat getUnderlinePosition(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        SkFontMetrics metrics;
+        getMetricsInternal(paintHandle, &metrics);
+        SkScalar position;
+        if (metrics.hasUnderlinePosition(&position)) {
+            return SkScalarToFloat(position);
+        } else {
+            const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize();
+            return SkScalarToFloat(Paint::kStdUnderline_Top * textSize);
+        }
+    }
+
+    static jfloat getUnderlineThickness(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        SkFontMetrics metrics;
+        getMetricsInternal(paintHandle, &metrics);
+        SkScalar thickness;
+        if (metrics.hasUnderlineThickness(&thickness)) {
+            return SkScalarToFloat(thickness);
+        } else {
+            const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize();
+            return SkScalarToFloat(Paint::kStdUnderline_Thickness * textSize);
+        }
+    }
+
+    static jfloat getStrikeThruPosition(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize();
+        return SkScalarToFloat(Paint::kStdStrikeThru_Top * textSize);
+    }
+
+    static jfloat getStrikeThruThickness(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize();
+        return SkScalarToFloat(Paint::kStdStrikeThru_Thickness * textSize);
+    }
+
+    static void setShadowLayer(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat radius,
+                               jfloat dx, jfloat dy, jlong colorSpaceHandle,
+                               jlong colorLong) {
+        SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
+        sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        if (radius <= 0) {
+            paint->setLooper(nullptr);
+        }
+        else {
+            SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
+            paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy));
+        }
+    }
+
+    static jboolean hasShadowLayer(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
+    }
+
+    static jboolean equalsForTextMeasurement(CRITICAL_JNI_PARAMS_COMMA jlong lPaint, jlong rPaint) {
+        if (lPaint == rPaint) {
+            return true;
+        }
+        Paint* leftPaint = reinterpret_cast<Paint*>(lPaint);
+        Paint* rightPaint = reinterpret_cast<Paint*>(rPaint);
+
+        const Typeface* leftTypeface = Typeface::resolveDefault(leftPaint->getAndroidTypeface());
+        const Typeface* rightTypeface = Typeface::resolveDefault(rightPaint->getAndroidTypeface());
+        minikin::MinikinPaint leftMinikinPaint
+                = MinikinUtils::prepareMinikinPaint(leftPaint, leftTypeface);
+        minikin::MinikinPaint rightMinikinPaint
+                = MinikinUtils::prepareMinikinPaint(rightPaint, rightTypeface);
+
+        return leftMinikinPaint == rightMinikinPaint;
+    }
+
+}; // namespace PaintGlue
+
+static const JNINativeMethod methods[] = {
+    {"nGetNativeFinalizer", "()J", (void*) PaintGlue::getNativeFinalizer},
+    {"nInit","()J", (void*) PaintGlue::init},
+    {"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
+    {"nBreakText","(J[CIIFI[F)I", (void*) PaintGlue::breakTextC},
+    {"nBreakText","(JLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
+    {"nGetTextAdvances","(J[CIIIII[FI)F",
+            (void*) PaintGlue::getTextAdvances___CIIIII_FI},
+    {"nGetTextAdvances","(JLjava/lang/String;IIIII[FI)F",
+            (void*) PaintGlue::getTextAdvances__StringIIIII_FI},
+
+    {"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
+    {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I",
+            (void*) PaintGlue::getTextRunCursor__String},
+    {"nGetTextPath", "(JI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
+    {"nGetTextPath", "(JILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
+    {"nGetStringBounds", "(JLjava/lang/String;IIILandroid/graphics/Rect;)V",
+            (void*) PaintGlue::getStringBounds },
+    {"nGetCharArrayBounds", "(J[CIIILandroid/graphics/Rect;)V",
+            (void*) PaintGlue::getCharArrayBounds },
+    {"nHasGlyph", "(JILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
+    {"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
+    {"nGetOffsetForAdvance", "(J[CIIIIZF)I",
+            (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
+
+    // --------------- @FastNative ----------------------
+
+    {"nSetTextLocales","(JLjava/lang/String;)I", (void*) PaintGlue::setTextLocales},
+    {"nSetFontFeatureSettings","(JLjava/lang/String;)V",
+                (void*) PaintGlue::setFontFeatureSettings},
+    {"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;)F",
+                (void*)PaintGlue::getFontMetrics},
+    {"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;)I",
+            (void*)PaintGlue::getFontMetricsInt},
+
+    // --------------- @CriticalNative ------------------
+
+    {"nReset","(J)V", (void*) PaintGlue::reset},
+    {"nSet","(JJ)V", (void*) PaintGlue::assign},
+    {"nGetFlags","(J)I", (void*) PaintGlue::getFlags},
+    {"nSetFlags","(JI)V", (void*) PaintGlue::setFlags},
+    {"nGetHinting","(J)I", (void*) PaintGlue::getHinting},
+    {"nSetHinting","(JI)V", (void*) PaintGlue::setHinting},
+    {"nSetAntiAlias","(JZ)V", (void*) PaintGlue::setAntiAlias},
+    {"nSetSubpixelText","(JZ)V", (void*) PaintGlue::setSubpixelText},
+    {"nSetLinearText","(JZ)V", (void*) PaintGlue::setLinearText},
+    {"nSetUnderlineText","(JZ)V", (void*) PaintGlue::setUnderlineText},
+    {"nSetStrikeThruText","(JZ)V", (void*) PaintGlue::setStrikeThruText},
+    {"nSetFakeBoldText","(JZ)V", (void*) PaintGlue::setFakeBoldText},
+    {"nSetFilterBitmap","(JZ)V", (void*) PaintGlue::setFilterBitmap},
+    {"nSetDither","(JZ)V", (void*) PaintGlue::setDither},
+    {"nGetStyle","(J)I", (void*) PaintGlue::getStyle},
+    {"nSetStyle","(JI)V", (void*) PaintGlue::setStyle},
+    {"nSetColor","(JI)V", (void*) PaintGlue::setColor},
+    {"nSetColor","(JJJ)V", (void*) PaintGlue::setColorLong},
+    {"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha},
+    {"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth},
+    {"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth},
+    {"nGetStrokeMiter","(J)F", (void*) PaintGlue::getStrokeMiter},
+    {"nSetStrokeMiter","(JF)V", (void*) PaintGlue::setStrokeMiter},
+    {"nGetStrokeCap","(J)I", (void*) PaintGlue::getStrokeCap},
+    {"nSetStrokeCap","(JI)V", (void*) PaintGlue::setStrokeCap},
+    {"nGetStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin},
+    {"nSetStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin},
+    {"nGetFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath},
+    {"nSetShader","(JJ)J", (void*) PaintGlue::setShader},
+    {"nSetColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter},
+    {"nSetXfermode","(JI)V", (void*) PaintGlue::setXfermode},
+    {"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect},
+    {"nSetMaskFilter","(JJ)J", (void*) PaintGlue::setMaskFilter},
+    {"nSetTypeface","(JJ)V", (void*) PaintGlue::setTypeface},
+    {"nGetTextAlign","(J)I", (void*) PaintGlue::getTextAlign},
+    {"nSetTextAlign","(JI)V", (void*) PaintGlue::setTextAlign},
+    {"nSetTextLocalesByMinikinLocaleListId","(JI)V",
+            (void*) PaintGlue::setTextLocalesByMinikinLocaleListId},
+    {"nIsElegantTextHeight","(J)Z", (void*) PaintGlue::isElegantTextHeight},
+    {"nSetElegantTextHeight","(JZ)V", (void*) PaintGlue::setElegantTextHeight},
+    {"nGetTextSize","(J)F", (void*) PaintGlue::getTextSize},
+    {"nSetTextSize","(JF)V", (void*) PaintGlue::setTextSize},
+    {"nGetTextScaleX","(J)F", (void*) PaintGlue::getTextScaleX},
+    {"nSetTextScaleX","(JF)V", (void*) PaintGlue::setTextScaleX},
+    {"nGetTextSkewX","(J)F", (void*) PaintGlue::getTextSkewX},
+    {"nSetTextSkewX","(JF)V", (void*) PaintGlue::setTextSkewX},
+    {"nGetLetterSpacing","(J)F", (void*) PaintGlue::getLetterSpacing},
+    {"nSetLetterSpacing","(JF)V", (void*) PaintGlue::setLetterSpacing},
+    {"nGetWordSpacing","(J)F", (void*) PaintGlue::getWordSpacing},
+    {"nSetWordSpacing","(JF)V", (void*) PaintGlue::setWordSpacing},
+    {"nGetStartHyphenEdit", "(J)I", (void*) PaintGlue::getStartHyphenEdit},
+    {"nGetEndHyphenEdit", "(J)I", (void*) PaintGlue::getEndHyphenEdit},
+    {"nSetStartHyphenEdit", "(JI)V", (void*) PaintGlue::setStartHyphenEdit},
+    {"nSetEndHyphenEdit", "(JI)V", (void*) PaintGlue::setEndHyphenEdit},
+    {"nAscent","(J)F", (void*) PaintGlue::ascent},
+    {"nDescent","(J)F", (void*) PaintGlue::descent},
+    {"nGetUnderlinePosition","(J)F", (void*) PaintGlue::getUnderlinePosition},
+    {"nGetUnderlineThickness","(J)F", (void*) PaintGlue::getUnderlineThickness},
+    {"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition},
+    {"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness},
+    {"nSetShadowLayer", "(JFFFJJ)V", (void*)PaintGlue::setShadowLayer},
+    {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
+    {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
+};
+
+int register_android_graphics_Paint(JNIEnv* env) {
+    gFontMetrics_class = FindClassOrDie(env, "android/graphics/Paint$FontMetrics");
+    gFontMetrics_class = MakeGlobalRefOrDie(env, gFontMetrics_class);
+
+    gFontMetrics_fieldID.top = GetFieldIDOrDie(env, gFontMetrics_class, "top", "F");
+    gFontMetrics_fieldID.ascent = GetFieldIDOrDie(env, gFontMetrics_class, "ascent", "F");
+    gFontMetrics_fieldID.descent = GetFieldIDOrDie(env, gFontMetrics_class, "descent", "F");
+    gFontMetrics_fieldID.bottom = GetFieldIDOrDie(env, gFontMetrics_class, "bottom", "F");
+    gFontMetrics_fieldID.leading = GetFieldIDOrDie(env, gFontMetrics_class, "leading", "F");
+
+    gFontMetricsInt_class = FindClassOrDie(env, "android/graphics/Paint$FontMetricsInt");
+    gFontMetricsInt_class = MakeGlobalRefOrDie(env, gFontMetricsInt_class);
+
+    gFontMetricsInt_fieldID.top = GetFieldIDOrDie(env, gFontMetricsInt_class, "top", "I");
+    gFontMetricsInt_fieldID.ascent = GetFieldIDOrDie(env, gFontMetricsInt_class, "ascent", "I");
+    gFontMetricsInt_fieldID.descent = GetFieldIDOrDie(env, gFontMetricsInt_class, "descent", "I");
+    gFontMetricsInt_fieldID.bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
+    gFontMetricsInt_fieldID.leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
+
+    return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
+}
+
+}
diff --git a/libs/hwui/jni/PaintFilter.cpp b/libs/hwui/jni/PaintFilter.cpp
new file mode 100644
index 0000000..4fe9140
--- /dev/null
+++ b/libs/hwui/jni/PaintFilter.cpp
@@ -0,0 +1,84 @@
+/* libs/android_runtime/android/graphics/ColorFilter.cpp
+**
+** Copyright 2006, 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 "jni.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "core_jni_helpers.h"
+
+#include "hwui/Paint.h"
+#include "hwui/PaintFilter.h"
+#include "SkPaint.h"
+
+namespace android {
+
+class PaintFlagsFilter : public PaintFilter {
+public:
+    PaintFlagsFilter(uint32_t clearFlags, uint32_t setFlags) {
+        fClearFlags = static_cast<uint16_t>(clearFlags);
+        fSetFlags = static_cast<uint16_t>(setFlags);
+    }
+    void filter(SkPaint* paint) override {
+        uint32_t flags = Paint::GetSkPaintJavaFlags(*paint);
+        Paint::SetSkPaintJavaFlags(paint, (flags & ~fClearFlags) | fSetFlags);
+    }
+    void filterFullPaint(Paint* paint) override {
+        paint->setJavaFlags((paint->getJavaFlags() & ~fClearFlags) | fSetFlags);
+    }
+
+private:
+    uint16_t fClearFlags;
+    uint16_t fSetFlags;
+};
+
+class PaintFilterGlue {
+public:
+
+    static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
+        PaintFilter* obj = reinterpret_cast<PaintFilter*>(objHandle);
+        SkSafeUnref(obj);
+    }
+
+    static jlong CreatePaintFlagsFilter(JNIEnv* env, jobject clazz,
+                                        jint clearFlags, jint setFlags) {
+        PaintFilter* filter = nullptr;
+        if (clearFlags | setFlags) {
+            filter = new PaintFlagsFilter(clearFlags, setFlags);
+        }
+        return reinterpret_cast<jlong>(filter);
+    }
+};
+
+static const JNINativeMethod drawfilter_methods[] = {
+    {"nativeDestructor", "(J)V", (void*) PaintFilterGlue::finalizer}
+};
+
+static const JNINativeMethod paintflags_methods[] = {
+    {"nativeConstructor","(II)J", (void*) PaintFilterGlue::CreatePaintFlagsFilter}
+};
+
+int register_android_graphics_DrawFilter(JNIEnv* env) {
+    int result = RegisterMethodsOrDie(env, "android/graphics/DrawFilter", drawfilter_methods,
+                                      NELEM(drawfilter_methods));
+    result |= RegisterMethodsOrDie(env, "android/graphics/PaintFlagsDrawFilter", paintflags_methods,
+                                   NELEM(paintflags_methods));
+
+    return 0;
+}
+
+}
diff --git a/libs/hwui/jni/Path.cpp b/libs/hwui/jni/Path.cpp
new file mode 100644
index 0000000..4814452
--- /dev/null
+++ b/libs/hwui/jni/Path.cpp
@@ -0,0 +1,562 @@
+/* libs/android_runtime/android/graphics/Path.cpp
+**
+** Copyright 2006, 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.
+*/
+
+// This file was generated from the C++ include file: SkPath.h
+// Any changes made to this file will be discarded by the build.
+// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
+// or one of the auxilary file specifications in device/tools/gluemaker.
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
+
+#include "SkPath.h"
+#include "SkPathOps.h"
+#include "SkGeometry.h" // WARNING: Internal Skia Header
+
+#include <vector>
+#include <map>
+
+namespace android {
+
+class SkPathGlue {
+public:
+
+    static void finalizer(SkPath* obj) {
+        delete obj;
+    }
+
+    // ---------------- Regular JNI -----------------------------
+
+    static jlong init(JNIEnv* env, jclass clazz) {
+        return reinterpret_cast<jlong>(new SkPath());
+    }
+
+    static jlong init_Path(JNIEnv* env, jclass clazz, jlong valHandle) {
+        SkPath* val = reinterpret_cast<SkPath*>(valHandle);
+        return reinterpret_cast<jlong>(new SkPath(*val));
+    }
+
+    static jlong getFinalizer(JNIEnv* env, jclass clazz) {
+        return static_cast<jlong>(reinterpret_cast<uintptr_t>(&finalizer));
+    }
+
+    static void set(JNIEnv* env, jclass clazz, jlong dstHandle, jlong srcHandle) {
+        SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
+        const SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
+        *dst = *src;
+    }
+
+    static void computeBounds(JNIEnv* env, jclass clazz, jlong objHandle, jobject jbounds) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        const SkRect& bounds = obj->getBounds();
+        GraphicsJNI::rect_to_jrectf(bounds, env, jbounds);
+    }
+
+    static void incReserve(JNIEnv* env, jclass clazz, jlong objHandle, jint extraPtCount) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->incReserve(extraPtCount);
+    }
+
+    static void moveTo__FF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x, jfloat y) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->moveTo(x, y);
+    }
+
+    static void rMoveTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->rMoveTo(dx, dy);
+    }
+
+    static void lineTo__FF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x, jfloat y) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->lineTo(x, y);
+    }
+
+    static void rLineTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->rLineTo(dx, dy);
+    }
+
+    static void quadTo__FFFF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1,
+            jfloat x2, jfloat y2) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->quadTo(x1, y1, x2, y2);
+    }
+
+    static void rQuadTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx1, jfloat dy1,
+            jfloat dx2, jfloat dy2) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->rQuadTo(dx1, dy1, dx2, dy2);
+    }
+
+    static void cubicTo__FFFFFF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1,
+            jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->cubicTo(x1, y1, x2, y2, x3, y3);
+    }
+
+    static void rCubicTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1,
+            jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->rCubicTo(x1, y1, x2, y2, x3, y3);
+    }
+
+    static void arcTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top,
+            jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
+            jboolean forceMoveTo) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+        obj->arcTo(oval, startAngle, sweepAngle, forceMoveTo);
+    }
+
+    static void close(JNIEnv* env, jclass clazz, jlong objHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->close();
+    }
+
+    static void addRect(JNIEnv* env, jclass clazz, jlong objHandle,
+            jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        SkPathDirection dir = static_cast<SkPathDirection>(dirHandle);
+        obj->addRect(left, top, right, bottom, dir);
+    }
+
+    static void addOval(JNIEnv* env, jclass clazz, jlong objHandle,
+            jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        SkPathDirection dir = static_cast<SkPathDirection>(dirHandle);
+        SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+        obj->addOval(oval, dir);
+    }
+
+    static void addCircle(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x, jfloat y,
+            jfloat radius, jint dirHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        SkPathDirection dir = static_cast<SkPathDirection>(dirHandle);
+        obj->addCircle(x, y, radius, dir);
+    }
+
+    static void addArc(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top,
+            jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle) {
+        SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->addArc(oval, startAngle, sweepAngle);
+    }
+
+    static void addRoundRectXY(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top,
+            jfloat right, jfloat bottom, jfloat rx, jfloat ry, jint dirHandle) {
+        SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        SkPathDirection dir = static_cast<SkPathDirection>(dirHandle);
+        obj->addRoundRect(rect, rx, ry, dir);
+    }
+
+    static void addRoundRect8(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top,
+                jfloat right, jfloat bottom, jfloatArray array, jint dirHandle) {
+        SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        SkPathDirection dir = static_cast<SkPathDirection>(dirHandle);
+        AutoJavaFloatArray  afa(env, array, 8);
+#ifdef SK_SCALAR_IS_FLOAT
+        const float* src = afa.ptr();
+#else
+        #error Need to convert float array to SkScalar array before calling the following function.
+#endif
+        obj->addRoundRect(rect, src, dir);
+    }
+
+    static void addPath__PathFF(JNIEnv* env, jclass clazz, jlong objHandle, jlong srcHandle,
+            jfloat dx, jfloat dy) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
+        obj->addPath(*src, dx, dy);
+    }
+
+    static void addPath__Path(JNIEnv* env, jclass clazz, jlong objHandle, jlong srcHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
+        obj->addPath(*src);
+    }
+
+    static void addPath__PathMatrix(JNIEnv* env, jclass clazz, jlong objHandle, jlong srcHandle,
+            jlong matrixHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        obj->addPath(*src, *matrix);
+    }
+
+    static void offset__FF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->offset(dx, dy);
+    }
+
+    static void setLastPoint(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->setLastPt(dx, dy);
+    }
+
+    static void transform__MatrixPath(JNIEnv* env, jclass clazz, jlong objHandle, jlong matrixHandle,
+            jlong dstHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
+        obj->transform(*matrix, dst);
+    }
+
+    static void transform__Matrix(JNIEnv* env, jclass clazz, jlong objHandle, jlong matrixHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        obj->transform(*matrix);
+    }
+
+    static jboolean op(JNIEnv* env, jclass clazz, jlong p1Handle, jlong p2Handle, jint opHandle,
+            jlong rHandle) {
+        SkPath* p1  = reinterpret_cast<SkPath*>(p1Handle);
+        SkPath* p2  = reinterpret_cast<SkPath*>(p2Handle);
+        SkPathOp op = static_cast<SkPathOp>(opHandle);
+        SkPath* r   = reinterpret_cast<SkPath*>(rHandle);
+        return Op(*p1, *p2, op, r);
+     }
+
+    typedef SkPoint (*bezierCalculation)(float t, const SkPoint* points);
+
+    static void addMove(std::vector<SkPoint>& segmentPoints, std::vector<float>& lengths,
+            const SkPoint& point) {
+        float length = 0;
+        if (!lengths.empty()) {
+            length = lengths.back();
+        }
+        segmentPoints.push_back(point);
+        lengths.push_back(length);
+    }
+
+    static void addLine(std::vector<SkPoint>& segmentPoints, std::vector<float>& lengths,
+            const SkPoint& toPoint) {
+        if (segmentPoints.empty()) {
+            segmentPoints.push_back(SkPoint::Make(0, 0));
+            lengths.push_back(0);
+        } else if (segmentPoints.back() == toPoint) {
+            return; // Empty line
+        }
+        float length = lengths.back() + SkPoint::Distance(segmentPoints.back(), toPoint);
+        segmentPoints.push_back(toPoint);
+        lengths.push_back(length);
+    }
+
+    static float cubicCoordinateCalculation(float t, float p0, float p1, float p2, float p3) {
+        float oneMinusT = 1 - t;
+        float oneMinusTSquared = oneMinusT * oneMinusT;
+        float oneMinusTCubed = oneMinusTSquared * oneMinusT;
+        float tSquared = t * t;
+        float tCubed = tSquared * t;
+        return (oneMinusTCubed * p0) + (3 * oneMinusTSquared * t * p1)
+                + (3 * oneMinusT * tSquared * p2) + (tCubed * p3);
+    }
+
+    static SkPoint cubicBezierCalculation(float t, const SkPoint* points) {
+        float x = cubicCoordinateCalculation(t, points[0].x(), points[1].x(),
+            points[2].x(), points[3].x());
+        float y = cubicCoordinateCalculation(t, points[0].y(), points[1].y(),
+            points[2].y(), points[3].y());
+        return SkPoint::Make(x, y);
+    }
+
+    static float quadraticCoordinateCalculation(float t, float p0, float p1, float p2) {
+        float oneMinusT = 1 - t;
+        return oneMinusT * ((oneMinusT * p0) + (t * p1)) + t * ((oneMinusT * p1) + (t * p2));
+    }
+
+    static SkPoint quadraticBezierCalculation(float t, const SkPoint* points) {
+        float x = quadraticCoordinateCalculation(t, points[0].x(), points[1].x(), points[2].x());
+        float y = quadraticCoordinateCalculation(t, points[0].y(), points[1].y(), points[2].y());
+        return SkPoint::Make(x, y);
+    }
+
+    // Subdivide a section of the Bezier curve, set the mid-point and the mid-t value.
+    // Returns true if further subdivision is necessary as defined by errorSquared.
+    static bool subdividePoints(const SkPoint* points, bezierCalculation bezierFunction,
+            float t0, const SkPoint &p0, float t1, const SkPoint &p1,
+            float& midT, SkPoint &midPoint, float errorSquared) {
+        midT = (t1 + t0) / 2;
+        float midX = (p1.x() + p0.x()) / 2;
+        float midY = (p1.y() + p0.y()) / 2;
+
+        midPoint = (*bezierFunction)(midT, points);
+        float xError = midPoint.x() - midX;
+        float yError = midPoint.y() - midY;
+        float midErrorSquared = (xError * xError) + (yError * yError);
+        return midErrorSquared > errorSquared;
+    }
+
+    // Divides Bezier curves until linear interpolation is very close to accurate, using
+    // errorSquared as a metric. Cubic Bezier curves can have an inflection point that improperly
+    // short-circuit subdivision. If you imagine an S shape, the top and bottom points being the
+    // starting and end points, linear interpolation would mark the center where the curve places
+    // the point. It is clearly not the case that we can linearly interpolate at that point.
+    // doubleCheckDivision forces a second examination between subdivisions to ensure that linear
+    // interpolation works.
+    static void addBezier(const SkPoint* points,
+            bezierCalculation bezierFunction, std::vector<SkPoint>& segmentPoints,
+            std::vector<float>& lengths, float errorSquared, bool doubleCheckDivision) {
+        typedef std::map<float, SkPoint> PointMap;
+        PointMap tToPoint;
+
+        tToPoint[0] = (*bezierFunction)(0, points);
+        tToPoint[1] = (*bezierFunction)(1, points);
+
+        PointMap::iterator iter = tToPoint.begin();
+        PointMap::iterator next = iter;
+        ++next;
+        while (next != tToPoint.end()) {
+            bool needsSubdivision = true;
+            SkPoint midPoint;
+            do {
+                float midT;
+                needsSubdivision = subdividePoints(points, bezierFunction, iter->first,
+                    iter->second, next->first, next->second, midT, midPoint, errorSquared);
+                if (!needsSubdivision && doubleCheckDivision) {
+                    SkPoint quarterPoint;
+                    float quarterT;
+                    needsSubdivision = subdividePoints(points, bezierFunction, iter->first,
+                        iter->second, midT, midPoint, quarterT, quarterPoint, errorSquared);
+                    if (needsSubdivision) {
+                        // Found an inflection point. No need to double-check.
+                        doubleCheckDivision = false;
+                    }
+                }
+                if (needsSubdivision) {
+                    next = tToPoint.insert(iter, PointMap::value_type(midT, midPoint));
+                }
+            } while (needsSubdivision);
+            iter = next;
+            next++;
+        }
+
+        // Now that each division can use linear interpolation with less than the allowed error
+        for (iter = tToPoint.begin(); iter != tToPoint.end(); ++iter) {
+            addLine(segmentPoints, lengths, iter->second);
+        }
+    }
+
+    static void createVerbSegments(const SkPath::Iter& pathIter, SkPath::Verb verb,
+            const SkPoint* points, std::vector<SkPoint>& segmentPoints,
+            std::vector<float>& lengths, float errorSquared, float errorConic) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                addMove(segmentPoints, lengths, points[0]);
+                break;
+            case SkPath::kClose_Verb:
+                addLine(segmentPoints, lengths, points[0]);
+                break;
+            case SkPath::kLine_Verb:
+                addLine(segmentPoints, lengths, points[1]);
+                break;
+            case SkPath::kQuad_Verb:
+                addBezier(points, quadraticBezierCalculation, segmentPoints, lengths,
+                    errorSquared, false);
+                break;
+            case SkPath::kCubic_Verb:
+                addBezier(points, cubicBezierCalculation, segmentPoints, lengths,
+                    errorSquared, true);
+                break;
+            case SkPath::kConic_Verb: {
+                SkAutoConicToQuads converter;
+                const SkPoint* quads = converter.computeQuads(
+                        points, pathIter.conicWeight(), errorConic);
+                for (int i = 0; i < converter.countQuads(); i++) {
+                    // Note: offset each subsequent quad by 2, since end points are shared
+                    const SkPoint* quad = quads + i * 2;
+                    addBezier(quad, quadraticBezierCalculation, segmentPoints, lengths,
+                        errorConic, false);
+                }
+                break;
+            }
+            default:
+                static_assert(SkPath::kMove_Verb == 0
+                                && SkPath::kLine_Verb == 1
+                                && SkPath::kQuad_Verb == 2
+                                && SkPath::kConic_Verb == 3
+                                && SkPath::kCubic_Verb == 4
+                                && SkPath::kClose_Verb == 5
+                                && SkPath::kDone_Verb == 6,
+                        "Path enum changed, new types may have been added.");
+                break;
+        }
+    }
+
+    // Returns a float[] with each point along the path represented by 3 floats
+    // * fractional length along the path that the point resides
+    // * x coordinate
+    // * y coordinate
+    // Note that more than one point may have the same length along the path in
+    // the case of a move.
+    // NULL can be returned if the Path is empty.
+    static jfloatArray approximate(JNIEnv* env, jclass clazz, jlong pathHandle,
+            float acceptableError) {
+        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+        SkASSERT(path);
+        SkPath::Iter pathIter(*path, false);
+        SkPath::Verb verb;
+        SkPoint points[4];
+        std::vector<SkPoint> segmentPoints;
+        std::vector<float> lengths;
+        float errorSquared = acceptableError * acceptableError;
+        float errorConic = acceptableError / 2; // somewhat arbitrary
+
+        while ((verb = pathIter.next(points)) != SkPath::kDone_Verb) {
+            createVerbSegments(pathIter, verb, points, segmentPoints, lengths,
+                    errorSquared, errorConic);
+        }
+
+        if (segmentPoints.empty()) {
+            int numVerbs = path->countVerbs();
+            if (numVerbs == 1) {
+                addMove(segmentPoints, lengths, path->getPoint(0));
+            } else {
+                // Invalid or empty path. Fall back to point(0,0)
+                addMove(segmentPoints, lengths, SkPoint());
+            }
+        }
+
+        float totalLength = lengths.back();
+        if (totalLength == 0) {
+            // Lone Move instructions should still be able to animate at the same value.
+            segmentPoints.push_back(segmentPoints.back());
+            lengths.push_back(1);
+            totalLength = 1;
+        }
+
+        size_t numPoints = segmentPoints.size();
+        size_t approximationArraySize = numPoints * 3;
+
+        float* approximation = new float[approximationArraySize];
+
+        int approximationIndex = 0;
+        for (size_t i = 0; i < numPoints; i++) {
+            const SkPoint& point = segmentPoints[i];
+            approximation[approximationIndex++] = lengths[i] / totalLength;
+            approximation[approximationIndex++] = point.x();
+            approximation[approximationIndex++] = point.y();
+        }
+
+        jfloatArray result = env->NewFloatArray(approximationArraySize);
+        env->SetFloatArrayRegion(result, 0, approximationArraySize, approximation);
+        delete[] approximation;
+        return result;
+    }
+
+    // ---------------- @FastNative -----------------------------
+
+    static jboolean isRect(JNIEnv* env, jclass clazz, jlong objHandle, jobject jrect) {
+        SkRect rect;
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        jboolean result = obj->isRect(&rect);
+        if (jrect) {
+            GraphicsJNI::rect_to_jrectf(rect, env, jrect);
+        }
+        return result;
+    }
+
+    // ---------------- @CriticalNative -------------------------
+
+    static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->reset();
+    }
+
+    static void rewind(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        obj->rewind();
+    }
+
+    static jboolean isEmpty(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        return obj->isEmpty();
+    }
+
+    static jboolean isConvex(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        return obj->isConvex();
+    }
+
+    static jint getFillType(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        return static_cast<int>(obj->getFillType());
+    }
+
+    static void setFillType(CRITICAL_JNI_PARAMS_COMMA jlong pathHandle, jint ftHandle) {;
+        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+        SkPathFillType ft = static_cast<SkPathFillType>(ftHandle);
+        path->setFillType(ft);
+    }
+};
+
+static const JNINativeMethod methods[] = {
+    {"nInit","()J", (void*) SkPathGlue::init},
+    {"nInit","(J)J", (void*) SkPathGlue::init_Path},
+    {"nGetFinalizer", "()J", (void*) SkPathGlue::getFinalizer},
+    {"nSet","(JJ)V", (void*) SkPathGlue::set},
+    {"nComputeBounds","(JLandroid/graphics/RectF;)V", (void*) SkPathGlue::computeBounds},
+    {"nIncReserve","(JI)V", (void*) SkPathGlue::incReserve},
+    {"nMoveTo","(JFF)V", (void*) SkPathGlue::moveTo__FF},
+    {"nRMoveTo","(JFF)V", (void*) SkPathGlue::rMoveTo},
+    {"nLineTo","(JFF)V", (void*) SkPathGlue::lineTo__FF},
+    {"nRLineTo","(JFF)V", (void*) SkPathGlue::rLineTo},
+    {"nQuadTo","(JFFFF)V", (void*) SkPathGlue::quadTo__FFFF},
+    {"nRQuadTo","(JFFFF)V", (void*) SkPathGlue::rQuadTo},
+    {"nCubicTo","(JFFFFFF)V", (void*) SkPathGlue::cubicTo__FFFFFF},
+    {"nRCubicTo","(JFFFFFF)V", (void*) SkPathGlue::rCubicTo},
+    {"nArcTo","(JFFFFFFZ)V", (void*) SkPathGlue::arcTo},
+    {"nClose","(J)V", (void*) SkPathGlue::close},
+    {"nAddRect","(JFFFFI)V", (void*) SkPathGlue::addRect},
+    {"nAddOval","(JFFFFI)V", (void*) SkPathGlue::addOval},
+    {"nAddCircle","(JFFFI)V", (void*) SkPathGlue::addCircle},
+    {"nAddArc","(JFFFFFF)V", (void*) SkPathGlue::addArc},
+    {"nAddRoundRect","(JFFFFFFI)V", (void*) SkPathGlue::addRoundRectXY},
+    {"nAddRoundRect","(JFFFF[FI)V", (void*) SkPathGlue::addRoundRect8},
+    {"nAddPath","(JJFF)V", (void*) SkPathGlue::addPath__PathFF},
+    {"nAddPath","(JJ)V", (void*) SkPathGlue::addPath__Path},
+    {"nAddPath","(JJJ)V", (void*) SkPathGlue::addPath__PathMatrix},
+    {"nOffset","(JFF)V", (void*) SkPathGlue::offset__FF},
+    {"nSetLastPoint","(JFF)V", (void*) SkPathGlue::setLastPoint},
+    {"nTransform","(JJJ)V", (void*) SkPathGlue::transform__MatrixPath},
+    {"nTransform","(JJ)V", (void*) SkPathGlue::transform__Matrix},
+    {"nOp","(JJIJ)Z", (void*) SkPathGlue::op},
+    {"nApproximate", "(JF)[F", (void*) SkPathGlue::approximate},
+
+    // ------- @FastNative below here ----------------------
+    {"nIsRect","(JLandroid/graphics/RectF;)Z", (void*) SkPathGlue::isRect},
+
+    // ------- @CriticalNative below here ------------------
+    {"nReset","(J)V", (void*) SkPathGlue::reset},
+    {"nRewind","(J)V", (void*) SkPathGlue::rewind},
+    {"nIsEmpty","(J)Z", (void*) SkPathGlue::isEmpty},
+    {"nIsConvex","(J)Z", (void*) SkPathGlue::isConvex},
+    {"nGetFillType","(J)I", (void*) SkPathGlue::getFillType},
+    {"nSetFillType","(JI)V", (void*) SkPathGlue::setFillType},
+};
+
+int register_android_graphics_Path(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/Path", methods, NELEM(methods));
+
+    static_assert(0 == (int)SkPathDirection::kCW,  "direction_mismatch");
+    static_assert(1 == (int)SkPathDirection::kCCW, "direction_mismatch");
+}
+
+}
diff --git a/libs/hwui/jni/PathEffect.cpp b/libs/hwui/jni/PathEffect.cpp
new file mode 100644
index 0000000..a4992de
--- /dev/null
+++ b/libs/hwui/jni/PathEffect.cpp
@@ -0,0 +1,120 @@
+#include "GraphicsJNI.h"
+#include "Sk1DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkDashPathEffect.h"
+#include "SkDiscretePathEffect.h"
+#include "SkPathEffect.h"
+#include "core_jni_helpers.h"
+
+#include <jni.h>
+
+class SkPathEffectGlue {
+public:
+
+    static void destructor(JNIEnv* env, jobject, jlong effectHandle) {
+        SkPathEffect* effect = reinterpret_cast<SkPathEffect*>(effectHandle);
+        SkSafeUnref(effect);
+    }
+
+    static jlong Compose_constructor(JNIEnv* env, jobject,
+                                     jlong outerHandle, jlong innerHandle) {
+        SkPathEffect* outer = reinterpret_cast<SkPathEffect*>(outerHandle);
+        SkPathEffect* inner = reinterpret_cast<SkPathEffect*>(innerHandle);
+        SkPathEffect* effect = SkPathEffect::MakeCompose(sk_ref_sp(outer),
+                sk_ref_sp(inner)).release();
+        return reinterpret_cast<jlong>(effect);
+    }
+
+    static jlong Sum_constructor(JNIEnv* env, jobject,
+                                 jlong firstHandle, jlong secondHandle) {
+        SkPathEffect* first = reinterpret_cast<SkPathEffect*>(firstHandle);
+        SkPathEffect* second = reinterpret_cast<SkPathEffect*>(secondHandle);
+        SkPathEffect* effect = SkPathEffect::MakeSum(sk_ref_sp(first),
+                sk_ref_sp(second)).release();
+        return reinterpret_cast<jlong>(effect);
+    }
+
+    static jlong Dash_constructor(JNIEnv* env, jobject,
+                                      jfloatArray intervalArray, jfloat phase) {
+        AutoJavaFloatArray autoInterval(env, intervalArray);
+        int         count = autoInterval.length() & ~1;  // even number
+#ifdef SK_SCALAR_IS_FLOAT
+        SkScalar*   intervals = autoInterval.ptr();
+#else
+        #error Need to convert float array to SkScalar array before calling the following function.
+#endif
+        SkPathEffect* effect = SkDashPathEffect::Make(intervals, count, phase).release();
+        return reinterpret_cast<jlong>(effect);
+    }
+
+    static jlong OneD_constructor(JNIEnv* env, jobject,
+                  jlong shapeHandle, jfloat advance, jfloat phase, jint style) {
+        const SkPath* shape = reinterpret_cast<SkPath*>(shapeHandle);
+        SkASSERT(shape != NULL);
+        SkPathEffect* effect = SkPath1DPathEffect::Make(*shape, advance, phase,
+                (SkPath1DPathEffect::Style)style).release();
+        return reinterpret_cast<jlong>(effect);
+    }
+
+    static jlong Corner_constructor(JNIEnv* env, jobject, jfloat radius){
+        SkPathEffect* effect = SkCornerPathEffect::Make(radius).release();
+        return reinterpret_cast<jlong>(effect);
+    }
+
+    static jlong Discrete_constructor(JNIEnv* env, jobject,
+                                      jfloat length, jfloat deviation) {
+        SkPathEffect* effect = SkDiscretePathEffect::Make(length, deviation).release();
+        return reinterpret_cast<jlong>(effect);
+    }
+
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gPathEffectMethods[] = {
+    { "nativeDestructor", "(J)V", (void*)SkPathEffectGlue::destructor }
+};
+
+static const JNINativeMethod gComposePathEffectMethods[] = {
+    { "nativeCreate", "(JJ)J", (void*)SkPathEffectGlue::Compose_constructor }
+};
+
+static const JNINativeMethod gSumPathEffectMethods[] = {
+    { "nativeCreate", "(JJ)J", (void*)SkPathEffectGlue::Sum_constructor }
+};
+
+static const JNINativeMethod gDashPathEffectMethods[] = {
+    { "nativeCreate", "([FF)J", (void*)SkPathEffectGlue::Dash_constructor }
+};
+
+static const JNINativeMethod gPathDashPathEffectMethods[] = {
+    { "nativeCreate", "(JFFI)J", (void*)SkPathEffectGlue::OneD_constructor }
+};
+
+static const JNINativeMethod gCornerPathEffectMethods[] = {
+    { "nativeCreate", "(F)J", (void*)SkPathEffectGlue::Corner_constructor }
+};
+
+static const JNINativeMethod gDiscretePathEffectMethods[] = {
+    { "nativeCreate", "(FF)J", (void*)SkPathEffectGlue::Discrete_constructor }
+};
+
+int register_android_graphics_PathEffect(JNIEnv* env)
+{
+    android::RegisterMethodsOrDie(env, "android/graphics/PathEffect", gPathEffectMethods,
+                         NELEM(gPathEffectMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/ComposePathEffect",
+                                  gComposePathEffectMethods, NELEM(gComposePathEffectMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/SumPathEffect", gSumPathEffectMethods,
+                                  NELEM(gSumPathEffectMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/DashPathEffect", gDashPathEffectMethods,
+                                  NELEM(gDashPathEffectMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/PathDashPathEffect",
+                                  gPathDashPathEffectMethods, NELEM(gPathDashPathEffectMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/CornerPathEffect",
+                                  gCornerPathEffectMethods, NELEM(gCornerPathEffectMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/DiscretePathEffect",
+                                  gDiscretePathEffectMethods, NELEM(gDiscretePathEffectMethods));
+
+    return 0;
+}
diff --git a/libs/hwui/jni/PathMeasure.cpp b/libs/hwui/jni/PathMeasure.cpp
new file mode 100644
index 0000000..70e528d
--- /dev/null
+++ b/libs/hwui/jni/PathMeasure.cpp
@@ -0,0 +1,162 @@
+/* libs/android_runtime/android/graphics/PathMeasure.cpp
+**
+** Copyright 2007, 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 "jni.h"
+#include "GraphicsJNI.h"
+#include <core_jni_helpers.h>
+
+#include "SkPathMeasure.h"
+
+/*  We declare an explicit pair, so that we don't have to rely on the java
+    client to be sure not to edit the path while we have an active measure
+    object associated with it.
+ 
+    This costs us the copy of the path, for the sake of not allowing a bad
+    java client to randomly crash (since we can't detect the case where the
+    native path has been modified).
+ 
+    The C side does have this risk, but it chooses for speed over safety. If it
+    later changes this, and is internally safe from changes to the path, then
+    we can remove this explicit copy from our JNI code.
+ 
+    Note that we do not have a reference on the java side to the java path.
+    Were we to not need the native copy here, we would want to add a java
+    reference, so that the java path would not get GD'd while the measure object
+    was still alive.
+*/
+struct PathMeasurePair {
+    PathMeasurePair() {}
+    PathMeasurePair(const SkPath& path, bool forceClosed)
+        : fPath(path), fMeasure(fPath, forceClosed) {}
+
+    SkPath          fPath;      // copy of the user's path
+    SkPathMeasure   fMeasure;   // this guy points to fPath
+};
+
+namespace android {
+    
+class SkPathMeasureGlue {
+public:
+
+    static jlong create(JNIEnv* env, jobject clazz, jlong pathHandle,
+                        jboolean forceClosedHandle) {
+        const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+        bool forceClosed = (forceClosedHandle == JNI_TRUE);
+        PathMeasurePair* pair;
+        if(path)
+            pair = new PathMeasurePair(*path, forceClosed);
+        else
+            pair = new PathMeasurePair;
+        return reinterpret_cast<jlong>(pair);
+    }
+
+    static void setPath(JNIEnv* env, jobject clazz, jlong pairHandle,
+                        jlong pathHandle, jboolean forceClosedHandle) {
+        PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle);
+        const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+        bool forceClosed = (forceClosedHandle == JNI_TRUE);
+
+        if (NULL == path) {
+            pair->fPath.reset();
+        } else {
+            pair->fPath = *path;
+        }
+        pair->fMeasure.setPath(&pair->fPath, forceClosed);
+    }
+
+    static jfloat getLength(JNIEnv* env, jobject clazz, jlong pairHandle) {
+        PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle);
+        return static_cast<jfloat>(SkScalarToFloat(pair->fMeasure.getLength()));
+    }
+
+    static void convertTwoElemFloatArray(JNIEnv* env, jfloatArray array, const SkScalar src[2]) {
+        AutoJavaFloatArray autoArray(env, array, 2);
+        jfloat* ptr = autoArray.ptr();
+        ptr[0] = SkScalarToFloat(src[0]);
+        ptr[1] = SkScalarToFloat(src[1]);
+    }
+
+    static jboolean getPosTan(JNIEnv* env, jobject clazz, jlong pairHandle, jfloat dist, jfloatArray pos, jfloatArray tan) {
+        PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle);
+        SkScalar    tmpPos[2], tmpTan[2];
+        SkScalar*   posPtr = pos ? tmpPos : NULL;
+        SkScalar*   tanPtr = tan ? tmpTan : NULL;
+        
+        if (!pair->fMeasure.getPosTan(dist, (SkPoint*)posPtr, (SkVector*)tanPtr)) {
+            return JNI_FALSE;
+        }
+    
+        if (pos) {
+            convertTwoElemFloatArray(env, pos, tmpPos);
+        }
+        if (tan) {
+            convertTwoElemFloatArray(env, tan, tmpTan);
+        }
+        return JNI_TRUE;
+    }
+
+    static jboolean getMatrix(JNIEnv* env, jobject clazz, jlong pairHandle, jfloat dist,
+                          jlong matrixHandle, jint flags) {
+        PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle);
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        bool result = pair->fMeasure.getMatrix(dist, matrix, (SkPathMeasure::MatrixFlags)flags);
+        return result ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static jboolean getSegment(JNIEnv* env, jobject clazz, jlong pairHandle, jfloat startF,
+                               jfloat stopF, jlong dstHandle, jboolean startWithMoveTo) {
+        PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle);
+        SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
+        bool result = pair->fMeasure.getSegment(startF, stopF, dst, startWithMoveTo);
+        return result ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static jboolean isClosed(JNIEnv* env, jobject clazz, jlong pairHandle) {
+        PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle);
+        bool result = pair->fMeasure.isClosed();
+        return result ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static jboolean nextContour(JNIEnv* env, jobject clazz, jlong pairHandle) {
+        PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle);
+        bool result = pair->fMeasure.nextContour();
+        return result ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static void destroy(JNIEnv* env, jobject clazz, jlong pairHandle) {
+        PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle);
+        delete pair;
+    } 
+};
+
+static const JNINativeMethod methods[] = {
+    {"native_create",       "(JZ)J",        (void*) SkPathMeasureGlue::create      },
+    {"native_setPath",      "(JJZ)V",       (void*) SkPathMeasureGlue::setPath     },
+    {"native_getLength",    "(J)F",         (void*) SkPathMeasureGlue::getLength   },
+    {"native_getPosTan",    "(JF[F[F)Z",    (void*) SkPathMeasureGlue::getPosTan   },
+    {"native_getMatrix",    "(JFJI)Z",      (void*) SkPathMeasureGlue::getMatrix   },
+    {"native_getSegment",   "(JFFJZ)Z",     (void*) SkPathMeasureGlue::getSegment  },
+    {"native_isClosed",     "(J)Z",         (void*) SkPathMeasureGlue::isClosed    },
+    {"native_nextContour",  "(J)Z",         (void*) SkPathMeasureGlue::nextContour },
+    {"native_destroy",      "(J)V",         (void*) SkPathMeasureGlue::destroy     }
+};
+
+int register_android_graphics_PathMeasure(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/PathMeasure", methods, NELEM(methods));
+}
+
+}
diff --git a/libs/hwui/jni/Picture.cpp b/libs/hwui/jni/Picture.cpp
new file mode 100644
index 0000000..d1b9521
--- /dev/null
+++ b/libs/hwui/jni/Picture.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#include "Picture.h"
+#include "SkStream.h"
+
+#include <memory>
+#include <hwui/Canvas.h>
+
+namespace android {
+
+Picture::Picture(const Picture* src) {
+    if (NULL != src) {
+        mWidth = src->width();
+        mHeight = src->height();
+        if (NULL != src->mPicture.get()) {
+            mPicture = src->mPicture;
+        } else if (NULL != src->mRecorder.get()) {
+            mPicture = src->makePartialCopy();
+        }
+    } else {
+        mWidth = 0;
+        mHeight = 0;
+    }
+}
+
+Picture::Picture(sk_sp<SkPicture>&& src) {
+    mPicture = std::move(src);
+    mWidth = 0;
+    mHeight = 0;
+}
+
+Canvas* Picture::beginRecording(int width, int height) {
+    mPicture.reset(NULL);
+    mRecorder.reset(new SkPictureRecorder);
+    mWidth = width;
+    mHeight = height;
+    SkCanvas* canvas = mRecorder->beginRecording(SkIntToScalar(width), SkIntToScalar(height));
+    return Canvas::create_canvas(canvas);
+}
+
+void Picture::endRecording() {
+    if (NULL != mRecorder.get()) {
+        mPicture = mRecorder->finishRecordingAsPicture();
+        mRecorder.reset(NULL);
+    }
+}
+
+int Picture::width() const {
+    return mWidth;
+}
+
+int Picture::height() const {
+    return mHeight;
+}
+
+Picture* Picture::CreateFromStream(SkStream* stream) {
+    Picture* newPict = new Picture;
+
+    sk_sp<SkPicture> skPicture = SkPicture::MakeFromStream(stream);
+    if (NULL != skPicture) {
+        newPict->mPicture = skPicture;
+
+        const SkIRect cullRect = skPicture->cullRect().roundOut();
+        newPict->mWidth = cullRect.width();
+        newPict->mHeight = cullRect.height();
+    }
+
+    return newPict;
+}
+
+void Picture::serialize(SkWStream* stream) const {
+    if (NULL != mRecorder.get()) {
+        this->makePartialCopy()->serialize(stream);
+    } else if (NULL != mPicture.get()) {
+        mPicture->serialize(stream);
+    } else {
+        // serialize "empty" picture
+        SkPictureRecorder recorder;
+        recorder.beginRecording(0, 0);
+        recorder.finishRecordingAsPicture()->serialize(stream);
+    }
+}
+
+void Picture::draw(Canvas* canvas) {
+    if (NULL != mRecorder.get()) {
+        this->endRecording();
+        SkASSERT(NULL != mPicture.get());
+    }
+
+    if (mPicture) {
+        canvas->drawPicture(*mPicture);
+    }
+}
+
+sk_sp<SkPicture> Picture::makePartialCopy() const {
+    SkASSERT(NULL != mRecorder.get());
+
+    SkPictureRecorder reRecorder;
+
+    SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0);
+    mRecorder->partialReplay(canvas);
+    return reRecorder.finishRecordingAsPicture();
+}
+
+}; // namespace android
diff --git a/libs/hwui/jni/Picture.h b/libs/hwui/jni/Picture.h
new file mode 100644
index 0000000..536f651
--- /dev/null
+++ b/libs/hwui/jni/Picture.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_GRAPHICS_PICTURE_H_
+#define ANDROID_GRAPHICS_PICTURE_H_
+
+#include "SkPicture.h"
+#include "SkPictureRecorder.h"
+#include "SkRefCnt.h"
+
+#include <memory>
+
+class SkStream;
+class SkWStream;
+
+namespace android {
+
+class Canvas;
+
+// Skia's SkPicture class has been split into an SkPictureRecorder
+// and an SkPicture. AndroidPicture recreates the functionality
+// of the old SkPicture interface by flip-flopping between the two
+// new classes.
+class Picture {
+public:
+    explicit Picture(const Picture* src = NULL);
+    explicit Picture(sk_sp<SkPicture>&& src);
+
+    Canvas* beginRecording(int width, int height);
+
+    void endRecording();
+
+    int width() const;
+
+    int height() const;
+
+    static Picture* CreateFromStream(SkStream* stream);
+
+    void serialize(SkWStream* stream) const;
+
+    void draw(Canvas* canvas);
+
+private:
+    int mWidth;
+    int mHeight;
+    sk_sp<SkPicture> mPicture;
+    std::unique_ptr<SkPictureRecorder> mRecorder;
+
+    // Make a copy of a picture that is in the midst of being recorded. The
+    // resulting picture will have balanced saves and restores.
+    sk_sp<SkPicture> makePartialCopy() const;
+};
+
+}; // namespace android
+#endif // ANDROID_GRAPHICS_PICTURE_H_
diff --git a/libs/hwui/jni/Region.cpp b/libs/hwui/jni/Region.cpp
new file mode 100644
index 0000000..87662f7
--- /dev/null
+++ b/libs/hwui/jni/Region.cpp
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2011 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 "SkRegion.h"
+#include "SkPath.h"
+#include "GraphicsJNI.h"
+
+#ifdef __ANDROID__ // Layoutlib does not support parcel
+#include <binder/Parcel.h>
+#endif
+#include "android_os_Parcel.h"
+#include "android_util_Binder.h"
+
+#include <jni.h>
+#include <core_jni_helpers.h>
+
+namespace android {
+
+static jfieldID gRegion_nativeInstanceFieldID;
+
+static inline jboolean boolTojboolean(bool value) {
+    return value ? JNI_TRUE : JNI_FALSE;
+}
+
+static inline SkRegion* GetSkRegion(JNIEnv* env, jobject regionObject) {
+    jlong regionHandle = env->GetLongField(regionObject, gRegion_nativeInstanceFieldID);
+    SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
+    SkASSERT(region != NULL);
+    return region;
+}
+
+static jlong Region_constructor(JNIEnv* env, jobject) {
+    return reinterpret_cast<jlong>(new SkRegion);
+}
+
+static void Region_destructor(JNIEnv* env, jobject, jlong regionHandle) {
+    SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
+    SkASSERT(region);
+    delete region;
+}
+
+static void Region_setRegion(JNIEnv* env, jobject, jlong dstHandle, jlong srcHandle) {
+    SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle);
+    const SkRegion* src = reinterpret_cast<SkRegion*>(srcHandle);
+    SkASSERT(dst && src);
+    *dst = *src;
+}
+
+static jboolean Region_setRect(JNIEnv* env, jobject, jlong dstHandle, jint left, jint top, jint right, jint bottom) {
+    SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle);
+    bool result = dst->setRect({left, top, right, bottom});
+    return boolTojboolean(result);
+}
+
+static jboolean Region_setPath(JNIEnv* env, jobject, jlong dstHandle,
+                               jlong pathHandle, jlong clipHandle) {
+    SkRegion*       dst  = reinterpret_cast<SkRegion*>(dstHandle);
+    const SkPath*   path = reinterpret_cast<SkPath*>(pathHandle);
+    const SkRegion* clip = reinterpret_cast<SkRegion*>(clipHandle);
+    SkASSERT(dst && path && clip);
+    bool result = dst->setPath(*path, *clip);
+    return boolTojboolean(result);
+
+}
+
+static jboolean Region_getBounds(JNIEnv* env, jobject, jlong regionHandle, jobject rectBounds) {
+    SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
+    GraphicsJNI::irect_to_jrect(region->getBounds(), env, rectBounds);
+    bool result = !region->isEmpty();
+    return boolTojboolean(result);
+}
+
+static jboolean Region_getBoundaryPath(JNIEnv* env, jobject, jlong regionHandle, jlong pathHandle) {
+    const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
+    SkPath*   path = reinterpret_cast<SkPath*>(pathHandle);
+    bool result = region->getBoundaryPath(path);
+    return boolTojboolean(result);
+}
+
+static jboolean Region_op0(JNIEnv* env, jobject, jlong dstHandle, jint left, jint top, jint right, jint bottom, jint op) {
+    SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle);
+    bool result = dst->op({left, top, right, bottom}, (SkRegion::Op)op);
+    return boolTojboolean(result);
+}
+
+static jboolean Region_op1(JNIEnv* env, jobject, jlong dstHandle, jobject rectObject, jlong regionHandle, jint op) {
+    SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle);
+    const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
+    SkIRect    ir;
+    GraphicsJNI::jrect_to_irect(env, rectObject, &ir);
+    bool result = dst->op(ir, *region, (SkRegion::Op)op);
+    return boolTojboolean(result);
+}
+
+static jboolean Region_op2(JNIEnv* env, jobject, jlong dstHandle, jlong region1Handle, jlong region2Handle, jint op) {
+    SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle);
+    const SkRegion* region1 = reinterpret_cast<SkRegion*>(region1Handle);
+    const SkRegion* region2 = reinterpret_cast<SkRegion*>(region2Handle);
+    bool result = dst->op(*region1, *region2, (SkRegion::Op)op);
+    return boolTojboolean(result);
+}
+
+////////////////////////////////////  These are methods, not static
+
+static jboolean Region_isEmpty(JNIEnv* env, jobject region) {
+    bool result = GetSkRegion(env, region)->isEmpty();
+    return boolTojboolean(result);
+}
+
+static jboolean Region_isRect(JNIEnv* env, jobject region) {
+    bool result = GetSkRegion(env, region)->isRect();
+    return boolTojboolean(result);
+}
+
+static jboolean Region_isComplex(JNIEnv* env, jobject region) {
+    bool result = GetSkRegion(env, region)->isComplex();
+    return boolTojboolean(result);
+}
+
+static jboolean Region_contains(JNIEnv* env, jobject region, jint x, jint y) {
+    bool result = GetSkRegion(env, region)->contains(x, y);
+    return boolTojboolean(result);
+}
+
+static jboolean Region_quickContains(JNIEnv* env, jobject region, jint left, jint top, jint right, jint bottom) {
+    bool result = GetSkRegion(env, region)->quickContains({left, top, right, bottom});
+    return boolTojboolean(result);
+}
+
+static jboolean Region_quickRejectIIII(JNIEnv* env, jobject region, jint left, jint top, jint right, jint bottom) {
+    SkIRect ir;
+    ir.setLTRB(left, top, right, bottom);
+    bool result = GetSkRegion(env, region)->quickReject(ir);
+    return boolTojboolean(result);
+}
+
+static jboolean Region_quickRejectRgn(JNIEnv* env, jobject region, jobject other) {
+    bool result = GetSkRegion(env, region)->quickReject(*GetSkRegion(env, other));
+    return boolTojboolean(result);
+}
+
+static void Region_translate(JNIEnv* env, jobject region, jint x, jint y, jobject dst) {
+    SkRegion* rgn = GetSkRegion(env, region);
+    if (dst)
+        rgn->translate(x, y, GetSkRegion(env, dst));
+    else
+        rgn->translate(x, y);
+}
+
+// Scale the rectangle by given scale and set the reuslt to the dst.
+static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
+   dst->fLeft = (int)::roundf(src.fLeft * scale);
+   dst->fTop = (int)::roundf(src.fTop * scale);
+   dst->fRight = (int)::roundf(src.fRight * scale);
+   dst->fBottom = (int)::roundf(src.fBottom * scale);
+}
+
+// Scale the region by given scale and set the reuslt to the dst.
+// dest and src can be the same region instance.
+static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
+   SkRegion tmp;
+   SkRegion::Iterator iter(src);
+
+   for (; !iter.done(); iter.next()) {
+       SkIRect r;
+       scale_rect(&r, iter.rect(), scale);
+       tmp.op(r, SkRegion::kUnion_Op);
+   }
+   dst->swap(tmp);
+}
+
+static void Region_scale(JNIEnv* env, jobject region, jfloat scale, jobject dst) {
+    SkRegion* rgn = GetSkRegion(env, region);
+    if (dst)
+        scale_rgn(GetSkRegion(env, dst), *rgn, scale);
+    else
+        scale_rgn(rgn, *rgn, scale);
+}
+
+static jstring Region_toString(JNIEnv* env, jobject clazz, jlong regionHandle) {
+    SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
+    char* str = region->toString();
+    if (str == NULL) {
+        return NULL;
+    }
+    jstring result = env->NewStringUTF(str);
+    free(str);
+    return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel)
+{
+#ifdef __ANDROID__ // Layoutlib does not support parcel
+    if (parcel == nullptr) {
+        return 0;
+    }
+
+    android::Parcel* p = android::parcelForJavaObject(env, parcel);
+
+    std::vector<int32_t> rects;
+    p->readInt32Vector(&rects);
+
+    if ((rects.size() % 4) != 0) {
+        return 0;
+    }
+
+    SkRegion* region = new SkRegion;
+    for (size_t x = 0; x + 4 <= rects.size(); x += 4) {
+        region->op({rects[x], rects[x+1], rects[x+2], rects[x+3]}, SkRegion::kUnion_Op);
+    }
+
+    return reinterpret_cast<jlong>(region);
+#else
+    return 0;
+#endif
+}
+
+static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, jlong regionHandle, jobject parcel)
+{
+#ifdef __ANDROID__ // Layoutlib does not support parcel
+    const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
+    if (parcel == nullptr) {
+        return JNI_FALSE;
+    }
+
+    android::Parcel* p = android::parcelForJavaObject(env, parcel);
+
+    std::vector<int32_t> rects;
+    SkRegion::Iterator it(*region);
+    while (!it.done()) {
+        const SkIRect& r = it.rect();
+        rects.push_back(r.fLeft);
+        rects.push_back(r.fTop);
+        rects.push_back(r.fRight);
+        rects.push_back(r.fBottom);
+        it.next();
+    }
+
+    p->writeInt32Vector(rects);
+    return JNI_TRUE;
+#else
+    return JNI_FALSE;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static jboolean Region_equals(JNIEnv* env, jobject clazz, jlong r1Handle, jlong r2Handle)
+{
+    const SkRegion *r1 = reinterpret_cast<SkRegion*>(r1Handle);
+    const SkRegion *r2 = reinterpret_cast<SkRegion*>(r2Handle);
+    return boolTojboolean(*r1 == *r2);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+struct RgnIterPair {
+    SkRegion            fRgn;   // a copy of the caller's region
+    SkRegion::Iterator  fIter;  // an iterator acting upon the copy (fRgn)
+
+    explicit RgnIterPair(const SkRegion& rgn) : fRgn(rgn) {
+        // have our iterator reference our copy (fRgn), so we know it will be
+        // unchanged for the lifetime of the iterator
+        fIter.reset(fRgn);
+    }
+};
+
+static jlong RegionIter_constructor(JNIEnv* env, jobject, jlong regionHandle)
+{
+    const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
+    SkASSERT(region);
+    return reinterpret_cast<jlong>(new RgnIterPair(*region));
+}
+
+static void RegionIter_destructor(JNIEnv* env, jobject, jlong pairHandle)
+{
+    RgnIterPair* pair = reinterpret_cast<RgnIterPair*>(pairHandle);
+    SkASSERT(pair);
+    delete pair;
+}
+
+static jboolean RegionIter_next(JNIEnv* env, jobject, jlong pairHandle, jobject rectObject)
+{
+    RgnIterPair* pair = reinterpret_cast<RgnIterPair*>(pairHandle);
+    // the caller has checked that rectObject is not nul
+    SkASSERT(pair);
+    SkASSERT(rectObject);
+
+    if (!pair->fIter.done()) {
+        GraphicsJNI::irect_to_jrect(pair->fIter.rect(), env, rectObject);
+        pair->fIter.next();
+        return JNI_TRUE;
+    }
+    return JNI_FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gRegionIterMethods[] = {
+    { "nativeConstructor",  "(J)J",                         (void*)RegionIter_constructor   },
+    { "nativeDestructor",   "(J)V",                         (void*)RegionIter_destructor    },
+    { "nativeNext",         "(JLandroid/graphics/Rect;)Z",  (void*)RegionIter_next          }
+};
+
+static const JNINativeMethod gRegionMethods[] = {
+    // these are static methods
+    { "nativeConstructor",      "()J",                              (void*)Region_constructor       },
+    { "nativeDestructor",       "(J)V",                             (void*)Region_destructor        },
+    { "nativeSetRegion",        "(JJ)V",                            (void*)Region_setRegion         },
+    { "nativeSetRect",          "(JIIII)Z",                         (void*)Region_setRect           },
+    { "nativeSetPath",          "(JJJ)Z",                           (void*)Region_setPath           },
+    { "nativeGetBounds",        "(JLandroid/graphics/Rect;)Z",      (void*)Region_getBounds         },
+    { "nativeGetBoundaryPath",  "(JJ)Z",                            (void*)Region_getBoundaryPath   },
+    { "nativeOp",               "(JIIIII)Z",                        (void*)Region_op0               },
+    { "nativeOp",               "(JLandroid/graphics/Rect;JI)Z",    (void*)Region_op1               },
+    { "nativeOp",               "(JJJI)Z",                          (void*)Region_op2               },
+    // these are methods that take the java region object
+    { "isEmpty",                "()Z",                              (void*)Region_isEmpty           },
+    { "isRect",                 "()Z",                              (void*)Region_isRect            },
+    { "isComplex",              "()Z",                              (void*)Region_isComplex         },
+    { "contains",               "(II)Z",                            (void*)Region_contains          },
+    { "quickContains",          "(IIII)Z",                          (void*)Region_quickContains     },
+    { "quickReject",            "(IIII)Z",                          (void*)Region_quickRejectIIII   },
+    { "quickReject",            "(Landroid/graphics/Region;)Z",     (void*)Region_quickRejectRgn    },
+    { "scale",                  "(FLandroid/graphics/Region;)V",    (void*)Region_scale             },
+    { "translate",              "(IILandroid/graphics/Region;)V",   (void*)Region_translate         },
+    { "nativeToString",         "(J)Ljava/lang/String;",            (void*)Region_toString          },
+    // parceling methods
+    { "nativeCreateFromParcel", "(Landroid/os/Parcel;)J",           (void*)Region_createFromParcel  },
+    { "nativeWriteToParcel",    "(JLandroid/os/Parcel;)Z",          (void*)Region_writeToParcel     },
+    { "nativeEquals",           "(JJ)Z",                            (void*)Region_equals            },
+};
+
+int register_android_graphics_Region(JNIEnv* env)
+{
+    jclass clazz = FindClassOrDie(env, "android/graphics/Region");
+
+    gRegion_nativeInstanceFieldID = GetFieldIDOrDie(env, clazz, "mNativeRegion", "J");
+
+    RegisterMethodsOrDie(env, "android/graphics/Region", gRegionMethods, NELEM(gRegionMethods));
+    return RegisterMethodsOrDie(env, "android/graphics/RegionIterator", gRegionIterMethods,
+                                NELEM(gRegionIterMethods));
+}
+
+} // namespace android
diff --git a/libs/hwui/jni/RtlProperties.h b/libs/hwui/jni/RtlProperties.h
new file mode 100644
index 0000000..907dd59
--- /dev/null
+++ b/libs/hwui/jni/RtlProperties.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 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 _ANDROID_GRAPHICS_RTL_PROPERTIES_H_
+#define _ANDROID_GRAPHICS_RTL_PROPERTIES_H_
+
+#include <cutils/properties.h>
+#include <stdlib.h>
+
+namespace android {
+
+/**
+ * Debug level for app developers.
+ */
+#define RTL_PROPERTY_DEBUG "rtl.debug_level"
+
+/**
+ * Debug levels. Debug levels are used as flags.
+ */
+enum RtlDebugLevel {
+    kRtlDebugDisabled = 0,
+    kRtlDebugMemory = 1,
+    kRtlDebugCaches = 2,
+    kRtlDebugAllocations = 3
+};
+
+static RtlDebugLevel readRtlDebugLevel() {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(RTL_PROPERTY_DEBUG, property, NULL) > 0) {
+        return (RtlDebugLevel) atoi(property);
+    }
+    return kRtlDebugDisabled;
+}
+
+} // namespace android
+#endif // _ANDROID_GRAPHICS_RTL_PROPERTIES_H_
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
new file mode 100644
index 0000000..f5e2a52
--- /dev/null
+++ b/libs/hwui/jni/Shader.cpp
@@ -0,0 +1,308 @@
+#include "GraphicsJNI.h"
+#include "SkColorFilter.h"
+#include "SkGradientShader.h"
+#include "SkImagePriv.h"
+#include "SkShader.h"
+#include "SkBlendMode.h"
+#include "core_jni_helpers.h"
+#include "include/effects/SkRuntimeEffect.h"
+
+#include <jni.h>
+
+#include <vector>
+
+using namespace android::uirenderer;
+
+/**
+ * By default Skia gradients will interpolate their colors in unpremul space
+ * and then premultiply each of the results. We must set this flag to preserve
+ * backwards compatiblity by premultiplying the colors of the gradient first,
+ * and then interpolating between them.
+ */
+static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag;
+
+#define ThrowIAE_IfNull(env, ptr)   \
+    if (nullptr == ptr) {           \
+        doThrowIAE(env);            \
+        return 0;                   \
+    }
+
+static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray)
+{
+    SkScalar hsv[3];
+    SkRGBToHSV(red, green, blue, hsv);
+
+    AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
+    float* values = autoHSV.ptr();
+    for (int i = 0; i < 3; i++) {
+        values[i] = SkScalarToFloat(hsv[i]);
+    }
+}
+
+static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
+{
+    AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
+#ifdef SK_SCALAR_IS_FLOAT
+    SkScalar*   hsv = autoHSV.ptr();
+#else
+    #error Need to convert float array to SkScalar array before calling the following function.
+#endif
+
+    return static_cast<jint>(SkHSVToColor(alpha, hsv));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static void Shader_safeUnref(SkShader* shader) {
+    SkSafeUnref(shader);
+}
+
+static jlong Shader_getNativeFinalizer(JNIEnv*, jobject) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
+        jint tileModeX, jint tileModeY) {
+    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+    sk_sp<SkImage> image;
+    if (bitmapHandle) {
+        // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise,
+        // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility.
+        image = android::bitmap::toBitmap(bitmapHandle).makeImage();
+    }
+
+    if (!image.get()) {
+        SkBitmap bitmap;
+        image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+    }
+    sk_sp<SkShader> shader = image->makeShader(
+            (SkTileMode)tileModeX, (SkTileMode)tileModeY);
+    ThrowIAE_IfNull(env, shader.get());
+
+    if (matrix) {
+        shader = shader->makeWithLocalMatrix(*matrix);
+    }
+
+    return reinterpret_cast<jlong>(shader.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static std::vector<SkColor4f> convertColorLongs(JNIEnv* env, jlongArray colorArray) {
+    const size_t count = env->GetArrayLength(colorArray);
+    const jlong* colorValues = env->GetLongArrayElements(colorArray, nullptr);
+
+    std::vector<SkColor4f> colors(count);
+    for (size_t i = 0; i < count; ++i) {
+        colors[i] = GraphicsJNI::convertColorLong(colorValues[i]);
+    }
+
+    env->ReleaseLongArrayElements(colorArray, const_cast<jlong*>(colorValues), JNI_ABORT);
+    return colors;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr,
+        jfloat x0, jfloat y0, jfloat x1, jfloat y1, jlongArray colorArray,
+        jfloatArray posArray, jint tileMode, jlong colorSpaceHandle) {
+    SkPoint pts[2];
+    pts[0].set(x0, y0);
+    pts[1].set(x1, y1);
+
+    std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
+
+    AutoJavaFloatArray autoPos(env, posArray, colors.size());
+#ifdef SK_SCALAR_IS_FLOAT
+    SkScalar* pos = autoPos.ptr();
+#else
+    #error Need to convert float array to SkScalar array before calling the following function.
+#endif
+
+    sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0],
+                GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
+                static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr));
+    ThrowIAE_IfNull(env, shader);
+
+    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+    if (matrix) {
+        shader = shader->makeWithLocalMatrix(*matrix);
+    }
+
+    return reinterpret_cast<jlong>(shader.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
+        jfloat radius, jlongArray colorArray, jfloatArray posArray, jint tileMode,
+        jlong colorSpaceHandle) {
+    SkPoint center;
+    center.set(x, y);
+
+    std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
+
+    AutoJavaFloatArray autoPos(env, posArray, colors.size());
+#ifdef SK_SCALAR_IS_FLOAT
+    SkScalar* pos = autoPos.ptr();
+#else
+    #error Need to convert float array to SkScalar array before calling the following function.
+#endif
+
+    sk_sp<SkShader> shader = SkGradientShader::MakeRadial(center, radius, &colors[0],
+            GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
+            static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr);
+    ThrowIAE_IfNull(env, shader);
+
+    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+    if (matrix) {
+        shader = shader->makeWithLocalMatrix(*matrix);
+    }
+
+    return reinterpret_cast<jlong>(shader.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
+        jlongArray colorArray, jfloatArray jpositions, jlong colorSpaceHandle) {
+    std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
+
+    AutoJavaFloatArray autoPos(env, jpositions, colors.size());
+#ifdef SK_SCALAR_IS_FLOAT
+    SkScalar* pos = autoPos.ptr();
+#else
+    #error Need to convert float array to SkScalar array before calling the following function.
+#endif
+
+    sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0],
+            GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
+            sGradientShaderFlags, nullptr);
+    ThrowIAE_IfNull(env, shader);
+
+    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+    if (matrix) {
+        shader = shader->makeWithLocalMatrix(*matrix);
+    }
+
+    return reinterpret_cast<jlong>(shader.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr,
+        jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) {
+    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+    SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
+    SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
+    SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
+    sk_sp<SkShader> baseShader(SkShaders::Blend(mode,
+            sk_ref_sp(shaderA), sk_ref_sp(shaderB)));
+
+    SkShader* shader;
+
+    if (matrix) {
+        shader = baseShader->makeWithLocalMatrix(*matrix).release();
+    } else {
+        shader = baseShader.release();
+    }
+    return reinterpret_cast<jlong>(shader);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr,
+        jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) {
+    SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory);
+    AutoJavaByteArray arInputs(env, inputs);
+
+    sk_sp<SkData> fData;
+    fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length());
+    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+    sk_sp<SkShader> shader = effect->makeShader(fData, nullptr, 0, matrix, isOpaque == JNI_TRUE);
+    ThrowIAE_IfNull(env, shader);
+
+    return reinterpret_cast<jlong>(shader.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl) {
+    ScopedUtfChars strSksl(env, sksl);
+    sk_sp<SkRuntimeEffect> effect = std::get<0>(SkRuntimeEffect::Make(SkString(strSksl.c_str())));
+    ThrowIAE_IfNull(env, effect);
+
+    return reinterpret_cast<jlong>(effect.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static void Effect_safeUnref(SkRuntimeEffect* effect) {
+    SkSafeUnref(effect);
+}
+
+static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Effect_safeUnref));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gColorMethods[] = {
+    { "nativeRGBToHSV",    "(III[F)V", (void*)Color_RGBToHSV   },
+    { "nativeHSVToColor",  "(I[F)I",   (void*)Color_HSVToColor }
+};
+
+static const JNINativeMethod gShaderMethods[] = {
+    { "nativeGetFinalizer",   "()J",    (void*)Shader_getNativeFinalizer },
+};
+
+static const JNINativeMethod gBitmapShaderMethods[] = {
+    { "nativeCreate",      "(JJII)J",  (void*)BitmapShader_constructor },
+};
+
+static const JNINativeMethod gLinearGradientMethods[] = {
+    { "nativeCreate",     "(JFFFF[J[FIJ)J",  (void*)LinearGradient_create     },
+};
+
+static const JNINativeMethod gRadialGradientMethods[] = {
+    { "nativeCreate",     "(JFFF[J[FIJ)J",  (void*)RadialGradient_create     },
+};
+
+static const JNINativeMethod gSweepGradientMethods[] = {
+    { "nativeCreate",     "(JFF[J[FJ)J",  (void*)SweepGradient_create     },
+};
+
+static const JNINativeMethod gComposeShaderMethods[] = {
+    { "nativeCreate",      "(JJJI)J",   (void*)ComposeShader_create     },
+};
+
+static const JNINativeMethod gRuntimeShaderMethods[] = {
+    { "nativeGetFinalizer",   "()J",    (void*)RuntimeShader_getNativeFinalizer },
+    { "nativeCreate",     "(JJ[BJZ)J",  (void*)RuntimeShader_create     },
+    { "nativeCreateShaderFactory",     "(Ljava/lang/String;)J",
+      (void*)RuntimeShader_createShaderFactory     },
+};
+
+int register_android_graphics_Shader(JNIEnv* env)
+{
+    android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
+                                  NELEM(gColorMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
+                                  NELEM(gShaderMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
+                                  NELEM(gBitmapShaderMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
+                                  NELEM(gLinearGradientMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
+                                  NELEM(gRadialGradientMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods,
+                                  NELEM(gSweepGradientMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
+                                  NELEM(gComposeShaderMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/RuntimeShader", gRuntimeShaderMethods,
+                                  NELEM(gRuntimeShaderMethods));
+
+    return 0;
+}
diff --git a/libs/hwui/jni/TEST_MAPPING b/libs/hwui/jni/TEST_MAPPING
new file mode 100644
index 0000000..10bd0ee
--- /dev/null
+++ b/libs/hwui/jni/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsGraphicsTestCases"
+    }
+  ]
+}
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
new file mode 100644
index 0000000..4ce56ba
--- /dev/null
+++ b/libs/hwui/jni/Typeface.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2013 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 "jni.h"
+#include "core_jni_helpers.h"
+
+#include "FontUtils.h"
+#include "GraphicsJNI.h"
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include "SkTypeface.h"
+#include <hwui/Typeface.h>
+#include <minikin/FontFamily.h>
+#include <minikin/SystemFonts.h>
+
+using namespace android;
+
+static inline Typeface* toTypeface(jlong ptr) {
+    return reinterpret_cast<Typeface*>(ptr);
+}
+
+template<typename Ptr> static inline jlong toJLong(Ptr ptr) {
+    return reinterpret_cast<jlong>(ptr);
+}
+
+static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandle, jint style) {
+    Typeface* family = toTypeface(familyHandle);
+    Typeface* face = Typeface::createRelative(family, (Typeface::Style)style);
+    // TODO: the following logic shouldn't be necessary, the above should always succeed.
+    // Try to find the closest matching font, using the standard heuristic
+    if (NULL == face) {
+        face = Typeface::createRelative(family, (Typeface::Style)(style ^ Typeface::kItalic));
+    }
+    for (int i = 0; NULL == face && i < 4; i++) {
+        face = Typeface::createRelative(family, (Typeface::Style)i);
+    }
+    return toJLong(face);
+}
+
+static jlong Typeface_createFromTypefaceWithExactStyle(JNIEnv* env, jobject, jlong nativeInstance,
+        jint weight, jboolean italic) {
+    return toJLong(Typeface::createAbsolute(toTypeface(nativeInstance), weight, italic));
+}
+
+static jlong Typeface_createFromTypefaceWithVariation(JNIEnv* env, jobject, jlong familyHandle,
+        jobject listOfAxis) {
+    std::vector<minikin::FontVariation> variations;
+    ListHelper list(env, listOfAxis);
+    for (jint i = 0; i < list.size(); i++) {
+        jobject axisObject = list.get(i);
+        if (axisObject == nullptr) {
+            continue;
+        }
+        AxisHelper axis(env, axisObject);
+        variations.push_back(minikin::FontVariation(axis.getTag(), axis.getStyleValue()));
+    }
+    return toJLong(Typeface::createFromTypefaceWithVariation(toTypeface(familyHandle), variations));
+}
+
+static jlong Typeface_createWeightAlias(JNIEnv* env, jobject, jlong familyHandle, jint weight) {
+    return toJLong(Typeface::createWithDifferentBaseWeight(toTypeface(familyHandle), weight));
+}
+
+static void releaseFunc(jlong ptr) {
+    delete toTypeface(ptr);
+}
+
+// CriticalNative
+static jlong Typeface_getReleaseFunc(CRITICAL_JNI_PARAMS) {
+    return toJLong(&releaseFunc);
+}
+
+// CriticalNative
+static jint Typeface_getStyle(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
+    return toTypeface(faceHandle)->fAPIStyle;
+}
+
+// CriticalNative
+static jint Typeface_getWeight(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
+    return toTypeface(faceHandle)->fStyle.weight();
+}
+
+static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray,
+        int weight, int italic) {
+    ScopedLongArrayRO families(env, familyArray);
+    std::vector<std::shared_ptr<minikin::FontFamily>> familyVec;
+    familyVec.reserve(families.size());
+    for (size_t i = 0; i < families.size(); i++) {
+        FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]);
+        familyVec.emplace_back(family->family);
+    }
+    return toJLong(Typeface::createFromFamilies(std::move(familyVec), weight, italic));
+}
+
+// CriticalNative
+static void Typeface_setDefault(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
+    Typeface::setDefault(toTypeface(faceHandle));
+    minikin::SystemFonts::registerDefault(toTypeface(faceHandle)->fFontCollection);
+}
+
+static jobject Typeface_getSupportedAxes(JNIEnv *env, jobject, jlong faceHandle) {
+    Typeface* face = toTypeface(faceHandle);
+    const std::unordered_set<minikin::AxisTag>& tagSet = face->fFontCollection->getSupportedTags();
+    const size_t length = tagSet.size();
+    if (length == 0) {
+        return nullptr;
+    }
+    std::vector<jint> tagVec(length);
+    int index = 0;
+    for (const auto& tag : tagSet) {
+        tagVec[index++] = tag;
+    }
+    std::sort(tagVec.begin(), tagVec.end());
+    const jintArray result = env->NewIntArray(length);
+    env->SetIntArrayRegion(result, 0, length, tagVec.data());
+    return result;
+}
+
+static void Typeface_registerGenericFamily(JNIEnv *env, jobject, jstring familyName, jlong ptr) {
+    ScopedUtfChars familyNameChars(env, familyName);
+    minikin::SystemFonts::registerFallback(familyNameChars.c_str(),
+                                           toTypeface(ptr)->fFontCollection);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gTypefaceMethods[] = {
+    { "nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface },
+    { "nativeCreateFromTypefaceWithExactStyle", "(JIZ)J",
+            (void*)Typeface_createFromTypefaceWithExactStyle },
+    { "nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J",
+            (void*)Typeface_createFromTypefaceWithVariation },
+    { "nativeCreateWeightAlias",  "(JI)J", (void*)Typeface_createWeightAlias },
+    { "nativeGetReleaseFunc",     "()J",  (void*)Typeface_getReleaseFunc },
+    { "nativeGetStyle",           "(J)I",  (void*)Typeface_getStyle },
+    { "nativeGetWeight",      "(J)I",  (void*)Typeface_getWeight },
+    { "nativeCreateFromArray",    "([JII)J",
+                                           (void*)Typeface_createFromArray },
+    { "nativeSetDefault",         "(J)V",   (void*)Typeface_setDefault },
+    { "nativeGetSupportedAxes",   "(J)[I",  (void*)Typeface_getSupportedAxes },
+    { "nativeRegisterGenericFamily", "(Ljava/lang/String;J)V",
+          (void*)Typeface_registerGenericFamily },
+};
+
+int register_android_graphics_Typeface(JNIEnv* env)
+{
+    return RegisterMethodsOrDie(env, "android/graphics/Typeface", gTypefaceMethods,
+                                NELEM(gTypefaceMethods));
+}
diff --git a/libs/hwui/jni/Utils.cpp b/libs/hwui/jni/Utils.cpp
new file mode 100644
index 0000000..17c194d
--- /dev/null
+++ b/libs/hwui/jni/Utils.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#include "Utils.h"
+#include "SkUtils.h"
+#include "SkData.h"
+
+#include <log/log.h>
+
+using namespace android;
+
+AssetStreamAdaptor::AssetStreamAdaptor(Asset* asset)
+    : fAsset(asset)
+{
+}
+
+bool AssetStreamAdaptor::rewind() {
+    off64_t pos = fAsset->seek(0, SEEK_SET);
+    if (pos == (off64_t)-1) {
+        SkDebugf("----- fAsset->seek(rewind) failed\n");
+        return false;
+    }
+    return true;
+}
+
+size_t AssetStreamAdaptor::getLength() const {
+    return fAsset->getLength();
+}
+
+bool AssetStreamAdaptor::isAtEnd() const {
+    return fAsset->getRemainingLength() == 0;
+}
+
+SkStreamRewindable* AssetStreamAdaptor::onDuplicate() const {
+    // Cannot create a duplicate, since each AssetStreamAdaptor
+    // would be modifying the Asset.
+    //return new AssetStreamAdaptor(fAsset);
+    return NULL;
+}
+
+bool AssetStreamAdaptor::hasPosition() const {
+    return fAsset->seek(0, SEEK_CUR) != -1;
+}
+
+size_t AssetStreamAdaptor::getPosition() const {
+    const off64_t offset = fAsset->seek(0, SEEK_CUR);
+    if (offset == -1) {
+        SkDebugf("---- fAsset->seek(0, SEEK_CUR) failed\n");
+        return 0;
+    }
+
+    return offset;
+}
+
+bool AssetStreamAdaptor::seek(size_t position) {
+    if (fAsset->seek(position, SEEK_SET) == -1) {
+        SkDebugf("---- fAsset->seek(0, SEEK_SET) failed\n");
+        return false;
+    }
+
+    return true;
+}
+
+bool AssetStreamAdaptor::move(long offset) {
+    if (fAsset->seek(offset, SEEK_CUR) == -1) {
+        SkDebugf("---- fAsset->seek(%i, SEEK_CUR) failed\n", offset);
+        return false;
+    }
+
+    return true;
+}
+
+size_t AssetStreamAdaptor::read(void* buffer, size_t size) {
+    ssize_t amount;
+
+    if (NULL == buffer) {
+        if (0 == size) {
+            return 0;
+        }
+        // asset->seek returns new total offset
+        // we want to return amount that was skipped
+
+        off64_t oldOffset = fAsset->seek(0, SEEK_CUR);
+        if (-1 == oldOffset) {
+            SkDebugf("---- fAsset->seek(oldOffset) failed\n");
+            return 0;
+        }
+        off64_t newOffset = fAsset->seek(size, SEEK_CUR);
+        if (-1 == newOffset) {
+            SkDebugf("---- fAsset->seek(%d) failed\n", size);
+            return 0;
+        }
+        amount = newOffset - oldOffset;
+    } else {
+        amount = fAsset->read(buffer, size);
+    }
+
+    if (amount < 0) {
+        amount = 0;
+    }
+    return amount;
+}
+
+SkMemoryStream* android::CopyAssetToStream(Asset* asset) {
+    if (NULL == asset) {
+        return NULL;
+    }
+
+    const off64_t seekReturnVal = asset->seek(0, SEEK_SET);
+    if ((off64_t)-1 == seekReturnVal) {
+        SkDebugf("---- copyAsset: asset rewind failed\n");
+        return NULL;
+    }
+
+    const off64_t size = asset->getLength();
+    if (size <= 0) {
+        SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
+        return NULL;
+    }
+
+    sk_sp<SkData> data(SkData::MakeUninitialized(size));
+    const off64_t len = asset->read(data->writable_data(), size);
+    if (len != size) {
+        SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
+        return NULL;
+    }
+
+    return new SkMemoryStream(std::move(data));
+}
+
+jobject android::nullObjectReturn(const char msg[]) {
+    if (msg) {
+        SkDebugf("--- %s\n", msg);
+    }
+    return NULL;
+}
+
+bool android::isSeekable(int descriptor) {
+    return ::lseek64(descriptor, 0, SEEK_CUR) != -1;
+}
+
+JNIEnv* android::get_env_or_die(JavaVM* jvm) {
+    JNIEnv* env;
+    if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", jvm);
+    }
+    return env;
+}
diff --git a/libs/hwui/jni/Utils.h b/libs/hwui/jni/Utils.h
new file mode 100644
index 0000000..8925517
--- /dev/null
+++ b/libs/hwui/jni/Utils.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2006 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 _ANDROID_GRAPHICS_UTILS_H_
+#define _ANDROID_GRAPHICS_UTILS_H_
+
+#include "SkStream.h"
+
+#include <jni.h>
+#include <androidfw/Asset.h>
+
+namespace android {
+
+class AssetStreamAdaptor : public SkStreamRewindable {
+public:
+    explicit AssetStreamAdaptor(Asset*);
+
+    virtual bool rewind();
+    virtual size_t read(void* buffer, size_t size);
+    virtual bool hasLength() const { return true; }
+    virtual size_t getLength() const;
+    virtual bool hasPosition() const;
+    virtual size_t getPosition() const;
+    virtual bool seek(size_t position);
+    virtual bool move(long offset);
+    virtual bool isAtEnd() const;
+
+protected:
+    SkStreamRewindable* onDuplicate() const override;
+
+private:
+    Asset* fAsset;
+};
+
+/**
+ *  Make a deep copy of the asset, and return it as a stream, or NULL if there
+ *  was an error.
+ *  FIXME: If we could "ref/reopen" the asset, we may not need to copy it here.
+ */
+
+SkMemoryStream* CopyAssetToStream(Asset*);
+
+/** Restore the file descriptor's offset in our destructor
+ */
+class AutoFDSeek {
+public:
+    explicit AutoFDSeek(int fd) : fFD(fd) {
+        fCurr = ::lseek(fd, 0, SEEK_CUR);
+    }
+    ~AutoFDSeek() {
+        if (fCurr >= 0) {
+            ::lseek(fFD, fCurr, SEEK_SET);
+        }
+    }
+private:
+    int     fFD;
+    off64_t   fCurr;
+};
+
+jobject nullObjectReturn(const char msg[]);
+
+/** Check if the file descriptor is seekable.
+ */
+bool isSeekable(int descriptor);
+
+JNIEnv* get_env_or_die(JavaVM* jvm);
+
+}; // namespace android
+
+#endif  // _ANDROID_GRAPHICS_UTILS_H_
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
new file mode 100644
index 0000000..09adc82
--- /dev/null
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -0,0 +1,270 @@
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "SkJPEGWriteUtility.h"
+#include "YuvToJpegEncoder.h"
+#include <ui/PixelFormat.h>
+#include <hardware/hardware.h>
+
+#include "core_jni_helpers.h"
+
+#include <jni.h>
+
+YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) {
+    // Only ImageFormat.NV21 and ImageFormat.YUY2 are supported
+    // for now.
+    if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
+        return new Yuv420SpToJpegEncoder(strides);
+    } else if (format == HAL_PIXEL_FORMAT_YCbCr_422_I) {
+        return new Yuv422IToJpegEncoder(strides);
+    } else {
+      return NULL;
+    }
+}
+
+YuvToJpegEncoder::YuvToJpegEncoder(int* strides) : fStrides(strides) {
+}
+
+struct ErrorMgr {
+    struct jpeg_error_mgr pub;
+    jmp_buf jmp;
+};
+
+void error_exit(j_common_ptr cinfo) {
+    ErrorMgr* err = (ErrorMgr*) cinfo->err;
+    (*cinfo->err->output_message) (cinfo);
+    longjmp(err->jmp, 1);
+}
+
+bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width,
+        int height, int* offsets, int jpegQuality) {
+    jpeg_compress_struct    cinfo;
+    ErrorMgr                err;
+    skjpeg_destination_mgr  sk_wstream(stream);
+
+    cinfo.err = jpeg_std_error(&err.pub);
+    err.pub.error_exit = error_exit;
+
+    if (setjmp(err.jmp)) {
+        jpeg_destroy_compress(&cinfo);
+        return false;
+    }
+    jpeg_create_compress(&cinfo);
+
+    cinfo.dest = &sk_wstream;
+
+    setJpegCompressStruct(&cinfo, width, height, jpegQuality);
+
+    jpeg_start_compress(&cinfo, TRUE);
+
+    compress(&cinfo, (uint8_t*) inYuv, offsets);
+
+    jpeg_finish_compress(&cinfo);
+
+    jpeg_destroy_compress(&cinfo);
+
+    return true;
+}
+
+void YuvToJpegEncoder::setJpegCompressStruct(jpeg_compress_struct* cinfo,
+        int width, int height, int quality) {
+    cinfo->image_width = width;
+    cinfo->image_height = height;
+    cinfo->input_components = 3;
+    cinfo->in_color_space = JCS_YCbCr;
+    jpeg_set_defaults(cinfo);
+
+    jpeg_set_quality(cinfo, quality, TRUE);
+    jpeg_set_colorspace(cinfo, JCS_YCbCr);
+    cinfo->raw_data_in = TRUE;
+    cinfo->dct_method = JDCT_IFAST;
+    configSamplingFactors(cinfo);
+}
+
+///////////////////////////////////////////////////////////////////
+Yuv420SpToJpegEncoder::Yuv420SpToJpegEncoder(int* strides) :
+        YuvToJpegEncoder(strides) {
+    fNumPlanes = 2;
+}
+
+void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo,
+        uint8_t* yuv, int* offsets) {
+    SkDebugf("onFlyCompress");
+    JSAMPROW y[16];
+    JSAMPROW cb[8];
+    JSAMPROW cr[8];
+    JSAMPARRAY planes[3];
+    planes[0] = y;
+    planes[1] = cb;
+    planes[2] = cr;
+
+    int width = cinfo->image_width;
+    int height = cinfo->image_height;
+    uint8_t* yPlanar = yuv + offsets[0];
+    uint8_t* vuPlanar = yuv + offsets[1]; //width * height;
+    uint8_t* uRows = new uint8_t [8 * (width >> 1)];
+    uint8_t* vRows = new uint8_t [8 * (width >> 1)];
+
+
+    // process 16 lines of Y and 8 lines of U/V each time.
+    while (cinfo->next_scanline < cinfo->image_height) {
+        //deitnerleave u and v
+        deinterleave(vuPlanar, uRows, vRows, cinfo->next_scanline, width, height);
+
+        // Jpeg library ignores the rows whose indices are greater than height.
+        for (int i = 0; i < 16; i++) {
+            // y row
+            y[i] = yPlanar + (cinfo->next_scanline + i) * fStrides[0];
+
+            // construct u row and v row
+            if ((i & 1) == 0) {
+                // height and width are both halved because of downsampling
+                int offset = (i >> 1) * (width >> 1);
+                cb[i/2] = uRows + offset;
+                cr[i/2] = vRows + offset;
+            }
+          }
+        jpeg_write_raw_data(cinfo, planes, 16);
+    }
+    delete [] uRows;
+    delete [] vRows;
+
+}
+
+void Yuv420SpToJpegEncoder::deinterleave(uint8_t* vuPlanar, uint8_t* uRows,
+        uint8_t* vRows, int rowIndex, int width, int height) {
+    int numRows = (height - rowIndex) / 2;
+    if (numRows > 8) numRows = 8;
+    for (int row = 0; row < numRows; ++row) {
+        int offset = ((rowIndex >> 1) + row) * fStrides[1];
+        uint8_t* vu = vuPlanar + offset;
+        for (int i = 0; i < (width >> 1); ++i) {
+            int index = row * (width >> 1) + i;
+            uRows[index] = vu[1];
+            vRows[index] = vu[0];
+            vu += 2;
+        }
+    }
+}
+
+void Yuv420SpToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) {
+    // cb and cr are horizontally downsampled and vertically downsampled as well.
+    cinfo->comp_info[0].h_samp_factor = 2;
+    cinfo->comp_info[0].v_samp_factor = 2;
+    cinfo->comp_info[1].h_samp_factor = 1;
+    cinfo->comp_info[1].v_samp_factor = 1;
+    cinfo->comp_info[2].h_samp_factor = 1;
+    cinfo->comp_info[2].v_samp_factor = 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+Yuv422IToJpegEncoder::Yuv422IToJpegEncoder(int* strides) :
+        YuvToJpegEncoder(strides) {
+    fNumPlanes = 1;
+}
+
+void Yuv422IToJpegEncoder::compress(jpeg_compress_struct* cinfo,
+        uint8_t* yuv, int* offsets) {
+    SkDebugf("onFlyCompress_422");
+    JSAMPROW y[16];
+    JSAMPROW cb[16];
+    JSAMPROW cr[16];
+    JSAMPARRAY planes[3];
+    planes[0] = y;
+    planes[1] = cb;
+    planes[2] = cr;
+
+    int width = cinfo->image_width;
+    int height = cinfo->image_height;
+    uint8_t* yRows = new uint8_t [16 * width];
+    uint8_t* uRows = new uint8_t [16 * (width >> 1)];
+    uint8_t* vRows = new uint8_t [16 * (width >> 1)];
+
+    uint8_t* yuvOffset = yuv + offsets[0];
+
+    // process 16 lines of Y and 16 lines of U/V each time.
+    while (cinfo->next_scanline < cinfo->image_height) {
+        deinterleave(yuvOffset, yRows, uRows, vRows, cinfo->next_scanline, width, height);
+
+        // Jpeg library ignores the rows whose indices are greater than height.
+        for (int i = 0; i < 16; i++) {
+            // y row
+            y[i] = yRows + i * width;
+
+            // construct u row and v row
+            // width is halved because of downsampling
+            int offset = i * (width >> 1);
+            cb[i] = uRows + offset;
+            cr[i] = vRows + offset;
+        }
+
+        jpeg_write_raw_data(cinfo, planes, 16);
+    }
+    delete [] yRows;
+    delete [] uRows;
+    delete [] vRows;
+}
+
+
+void Yuv422IToJpegEncoder::deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows,
+        uint8_t* vRows, int rowIndex, int width, int height) {
+    int numRows = height - rowIndex;
+    if (numRows > 16) numRows = 16;
+    for (int row = 0; row < numRows; ++row) {
+        uint8_t* yuvSeg = yuv + (rowIndex + row) * fStrides[0];
+        for (int i = 0; i < (width >> 1); ++i) {
+            int indexY = row * width + (i << 1);
+            int indexU = row * (width >> 1) + i;
+            yRows[indexY] = yuvSeg[0];
+            yRows[indexY + 1] = yuvSeg[2];
+            uRows[indexU] = yuvSeg[1];
+            vRows[indexU] = yuvSeg[3];
+            yuvSeg += 4;
+        }
+    }
+}
+
+void Yuv422IToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) {
+    // cb and cr are horizontally downsampled and vertically downsampled as well.
+    cinfo->comp_info[0].h_samp_factor = 2;
+    cinfo->comp_info[0].v_samp_factor = 2;
+    cinfo->comp_info[1].h_samp_factor = 1;
+    cinfo->comp_info[1].v_samp_factor = 2;
+    cinfo->comp_info[2].h_samp_factor = 1;
+    cinfo->comp_info[2].v_samp_factor = 2;
+}
+///////////////////////////////////////////////////////////////////////////////
+
+static jboolean YuvImage_compressToJpeg(JNIEnv* env, jobject, jbyteArray inYuv,
+        jint format, jint width, jint height, jintArray offsets,
+        jintArray strides, jint jpegQuality, jobject jstream,
+        jbyteArray jstorage) {
+    jbyte* yuv = env->GetByteArrayElements(inYuv, NULL);
+    SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
+
+    jint* imgOffsets = env->GetIntArrayElements(offsets, NULL);
+    jint* imgStrides = env->GetIntArrayElements(strides, NULL);
+    YuvToJpegEncoder* encoder = YuvToJpegEncoder::create(format, imgStrides);
+    jboolean result = JNI_FALSE;
+    if (encoder != NULL) {
+        encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality);
+        delete encoder;
+        result = JNI_TRUE;
+    }
+
+    env->ReleaseByteArrayElements(inYuv, yuv, 0);
+    env->ReleaseIntArrayElements(offsets, imgOffsets, 0);
+    env->ReleaseIntArrayElements(strides, imgStrides, 0);
+    delete strm;
+    return result;
+}
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gYuvImageMethods[] = {
+    {   "nativeCompressToJpeg",  "([BIII[I[IILjava/io/OutputStream;[B)Z",
+        (void*)YuvImage_compressToJpeg }
+};
+
+int register_android_graphics_YuvImage(JNIEnv* env)
+{
+    return android::RegisterMethodsOrDie(env, "android/graphics/YuvImage", gYuvImageMethods,
+                                         NELEM(gYuvImageMethods));
+}
diff --git a/libs/hwui/jni/YuvToJpegEncoder.h b/libs/hwui/jni/YuvToJpegEncoder.h
new file mode 100644
index 0000000..7e7b935
--- /dev/null
+++ b/libs/hwui/jni/YuvToJpegEncoder.h
@@ -0,0 +1,74 @@
+#ifndef _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_
+#define _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_
+
+#include "SkTypes.h"
+#include "SkStream.h"
+extern "C" {
+    #include "jpeglib.h"
+    #include "jerror.h"
+}
+
+class YuvToJpegEncoder {
+public:
+    /** Create an encoder based on the YUV format.
+     *
+     *  @param pixelFormat The yuv pixel format as defined in ui/PixelFormat.h.
+     *  @param strides The number of row bytes in each image plane.
+     *  @return an encoder based on the pixelFormat.
+     */
+    static YuvToJpegEncoder* create(int pixelFormat, int* strides);
+
+    explicit YuvToJpegEncoder(int* strides);
+
+    /** Encode YUV data to jpeg,  which is output to a stream.
+     *
+     *  @param stream The jpeg output stream.
+     *  @param inYuv The input yuv data.
+     *  @param width Width of the the Yuv data in terms of pixels.
+     *  @param height Height of the Yuv data in terms of pixels.
+     *  @param offsets The offsets in each image plane with respect to inYuv.
+     *  @param jpegQuality Picture quality in [0, 100].
+     *  @return true if successfully compressed the stream.
+     */
+    bool encode(SkWStream* stream,  void* inYuv, int width,
+           int height, int* offsets, int jpegQuality);
+
+    virtual ~YuvToJpegEncoder() {}
+
+protected:
+    int fNumPlanes;
+    int* fStrides;
+    void setJpegCompressStruct(jpeg_compress_struct* cinfo, int width,
+            int height, int quality);
+    virtual void configSamplingFactors(jpeg_compress_struct* cinfo) = 0;
+    virtual void compress(jpeg_compress_struct* cinfo,
+            uint8_t* yuv, int* offsets) = 0;
+};
+
+class Yuv420SpToJpegEncoder : public YuvToJpegEncoder {
+public:
+    explicit Yuv420SpToJpegEncoder(int* strides);
+    virtual ~Yuv420SpToJpegEncoder() {}
+
+private:
+    void configSamplingFactors(jpeg_compress_struct* cinfo);
+    void deinterleaveYuv(uint8_t* yuv, int width, int height,
+            uint8_t*& yPlanar, uint8_t*& uPlanar, uint8_t*& vPlanar);
+    void deinterleave(uint8_t* vuPlanar, uint8_t* uRows, uint8_t* vRows,
+            int rowIndex, int width, int height);
+    void compress(jpeg_compress_struct* cinfo, uint8_t* yuv, int* offsets);
+};
+
+class Yuv422IToJpegEncoder : public YuvToJpegEncoder {
+public:
+    explicit Yuv422IToJpegEncoder(int* strides);
+    virtual ~Yuv422IToJpegEncoder() {}
+
+private:
+    void configSamplingFactors(jpeg_compress_struct* cinfo);
+    void compress(jpeg_compress_struct* cinfo, uint8_t* yuv, int* offsets);
+    void deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows,
+            uint8_t* vRows, int rowIndex, int width, int height);
+};
+
+#endif  // _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
new file mode 100644
index 0000000..0ad3339
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -0,0 +1,741 @@
+/*
+ * 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.
+ */
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
+
+#ifdef __ANDROID_
+#include <android/api-level.h>
+#else
+#define __ANDROID_API_P__ 28
+#endif
+#include <androidfw/ResourceTypes.h>
+#include <hwui/Canvas.h>
+#include <hwui/Paint.h>
+#include <hwui/PaintFilter.h>
+#include <hwui/Typeface.h>
+#include <minikin/Layout.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedStringChars.h>
+
+#include "Bitmap.h"
+#include "SkGraphics.h"
+#include "SkRegion.h"
+#include "SkVertices.h"
+
+namespace minikin {
+class MeasuredText;
+}  // namespace minikin
+
+namespace android {
+
+namespace CanvasJNI {
+
+static Canvas* get_canvas(jlong canvasHandle) {
+    return reinterpret_cast<Canvas*>(canvasHandle);
+}
+
+static void delete_canvas(Canvas* canvas) {
+    delete canvas;
+}
+
+static jlong getNativeFinalizer(JNIEnv* env, jobject clazz) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&delete_canvas));
+}
+
+// Native wrapper constructor used by Canvas(Bitmap)
+static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap bitmap;
+    if (bitmapHandle != 0) {
+        bitmap::toBitmap(bitmapHandle).getSkBitmap(&bitmap);
+    }
+    return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
+}
+
+// Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
+// optionally copying canvas matrix & clip state.
+static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle) {
+    SkBitmap bitmap;
+    if (bitmapHandle != 0) {
+        bitmap::toBitmap(bitmapHandle).getSkBitmap(&bitmap);
+    }
+    get_canvas(canvasHandle)->setBitmap(bitmap);
+}
+
+static jboolean isOpaque(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
+    return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
+}
+
+static jint getWidth(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
+    return static_cast<jint>(get_canvas(canvasHandle)->width());
+}
+
+static jint getHeight(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
+    return static_cast<jint>(get_canvas(canvasHandle)->height());
+}
+
+static jint save(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint flagsHandle) {
+    SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
+    return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
+}
+
+static jint saveLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
+                      jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
+    Paint* paint  = reinterpret_cast<Paint*>(paintHandle);
+    SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
+    return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
+}
+
+static jint saveLayerAlpha(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
+                           jfloat r, jfloat b, jint alpha, jint flagsHandle) {
+    SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
+    return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
+}
+
+static jint saveUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint l, jint t, jint r, jint b) {
+    return reinterpret_cast<jint>(get_canvas(canvasHandle)->saveUnclippedLayer(l, t, r, b));
+}
+
+static void restoreUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint saveCount, jlong paintHandle) {
+    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->restoreUnclippedLayer(saveCount, *paint);
+}
+
+static bool restore(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
+    Canvas* canvas = get_canvas(canvasHandle);
+    if (canvas->getSaveCount() <= 1) {
+        return false; // cannot restore anymore
+    }
+    canvas->restore();
+    return true; // success
+}
+
+static void restoreToCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint saveCount) {
+    Canvas* canvas = get_canvas(canvasHandle);
+    canvas->restoreToCount(saveCount);
+}
+
+static jint getSaveCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
+    return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
+}
+
+static void getMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) {
+    SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+    get_canvas(canvasHandle)->getMatrix(matrix);
+}
+
+static void setMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) {
+    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+    get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I());
+}
+
+static void concat(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) {
+    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+    get_canvas(canvasHandle)->concat(*matrix);
+}
+
+static void rotate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat degrees) {
+    get_canvas(canvasHandle)->rotate(degrees);
+}
+
+static void scale(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat sx, jfloat sy) {
+    get_canvas(canvasHandle)->scale(sx, sy);
+}
+
+static void skew(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat sx, jfloat sy) {
+    get_canvas(canvasHandle)->skew(sx, sy);
+}
+
+static void translate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat dx, jfloat dy) {
+    get_canvas(canvasHandle)->translate(dx, dy);
+}
+
+static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) {
+    SkRect   r;
+    SkIRect ir;
+    bool result = get_canvas(canvasHandle)->getClipBounds(&r);
+
+    if (!result) {
+        r.setEmpty();
+    }
+    r.round(&ir);
+
+    (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
+    return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean quickRejectRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,
+                                jfloat left, jfloat top, jfloat right, jfloat bottom) {
+    bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom);
+    return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean quickRejectPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pathHandle) {
+    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    bool result = get_canvas(canvasHandle)->quickRejectPath(*path);
+    return result ? JNI_TRUE : JNI_FALSE;
+}
+
+// SkRegion::Op and SkClipOp are numerically identical, so we can freely cast
+// from one to the other (though SkClipOp is destined to become a strict subset)
+static_assert(SkRegion::kDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kDifference), "");
+static_assert(SkRegion::kIntersect_Op == static_cast<SkRegion::Op>(SkClipOp::kIntersect), "");
+static_assert(SkRegion::kUnion_Op == static_cast<SkRegion::Op>(SkClipOp::kUnion_deprecated), "");
+static_assert(SkRegion::kXOR_Op == static_cast<SkRegion::Op>(SkClipOp::kXOR_deprecated), "");
+static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kReverseDifference_deprecated), "");
+static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(SkClipOp::kReplace_deprecated), "");
+
+static SkClipOp opHandleToClipOp(jint opHandle) {
+    // The opHandle is defined in Canvas.java to be Region::Op
+    SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
+
+    // In the future, when we no longer support the wide range of ops (e.g. Union, Xor)
+    // this function can perform a range check and throw an unsupported-exception.
+    // e.g. if (rgnOp != kIntersect && rgnOp != kDifference) throw...
+
+    // Skia now takes a different type, SkClipOp, as the parameter to clipping calls
+    // This type is binary compatible with SkRegion::Op, so a static_cast<> is safe.
+    return static_cast<SkClipOp>(rgnOp);
+}
+
+static jboolean clipRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
+                         jfloat r, jfloat b, jint opHandle) {
+    bool nonEmptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b,
+            opHandleToClipOp(opHandle));
+    return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean clipPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pathHandle,
+                         jint opHandle) {
+    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    bool nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, opHandleToClipOp(opHandle));
+    return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
+}
+
+static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
+    SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
+    get_canvas(canvasHandle)->drawColor(color, mode);
+}
+
+static void drawColorLong(JNIEnv* env, jobject, jlong canvasHandle, jlong colorSpaceHandle,
+        jlong colorLong, jint modeHandle) {
+    SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
+    sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+    SkPaint p;
+    p.setColor4f(color, cs.get());
+
+    SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
+    p.setBlendMode(mode);
+    get_canvas(canvasHandle)->drawPaint(p);
+}
+
+static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) {
+    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawPaint(*paint);
+}
+
+static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
+                      jlong paintHandle) {
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawPoint(x, y, *paint);
+}
+
+static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
+                       jint offset, jint count, jlong paintHandle) {
+    NPE_CHECK_RETURN_VOID(env, jptsArray);
+    AutoJavaFloatArray autoPts(env, jptsArray);
+    float* floats = autoPts.ptr();
+    const int length = autoPts.length();
+
+    if ((offset | count) < 0 || offset + count > length) {
+        doThrowAIOOBE(env);
+        return;
+    }
+
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint);
+}
+
+static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
+                     jfloat stopX, jfloat stopY, jlong paintHandle) {
+    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
+}
+
+static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
+                      jint offset, jint count, jlong paintHandle) {
+    NPE_CHECK_RETURN_VOID(env, jptsArray);
+    AutoJavaFloatArray autoPts(env, jptsArray);
+    float* floats = autoPts.ptr();
+    const int length = autoPts.length();
+
+    if ((offset | count) < 0 || offset + count > length) {
+        doThrowAIOOBE(env);
+        return;
+    }
+
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint);
+}
+
+static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+                     jfloat right, jfloat bottom, jlong paintHandle) {
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
+}
+
+static void drawDoubleRoundRectXY(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft,
+                    jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloat outerRx,
+                    jfloat outerRy, jfloat innerLeft, jfloat innerTop, jfloat innerRight,
+                    jfloat innerBottom, jfloat innerRx, jfloat innerRy, jlong paintHandle) {
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawDoubleRoundRectXY(
+                    outerLeft, outerTop, outerRight, outerBottom, outerRx, outerRy,
+                    innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy, *paint);
+}
+
+static void drawDoubleRoundRectRadii(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft,
+                     jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloatArray jouterRadii,
+                     jfloat innerLeft, jfloat innerTop, jfloat innerRight,
+                     jfloat innerBottom, jfloatArray jinnerRadii, jlong paintHandle) {
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+    float outerRadii[8];
+    float innerRadii[8];
+    env->GetFloatArrayRegion(jouterRadii, 0, 8, outerRadii);
+    env->GetFloatArrayRegion(jinnerRadii, 0, 8, innerRadii);
+    get_canvas(canvasHandle)->drawDoubleRoundRectRadii(
+                    outerLeft, outerTop, outerRight, outerBottom, outerRadii,
+                    innerLeft, innerTop, innerRight, innerBottom, innerRadii, *paint);
+
+}
+
+static void drawRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong regionHandle,
+                       jlong paintHandle) {
+    const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawRegion(*region, *paint);
+}
+
+static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+                          jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint);
+}
+
+static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy,
+                       jfloat radius, jlong paintHandle) {
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint);
+}
+
+static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+                     jfloat right, jfloat bottom, jlong paintHandle) {
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
+}
+
+static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+                    jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
+                    jboolean useCenter, jlong paintHandle) {
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle,
+                                       useCenter, *paint);
+}
+
+static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
+                     jlong paintHandle) {
+    const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawPath(*path, *paint);
+}
+
+static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
+                         jint modeHandle, jint floatCount,
+                         jfloatArray jverts, jint vertIndex,
+                         jfloatArray jtexs, jint texIndex,
+                         jintArray jcolors, jint colorIndex,
+                         jshortArray jindices, jint indexIndex,
+                         jint indexCount, jlong paintHandle) {
+
+    const int vertexCount = floatCount >> 1;  // 2 floats per SkPoint
+
+    AutoJavaFloatArray  vertA(env, jverts, vertIndex + floatCount);
+    AutoJavaFloatArray  texA(env, jtexs, texIndex + floatCount);
+    AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
+    AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
+
+    const float* verts = vertA.ptr() + vertIndex;
+    const float* texs = texA.ptr() + vertIndex;
+    const int* colors = NULL;
+    const uint16_t* indices = NULL;
+
+    if (jcolors != NULL) {
+        colors = colorA.ptr() + colorIndex;
+    }
+    if (jindices != NULL) {
+        indices = (const uint16_t*)(indexA.ptr() + indexIndex);
+    }
+
+    SkVertices::VertexMode mode = static_cast<SkVertices::VertexMode>(modeHandle);
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawVertices(SkVertices::MakeCopy(mode, vertexCount,
+                                           reinterpret_cast<const SkPoint*>(verts),
+                                           reinterpret_cast<const SkPoint*>(texs),
+                                           reinterpret_cast<const SkColor*>(colors),
+                                           indexCount, indices).get(),
+                                           SkBlendMode::kModulate, *paint);
+}
+
+static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+        jlong chunkHandle, jfloat left, jfloat top, jfloat right, jfloat bottom,
+        jlong paintHandle, jint dstDensity, jint srcDensity) {
+
+    Canvas* canvas = get_canvas(canvasHandle);
+    Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
+    const android::Res_png_9patch* chunk = reinterpret_cast<android::Res_png_9patch*>(chunkHandle);
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+    if (CC_LIKELY(dstDensity == srcDensity || dstDensity == 0 || srcDensity == 0)) {
+        canvas->drawNinePatch(bitmap, *chunk, left, top, right, bottom, paint);
+    } else {
+        canvas->save(SaveFlags::MatrixClip);
+
+        SkScalar scale = dstDensity / (float)srcDensity;
+        canvas->translate(left, top);
+        canvas->scale(scale, scale);
+
+        Paint filteredPaint;
+        if (paint) {
+            filteredPaint = *paint;
+        }
+        filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+
+        canvas->drawNinePatch(bitmap, *chunk, 0, 0, (right-left)/scale, (bottom-top)/scale,
+                &filteredPaint);
+
+        canvas->restore();
+    }
+}
+
+static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                       jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
+                       jint screenDensity, jint bitmapDensity) {
+    Canvas* canvas = get_canvas(canvasHandle);
+    Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+    if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
+        if (screenDensity != 0 && screenDensity != bitmapDensity) {
+            Paint filteredPaint;
+            if (paint) {
+                filteredPaint = *paint;
+            }
+            filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+            canvas->drawBitmap(bitmap, left, top, &filteredPaint);
+        } else {
+            canvas->drawBitmap(bitmap, left, top, paint);
+        }
+    } else {
+        canvas->save(SaveFlags::MatrixClip);
+        SkScalar scale = canvasDensity / (float)bitmapDensity;
+        canvas->translate(left, top);
+        canvas->scale(scale, scale);
+
+        Paint filteredPaint;
+        if (paint) {
+            filteredPaint = *paint;
+        }
+        filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+
+        canvas->drawBitmap(bitmap, 0, 0, &filteredPaint);
+        canvas->restore();
+    }
+}
+
+static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                             jlong matrixHandle, jlong paintHandle) {
+    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
+    get_canvas(canvasHandle)->drawBitmap(bitmap, *matrix, paint);
+}
+
+static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                           float srcLeft, float srcTop, float srcRight, float srcBottom,
+                           float dstLeft, float dstTop, float dstRight, float dstBottom,
+                           jlong paintHandle, jint screenDensity, jint bitmapDensity) {
+    Canvas* canvas = get_canvas(canvasHandle);
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+    Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
+    if (screenDensity != 0 && screenDensity != bitmapDensity) {
+        Paint filteredPaint;
+        if (paint) {
+            filteredPaint = *paint;
+        }
+        filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+        canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
+                           dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
+    } else {
+        canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
+                           dstLeft, dstTop, dstRight, dstBottom, paint);
+    }
+}
+
+static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
+                            jintArray jcolors, jint offset, jint stride,
+                            jfloat x, jfloat y, jint width, jint height,
+                            jboolean hasAlpha, jlong paintHandle) {
+    // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
+    // correct the alphaType to kOpaque_SkAlphaType.
+    SkImageInfo info = SkImageInfo::Make(width, height,
+                           hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+                           kPremul_SkAlphaType);
+    SkBitmap bitmap;
+    bitmap.setInfo(info);
+    sk_sp<Bitmap> androidBitmap = Bitmap::allocateHeapBitmap(&bitmap);
+    if (!androidBitmap) {
+        return;
+    }
+
+    if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, &bitmap)) {
+        return;
+    }
+
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawBitmap(*androidBitmap, x, y, paint);
+}
+
+static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                           jint meshWidth, jint meshHeight, jfloatArray jverts,
+                           jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
+    if (Canvas::GetApiLevel() < __ANDROID_API_P__) {
+        // Before P we forgot to respect these. Now that we do respect them, explicitly
+        // zero them for backward compatibility.
+        vertIndex = 0;
+        colorIndex = 0;
+    }
+
+    const int ptCount = (meshWidth + 1) * (meshHeight + 1);
+    AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
+    AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
+
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
+    get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight,
+                                             vertA.ptr() + vertIndex*2,
+                                             colorA.ptr() + colorIndex, paint);
+}
+
+static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray charArray,
+                          jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
+                          jlong paintHandle) {
+    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    const Typeface* typeface = paint->getAndroidTypeface();
+    ScopedCharArrayRO text(env, charArray);
+    // drawTextString and drawTextChars doesn't use context info
+    get_canvas(canvasHandle)->drawText(
+            text.get() + index, count,  // text buffer
+            0, count,  // draw range
+            0, count,  // context range
+            x, y,  // draw position
+            static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr /* measured text */);
+}
+
+static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring strObj,
+                           jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
+                           jlong paintHandle) {
+    ScopedStringChars text(env, strObj);
+    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    const Typeface* typeface = paint->getAndroidTypeface();
+    const int count = end - start;
+    // drawTextString and drawTextChars doesn't use context info
+    get_canvas(canvasHandle)->drawText(
+            text.get() + start, count,  // text buffer
+            0, count,  // draw range
+            0, count,  // context range
+            x, y,  // draw position
+            static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr /* measured text */);
+}
+
+static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray charArray,
+                             jint index, jint count, jint contextIndex, jint contextCount,
+                             jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
+                             jlong mtHandle) {
+    minikin::MeasuredText* mt = reinterpret_cast<minikin::MeasuredText*>(mtHandle);
+    const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+
+    ScopedCharArrayRO text(env, charArray);
+    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    const Typeface* typeface = paint->getAndroidTypeface();
+    get_canvas(canvasHandle)->drawText(
+            text.get(), text.size(),  // text buffer
+            index, count,  // draw range
+            contextIndex, contextCount,  // context range,
+            x, y,  // draw position
+            bidiFlags, *paint, typeface, mt);
+}
+
+static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring strObj,
+                              jint start, jint end, jint contextStart, jint contextEnd,
+                              jfloat x, jfloat y, jboolean isRtl, jlong paintHandle) {
+    const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+
+    ScopedStringChars text(env, strObj);
+    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    const Typeface* typeface = paint->getAndroidTypeface();
+    get_canvas(canvasHandle)->drawText(
+            text.get(), text.size(),  // text buffer
+            start, end - start,  // draw range
+            contextStart, contextEnd - contextStart,  // context range
+            x, y,  // draw position
+            bidiFlags, *paint, typeface, nullptr /* measured text */);
+}
+
+static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
+                                jint index, jint count, jlong pathHandle, jfloat hOffset,
+                                jfloat vOffset, jint bidiFlags, jlong paintHandle) {
+    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    const Typeface* typeface = paint->getAndroidTypeface();
+
+    jchar* jchars = env->GetCharArrayElements(text, NULL);
+
+    get_canvas(canvasHandle)->drawTextOnPath(jchars + index, count,
+            static_cast<minikin::Bidi>(bidiFlags), *path, hOffset, vOffset, *paint, typeface);
+
+    env->ReleaseCharArrayElements(text, jchars, 0);
+}
+
+static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
+                                 jlong pathHandle, jfloat hOffset, jfloat vOffset,
+                                 jint bidiFlags, jlong paintHandle) {
+    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    const Typeface* typeface = paint->getAndroidTypeface();
+
+    const jchar* jchars = env->GetStringChars(text, NULL);
+    int count = env->GetStringLength(text);
+
+    get_canvas(canvasHandle)->drawTextOnPath(jchars, count, static_cast<minikin::Bidi>(bidiFlags),
+            *path, hOffset, vOffset, *paint, typeface);
+
+    env->ReleaseStringChars(text, jchars);
+}
+
+static void setPaintFilter(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong filterHandle) {
+    PaintFilter* paintFilter = reinterpret_cast<PaintFilter*>(filterHandle);
+    get_canvas(canvasHandle)->setPaintFilter(sk_ref_sp(paintFilter));
+}
+
+static void freeCaches(JNIEnv* env, jobject) {
+    SkGraphics::PurgeFontCache();
+}
+
+static void freeTextLayoutCaches(JNIEnv* env, jobject) {
+    minikin::Layout::purgeCaches();
+}
+
+static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) {
+    Canvas::setCompatibilityVersion(apiLevel);
+}
+
+
+}; // namespace CanvasJNI
+
+static const JNINativeMethod gMethods[] = {
+    {"nGetNativeFinalizer", "()J", (void*) CanvasJNI::getNativeFinalizer},
+    {"nFreeCaches", "()V", (void*) CanvasJNI::freeCaches},
+    {"nFreeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches},
+    {"nSetCompatibilityVersion", "(I)V", (void*) CanvasJNI::setCompatibilityVersion},
+
+    // ------------ @FastNative ----------------
+    {"nInitRaster", "(J)J", (void*) CanvasJNI::initRaster},
+    {"nSetBitmap", "(JJ)V", (void*) CanvasJNI::setBitmap},
+    {"nGetClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
+
+    // ------------ @CriticalNative ----------------
+    {"nIsOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
+    {"nGetWidth","(J)I", (void*) CanvasJNI::getWidth},
+    {"nGetHeight","(J)I", (void*) CanvasJNI::getHeight},
+    {"nSave","(JI)I", (void*) CanvasJNI::save},
+    {"nSaveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
+    {"nSaveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
+    {"nSaveUnclippedLayer","(JIIII)I", (void*) CanvasJNI::saveUnclippedLayer},
+    {"nRestoreUnclippedLayer","(JIJ)V", (void*) CanvasJNI::restoreUnclippedLayer},
+    {"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
+    {"nRestore","(J)Z", (void*) CanvasJNI::restore},
+    {"nRestoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
+    {"nGetMatrix", "(JJ)V", (void*)CanvasJNI::getMatrix},
+    {"nSetMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
+    {"nConcat","(JJ)V", (void*) CanvasJNI::concat},
+    {"nRotate","(JF)V", (void*) CanvasJNI::rotate},
+    {"nScale","(JFF)V", (void*) CanvasJNI::scale},
+    {"nSkew","(JFF)V", (void*) CanvasJNI::skew},
+    {"nTranslate","(JFF)V", (void*) CanvasJNI::translate},
+    {"nQuickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
+    {"nQuickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
+    {"nClipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
+    {"nClipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
+    {"nSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setPaintFilter},
+};
+
+// If called from Canvas these are regular JNI
+// If called from DisplayListCanvas they are @FastNative
+static const JNINativeMethod gDrawMethods[] = {
+    {"nDrawColor","(JII)V", (void*) CanvasJNI::drawColor},
+    {"nDrawColor","(JJJI)V", (void*) CanvasJNI::drawColorLong},
+    {"nDrawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
+    {"nDrawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
+    {"nDrawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
+    {"nDrawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
+    {"nDrawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
+    {"nDrawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
+    {"nDrawRegion", "(JJJ)V", (void*) CanvasJNI::drawRegion },
+    {"nDrawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
+    {"nDrawDoubleRoundRect", "(JFFFFFFFFFFFFJ)V", (void*) CanvasJNI::drawDoubleRoundRectXY},
+    {"nDrawDoubleRoundRect", "(JFFFF[FFFFF[FJ)V", (void*) CanvasJNI::drawDoubleRoundRectRadii},
+    {"nDrawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
+    {"nDrawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
+    {"nDrawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
+    {"nDrawPath","(JJJ)V", (void*) CanvasJNI::drawPath},
+    {"nDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
+    {"nDrawNinePatch", "(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch},
+    {"nDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix},
+    {"nDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
+    {"nDrawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap},
+    {"nDrawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
+    {"nDrawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
+    {"nDrawText","(J[CIIFFIJ)V", (void*) CanvasJNI::drawTextChars},
+    {"nDrawText","(JLjava/lang/String;IIFFIJ)V", (void*) CanvasJNI::drawTextString},
+    {"nDrawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
+    {"nDrawTextRun","(JLjava/lang/String;IIIIFFZJ)V", (void*) CanvasJNI::drawTextRunString},
+    {"nDrawTextOnPath","(J[CIIJFFIJ)V", (void*) CanvasJNI::drawTextOnPathChars},
+    {"nDrawTextOnPath","(JLjava/lang/String;JFFIJ)V", (void*) CanvasJNI::drawTextOnPathString},
+};
+
+int register_android_graphics_Canvas(JNIEnv* env) {
+    int ret = 0;
+    ret |= RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
+    ret |= RegisterMethodsOrDie(env, "android/graphics/BaseCanvas", gDrawMethods, NELEM(gDrawMethods));
+    ret |= RegisterMethodsOrDie(env, "android/graphics/BaseRecordingCanvas", gDrawMethods, NELEM(gDrawMethods));
+    return ret;
+
+}
+
+}; // namespace android
diff --git a/libs/hwui/jni/android_graphics_ColorSpace.cpp b/libs/hwui/jni/android_graphics_ColorSpace.cpp
new file mode 100644
index 0000000..7648fd0
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_ColorSpace.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 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 "jni.h"
+#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
+
+#include "SkColor.h"
+#include "SkColorSpace.h"
+
+using namespace android;
+
+static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) {
+    skcms_Matrix3x3 xyzMatrix;
+    jfloat* array = env->GetFloatArrayElements(xyzD50, NULL);
+    xyzMatrix.vals[0][0] = array[0];
+    xyzMatrix.vals[1][0] = array[1];
+    xyzMatrix.vals[2][0] = array[2];
+    xyzMatrix.vals[0][1] = array[3];
+    xyzMatrix.vals[1][1] = array[4];
+    xyzMatrix.vals[2][1] = array[5];
+    xyzMatrix.vals[0][2] = array[6];
+    xyzMatrix.vals[1][2] = array[7];
+    xyzMatrix.vals[2][2] = array[8];
+    env->ReleaseFloatArrayElements(xyzD50, array, 0);
+    return xyzMatrix;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static float halfToFloat(uint16_t bits) {
+  __fp16 h;
+  memcpy(&h, &bits, 2);
+  return (float)h;
+}
+
+SkColor4f GraphicsJNI::convertColorLong(jlong color) {
+    if ((color & 0x3f) == 0) {
+        // This corresponds to sRGB, which is treated differently than the rest.
+        uint8_t a = color >> 56 & 0xff;
+        uint8_t r = color >> 48 & 0xff;
+        uint8_t g = color >> 40 & 0xff;
+        uint8_t b = color >> 32 & 0xff;
+        SkColor c = SkColorSetARGB(a, r, g, b);
+        return SkColor4f::FromColor(c);
+    }
+
+    // These match the implementation of android.graphics.Color#red(long) etc.
+    float r = halfToFloat((uint16_t)(color >> 48 & 0xffff));
+    float g = halfToFloat((uint16_t)(color >> 32 & 0xffff));
+    float b = halfToFloat((uint16_t)(color >> 16 & 0xffff));
+    float a =                       (color >>  6 &  0x3ff) / 1023.0f;
+
+    return SkColor4f{r, g, b, a};
+}
+
+sk_sp<SkColorSpace> GraphicsJNI::getNativeColorSpace(jlong colorSpaceHandle) {
+    if (colorSpaceHandle == 0) return nullptr;
+    return sk_ref_sp(reinterpret_cast<SkColorSpace*>(colorSpaceHandle));
+}
+
+static void unref_colorSpace(SkColorSpace* cs) {
+    SkSafeUnref(cs);
+}
+
+static jlong ColorSpace_getNativeFinalizer(JNIEnv*, jobject) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&unref_colorSpace));
+}
+
+static jlong ColorSpace_creator(JNIEnv* env, jobject, jfloat a, jfloat b, jfloat c,
+        jfloat d, jfloat e, jfloat f, jfloat g, jfloatArray xyzD50) {
+    skcms_TransferFunction p;
+    p.a = a;
+    p.b = b;
+    p.c = c;
+    p.d = d;
+    p.e = e;
+    p.f = f;
+    p.g = g;
+    skcms_Matrix3x3 xyzMatrix = getNativeXYZMatrix(env, xyzD50);
+
+    return reinterpret_cast<jlong>(SkColorSpace::MakeRGB(p, xyzMatrix).release());
+}
+
+static const JNINativeMethod gColorSpaceRgbMethods[] = {
+    {   "nativeGetNativeFinalizer", "()J", (void*)ColorSpace_getNativeFinalizer },
+    {   "nativeCreate", "(FFFFFFF[F)J", (void*)ColorSpace_creator }
+};
+
+namespace android {
+
+int register_android_graphics_ColorSpace(JNIEnv* env) {
+    return android::RegisterMethodsOrDie(env, "android/graphics/ColorSpace$Rgb",
+                                         gColorSpaceRgbMethods, NELEM(gColorSpaceRgbMethods));
+}
+
+}; // namespace android
diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
new file mode 100644
index 0000000..9907da5
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <nativehelper/JNIHelp.h>
+
+#include <android_runtime/AndroidRuntime.h>
+#ifdef __ANDROID__ // Layoutlib does not support Looper and device properties
+#include <utils/Looper.h>
+#endif
+
+#include <SkBitmap.h>
+#include <SkRegion.h>
+
+#include <Rect.h>
+#include <RenderNode.h>
+#include <CanvasProperty.h>
+#include <hwui/Canvas.h>
+#include <hwui/Paint.h>
+#include <minikin/Layout.h>
+#ifdef __ANDROID__ // Layoutlib does not support RenderThread
+#include <renderthread/RenderProxy.h>
+#endif
+
+#include "core_jni_helpers.h"
+
+namespace android {
+
+using namespace uirenderer;
+
+jmethodID gRunnableMethodId;
+
+static JNIEnv* jnienv(JavaVM* vm) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
+    }
+    return env;
+}
+
+#ifdef __ANDROID__ // Layoutlib does not support GL, Looper
+class InvokeRunnableMessage : public MessageHandler {
+public:
+    InvokeRunnableMessage(JNIEnv* env, jobject runnable) {
+        mRunnable = env->NewGlobalRef(runnable);
+        env->GetJavaVM(&mVm);
+    }
+
+    virtual ~InvokeRunnableMessage() {
+        jnienv(mVm)->DeleteGlobalRef(mRunnable);
+    }
+
+    virtual void handleMessage(const Message&) {
+        jnienv(mVm)->CallVoidMethod(mRunnable, gRunnableMethodId);
+    }
+
+private:
+    JavaVM* mVm;
+    jobject mRunnable;
+};
+
+class GlFunctorReleasedCallbackBridge : public GlFunctorLifecycleListener {
+public:
+    GlFunctorReleasedCallbackBridge(JNIEnv* env, jobject javaCallback) {
+        mLooper = Looper::getForThread();
+        mMessage = new InvokeRunnableMessage(env, javaCallback);
+    }
+
+    virtual void onGlFunctorReleased(Functor* functor) override {
+        mLooper->sendMessage(mMessage, 0);
+    }
+
+private:
+    sp<Looper> mLooper;
+    sp<InvokeRunnableMessage> mMessage;
+};
+#endif
+
+// ---------------- @FastNative -----------------------------
+
+static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
+        jlong canvasPtr, jlong functorPtr, jobject releasedCallback) {
+#ifdef __ANDROID__ // Layoutlib does not support GL
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    Functor* functor = reinterpret_cast<Functor*>(functorPtr);
+    sp<GlFunctorReleasedCallbackBridge> bridge;
+    if (releasedCallback) {
+        bridge = new GlFunctorReleasedCallbackBridge(env, releasedCallback);
+    }
+    canvas->callDrawGLFunction(functor, bridge.get());
+#endif
+}
+
+
+// ---------------- @CriticalNative -------------------------
+
+static jlong android_view_DisplayListCanvas_createDisplayListCanvas(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+        jint width, jint height) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return reinterpret_cast<jlong>(Canvas::create_recording_canvas(width, height, renderNode));
+}
+
+static void android_view_DisplayListCanvas_resetDisplayListCanvas(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
+        jlong renderNodePtr, jint width, jint height) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    canvas->resetRecording(width, height, renderNode);
+}
+
+static jint android_view_DisplayListCanvas_getMaxTextureSize(CRITICAL_JNI_PARAMS) {
+#ifdef __ANDROID__ // Layoutlib does not support RenderProxy (RenderThread)
+    return android::uirenderer::renderthread::RenderProxy::maxTextureSize();
+#else
+    return 4096;
+#endif
+}
+
+static void android_view_DisplayListCanvas_insertReorderBarrier(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
+        jboolean reorderEnable) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    canvas->insertReorderBarrier(reorderEnable);
+}
+
+static jlong android_view_DisplayListCanvas_finishRecording(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    return reinterpret_cast<jlong>(canvas->finishRecording());
+}
+
+static void android_view_DisplayListCanvas_drawRenderNode(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong renderNodePtr) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    canvas->drawRenderNode(renderNode);
+}
+
+static void android_view_DisplayListCanvas_drawTextureLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong layerPtr) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
+    canvas->drawLayer(layer);
+}
+
+static void android_view_DisplayListCanvas_drawRoundRectProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
+        jlong leftPropPtr, jlong topPropPtr, jlong rightPropPtr, jlong bottomPropPtr,
+        jlong rxPropPtr, jlong ryPropPtr, jlong paintPropPtr) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    CanvasPropertyPrimitive* leftProp = reinterpret_cast<CanvasPropertyPrimitive*>(leftPropPtr);
+    CanvasPropertyPrimitive* topProp = reinterpret_cast<CanvasPropertyPrimitive*>(topPropPtr);
+    CanvasPropertyPrimitive* rightProp = reinterpret_cast<CanvasPropertyPrimitive*>(rightPropPtr);
+    CanvasPropertyPrimitive* bottomProp = reinterpret_cast<CanvasPropertyPrimitive*>(bottomPropPtr);
+    CanvasPropertyPrimitive* rxProp = reinterpret_cast<CanvasPropertyPrimitive*>(rxPropPtr);
+    CanvasPropertyPrimitive* ryProp = reinterpret_cast<CanvasPropertyPrimitive*>(ryPropPtr);
+    CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr);
+    canvas->drawRoundRect(leftProp, topProp, rightProp, bottomProp, rxProp, ryProp, paintProp);
+}
+
+static void android_view_DisplayListCanvas_drawCircleProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
+        jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
+    CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr);
+    CanvasPropertyPrimitive* radiusProp = reinterpret_cast<CanvasPropertyPrimitive*>(radiusPropPtr);
+    CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr);
+    canvas->drawCircle(xProp, yProp, radiusProp, paintProp);
+}
+
+static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jint functor) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    canvas->drawWebViewFunctor(functor);
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/graphics/RecordingCanvas";
+
+static JNINativeMethod gMethods[] = {
+
+    // ------------ @FastNative ------------------
+
+    { "nCallDrawGLFunction", "(JJLjava/lang/Runnable;)V",
+            (void*) android_view_DisplayListCanvas_callDrawGLFunction },
+
+    // ------------ @CriticalNative --------------
+    { "nCreateDisplayListCanvas", "(JII)J",     (void*) android_view_DisplayListCanvas_createDisplayListCanvas },
+    { "nResetDisplayListCanvas",  "(JJII)V",    (void*) android_view_DisplayListCanvas_resetDisplayListCanvas },
+    { "nGetMaximumTextureWidth",  "()I",        (void*) android_view_DisplayListCanvas_getMaxTextureSize },
+    { "nGetMaximumTextureHeight", "()I",        (void*) android_view_DisplayListCanvas_getMaxTextureSize },
+    { "nInsertReorderBarrier",    "(JZ)V",      (void*) android_view_DisplayListCanvas_insertReorderBarrier },
+    { "nFinishRecording",         "(J)J",       (void*) android_view_DisplayListCanvas_finishRecording },
+    { "nDrawRenderNode",          "(JJ)V",      (void*) android_view_DisplayListCanvas_drawRenderNode },
+    { "nDrawTextureLayer",        "(JJ)V",      (void*) android_view_DisplayListCanvas_drawTextureLayer },
+    { "nDrawCircle",              "(JJJJJ)V",   (void*) android_view_DisplayListCanvas_drawCircleProps },
+    { "nDrawRoundRect",           "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
+    { "nDrawWebViewFunctor",      "(JI)V",      (void*) android_view_DisplayListCanvas_drawWebViewFunctor },
+};
+
+int register_android_view_DisplayListCanvas(JNIEnv* env) {
+    jclass runnableClass = FindClassOrDie(env, "java/lang/Runnable");
+    gRunnableMethodId = GetMethodIDOrDie(env, runnableClass, "run", "()V");
+
+    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+};
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
new file mode 100644
index 0000000..93449ff
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -0,0 +1,746 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "ThreadedRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include <FrameInfo.h>
+#include <GraphicsJNI.h>
+#include <Picture.h>
+#include <Properties.h>
+#include <RootRenderNode.h>
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <media/NdkImage.h>
+#include <media/NdkImageReader.h>
+#include <nativehelper/JNIHelp.h>
+#include <pipeline/skia/ShaderCache.h>
+#include <private/EGL/cache.h>
+#include <renderthread/CanvasContext.h>
+#include <renderthread/RenderProxy.h>
+#include <renderthread/RenderTask.h>
+#include <renderthread/RenderThread.h>
+#include <utils/Color.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+#include <utils/TraceUtils.h>
+
+#include <algorithm>
+#include <atomic>
+
+#include "android_graphics_HardwareRendererObserver.h"
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+namespace android {
+
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+
+struct {
+    jclass clazz;
+    jmethodID invokePictureCapturedCallback;
+} gHardwareRenderer;
+
+struct {
+    jmethodID onFrameDraw;
+} gFrameDrawingCallback;
+
+struct {
+    jmethodID onFrameComplete;
+} gFrameCompleteCallback;
+
+static JNIEnv* getenv(JavaVM* vm) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
+    }
+    return env;
+}
+
+typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface);
+ANW_fromSurface fromSurface;
+
+class JvmErrorReporter : public ErrorHandler {
+public:
+    JvmErrorReporter(JNIEnv* env) {
+        env->GetJavaVM(&mVm);
+    }
+
+    virtual void onError(const std::string& message) override {
+        JNIEnv* env = getenv(mVm);
+        jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
+    }
+private:
+    JavaVM* mVm;
+};
+
+class FrameCompleteWrapper : public LightRefBase<FrameCompleteWrapper> {
+public:
+    explicit FrameCompleteWrapper(JNIEnv* env, jobject jobject) {
+        env->GetJavaVM(&mVm);
+        mObject = env->NewGlobalRef(jobject);
+        LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref");
+    }
+
+    ~FrameCompleteWrapper() {
+        releaseObject();
+    }
+
+    void onFrameComplete(int64_t frameNr) {
+        if (mObject) {
+            ATRACE_FORMAT("frameComplete %" PRId64, frameNr);
+            getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, frameNr);
+            releaseObject();
+        }
+    }
+
+private:
+    JavaVM* mVm;
+    jobject mObject;
+
+    void releaseObject() {
+        if (mObject) {
+            getenv(mVm)->DeleteGlobalRef(mObject);
+            mObject = nullptr;
+        }
+    }
+};
+
+static void android_view_ThreadedRenderer_rotateProcessStatsBuffer(JNIEnv* env, jobject clazz) {
+    RenderProxy::rotateProcessStatsBuffer();
+}
+
+static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, jobject clazz,
+        jint fd) {
+    RenderProxy::setProcessStatsBuffer(fd);
+}
+
+static jint android_view_ThreadedRenderer_getRenderThreadTid(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    return proxy->getRenderThreadTid();
+}
+
+static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
+    RootRenderNode* node = new RootRenderNode(std::make_unique<JvmErrorReporter>(env));
+    node->incStrong(0);
+    node->setName("RootRenderNode");
+    return reinterpret_cast<jlong>(node);
+}
+
+static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
+        jboolean translucent, jboolean isWideGamut, jlong rootRenderNodePtr) {
+    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
+    ContextFactoryImpl factory(rootRenderNode);
+    RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory);
+    proxy->setWideGamut(isWideGamut);
+    return (jlong) proxy;
+}
+
+static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    delete proxy;
+}
+
+static jboolean android_view_ThreadedRenderer_loadSystemProperties(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    return proxy->loadSystemProperties();
+}
+
+static void android_view_ThreadedRenderer_setName(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jstring jname) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    const char* name = env->GetStringUTFChars(jname, NULL);
+    proxy->setName(name);
+    env->ReleaseStringUTFChars(jname, name);
+}
+
+static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jobject jsurface, jboolean discardBuffer) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    ANativeWindow* window = nullptr;
+    if (jsurface) {
+        window = fromSurface(env, jsurface);
+    }
+    bool enableTimeout = true;
+    if (discardBuffer) {
+        // Currently only Surface#lockHardwareCanvas takes this path
+        enableTimeout = false;
+        proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer);
+    }
+    proxy->setSurface(window, enableTimeout);
+    ANativeWindow_release(window);
+}
+
+static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    return proxy->pause();
+}
+
+static void android_view_ThreadedRenderer_setStopped(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jboolean stopped) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setStopped(stopped);
+}
+
+static void android_view_ThreadedRenderer_setLightAlpha(JNIEnv* env, jobject clazz, jlong proxyPtr,
+        jfloat ambientShadowAlpha, jfloat spotShadowAlpha) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setLightAlpha((uint8_t) (255 * ambientShadowAlpha), (uint8_t) (255 * spotShadowAlpha));
+}
+
+static void android_view_ThreadedRenderer_setLightGeometry(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jfloat lightX, jfloat lightY, jfloat lightZ, jfloat lightRadius) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setLightGeometry((Vector3){lightX, lightY, lightZ}, lightRadius);
+}
+
+static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jboolean opaque) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setOpaque(opaque);
+}
+
+static void android_view_ThreadedRenderer_setWideGamut(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jboolean wideGamut) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setWideGamut(wideGamut);
+}
+
+static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
+    LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
+            "Mismatched size expectations, given %d expected %d",
+            frameInfoSize, UI_THREAD_FRAME_INFO_SIZE);
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo());
+    return proxy->syncAndDrawFrame();
+}
+
+static void android_view_ThreadedRenderer_destroy(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong rootNodePtr) {
+    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr);
+    rootRenderNode->destroy();
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->destroy();
+}
+
+static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* env, jobject clazz,
+        jlong rootNodePtr, jlong animatingNodePtr) {
+    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr);
+    RenderNode* animatingNode = reinterpret_cast<RenderNode*>(animatingNodePtr);
+    rootRenderNode->attachAnimatingNode(animatingNode);
+}
+
+static void android_view_ThreadedRenderer_registerVectorDrawableAnimator(JNIEnv* env, jobject clazz,
+        jlong rootNodePtr, jlong animatorPtr) {
+    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr);
+    PropertyValuesAnimatorSet* animator = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorPtr);
+    rootRenderNode->addVectorDrawableAnimator(animator);
+}
+
+static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz,
+        jlong functorPtr, jboolean waitForCompletion) {
+    Functor* functor = reinterpret_cast<Functor*>(functorPtr);
+    RenderProxy::invokeFunctor(functor, waitForCompletion);
+}
+
+static jlong android_view_ThreadedRenderer_createTextureLayer(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    DeferredLayerUpdater* layer = proxy->createTextureLayer();
+    return reinterpret_cast<jlong>(layer);
+}
+
+static void android_view_ThreadedRenderer_buildLayer(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong nodePtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    RenderNode* node = reinterpret_cast<RenderNode*>(nodePtr);
+    proxy->buildLayer(node);
+}
+
+static jboolean android_view_ThreadedRenderer_copyLayerInto(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong layerPtr, jlong bitmapPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
+    SkBitmap bitmap;
+    bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
+    return proxy->copyLayerInto(layer, bitmap);
+}
+
+static void android_view_ThreadedRenderer_pushLayerUpdate(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong layerPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
+    proxy->pushLayerUpdate(layer);
+}
+
+static void android_view_ThreadedRenderer_cancelLayerUpdate(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong layerPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
+    proxy->cancelLayerUpdate(layer);
+}
+
+static void android_view_ThreadedRenderer_detachSurfaceTexture(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong layerPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
+    proxy->detachSurfaceTexture(layer);
+}
+
+static void android_view_ThreadedRenderer_destroyHardwareResources(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->destroyHardwareResources();
+}
+
+static void android_view_ThreadedRenderer_trimMemory(JNIEnv* env, jobject clazz,
+        jint level) {
+    RenderProxy::trimMemory(level);
+}
+
+static void android_view_ThreadedRenderer_overrideProperty(JNIEnv* env, jobject clazz,
+        jstring name, jstring value) {
+    const char* nameCharArray = env->GetStringUTFChars(name, NULL);
+    const char* valueCharArray = env->GetStringUTFChars(value, NULL);
+    RenderProxy::overrideProperty(nameCharArray, valueCharArray);
+    env->ReleaseStringUTFChars(name, nameCharArray);
+    env->ReleaseStringUTFChars(name, valueCharArray);
+}
+
+static void android_view_ThreadedRenderer_fence(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->fence();
+}
+
+static void android_view_ThreadedRenderer_stopDrawing(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->stopDrawing();
+}
+
+static void android_view_ThreadedRenderer_notifyFramePending(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->notifyFramePending();
+}
+
+static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jobject javaFileDescriptor, jint dumpFlags) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
+    proxy->dumpProfileInfo(fd, dumpFlags);
+}
+
+static void android_view_ThreadedRenderer_addRenderNode(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong renderNodePtr, jboolean placeFront) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    proxy->addRenderNode(renderNode, placeFront);
+}
+
+static void android_view_ThreadedRenderer_removeRenderNode(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong renderNodePtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    proxy->removeRenderNode(renderNode);
+}
+
+static void android_view_ThreadedRendererd_drawRenderNode(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong renderNodePtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    proxy->drawRenderNode(renderNode);
+}
+
+static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env,
+        jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setContentDrawBounds(left, top, right, bottom);
+}
+
+class JGlobalRefHolder {
+public:
+    JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {}
+
+    virtual ~JGlobalRefHolder() {
+        getenv(mVm)->DeleteGlobalRef(mObject);
+        mObject = nullptr;
+    }
+
+    jobject object() { return mObject; }
+    JavaVM* vm() { return mVm; }
+
+private:
+    JGlobalRefHolder(const JGlobalRefHolder&) = delete;
+    void operator=(const JGlobalRefHolder&) = delete;
+
+    JavaVM* mVm;
+    jobject mObject;
+};
+
+static void android_view_ThreadedRenderer_setPictureCapturedCallbackJNI(JNIEnv* env,
+        jobject clazz, jlong proxyPtr, jobject pictureCallback) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    if (!pictureCallback) {
+        proxy->setPictureCapturedCallback(nullptr);
+    } else {
+        JavaVM* vm = nullptr;
+        LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+        auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm,
+                env->NewGlobalRef(pictureCallback));
+        proxy->setPictureCapturedCallback([globalCallbackRef](sk_sp<SkPicture>&& picture) {
+            JNIEnv* env = getenv(globalCallbackRef->vm());
+            Picture* wrapper = new Picture{std::move(picture)};
+            env->CallStaticVoidMethod(gHardwareRenderer.clazz,
+                    gHardwareRenderer.invokePictureCapturedCallback,
+                    static_cast<jlong>(reinterpret_cast<intptr_t>(wrapper)),
+                    globalCallbackRef->object());
+        });
+    }
+}
+
+static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env,
+        jobject clazz, jlong proxyPtr, jobject frameCallback) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    if (!frameCallback) {
+        proxy->setFrameCallback(nullptr);
+    } else {
+        JavaVM* vm = nullptr;
+        LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+        auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm,
+                env->NewGlobalRef(frameCallback));
+        proxy->setFrameCallback([globalCallbackRef](int64_t frameNr) {
+            JNIEnv* env = getenv(globalCallbackRef->vm());
+            env->CallVoidMethod(globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw,
+                    static_cast<jlong>(frameNr));
+        });
+    }
+}
+
+static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env,
+        jobject clazz, jlong proxyPtr, jobject callback) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    if (!callback) {
+        proxy->setFrameCompleteCallback(nullptr);
+    } else {
+        sp<FrameCompleteWrapper> wrapper = new FrameCompleteWrapper{env, callback};
+        proxy->setFrameCompleteCallback([wrapper](int64_t frameNr) {
+            wrapper->onFrameComplete(frameNr);
+        });
+    }
+}
+
+static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env,
+        jobject clazz, jobject jsurface, jint left, jint top,
+        jint right, jint bottom, jlong bitmapPtr) {
+    SkBitmap bitmap;
+    bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
+    ANativeWindow* window = fromSurface(env, jsurface);
+    jint result = RenderProxy::copySurfaceInto(window, left, top, right, bottom, &bitmap);
+    ANativeWindow_release(window);
+    return result;
+}
+
+class ContextFactory : public IContextFactory {
+public:
+    virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) {
+        return new AnimationContext(clock);
+    }
+};
+
+static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(JNIEnv* env,
+        jobject clazz, jlong renderNodePtr, jint jwidth, jint jheight) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    if (jwidth <= 0 || jheight <= 0) {
+        ALOGW("Invalid width %d or height %d", jwidth, jheight);
+        return nullptr;
+    }
+
+    uint32_t width = jwidth;
+    uint32_t height = jheight;
+
+    // Create an ImageReader wired up to a BufferItemConsumer
+    AImageReader* rawReader;
+    media_status_t result =
+            AImageReader_newWithUsage(width, height, AIMAGE_FORMAT_RGBA_8888,
+                                      AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE, 2, &rawReader);
+    std::unique_ptr<AImageReader, decltype(&AImageReader_delete)> reader(rawReader,
+                                                                         AImageReader_delete);
+
+    if (result != AMEDIA_OK) {
+        ALOGW("Error creating image reader!");
+        return nullptr;
+    }
+
+    // Note that ownership of this window is maintained by AImageReader, so we
+    // shouldn't need to wrap around a smart pointer.
+    ANativeWindow* window;
+    result = AImageReader_getWindow(rawReader, &window);
+
+    if (result != AMEDIA_OK) {
+        ALOGW("Error retrieving the native window!");
+        return nullptr;
+    }
+
+    // Render into the surface
+    {
+        ContextFactory factory;
+        RenderProxy proxy{true, renderNode, &factory};
+        proxy.setSwapBehavior(SwapBehavior::kSwap_discardBuffer);
+        proxy.setSurface(window);
+        // Shadows can't be used via this interface, so just set the light source
+        // to all 0s.
+        proxy.setLightAlpha(0, 0);
+        proxy.setLightGeometry((Vector3){0, 0, 0}, 0);
+        nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
+        UiFrameInfoBuilder(proxy.frameInfo())
+                .setVsync(vsync, vsync)
+                .addFlag(FrameInfoFlags::SurfaceCanvas);
+        proxy.syncAndDrawFrame();
+    }
+
+    AImage* rawImage;
+    result = AImageReader_acquireNextImage(rawReader, &rawImage);
+    std::unique_ptr<AImage, decltype(&AImage_delete)> image(rawImage, AImage_delete);
+    if (result != AMEDIA_OK) {
+        ALOGW("Error reading image: %d!", result);
+        return nullptr;
+    }
+
+    AHardwareBuffer* buffer;
+    result = AImage_getHardwareBuffer(rawImage, &buffer);
+
+    AHardwareBuffer_Desc desc;
+    AHardwareBuffer_describe(buffer, &desc);
+
+    if (desc.width != width || desc.height != height) {
+        ALOGW("AHardwareBuffer size mismatch, got %dx%d expected %dx%d", desc.width, desc.height,
+              width, height);
+        // Continue I guess?
+    }
+
+    sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace(
+            static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
+    if (cs == nullptr) {
+        // nullptr is treated as SRGB in Skia, thus explicitly use SRGB in order to make sure
+        // the returned bitmap has a color space.
+        cs = SkColorSpace::MakeSRGB();
+    }
+    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, cs);
+    return bitmap::createBitmap(env, bitmap.release(),
+            android::bitmap::kBitmapCreateFlag_Premultiplied);
+}
+
+static void android_view_ThreadedRenderer_disableVsync(JNIEnv*, jclass) {
+    RenderProxy::disableVsync();
+}
+
+static void android_view_ThreadedRenderer_setHighContrastText(JNIEnv*, jclass, jboolean enable) {
+    Properties::enableHighContrastText = enable;
+}
+
+static void android_view_ThreadedRenderer_hackySetRTAnimationsEnabled(JNIEnv*, jclass,
+        jboolean enable) {
+    Properties::enableRTAnimations = enable;
+}
+
+static void android_view_ThreadedRenderer_setDebuggingEnabled(JNIEnv*, jclass, jboolean enable) {
+    Properties::debuggingEnabled = enable;
+}
+
+static void android_view_ThreadedRenderer_setIsolatedProcess(JNIEnv*, jclass, jboolean isolated) {
+    Properties::isolatedProcess = isolated;
+}
+
+static void android_view_ThreadedRenderer_setContextPriority(JNIEnv*, jclass,
+        jint contextPriority) {
+    Properties::contextPriority = contextPriority;
+}
+
+static void android_view_ThreadedRenderer_allocateBuffers(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->allocateBuffers();
+}
+
+static void android_view_ThreadedRenderer_setForceDark(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jboolean enable) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setForceDark(enable);
+}
+
+static void android_view_ThreadedRenderer_preload(JNIEnv*, jclass) {
+    RenderProxy::preload();
+}
+
+// ----------------------------------------------------------------------------
+// HardwareRendererObserver
+// ----------------------------------------------------------------------------
+
+static void android_view_ThreadedRenderer_addObserver(JNIEnv* env, jclass clazz,
+        jlong proxyPtr, jlong observerPtr) {
+    HardwareRendererObserver* observer = reinterpret_cast<HardwareRendererObserver*>(observerPtr);
+    renderthread::RenderProxy* renderProxy =
+            reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
+
+    renderProxy->addFrameMetricsObserver(observer);
+}
+
+static void android_view_ThreadedRenderer_removeObserver(JNIEnv* env, jclass clazz,
+        jlong proxyPtr, jlong observerPtr) {
+    HardwareRendererObserver* observer = reinterpret_cast<HardwareRendererObserver*>(observerPtr);
+    renderthread::RenderProxy* renderProxy =
+            reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
+
+    renderProxy->removeFrameMetricsObserver(observer);
+}
+
+// ----------------------------------------------------------------------------
+// Shaders
+// ----------------------------------------------------------------------------
+
+static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
+        jstring diskCachePath, jstring skiaDiskCachePath) {
+    const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
+    android::egl_set_cache_filename(cacheArray);
+    env->ReleaseStringUTFChars(diskCachePath, cacheArray);
+
+    const char* skiaCacheArray = env->GetStringUTFChars(skiaDiskCachePath, NULL);
+    uirenderer::skiapipeline::ShaderCache::get().setFilename(skiaCacheArray);
+    env->ReleaseStringUTFChars(skiaDiskCachePath, skiaCacheArray);
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/graphics/HardwareRenderer";
+
+static const JNINativeMethod gMethods[] = {
+    { "nRotateProcessStatsBuffer", "()V", (void*) android_view_ThreadedRenderer_rotateProcessStatsBuffer },
+    { "nSetProcessStatsBuffer", "(I)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
+    { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid },
+    { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
+    { "nCreateProxy", "(ZZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
+    { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
+    { "nLoadSystemProperties", "(J)Z", (void*) android_view_ThreadedRenderer_loadSystemProperties },
+    { "nSetName", "(JLjava/lang/String;)V", (void*) android_view_ThreadedRenderer_setName },
+    { "nSetSurface", "(JLandroid/view/Surface;Z)V", (void*) android_view_ThreadedRenderer_setSurface },
+    { "nPause", "(J)Z", (void*) android_view_ThreadedRenderer_pause },
+    { "nSetStopped", "(JZ)V", (void*) android_view_ThreadedRenderer_setStopped },
+    { "nSetLightAlpha", "(JFF)V", (void*) android_view_ThreadedRenderer_setLightAlpha },
+    { "nSetLightGeometry", "(JFFFF)V", (void*) android_view_ThreadedRenderer_setLightGeometry },
+    { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
+    { "nSetWideGamut", "(JZ)V", (void*) android_view_ThreadedRenderer_setWideGamut },
+    { "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
+    { "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy },
+    { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode },
+    { "nRegisterVectorDrawableAnimator", "(JJ)V", (void*) android_view_ThreadedRenderer_registerVectorDrawableAnimator },
+    { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
+    { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer },
+    { "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer },
+    { "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto },
+    { "nPushLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_pushLayerUpdate },
+    { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_cancelLayerUpdate },
+    { "nDetachSurfaceTexture", "(JJ)V", (void*) android_view_ThreadedRenderer_detachSurfaceTexture },
+    { "nDestroyHardwareResources", "(J)V", (void*) android_view_ThreadedRenderer_destroyHardwareResources },
+    { "nTrimMemory", "(I)V", (void*) android_view_ThreadedRenderer_trimMemory },
+    { "nOverrideProperty", "(Ljava/lang/String;Ljava/lang/String;)V",  (void*) android_view_ThreadedRenderer_overrideProperty },
+    { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence },
+    { "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing },
+    { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
+    { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
+    { "setupShadersDiskCache", "(Ljava/lang/String;Ljava/lang/String;)V",
+                (void*) android_view_ThreadedRenderer_setupShadersDiskCache },
+    { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode},
+    { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
+    { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode},
+    { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds},
+    { "nSetPictureCaptureCallback", "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V",
+            (void*) android_view_ThreadedRenderer_setPictureCapturedCallbackJNI },
+    { "nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V",
+            (void*)android_view_ThreadedRenderer_setFrameCallback},
+    { "nSetFrameCompleteCallback", "(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V",
+            (void*)android_view_ThreadedRenderer_setFrameCompleteCallback },
+    { "nAddObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_addObserver },
+    { "nRemoveObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeObserver },
+    { "nCopySurfaceInto", "(Landroid/view/Surface;IIIIJ)I",
+                (void*)android_view_ThreadedRenderer_copySurfaceInto },
+    { "nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;",
+            (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode },
+    { "disableVsync", "()V", (void*)android_view_ThreadedRenderer_disableVsync },
+    { "nSetHighContrastText", "(Z)V", (void*)android_view_ThreadedRenderer_setHighContrastText },
+    { "nHackySetRTAnimationsEnabled", "(Z)V",
+            (void*)android_view_ThreadedRenderer_hackySetRTAnimationsEnabled },
+    { "nSetDebuggingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDebuggingEnabled },
+    { "nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess },
+    { "nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority },
+    { "nAllocateBuffers", "(J)V", (void*)android_view_ThreadedRenderer_allocateBuffers },
+    { "nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark },
+    { "preload", "()V", (void*)android_view_ThreadedRenderer_preload },
+};
+
+static JavaVM* mJvm = nullptr;
+
+static void attachRenderThreadToJvm(const char* name) {
+    LOG_ALWAYS_FATAL_IF(!mJvm, "No jvm but we set the hook??");
+
+    JavaVMAttachArgs args;
+    args.version = JNI_VERSION_1_4;
+    args.name = name;
+    args.group = NULL;
+    JNIEnv* env;
+    mJvm->AttachCurrentThreadAsDaemon(&env, (void*) &args);
+}
+
+int register_android_view_ThreadedRenderer(JNIEnv* env) {
+    env->GetJavaVM(&mJvm);
+    RenderThread::setOnStartHook(&attachRenderThreadToJvm);
+
+    jclass hardwareRenderer = FindClassOrDie(env,
+            "android/graphics/HardwareRenderer");
+    gHardwareRenderer.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(hardwareRenderer));
+    gHardwareRenderer.invokePictureCapturedCallback = GetStaticMethodIDOrDie(env, hardwareRenderer,
+            "invokePictureCapturedCallback",
+            "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V");
+
+    jclass frameCallbackClass = FindClassOrDie(env,
+            "android/graphics/HardwareRenderer$FrameDrawingCallback");
+    gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass,
+            "onFrameDraw", "(J)V");
+
+    jclass frameCompleteClass = FindClassOrDie(env,
+            "android/graphics/HardwareRenderer$FrameCompleteCallback");
+    gFrameCompleteCallback.onFrameComplete = GetMethodIDOrDie(env, frameCompleteClass,
+            "onFrameComplete", "(J)V");
+
+    void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+    fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface");
+    LOG_ALWAYS_FATAL_IF(fromSurface == nullptr,
+                        "Failed to find required symbol ANativeWindow_fromSurface!");
+
+    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp
new file mode 100644
index 0000000..89b77b0
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 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 "android_graphics_HardwareRendererObserver.h"
+
+#include "core_jni_helpers.h"
+#include "nativehelper/jni_macros.h"
+
+#include <array>
+
+namespace android {
+
+struct {
+    jmethodID callback;
+} gHardwareRendererObserverClassInfo;
+
+static JNIEnv* getenv(JavaVM* vm) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
+    }
+    return env;
+}
+
+HardwareRendererObserver::HardwareRendererObserver(JavaVM *vm, jobject observer) : mVm(vm) {
+    mObserverWeak = getenv(mVm)->NewWeakGlobalRef(observer);
+    LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr,
+            "unable to create frame stats observer reference");
+}
+
+HardwareRendererObserver::~HardwareRendererObserver() {
+    JNIEnv* env = getenv(mVm);
+    env->DeleteWeakGlobalRef(mObserverWeak);
+}
+
+bool HardwareRendererObserver::getNextBuffer(JNIEnv* env, jlongArray metrics, int* dropCount) {
+    jsize bufferSize = env->GetArrayLength(reinterpret_cast<jarray>(metrics));
+    LOG_ALWAYS_FATAL_IF(bufferSize != HardwareRendererObserver::kBufferSize,
+                        "Mismatched Java/Native FrameMetrics data format.");
+
+    FrameMetricsNotification& elem = mRingBuffer[mNextInQueue];
+    if (elem.hasData.load()) {
+        env->SetLongArrayRegion(metrics, 0, kBufferSize, elem.buffer);
+        *dropCount = elem.dropCount;
+        mNextInQueue = (mNextInQueue + 1) % kRingSize;
+        elem.hasData = false;
+        return true;
+    }
+
+    return false;
+}
+
+void HardwareRendererObserver::notify(const int64_t* stats) {
+    FrameMetricsNotification& elem = mRingBuffer[mNextFree];
+
+    if (!elem.hasData.load()) {
+        memcpy(elem.buffer, stats, kBufferSize * sizeof(stats[0]));
+
+        elem.dropCount = mDroppedReports;
+        mDroppedReports = 0;
+        mNextFree = (mNextFree + 1) % kRingSize;
+        elem.hasData = true;
+
+        JNIEnv* env = getenv(mVm);
+        jobject target = env->NewLocalRef(mObserverWeak);
+        if (target != nullptr) {
+            env->CallVoidMethod(target, gHardwareRendererObserverClassInfo.callback);
+            env->DeleteLocalRef(target);
+        }
+    } else {
+        mDroppedReports++;
+    }
+}
+
+static jlong android_graphics_HardwareRendererObserver_createObserver(JNIEnv* env,
+                                                                      jobject observerObj) {
+    JavaVM* vm = nullptr;
+    if (env->GetJavaVM(&vm) != JNI_OK) {
+        LOG_ALWAYS_FATAL("Unable to get Java VM");
+        return 0;
+    }
+
+    HardwareRendererObserver* observer = new HardwareRendererObserver(vm, observerObj);
+    return reinterpret_cast<jlong>(observer);
+}
+
+static jint android_graphics_HardwareRendererObserver_getNextBuffer(JNIEnv* env, jobject,
+                                                                    jlong observerPtr,
+                                                                    jlongArray metrics) {
+    HardwareRendererObserver* observer = reinterpret_cast<HardwareRendererObserver*>(observerPtr);
+    int dropCount = 0;
+    if (observer->getNextBuffer(env, metrics, &dropCount)) {
+        return dropCount;
+    } else {
+        return -1;
+    }
+}
+
+static const std::array gMethods = {
+    MAKE_JNI_NATIVE_METHOD("nCreateObserver", "()J",
+                           android_graphics_HardwareRendererObserver_createObserver),
+    MAKE_JNI_NATIVE_METHOD("nGetNextBuffer", "(J[J)I",
+                           android_graphics_HardwareRendererObserver_getNextBuffer),
+};
+
+int register_android_graphics_HardwareRendererObserver(JNIEnv* env) {
+
+    jclass observerClass = FindClassOrDie(env, "android/graphics/HardwareRendererObserver");
+    gHardwareRendererObserverClassInfo.callback = GetMethodIDOrDie(env, observerClass,
+                                                                   "notifyDataAvailable", "()V");
+
+    return RegisterMethodsOrDie(env, "android/graphics/HardwareRendererObserver",
+                                gMethods.data(), gMethods.size());
+
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/hwui/jni/android_graphics_HardwareRendererObserver.h b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h
new file mode 100644
index 0000000..62111fd
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 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 "jni.h"
+
+#include <FrameInfo.h>
+#include <FrameMetricsObserver.h>
+
+namespace android {
+
+/*
+ * Implements JNI layer for hwui frame metrics reporting.
+ */
+class HardwareRendererObserver : public uirenderer::FrameMetricsObserver {
+public:
+    HardwareRendererObserver(JavaVM *vm, jobject observer);
+    ~HardwareRendererObserver();
+
+    /**
+     * Retrieves frame metrics for the oldest frame that the renderer has retained. The renderer
+     * will retain a buffer until it has been retrieved, via this method, or its internal storage
+     * is exhausted at which point it informs the caller of how many frames it has failed to store
+     * since the last time this method was invoked.
+     * @param env java env required to populate the provided buffer array
+     * @param metrics output parameter that represents the buffer of metrics that is to be filled
+     * @param dropCount output parameter that is updated to reflect the number of buffers that were
+                        discarded since the last successful invocation of this method.
+     * @return true if there was data to populate the array and false otherwise. If false then
+     *         neither the metrics buffer or dropCount will be modified.
+     */
+    bool getNextBuffer(JNIEnv* env, jlongArray metrics, int* dropCount);
+
+    void notify(const int64_t* stats) override;
+
+private:
+    static constexpr int kBufferSize = static_cast<int>(uirenderer::FrameInfoIndex::NumIndexes);
+    static constexpr int kRingSize = 3;
+
+    class FrameMetricsNotification {
+    public:
+        FrameMetricsNotification() {}
+
+        std::atomic_bool hasData = false;
+        int64_t buffer[kBufferSize];
+        int dropCount = 0;
+    private:
+        // non-copyable
+        FrameMetricsNotification(const FrameMetricsNotification&) = delete;
+        FrameMetricsNotification& operator=(const FrameMetricsNotification& ) = delete;
+    };
+
+    JavaVM* const mVm;
+    jweak mObserverWeak;
+
+    int mNextFree = 0;
+    int mNextInQueue = 0;
+    FrameMetricsNotification mRingBuffer[kRingSize];
+
+    int mDroppedReports = 0;
+};
+
+} // namespace android
diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp
new file mode 100644
index 0000000..1336976
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_Matrix.cpp
@@ -0,0 +1,399 @@
+/* libs/android_runtime/android/graphics/Matrix.cpp
+**
+** Copyright 2006, 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 "GraphicsJNI.h"
+#include "Matrix.h"
+#include "SkMatrix.h"
+#include "core_jni_helpers.h"
+
+#include <jni.h>
+
+namespace android {
+
+static_assert(sizeof(SkMatrix) == 40, "Unexpected sizeof(SkMatrix), "
+        "update size in Matrix.java#NATIVE_ALLOCATION_SIZE and here");
+static_assert(SK_SCALAR_IS_FLOAT, "SK_SCALAR_IS_FLOAT is false, "
+        "only float scalar is supported");
+
+class SkMatrixGlue {
+public:
+
+    // ---------------- Regular JNI -----------------------------
+
+    static void finalizer(jlong objHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        delete obj;
+    }
+
+    static jlong getNativeFinalizer(JNIEnv* env, jobject clazz) {
+        return static_cast<jlong>(reinterpret_cast<uintptr_t>(&finalizer));
+    }
+
+    static jlong create(JNIEnv* env, jobject clazz, jlong srcHandle) {
+        const SkMatrix* src = reinterpret_cast<SkMatrix*>(srcHandle);
+        SkMatrix* obj = new SkMatrix();
+        if (src)
+            *obj = *src;
+        else
+            obj->reset();
+        return reinterpret_cast<jlong>(obj);
+    }
+
+    // ---------------- @FastNative -----------------------------
+
+    static void mapPoints(JNIEnv* env, jobject clazz, jlong matrixHandle,
+            jfloatArray dst, jint dstIndex, jfloatArray src, jint srcIndex,
+            jint ptCount, jboolean isPts) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        SkASSERT(ptCount >= 0);
+        AutoJavaFloatArray autoSrc(env, src, srcIndex + (ptCount << 1),
+                kRO_JNIAccess);
+        AutoJavaFloatArray autoDst(env, dst, dstIndex + (ptCount << 1),
+                kRW_JNIAccess);
+        float* srcArray = autoSrc.ptr() + srcIndex;
+        float* dstArray = autoDst.ptr() + dstIndex;
+        if (isPts)
+            matrix->mapPoints((SkPoint*) dstArray, (const SkPoint*) srcArray,
+                    ptCount);
+        else
+            matrix->mapVectors((SkVector*) dstArray, (const SkVector*) srcArray,
+                    ptCount);
+    }
+
+    static jboolean mapRect__RectFRectF(JNIEnv* env, jobject clazz,
+            jlong matrixHandle, jobjectArray dst, jobject src) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        SkRect dst_, src_;
+        GraphicsJNI::jrectf_to_rect(env, src, &src_);
+        jboolean rectStaysRect = matrix->mapRect(&dst_, src_);
+        GraphicsJNI::rect_to_jrectf(dst_, env, dst);
+        return rectStaysRect ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static jboolean setRectToRect(JNIEnv* env, jobject clazz,
+            jlong matrixHandle, jobject src, jobject dst, jint stfHandle) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        SkMatrix::ScaleToFit stf = static_cast<SkMatrix::ScaleToFit>(stfHandle);
+        SkRect src_;
+        GraphicsJNI::jrectf_to_rect(env, src, &src_);
+        SkRect dst_;
+        GraphicsJNI::jrectf_to_rect(env, dst, &dst_);
+        return matrix->setRectToRect(src_, dst_, stf) ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static jboolean setPolyToPoly(JNIEnv* env, jobject clazz,
+            jlong matrixHandle, jfloatArray jsrc, jint srcIndex,
+            jfloatArray jdst, jint dstIndex, jint ptCount) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        SkASSERT(srcIndex >= 0);
+        SkASSERT(dstIndex >= 0);
+        SkASSERT((unsigned )ptCount <= 4);
+
+        AutoJavaFloatArray autoSrc(env, jsrc, srcIndex + (ptCount << 1),
+                kRO_JNIAccess);
+        AutoJavaFloatArray autoDst(env, jdst, dstIndex + (ptCount << 1),
+                kRW_JNIAccess);
+        float* src = autoSrc.ptr() + srcIndex;
+        float* dst = autoDst.ptr() + dstIndex;
+        bool result;
+
+        result = matrix->setPolyToPoly((const SkPoint*) src,
+                (const SkPoint*) dst, ptCount);
+        return result ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static void getValues(JNIEnv* env, jobject clazz, jlong matrixHandle,
+            jfloatArray values) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        AutoJavaFloatArray autoValues(env, values, 9, kRW_JNIAccess);
+        float* dst = autoValues.ptr();
+        for (int i = 0; i < 9; i++) {
+            dst[i] = matrix->get(i);
+        }
+    }
+
+    static void setValues(JNIEnv* env, jobject clazz, jlong matrixHandle,
+            jfloatArray values) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        AutoJavaFloatArray autoValues(env, values, 9, kRO_JNIAccess);
+        const float* src = autoValues.ptr();
+
+        for (int i = 0; i < 9; i++) {
+            matrix->set(i, src[i]);
+        }
+    }
+
+    // ---------------- @CriticalNative -----------------------------
+
+    static jboolean isIdentity(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        return obj->isIdentity() ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static jboolean isAffine(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        return obj->asAffine(NULL) ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static jboolean rectStaysRect(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        return obj->rectStaysRect() ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->reset();
+    }
+
+    static void set(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong otherHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
+        *obj = *other;
+    }
+
+    static void setTranslate(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat dx, jfloat dy) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setTranslate(dx, dy);
+    }
+
+    static void setScale__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setScale(sx, sy, px, py);
+    }
+
+    static void setScale__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setScale(sx, sy);
+    }
+
+    static void setRotate__FFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setRotate(degrees, px, py);
+    }
+
+    static void setRotate__F(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setRotate(degrees);
+    }
+
+    static void setSinCos__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sinValue,
+            jfloat cosValue, jfloat px, jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setSinCos(sinValue, cosValue, px, py);
+    }
+
+    static void setSinCos__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sinValue,
+            jfloat cosValue) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setSinCos(sinValue, cosValue);
+    }
+
+    static void setSkew__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setSkew(kx, ky, px, py);
+    }
+
+    static void setSkew__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setSkew(kx, ky);
+    }
+
+    static void setConcat(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong aHandle, jlong bHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        SkMatrix* a = reinterpret_cast<SkMatrix*>(aHandle);
+        SkMatrix* b = reinterpret_cast<SkMatrix*>(bHandle);
+        obj->setConcat(*a, *b);
+    }
+
+    static void preTranslate(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat dx, jfloat dy) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preTranslate(dx, dy);
+    }
+
+    static void preScale__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preScale(sx, sy, px, py);
+    }
+
+    static void preScale__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preScale(sx, sy);
+    }
+
+    static void preRotate__FFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preRotate(degrees, px, py);
+    }
+
+    static void preRotate__F(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preRotate(degrees);
+    }
+
+    static void preSkew__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preSkew(kx, ky, px, py);
+    }
+
+    static void preSkew__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preSkew(kx, ky);
+    }
+
+    static void preConcat(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong otherHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
+        obj->preConcat(*other);
+    }
+
+    static void postTranslate(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat dx, jfloat dy) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->postTranslate(dx, dy);
+    }
+
+    static void postScale__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy,
+            jfloat px, jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->postScale(sx, sy, px, py);
+    }
+
+    static void postScale__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->postScale(sx, sy);
+    }
+
+    static void postRotate__FFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->postRotate(degrees, px, py);
+    }
+
+    static void postRotate__F(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->postRotate(degrees);
+    }
+
+    static void postSkew__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->postSkew(kx, ky, px, py);
+    }
+
+    static void postSkew__FF(CRITICAL_JNI_PARAMS_COMMA jlong matrixHandle, jfloat kx, jfloat ky) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        matrix->postSkew(kx, ky);
+    }
+
+    static void postConcat(CRITICAL_JNI_PARAMS_COMMA jlong matrixHandle, jlong otherHandle) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
+        matrix->postConcat(*other);
+    }
+
+    static jboolean invert(CRITICAL_JNI_PARAMS_COMMA jlong matrixHandle, jlong inverseHandle) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        SkMatrix* inverse = reinterpret_cast<SkMatrix*>(inverseHandle);
+        return matrix->invert(inverse);
+    }
+
+    static jfloat mapRadius(CRITICAL_JNI_PARAMS_COMMA jlong matrixHandle, jfloat radius) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        float result;
+        result = SkScalarToFloat(matrix->mapRadius(radius));
+        return static_cast<jfloat>(result);
+    }
+
+    static jboolean equals(CRITICAL_JNI_PARAMS_COMMA jlong aHandle, jlong bHandle) {
+        const SkMatrix* a = reinterpret_cast<SkMatrix*>(aHandle);
+        const SkMatrix* b = reinterpret_cast<SkMatrix*>(bHandle);
+        return *a == *b;
+    }
+};
+
+static const JNINativeMethod methods[] = {
+    {"nGetNativeFinalizer", "()J", (void*) SkMatrixGlue::getNativeFinalizer},
+    {"nCreate","(J)J", (void*) SkMatrixGlue::create},
+
+    // ------- @FastNative below here ---------------
+    {"nMapPoints","(J[FI[FIIZ)V", (void*) SkMatrixGlue::mapPoints},
+    {"nMapRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;)Z",
+            (void*) SkMatrixGlue::mapRect__RectFRectF},
+    {"nSetRectToRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;I)Z",
+            (void*) SkMatrixGlue::setRectToRect},
+    {"nSetPolyToPoly","(J[FI[FII)Z", (void*) SkMatrixGlue::setPolyToPoly},
+    {"nGetValues","(J[F)V", (void*) SkMatrixGlue::getValues},
+    {"nSetValues","(J[F)V", (void*) SkMatrixGlue::setValues},
+
+    // ------- @CriticalNative below here ---------------
+    {"nIsIdentity","(J)Z", (void*) SkMatrixGlue::isIdentity},
+    {"nIsAffine","(J)Z", (void*) SkMatrixGlue::isAffine},
+    {"nRectStaysRect","(J)Z", (void*) SkMatrixGlue::rectStaysRect},
+    {"nReset","(J)V", (void*) SkMatrixGlue::reset},
+    {"nSet","(JJ)V", (void*) SkMatrixGlue::set},
+    {"nSetTranslate","(JFF)V", (void*) SkMatrixGlue::setTranslate},
+    {"nSetScale","(JFFFF)V", (void*) SkMatrixGlue::setScale__FFFF},
+    {"nSetScale","(JFF)V", (void*) SkMatrixGlue::setScale__FF},
+    {"nSetRotate","(JFFF)V", (void*) SkMatrixGlue::setRotate__FFF},
+    {"nSetRotate","(JF)V", (void*) SkMatrixGlue::setRotate__F},
+    {"nSetSinCos","(JFFFF)V", (void*) SkMatrixGlue::setSinCos__FFFF},
+    {"nSetSinCos","(JFF)V", (void*) SkMatrixGlue::setSinCos__FF},
+    {"nSetSkew","(JFFFF)V", (void*) SkMatrixGlue::setSkew__FFFF},
+    {"nSetSkew","(JFF)V", (void*) SkMatrixGlue::setSkew__FF},
+    {"nSetConcat","(JJJ)V", (void*) SkMatrixGlue::setConcat},
+    {"nPreTranslate","(JFF)V", (void*) SkMatrixGlue::preTranslate},
+    {"nPreScale","(JFFFF)V", (void*) SkMatrixGlue::preScale__FFFF},
+    {"nPreScale","(JFF)V", (void*) SkMatrixGlue::preScale__FF},
+    {"nPreRotate","(JFFF)V", (void*) SkMatrixGlue::preRotate__FFF},
+    {"nPreRotate","(JF)V", (void*) SkMatrixGlue::preRotate__F},
+    {"nPreSkew","(JFFFF)V", (void*) SkMatrixGlue::preSkew__FFFF},
+    {"nPreSkew","(JFF)V", (void*) SkMatrixGlue::preSkew__FF},
+    {"nPreConcat","(JJ)V", (void*) SkMatrixGlue::preConcat},
+    {"nPostTranslate","(JFF)V", (void*) SkMatrixGlue::postTranslate},
+    {"nPostScale","(JFFFF)V", (void*) SkMatrixGlue::postScale__FFFF},
+    {"nPostScale","(JFF)V", (void*) SkMatrixGlue::postScale__FF},
+    {"nPostRotate","(JFFF)V", (void*) SkMatrixGlue::postRotate__FFF},
+    {"nPostRotate","(JF)V", (void*) SkMatrixGlue::postRotate__F},
+    {"nPostSkew","(JFFFF)V", (void*) SkMatrixGlue::postSkew__FFFF},
+    {"nPostSkew","(JFF)V", (void*) SkMatrixGlue::postSkew__FF},
+    {"nPostConcat","(JJ)V", (void*) SkMatrixGlue::postConcat},
+    {"nInvert","(JJ)Z", (void*) SkMatrixGlue::invert},
+    {"nMapRadius","(JF)F", (void*) SkMatrixGlue::mapRadius},
+    {"nEquals", "(JJ)Z", (void*) SkMatrixGlue::equals}
+};
+
+static jfieldID sNativeInstanceField;
+
+int register_android_graphics_Matrix(JNIEnv* env) {
+    int result = RegisterMethodsOrDie(env, "android/graphics/Matrix", methods, NELEM(methods));
+
+    jclass clazz = FindClassOrDie(env, "android/graphics/Matrix");
+    sNativeInstanceField = GetFieldIDOrDie(env, clazz, "native_instance", "J");
+
+    return result;
+}
+
+SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj) {
+    return reinterpret_cast<SkMatrix*>(env->GetLongField(matrixObj, sNativeInstanceField));
+}
+
+}
diff --git a/libs/hwui/jni/android_graphics_Matrix.h b/libs/hwui/jni/android_graphics_Matrix.h
new file mode 100644
index 0000000..fe90d2e
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_Matrix.h
@@ -0,0 +1,30 @@
+/*
+ * 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 _ANDROID_GRAPHICS_MATRIX_H_
+#define _ANDROID_GRAPHICS_MATRIX_H_
+
+#include "jni.h"
+#include "SkMatrix.h"
+
+namespace android {
+
+/* Gets the underlying SkMatrix from a Matrix object. */
+SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj);
+
+} // namespace android
+
+#endif // _ANDROID_GRAPHICS_MATRIX_H_
diff --git a/libs/hwui/jni/android_graphics_Picture.cpp b/libs/hwui/jni/android_graphics_Picture.cpp
new file mode 100644
index 0000000..1d085e5
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_Picture.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2008 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 "CreateJavaOutputStreamAdaptor.h"
+#include "GraphicsJNI.h"
+#include "Picture.h"
+#include "SkCanvas.h"
+#include "SkStream.h"
+#include "core_jni_helpers.h"
+#include "nativehelper/jni_macros.h"
+
+#include <jni.h>
+
+#include <array>
+
+namespace android {
+
+static jlong android_graphics_Picture_newPicture(JNIEnv* env, jobject, jlong srcHandle) {
+    const Picture* src = reinterpret_cast<Picture*>(srcHandle);
+    return reinterpret_cast<jlong>(new Picture(src));
+}
+
+static jlong android_graphics_Picture_deserialize(JNIEnv* env, jobject, jobject jstream,
+                                                  jbyteArray jstorage) {
+    Picture* picture = NULL;
+    SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage);
+    if (strm) {
+        picture = Picture::CreateFromStream(strm);
+        delete strm;
+    }
+    return reinterpret_cast<jlong>(picture);
+}
+
+static void android_graphics_Picture_killPicture(JNIEnv* env, jobject, jlong pictureHandle) {
+    Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
+    SkASSERT(picture);
+    delete picture;
+}
+
+static void android_graphics_Picture_draw(JNIEnv* env, jobject, jlong canvasHandle,
+                                          jlong pictureHandle) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasHandle);
+    Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
+    SkASSERT(canvas);
+    SkASSERT(picture);
+    picture->draw(canvas);
+}
+
+static jboolean android_graphics_Picture_serialize(JNIEnv* env, jobject, jlong pictureHandle,
+                                                   jobject jstream, jbyteArray jstorage) {
+    Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
+    SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
+
+    if (NULL != strm) {
+        picture->serialize(strm);
+        delete strm;
+        return JNI_TRUE;
+    }
+    return JNI_FALSE;
+}
+
+static jint android_graphics_Picture_getWidth(JNIEnv* env, jobject, jlong pictureHandle) {
+    Picture* pict = reinterpret_cast<Picture*>(pictureHandle);
+    return static_cast<jint>(pict->width());
+}
+
+static jint android_graphics_Picture_getHeight(JNIEnv* env, jobject, jlong pictureHandle) {
+    Picture* pict = reinterpret_cast<Picture*>(pictureHandle);
+    return static_cast<jint>(pict->height());
+}
+
+static jlong android_graphics_Picture_beginRecording(JNIEnv* env, jobject, jlong pictHandle,
+                                                     jint w, jint h) {
+    Picture* pict = reinterpret_cast<Picture*>(pictHandle);
+    Canvas* canvas = pict->beginRecording(w, h);
+    return reinterpret_cast<jlong>(canvas);
+}
+
+static void android_graphics_Picture_endRecording(JNIEnv* env, jobject, jlong pictHandle) {
+    Picture* pict = reinterpret_cast<Picture*>(pictHandle);
+    pict->endRecording();
+}
+
+static const std::array gMethods = {
+    MAKE_JNI_NATIVE_METHOD("nativeGetWidth", "(J)I",  android_graphics_Picture_getWidth),
+    MAKE_JNI_NATIVE_METHOD("nativeGetHeight", "(J)I",  android_graphics_Picture_getHeight),
+    MAKE_JNI_NATIVE_METHOD("nativeConstructor", "(J)J",  android_graphics_Picture_newPicture),
+    MAKE_JNI_NATIVE_METHOD("nativeCreateFromStream", "(Ljava/io/InputStream;[B)J", android_graphics_Picture_deserialize),
+    MAKE_JNI_NATIVE_METHOD("nativeBeginRecording", "(JII)J",  android_graphics_Picture_beginRecording),
+    MAKE_JNI_NATIVE_METHOD("nativeEndRecording", "(J)V",  android_graphics_Picture_endRecording),
+    MAKE_JNI_NATIVE_METHOD("nativeDraw", "(JJ)V",  android_graphics_Picture_draw),
+    MAKE_JNI_NATIVE_METHOD("nativeWriteToStream", "(JLjava/io/OutputStream;[B)Z", android_graphics_Picture_serialize),
+    MAKE_JNI_NATIVE_METHOD("nativeDestructor","(J)V",  android_graphics_Picture_killPicture)
+};
+
+int register_android_graphics_Picture(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/Picture", gMethods.data(), gMethods.size());
+}
+
+}; // namespace android
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
new file mode 100644
index 0000000..a8246c7
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -0,0 +1,765 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <Animator.h>
+#include <DamageAccumulator.h>
+#include <Matrix.h>
+#include <RenderNode.h>
+#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
+#include <renderthread/CanvasContext.h>
+#endif
+#include <TreeInfo.h>
+#include <hwui/Paint.h>
+#include <utils/TraceUtils.h>
+
+#include "core_jni_helpers.h"
+
+namespace android {
+
+using namespace uirenderer;
+
+#define SET_AND_DIRTY(prop, val, dirtyFlag) \
+    (reinterpret_cast<RenderNode*>(renderNodePtr)->mutateStagingProperties().prop(val) \
+        ? (reinterpret_cast<RenderNode*>(renderNodePtr)->setPropertyFieldsDirty(dirtyFlag), true) \
+        : false)
+
+// ----------------------------------------------------------------------------
+// DisplayList view properties
+// ----------------------------------------------------------------------------
+
+static void android_view_RenderNode_output(JNIEnv* env, jobject clazz, jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->output();
+}
+
+static jint android_view_RenderNode_getUsageSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->getUsageSize();
+}
+
+static jint android_view_RenderNode_getAllocatedSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->getAllocatedSize();
+}
+
+static jlong android_view_RenderNode_create(JNIEnv* env, jobject, jstring name) {
+    RenderNode* renderNode = new RenderNode();
+    renderNode->incStrong(0);
+    if (name != NULL) {
+        const char* textArray = env->GetStringUTFChars(name, NULL);
+        renderNode->setName(textArray);
+        env->ReleaseStringUTFChars(name, textArray);
+    }
+    return reinterpret_cast<jlong>(renderNode);
+}
+
+static void releaseRenderNode(RenderNode* renderNode) {
+    renderNode->decStrong(0);
+}
+
+static jlong android_view_RenderNode_getNativeFinalizer(JNIEnv* env,
+        jobject clazz) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&releaseRenderNode));
+}
+
+static void android_view_RenderNode_setDisplayList(JNIEnv* env,
+        jobject clazz, jlong renderNodePtr, jlong displayListPtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    DisplayList* newData = reinterpret_cast<DisplayList*>(displayListPtr);
+    renderNode->setStagingDisplayList(newData);
+}
+
+static jboolean android_view_RenderNode_isValid(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->isValid();
+}
+
+// ----------------------------------------------------------------------------
+// RenderProperties - setters
+// ----------------------------------------------------------------------------
+
+static jboolean android_view_RenderNode_setLayerType(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint jlayerType) {
+    LayerType layerType = static_cast<LayerType>(jlayerType);
+    return SET_AND_DIRTY(mutateLayerProperties().setType, layerType, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setLayerPaint(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong paintPtr) {
+    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
+    return SET_AND_DIRTY(mutateLayerProperties().setFromPaint, paint, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setStaticMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong matrixPtr) {
+    SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
+    return SET_AND_DIRTY(setStaticMatrix, matrix, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setAnimationMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong matrixPtr) {
+    SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
+    return SET_AND_DIRTY(setAnimationMatrix, matrix, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setClipToBounds(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+        jboolean clipToBounds) {
+    return SET_AND_DIRTY(setClipToBounds, clipToBounds, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setClipBounds(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+        jint left, jint top, jint right, jint bottom) {
+    android::uirenderer::Rect clipBounds(left, top, right, bottom);
+    return SET_AND_DIRTY(setClipBounds, clipBounds, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setClipBoundsEmpty(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    return SET_AND_DIRTY(setClipBoundsEmpty,, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setProjectBackwards(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+        jboolean shouldProject) {
+    return SET_AND_DIRTY(setProjectBackwards, shouldProject, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setProjectionReceiver(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+        jboolean shouldRecieve) {
+    return SET_AND_DIRTY(setProjectionReceiver, shouldRecieve, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setOutlineRoundRect(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+        jint left, jint top, jint right, jint bottom, jfloat radius, jfloat alpha) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->mutateStagingProperties().mutableOutline().setRoundRect(left, top, right, bottom,
+            radius, alpha);
+    renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+    return true;
+}
+
+static jboolean android_view_RenderNode_setOutlinePath(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+        jlong outlinePathPtr, jfloat alpha) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    SkPath* outlinePath = reinterpret_cast<SkPath*>(outlinePathPtr);
+    renderNode->mutateStagingProperties().mutableOutline().setPath(outlinePath, alpha);
+    renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+    return true;
+}
+
+static jboolean android_view_RenderNode_setOutlineEmpty(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->mutateStagingProperties().mutableOutline().setEmpty();
+    renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+    return true;
+}
+
+static jboolean android_view_RenderNode_setOutlineNone(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->mutateStagingProperties().mutableOutline().setNone();
+    renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+    return true;
+}
+
+static jboolean android_view_RenderNode_hasShadow(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().hasShadow();
+}
+
+static jboolean android_view_RenderNode_setSpotShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint shadowColor) {
+    return SET_AND_DIRTY(setSpotShadowColor,
+            static_cast<SkColor>(shadowColor), RenderNode::GENERIC);
+}
+
+static jint android_view_RenderNode_getSpotShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getSpotShadowColor();
+}
+
+static jboolean android_view_RenderNode_setAmbientShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+        jint shadowColor) {
+    return SET_AND_DIRTY(setAmbientShadowColor,
+            static_cast<SkColor>(shadowColor), RenderNode::GENERIC);
+}
+
+static jint android_view_RenderNode_getAmbientShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getAmbientShadowColor();
+}
+
+static jboolean android_view_RenderNode_setClipToOutline(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+        jboolean clipToOutline) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->mutateStagingProperties().mutableOutline().setShouldClip(clipToOutline);
+    renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+    return true;
+}
+
+static jboolean android_view_RenderNode_setRevealClip(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jboolean shouldClip,
+        jfloat x, jfloat y, jfloat radius) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->mutateStagingProperties().mutableRevealClip().set(
+            shouldClip, x, y, radius);
+    renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+    return true;
+}
+
+static jboolean android_view_RenderNode_setAlpha(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float alpha) {
+    return SET_AND_DIRTY(setAlpha, alpha, RenderNode::ALPHA);
+}
+
+static jboolean android_view_RenderNode_setHasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+        bool hasOverlappingRendering) {
+    return SET_AND_DIRTY(setHasOverlappingRendering, hasOverlappingRendering,
+            RenderNode::GENERIC);
+}
+
+static void android_view_RenderNode_setUsageHint(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint usageHint) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->setUsageHint(static_cast<UsageHint>(usageHint));
+}
+
+static jboolean android_view_RenderNode_setElevation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float elevation) {
+    return SET_AND_DIRTY(setElevation, elevation, RenderNode::Z);
+}
+
+static jboolean android_view_RenderNode_setTranslationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float tx) {
+    return SET_AND_DIRTY(setTranslationX, tx, RenderNode::TRANSLATION_X | RenderNode::X);
+}
+
+static jboolean android_view_RenderNode_setTranslationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float ty) {
+    return SET_AND_DIRTY(setTranslationY, ty, RenderNode::TRANSLATION_Y | RenderNode::Y);
+}
+
+static jboolean android_view_RenderNode_setTranslationZ(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float tz) {
+    return SET_AND_DIRTY(setTranslationZ, tz, RenderNode::TRANSLATION_Z | RenderNode::Z);
+}
+
+static jboolean android_view_RenderNode_setRotation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float rotation) {
+    return SET_AND_DIRTY(setRotation, rotation, RenderNode::ROTATION);
+}
+
+static jboolean android_view_RenderNode_setRotationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float rx) {
+    return SET_AND_DIRTY(setRotationX, rx, RenderNode::ROTATION_X);
+}
+
+static jboolean android_view_RenderNode_setRotationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float ry) {
+    return SET_AND_DIRTY(setRotationY, ry, RenderNode::ROTATION_Y);
+}
+
+static jboolean android_view_RenderNode_setScaleX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float sx) {
+    return SET_AND_DIRTY(setScaleX, sx, RenderNode::SCALE_X);
+}
+
+static jboolean android_view_RenderNode_setScaleY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float sy) {
+    return SET_AND_DIRTY(setScaleY, sy, RenderNode::SCALE_Y);
+}
+
+static jboolean android_view_RenderNode_setPivotX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float px) {
+    return SET_AND_DIRTY(setPivotX, px, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setPivotY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float py) {
+    return SET_AND_DIRTY(setPivotY, py, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_resetPivot(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    return SET_AND_DIRTY(resetPivot, /* void */, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setCameraDistance(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float distance) {
+    return SET_AND_DIRTY(setCameraDistance, distance, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setLeft(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int left) {
+    return SET_AND_DIRTY(setLeft, left, RenderNode::X);
+}
+
+static jboolean android_view_RenderNode_setTop(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int top) {
+    return SET_AND_DIRTY(setTop, top, RenderNode::Y);
+}
+
+static jboolean android_view_RenderNode_setRight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int right) {
+    return SET_AND_DIRTY(setRight, right, RenderNode::X);
+}
+
+static jboolean android_view_RenderNode_setBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int bottom) {
+    return SET_AND_DIRTY(setBottom, bottom, RenderNode::Y);
+}
+
+static jint android_view_RenderNode_getLeft(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getLeft();
+}
+
+static jint android_view_RenderNode_getTop(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getTop();
+}
+
+static jint android_view_RenderNode_getRight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getRight();
+}
+
+static jint android_view_RenderNode_getBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getBottom();
+}
+
+static jboolean android_view_RenderNode_setLeftTopRightBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+        int left, int top, int right, int bottom) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    if (renderNode->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom)) {
+        renderNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+        return true;
+    }
+    return false;
+}
+
+static jboolean android_view_RenderNode_offsetLeftAndRight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint offset) {
+    return SET_AND_DIRTY(offsetLeftRight, offset, RenderNode::X);
+}
+
+static jboolean android_view_RenderNode_offsetTopAndBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint offset) {
+    return SET_AND_DIRTY(offsetTopBottom, offset, RenderNode::Y);
+}
+
+// ----------------------------------------------------------------------------
+// RenderProperties - getters
+// ----------------------------------------------------------------------------
+
+static jboolean android_view_RenderNode_hasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().hasOverlappingRendering();
+}
+
+static jboolean android_view_RenderNode_getAnimationMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong outMatrixPtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    SkMatrix* outMatrix = reinterpret_cast<SkMatrix*>(outMatrixPtr);
+
+    const SkMatrix* animationMatrix = renderNode->stagingProperties().getAnimationMatrix();
+
+    if (animationMatrix) {
+        *outMatrix = *animationMatrix;
+        return JNI_TRUE;
+    }
+    return JNI_FALSE;
+}
+
+static jboolean android_view_RenderNode_getClipToBounds(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getClipToBounds();
+}
+
+static jboolean android_view_RenderNode_getClipToOutline(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getOutline().getShouldClip();
+}
+
+static jfloat android_view_RenderNode_getAlpha(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getAlpha();
+}
+
+static jfloat android_view_RenderNode_getCameraDistance(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getCameraDistance();
+}
+
+static jfloat android_view_RenderNode_getScaleX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getScaleX();
+}
+
+static jfloat android_view_RenderNode_getScaleY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getScaleY();
+}
+
+static jfloat android_view_RenderNode_getElevation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getElevation();
+}
+
+static jfloat android_view_RenderNode_getTranslationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getTranslationX();
+}
+
+static jfloat android_view_RenderNode_getTranslationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getTranslationY();
+}
+
+static jfloat android_view_RenderNode_getTranslationZ(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getTranslationZ();
+}
+
+static jfloat android_view_RenderNode_getRotation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getRotation();
+}
+
+static jfloat android_view_RenderNode_getRotationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getRotationX();
+}
+
+static jfloat android_view_RenderNode_getRotationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getRotationY();
+}
+
+static jboolean android_view_RenderNode_isPivotExplicitlySet(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().isPivotExplicitlySet();
+}
+
+static jboolean android_view_RenderNode_hasIdentityMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->mutateStagingProperties().updateMatrix();
+    return !renderNode->stagingProperties().hasTransformMatrix();
+}
+
+static jint android_view_RenderNode_getLayerType(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return static_cast<int>(renderNode->stagingProperties().layerProperties().type());
+}
+
+// ----------------------------------------------------------------------------
+// RenderProperties - computed getters
+// ----------------------------------------------------------------------------
+
+static void getTransformMatrix(jlong renderNodePtr, jlong outMatrixPtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    SkMatrix* outMatrix = reinterpret_cast<SkMatrix*>(outMatrixPtr);
+
+    renderNode->mutateStagingProperties().updateMatrix();
+    const SkMatrix* transformMatrix = renderNode->stagingProperties().getTransformMatrix();
+
+    if (transformMatrix) {
+        *outMatrix = *transformMatrix;
+    } else {
+        outMatrix->setIdentity();
+    }
+}
+
+static void android_view_RenderNode_getTransformMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong outMatrixPtr) {
+    getTransformMatrix(renderNodePtr, outMatrixPtr);
+}
+
+static void android_view_RenderNode_getInverseTransformMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+        jlong outMatrixPtr) {
+    // load transform matrix
+    getTransformMatrix(renderNodePtr, outMatrixPtr);
+    SkMatrix* outMatrix = reinterpret_cast<SkMatrix*>(outMatrixPtr);
+
+    // return it inverted
+    if (!outMatrix->invert(outMatrix)) {
+        // failed to load inverse, pass back identity
+        outMatrix->setIdentity();
+    }
+}
+
+static jfloat android_view_RenderNode_getPivotX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->mutateStagingProperties().updateMatrix();
+    return renderNode->stagingProperties().getPivotX();
+}
+
+static jfloat android_view_RenderNode_getPivotY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->mutateStagingProperties().updateMatrix();
+    return renderNode->stagingProperties().getPivotY();
+}
+
+static jint android_view_RenderNode_getWidth(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getWidth();
+}
+
+static jint android_view_RenderNode_getHeight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getHeight();
+}
+
+static jboolean android_view_RenderNode_setAllowForceDark(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jboolean allow) {
+    return SET_AND_DIRTY(setAllowForceDark, allow, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_getAllowForceDark(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getAllowForceDark();
+}
+
+static jlong android_view_RenderNode_getUniqueId(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->uniqueId();
+}
+
+// ----------------------------------------------------------------------------
+// RenderProperties - Animations
+// ----------------------------------------------------------------------------
+
+static void android_view_RenderNode_addAnimator(JNIEnv* env, jobject clazz, jlong renderNodePtr,
+        jlong animatorPtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr);
+    renderNode->addAnimator(animator);
+}
+
+static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz,
+        jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->animators().endAllStagingAnimators();
+}
+
+// ----------------------------------------------------------------------------
+// SurfaceView position callback
+// ----------------------------------------------------------------------------
+
+jmethodID gPositionListener_PositionChangedMethod;
+jmethodID gPositionListener_PositionLostMethod;
+
+static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
+        jlong renderNodePtr, jobject listener) {
+    class PositionListenerTrampoline : public RenderNode::PositionListener {
+    public:
+        PositionListenerTrampoline(JNIEnv* env, jobject listener) {
+            env->GetJavaVM(&mVm);
+            mWeakRef = env->NewWeakGlobalRef(listener);
+        }
+
+        virtual ~PositionListenerTrampoline() {
+            jnienv()->DeleteWeakGlobalRef(mWeakRef);
+            mWeakRef = nullptr;
+        }
+
+        virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) override {
+            if (CC_UNLIKELY(!mWeakRef || !info.updateWindowPositions)) return;
+
+            Matrix4 transform;
+            info.damageAccumulator->computeCurrentTransform(&transform);
+            const RenderProperties& props = node.properties();
+            uirenderer::Rect bounds(props.getWidth(), props.getHeight());
+            transform.mapRect(bounds);
+
+            if (CC_LIKELY(transform.isPureTranslate())) {
+                // snap/round the computed bounds, so they match the rounding behavior
+                // of the clear done in SurfaceView#draw().
+                bounds.snapGeometryToPixelBoundaries(false);
+            } else {
+                // Conservatively round out so the punched hole (in the ZOrderOnTop = true case)
+                // doesn't extend beyond the other window
+                bounds.roundOut();
+            }
+
+            if (mPreviousPosition == bounds) {
+                return;
+            }
+            mPreviousPosition = bounds;
+
+#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
+            incStrong(0);
+            auto functor = std::bind(
+                std::mem_fn(&PositionListenerTrampoline::doUpdatePositionAsync), this,
+                (jlong) info.canvasContext.getFrameNumber(),
+                (jint) bounds.left, (jint) bounds.top,
+                (jint) bounds.right, (jint) bounds.bottom);
+
+            info.canvasContext.enqueueFrameWork(std::move(functor));
+#endif
+        }
+
+        virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override {
+            if (CC_UNLIKELY(!mWeakRef || (info && !info->updateWindowPositions))) return;
+
+            if (mPreviousPosition.isEmpty()) {
+                return;
+            }
+            mPreviousPosition.setEmpty();
+
+            ATRACE_NAME("SurfaceView position lost");
+            JNIEnv* env = jnienv();
+            jobject localref = env->NewLocalRef(mWeakRef);
+            if (CC_UNLIKELY(!localref)) {
+                jnienv()->DeleteWeakGlobalRef(mWeakRef);
+                mWeakRef = nullptr;
+                return;
+            }
+#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
+            // TODO: Remember why this is synchronous and then make a comment
+            env->CallVoidMethod(localref, gPositionListener_PositionLostMethod,
+                    info ? info->canvasContext.getFrameNumber() : 0);
+#endif
+            env->DeleteLocalRef(localref);
+        }
+
+    private:
+        JNIEnv* jnienv() {
+            JNIEnv* env;
+            if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+                LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", mVm);
+            }
+            return env;
+        }
+
+        void doUpdatePositionAsync(jlong frameNumber, jint left, jint top,
+                jint right, jint bottom) {
+            ATRACE_NAME("Update SurfaceView position");
+
+            JNIEnv* env = jnienv();
+            jobject localref = env->NewLocalRef(mWeakRef);
+            if (CC_UNLIKELY(!localref)) {
+                env->DeleteWeakGlobalRef(mWeakRef);
+                mWeakRef = nullptr;
+            } else {
+                env->CallVoidMethod(localref, gPositionListener_PositionChangedMethod,
+                        frameNumber, left, top, right, bottom);
+                env->DeleteLocalRef(localref);
+            }
+
+            // We need to release ourselves here
+            decStrong(0);
+        }
+
+        JavaVM* mVm;
+        jobject mWeakRef;
+        uirenderer::Rect mPreviousPosition;
+    };
+
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->setPositionListener(new PositionListenerTrampoline(env, listener));
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/graphics/RenderNode";
+
+static const JNINativeMethod gMethods[] = {
+// ----------------------------------------------------------------------------
+// Regular JNI
+// ----------------------------------------------------------------------------
+    { "nCreate",               "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create },
+    { "nGetNativeFinalizer",   "()J",    (void*) android_view_RenderNode_getNativeFinalizer },
+    { "nOutput",               "(J)V",    (void*) android_view_RenderNode_output },
+    { "nGetUsageSize",         "(J)I",    (void*) android_view_RenderNode_getUsageSize },
+    { "nGetAllocatedSize",         "(J)I",    (void*) android_view_RenderNode_getAllocatedSize },
+    { "nAddAnimator",              "(JJ)V", (void*) android_view_RenderNode_addAnimator },
+    { "nEndAllAnimators",          "(J)V", (void*) android_view_RenderNode_endAllAnimators },
+    { "nRequestPositionUpdates",   "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V", (void*) android_view_RenderNode_requestPositionUpdates },
+    { "nSetDisplayList",       "(JJ)V",   (void*) android_view_RenderNode_setDisplayList },
+
+
+// ----------------------------------------------------------------------------
+// Fast JNI via @CriticalNative annotation in RenderNode.java
+// ----------------------------------------------------------------------------
+    { "nSetDisplayList",       "(JJ)V",   (void*) android_view_RenderNode_setDisplayList },
+
+
+// ----------------------------------------------------------------------------
+// Critical JNI via @CriticalNative annotation in RenderNode.java
+// ----------------------------------------------------------------------------
+    { "nIsValid",              "(J)Z",   (void*) android_view_RenderNode_isValid },
+    { "nSetLayerType",         "(JI)Z",  (void*) android_view_RenderNode_setLayerType },
+    { "nGetLayerType",         "(J)I",   (void*) android_view_RenderNode_getLayerType },
+    { "nSetLayerPaint",        "(JJ)Z",  (void*) android_view_RenderNode_setLayerPaint },
+    { "nSetStaticMatrix",      "(JJ)Z",  (void*) android_view_RenderNode_setStaticMatrix },
+    { "nSetAnimationMatrix",   "(JJ)Z",  (void*) android_view_RenderNode_setAnimationMatrix },
+    { "nGetAnimationMatrix",   "(JJ)Z",  (void*) android_view_RenderNode_getAnimationMatrix },
+    { "nSetClipToBounds",      "(JZ)Z",  (void*) android_view_RenderNode_setClipToBounds },
+    { "nGetClipToBounds",      "(J)Z",   (void*) android_view_RenderNode_getClipToBounds },
+    { "nSetClipBounds",        "(JIIII)Z", (void*) android_view_RenderNode_setClipBounds },
+    { "nSetClipBoundsEmpty",   "(J)Z",   (void*) android_view_RenderNode_setClipBoundsEmpty },
+    { "nSetProjectBackwards",  "(JZ)Z",  (void*) android_view_RenderNode_setProjectBackwards },
+    { "nSetProjectionReceiver","(JZ)Z",  (void*) android_view_RenderNode_setProjectionReceiver },
+
+    { "nSetOutlineRoundRect",  "(JIIIIFF)Z", (void*) android_view_RenderNode_setOutlineRoundRect },
+    { "nSetOutlinePath",       "(JJF)Z", (void*) android_view_RenderNode_setOutlinePath },
+    { "nSetOutlineEmpty",      "(J)Z",   (void*) android_view_RenderNode_setOutlineEmpty },
+    { "nSetOutlineNone",       "(J)Z",   (void*) android_view_RenderNode_setOutlineNone },
+    { "nHasShadow",            "(J)Z",   (void*) android_view_RenderNode_hasShadow },
+    { "nSetSpotShadowColor",   "(JI)Z",  (void*) android_view_RenderNode_setSpotShadowColor },
+    { "nGetSpotShadowColor",   "(J)I",   (void*) android_view_RenderNode_getSpotShadowColor },
+    { "nSetAmbientShadowColor","(JI)Z",  (void*) android_view_RenderNode_setAmbientShadowColor },
+    { "nGetAmbientShadowColor","(J)I",   (void*) android_view_RenderNode_getAmbientShadowColor },
+    { "nSetClipToOutline",     "(JZ)Z",  (void*) android_view_RenderNode_setClipToOutline },
+    { "nSetRevealClip",        "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
+
+    { "nSetAlpha",             "(JF)Z",  (void*) android_view_RenderNode_setAlpha },
+    { "nSetHasOverlappingRendering", "(JZ)Z",
+            (void*) android_view_RenderNode_setHasOverlappingRendering },
+    { "nSetUsageHint",    "(JI)V", (void*) android_view_RenderNode_setUsageHint },
+    { "nSetElevation",         "(JF)Z",  (void*) android_view_RenderNode_setElevation },
+    { "nSetTranslationX",      "(JF)Z",  (void*) android_view_RenderNode_setTranslationX },
+    { "nSetTranslationY",      "(JF)Z",  (void*) android_view_RenderNode_setTranslationY },
+    { "nSetTranslationZ",      "(JF)Z",  (void*) android_view_RenderNode_setTranslationZ },
+    { "nSetRotation",          "(JF)Z",  (void*) android_view_RenderNode_setRotation },
+    { "nSetRotationX",         "(JF)Z",  (void*) android_view_RenderNode_setRotationX },
+    { "nSetRotationY",         "(JF)Z",  (void*) android_view_RenderNode_setRotationY },
+    { "nSetScaleX",            "(JF)Z",  (void*) android_view_RenderNode_setScaleX },
+    { "nSetScaleY",            "(JF)Z",  (void*) android_view_RenderNode_setScaleY },
+    { "nSetPivotX",            "(JF)Z",  (void*) android_view_RenderNode_setPivotX },
+    { "nSetPivotY",            "(JF)Z",  (void*) android_view_RenderNode_setPivotY },
+    { "nResetPivot",           "(J)Z",   (void*) android_view_RenderNode_resetPivot },
+    { "nSetCameraDistance",    "(JF)Z",  (void*) android_view_RenderNode_setCameraDistance },
+    { "nSetLeft",              "(JI)Z",  (void*) android_view_RenderNode_setLeft },
+    { "nSetTop",               "(JI)Z",  (void*) android_view_RenderNode_setTop },
+    { "nSetRight",             "(JI)Z",  (void*) android_view_RenderNode_setRight },
+    { "nSetBottom",            "(JI)Z",  (void*) android_view_RenderNode_setBottom },
+    { "nGetLeft",              "(J)I",  (void*) android_view_RenderNode_getLeft },
+    { "nGetTop",               "(J)I",  (void*) android_view_RenderNode_getTop },
+    { "nGetRight",             "(J)I",  (void*) android_view_RenderNode_getRight },
+    { "nGetBottom",            "(J)I",  (void*) android_view_RenderNode_getBottom },
+    { "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom },
+    { "nOffsetLeftAndRight",   "(JI)Z",  (void*) android_view_RenderNode_offsetLeftAndRight },
+    { "nOffsetTopAndBottom",   "(JI)Z",  (void*) android_view_RenderNode_offsetTopAndBottom },
+
+    { "nHasOverlappingRendering", "(J)Z",  (void*) android_view_RenderNode_hasOverlappingRendering },
+    { "nGetClipToOutline",        "(J)Z",  (void*) android_view_RenderNode_getClipToOutline },
+    { "nGetAlpha",                "(J)F",  (void*) android_view_RenderNode_getAlpha },
+    { "nGetCameraDistance",       "(J)F",  (void*) android_view_RenderNode_getCameraDistance },
+    { "nGetScaleX",               "(J)F",  (void*) android_view_RenderNode_getScaleX },
+    { "nGetScaleY",               "(J)F",  (void*) android_view_RenderNode_getScaleY },
+    { "nGetElevation",            "(J)F",  (void*) android_view_RenderNode_getElevation },
+    { "nGetTranslationX",         "(J)F",  (void*) android_view_RenderNode_getTranslationX },
+    { "nGetTranslationY",         "(J)F",  (void*) android_view_RenderNode_getTranslationY },
+    { "nGetTranslationZ",         "(J)F",  (void*) android_view_RenderNode_getTranslationZ },
+    { "nGetRotation",             "(J)F",  (void*) android_view_RenderNode_getRotation },
+    { "nGetRotationX",            "(J)F",  (void*) android_view_RenderNode_getRotationX },
+    { "nGetRotationY",            "(J)F",  (void*) android_view_RenderNode_getRotationY },
+    { "nIsPivotExplicitlySet",    "(J)Z",  (void*) android_view_RenderNode_isPivotExplicitlySet },
+    { "nHasIdentityMatrix",       "(J)Z",  (void*) android_view_RenderNode_hasIdentityMatrix },
+
+    { "nGetTransformMatrix",       "(JJ)V", (void*) android_view_RenderNode_getTransformMatrix },
+    { "nGetInverseTransformMatrix","(JJ)V", (void*) android_view_RenderNode_getInverseTransformMatrix },
+
+    { "nGetPivotX",                "(J)F",  (void*) android_view_RenderNode_getPivotX },
+    { "nGetPivotY",                "(J)F",  (void*) android_view_RenderNode_getPivotY },
+    { "nGetWidth",                 "(J)I",  (void*) android_view_RenderNode_getWidth },
+    { "nGetHeight",                "(J)I",  (void*) android_view_RenderNode_getHeight },
+    { "nSetAllowForceDark",        "(JZ)Z", (void*) android_view_RenderNode_setAllowForceDark },
+    { "nGetAllowForceDark",        "(J)Z",  (void*) android_view_RenderNode_getAllowForceDark },
+    { "nGetUniqueId",              "(J)J",  (void*) android_view_RenderNode_getUniqueId },
+};
+
+int register_android_view_RenderNode(JNIEnv* env) {
+    jclass clazz = FindClassOrDie(env, "android/graphics/RenderNode$PositionUpdateListener");
+    gPositionListener_PositionChangedMethod = GetMethodIDOrDie(env, clazz,
+            "positionChanged", "(JIIII)V");
+    gPositionListener_PositionLostMethod = GetMethodIDOrDie(env, clazz,
+            "positionLost", "(J)V");
+    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+};
+
diff --git a/libs/hwui/jni/android_graphics_TextureLayer.cpp b/libs/hwui/jni/android_graphics_TextureLayer.cpp
new file mode 100644
index 0000000..40f6180
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_TextureLayer.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+
+#include <android/surface_texture_jni.h>
+#include "core_jni_helpers.h"
+
+#include <hwui/Paint.h>
+#include <SkMatrix.h>
+#include <DeferredLayerUpdater.h>
+
+namespace android {
+
+using namespace uirenderer;
+
+static jboolean TextureLayer_prepare(JNIEnv* env, jobject clazz,
+        jlong layerUpdaterPtr, jint width, jint height, jboolean isOpaque) {
+    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
+    bool changed = false;
+    changed |= layer->setSize(width, height);
+    changed |= layer->setBlend(!isOpaque);
+    return changed;
+}
+
+static void TextureLayer_setLayerPaint(JNIEnv* env, jobject clazz,
+        jlong layerUpdaterPtr, jlong paintPtr) {
+    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
+    if (layer) {
+        Paint* paint = reinterpret_cast<Paint*>(paintPtr);
+        layer->setPaint(paint);
+    }
+}
+
+static void TextureLayer_setTransform(JNIEnv* env, jobject clazz,
+        jlong layerUpdaterPtr, jlong matrixPtr) {
+    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
+    SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
+    layer->setTransform(matrix);
+}
+
+static void TextureLayer_setSurfaceTexture(JNIEnv* env, jobject clazz,
+        jlong layerUpdaterPtr, jobject surface) {
+    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
+    ASurfaceTexture* surfaceTexture = ASurfaceTexture_fromSurfaceTexture(env, surface);
+    layer->setSurfaceTexture(AutoTextureRelease(surfaceTexture, &ASurfaceTexture_release));
+}
+
+static void TextureLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz,
+        jlong layerUpdaterPtr) {
+    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
+    layer->updateTexImage();
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/view/TextureLayer";
+
+static const JNINativeMethod gMethods[] = {
+    { "nPrepare",                "(JIIZ)Z",    (void*) TextureLayer_prepare },
+    { "nSetLayerPaint",          "(JJ)V",      (void*) TextureLayer_setLayerPaint },
+    { "nSetTransform",           "(JJ)V",      (void*) TextureLayer_setTransform },
+    { "nSetSurfaceTexture",      "(JLandroid/graphics/SurfaceTexture;)V",
+            (void*) TextureLayer_setSurfaceTexture },
+    { "nUpdateSurfaceTexture",   "(J)V",       (void*) TextureLayer_updateSurfaceTexture },
+};
+
+int register_android_view_TextureLayer(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+};
diff --git a/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
new file mode 100644
index 0000000..2073ac2
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <cutils/log.h>
+#include "core_jni_helpers.h"
+
+#include <Interpolator.h>
+
+namespace android {
+
+using namespace uirenderer;
+
+static jlong createAccelerateDecelerateInterpolator(JNIEnv* env, jobject clazz) {
+    return reinterpret_cast<jlong>(new AccelerateDecelerateInterpolator());
+}
+
+static jlong createAccelerateInterpolator(JNIEnv* env, jobject clazz, jfloat factor) {
+    return reinterpret_cast<jlong>(new AccelerateInterpolator(factor));
+}
+
+static jlong createAnticipateInterpolator(JNIEnv* env, jobject clazz, jfloat tension) {
+    return reinterpret_cast<jlong>(new AnticipateInterpolator(tension));
+}
+
+static jlong createAnticipateOvershootInterpolator(JNIEnv* env, jobject clazz, jfloat tension) {
+    return reinterpret_cast<jlong>(new AnticipateOvershootInterpolator(tension));
+}
+
+static jlong createBounceInterpolator(JNIEnv* env, jobject clazz) {
+    return reinterpret_cast<jlong>(new BounceInterpolator());
+}
+
+static jlong createCycleInterpolator(JNIEnv* env, jobject clazz, jfloat cycles) {
+    return reinterpret_cast<jlong>(new CycleInterpolator(cycles));
+}
+
+static jlong createDecelerateInterpolator(JNIEnv* env, jobject clazz, jfloat factor) {
+    return reinterpret_cast<jlong>(new DecelerateInterpolator(factor));
+}
+
+static jlong createLinearInterpolator(JNIEnv* env, jobject clazz) {
+    return reinterpret_cast<jlong>(new LinearInterpolator());
+}
+
+static jlong createOvershootInterpolator(JNIEnv* env, jobject clazz, jfloat tension) {
+    return reinterpret_cast<jlong>(new OvershootInterpolator(tension));
+}
+
+static jlong createPathInterpolator(JNIEnv* env, jobject clazz, jfloatArray jX, jfloatArray jY) {
+    jsize lenX = env->GetArrayLength(jX);
+    jsize lenY = env->GetArrayLength(jY);
+    LOG_ALWAYS_FATAL_IF(lenX != lenY || lenX <= 0, "Invalid path interpolator, x size: %d,"
+            " y size: %d", lenX, lenY);
+    std::vector<float> x(lenX);
+    std::vector<float> y(lenY);
+    env->GetFloatArrayRegion(jX, 0, lenX, x.data());
+    env->GetFloatArrayRegion(jY, 0, lenX, y.data());
+
+    return reinterpret_cast<jlong>(new PathInterpolator(std::move(x), std::move(y)));
+}
+
+static jlong createLutInterpolator(JNIEnv* env, jobject clazz, jfloatArray jlut) {
+    jsize len = env->GetArrayLength(jlut);
+    if (len <= 0) {
+        return 0;
+    }
+    float* lut = new float[len];
+    env->GetFloatArrayRegion(jlut, 0, len, lut);
+    return reinterpret_cast<jlong>(new LUTInterpolator(lut, len));
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/graphics/animation/NativeInterpolatorFactory";
+
+static const JNINativeMethod gMethods[] = {
+    { "createAccelerateDecelerateInterpolator", "()J", (void*) createAccelerateDecelerateInterpolator },
+    { "createAccelerateInterpolator", "(F)J", (void*) createAccelerateInterpolator },
+    { "createAnticipateInterpolator", "(F)J", (void*) createAnticipateInterpolator },
+    { "createAnticipateOvershootInterpolator", "(F)J", (void*) createAnticipateOvershootInterpolator },
+    { "createBounceInterpolator", "()J", (void*) createBounceInterpolator },
+    { "createCycleInterpolator", "(F)J", (void*) createCycleInterpolator },
+    { "createDecelerateInterpolator", "(F)J", (void*) createDecelerateInterpolator },
+    { "createLinearInterpolator", "()J", (void*) createLinearInterpolator },
+    { "createOvershootInterpolator", "(F)J", (void*) createOvershootInterpolator },
+    { "createPathInterpolator", "([F[F)J", (void*) createPathInterpolator },
+    { "createLutInterpolator", "([F)J", (void*) createLutInterpolator },
+};
+
+int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+
+} // namespace android
diff --git a/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp
new file mode 100644
index 0000000..878d4fc
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <Animator.h>
+#include <Interpolator.h>
+#include <RenderProperties.h>
+
+#include "core_jni_helpers.h"
+
+namespace android {
+
+using namespace uirenderer;
+
+static struct {
+    jclass clazz;
+
+    jmethodID callOnFinished;
+} gRenderNodeAnimatorClassInfo;
+
+static JNIEnv* getEnv(JavaVM* vm) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        return 0;
+    }
+    return env;
+}
+
+class AnimationListenerLifecycleChecker : public AnimationListener {
+public:
+    virtual void onAnimationFinished(BaseRenderNodeAnimator* animator) {
+        LOG_ALWAYS_FATAL("Lifecycle failure, nStart(%p) wasn't called", animator);
+    }
+};
+
+static AnimationListenerLifecycleChecker sLifecycleChecker;
+
+class AnimationListenerBridge : public AnimationListener {
+public:
+    // This holds a strong reference to a Java WeakReference<T> object. This avoids
+    // cyclic-references-of-doom. If you think "I know, just use NewWeakGlobalRef!"
+    // then you end up with basically a PhantomReference, which is totally not
+    // what we want.
+    AnimationListenerBridge(JNIEnv* env, jobject finishListener) {
+        mFinishListener = env->NewGlobalRef(finishListener);
+        env->GetJavaVM(&mJvm);
+    }
+
+    virtual ~AnimationListenerBridge() {
+        if (mFinishListener) {
+            onAnimationFinished(NULL);
+        }
+    }
+
+    virtual void onAnimationFinished(BaseRenderNodeAnimator*) {
+        LOG_ALWAYS_FATAL_IF(!mFinishListener, "Finished listener twice?");
+        JNIEnv* env = getEnv(mJvm);
+        env->CallStaticVoidMethod(
+                gRenderNodeAnimatorClassInfo.clazz,
+                gRenderNodeAnimatorClassInfo.callOnFinished,
+                mFinishListener);
+        releaseJavaObject();
+    }
+
+private:
+    void releaseJavaObject() {
+        JNIEnv* env = getEnv(mJvm);
+        env->DeleteGlobalRef(mFinishListener);
+        mFinishListener = NULL;
+    }
+
+    JavaVM* mJvm;
+    jobject mFinishListener;
+};
+
+static inline RenderPropertyAnimator::RenderProperty toRenderProperty(jint property) {
+    LOG_ALWAYS_FATAL_IF(property < 0 || property > RenderPropertyAnimator::ALPHA,
+            "Invalid property %d", property);
+    return static_cast<RenderPropertyAnimator::RenderProperty>(property);
+}
+
+static inline CanvasPropertyPaintAnimator::PaintField toPaintField(jint field) {
+    LOG_ALWAYS_FATAL_IF(field < 0
+            || field > CanvasPropertyPaintAnimator::ALPHA,
+            "Invalid paint field %d", field);
+    return static_cast<CanvasPropertyPaintAnimator::PaintField>(field);
+}
+
+static jlong createAnimator(JNIEnv* env, jobject clazz,
+        jint propertyRaw, jfloat finalValue) {
+    RenderPropertyAnimator::RenderProperty property = toRenderProperty(propertyRaw);
+    BaseRenderNodeAnimator* animator = new RenderPropertyAnimator(property, finalValue);
+    animator->setListener(&sLifecycleChecker);
+    return reinterpret_cast<jlong>( animator );
+}
+
+static jlong createCanvasPropertyFloatAnimator(JNIEnv* env, jobject clazz,
+        jlong canvasPropertyPtr, jfloat finalValue) {
+    CanvasPropertyPrimitive* canvasProperty = reinterpret_cast<CanvasPropertyPrimitive*>(canvasPropertyPtr);
+    BaseRenderNodeAnimator* animator = new CanvasPropertyPrimitiveAnimator(canvasProperty, finalValue);
+    animator->setListener(&sLifecycleChecker);
+    return reinterpret_cast<jlong>( animator );
+}
+
+static jlong createCanvasPropertyPaintAnimator(JNIEnv* env, jobject clazz,
+        jlong canvasPropertyPtr, jint paintFieldRaw,
+        jfloat finalValue) {
+    CanvasPropertyPaint* canvasProperty = reinterpret_cast<CanvasPropertyPaint*>(canvasPropertyPtr);
+    CanvasPropertyPaintAnimator::PaintField paintField = toPaintField(paintFieldRaw);
+    BaseRenderNodeAnimator* animator = new CanvasPropertyPaintAnimator(
+            canvasProperty, paintField, finalValue);
+    animator->setListener(&sLifecycleChecker);
+    return reinterpret_cast<jlong>( animator );
+}
+
+static jlong createRevealAnimator(JNIEnv* env, jobject clazz,
+        jint centerX, jint centerY, jfloat startRadius, jfloat endRadius) {
+    BaseRenderNodeAnimator* animator = new RevealAnimator(centerX, centerY, startRadius, endRadius);
+    animator->setListener(&sLifecycleChecker);
+    return reinterpret_cast<jlong>( animator );
+}
+
+static void setStartValue(JNIEnv* env, jobject clazz, jlong animatorPtr, jfloat startValue) {
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    animator->setStartValue(startValue);
+}
+
+static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong duration) {
+    LOG_ALWAYS_FATAL_IF(duration < 0, "Duration cannot be negative");
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    animator->setDuration(duration);
+}
+
+static jlong getDuration(JNIEnv* env, jobject clazz, jlong animatorPtr) {
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    return static_cast<jlong>(animator->duration());
+}
+
+static void setStartDelay(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong startDelay) {
+    LOG_ALWAYS_FATAL_IF(startDelay < 0, "Start delay cannot be negative");
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    animator->setStartDelay(startDelay);
+}
+
+static void setInterpolator(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong interpolatorPtr) {
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    Interpolator* interpolator = reinterpret_cast<Interpolator*>(interpolatorPtr);
+    animator->setInterpolator(interpolator);
+}
+
+static void setAllowRunningAsync(JNIEnv* env, jobject clazz, jlong animatorPtr, jboolean mayRunAsync) {
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    animator->setAllowRunningAsync(mayRunAsync);
+}
+
+static void setListener(JNIEnv* env, jobject clazz, jlong animatorPtr, jobject finishListener) {
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    animator->setListener(new AnimationListenerBridge(env, finishListener));
+}
+
+static void start(JNIEnv* env, jobject clazz, jlong animatorPtr) {
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    animator->start();
+}
+
+static void end(JNIEnv* env, jobject clazz, jlong animatorPtr) {
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    animator->cancel();
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/graphics/animation/RenderNodeAnimator";
+
+static const JNINativeMethod gMethods[] = {
+    { "nCreateAnimator", "(IF)J", (void*) createAnimator },
+    { "nCreateCanvasPropertyFloatAnimator", "(JF)J", (void*) createCanvasPropertyFloatAnimator },
+    { "nCreateCanvasPropertyPaintAnimator", "(JIF)J", (void*) createCanvasPropertyPaintAnimator },
+    { "nCreateRevealAnimator", "(IIFF)J", (void*) createRevealAnimator },
+    { "nSetStartValue", "(JF)V", (void*) setStartValue },
+    { "nSetDuration", "(JJ)V", (void*) setDuration },
+    { "nGetDuration", "(J)J", (void*) getDuration },
+    { "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
+    { "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
+    { "nSetAllowRunningAsync", "(JZ)V", (void*) setAllowRunningAsync },
+    { "nSetListener", "(JLandroid/graphics/animation/RenderNodeAnimator;)V", (void*) setListener},
+    { "nStart", "(J)V", (void*) start},
+    { "nEnd", "(J)V", (void*) end },
+};
+
+int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env) {
+    sLifecycleChecker.incStrong(0);
+    gRenderNodeAnimatorClassInfo.clazz = FindClassOrDie(env, kClassPathName);
+    gRenderNodeAnimatorClassInfo.clazz = MakeGlobalRefOrDie(env,
+                                                            gRenderNodeAnimatorClassInfo.clazz);
+
+    gRenderNodeAnimatorClassInfo.callOnFinished = GetStaticMethodIDOrDie(
+            env, gRenderNodeAnimatorClassInfo.clazz, "callOnFinished",
+            "(Landroid/graphics/animation/RenderNodeAnimator;)V");
+
+    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+
+} // namespace android
diff --git a/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
new file mode 100644
index 0000000..b6b5366
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
@@ -0,0 +1,221 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "OpenGLRenderer"
+
+#include "android/log.h"
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
+
+#include "Animator.h"
+#include "Interpolator.h"
+#include "PropertyValuesAnimatorSet.h"
+#include "PropertyValuesHolder.h"
+#include "VectorDrawable.h"
+
+namespace android {
+using namespace uirenderer;
+using namespace VectorDrawable;
+
+static struct {
+    jclass clazz;
+    jmethodID callOnFinished;
+} gVectorDrawableAnimatorClassInfo;
+
+static JNIEnv* getEnv(JavaVM* vm) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        return 0;
+    }
+    return env;
+}
+
+static AnimationListener* createAnimationListener(JNIEnv* env, jobject finishListener, jint id) {
+    class AnimationListenerBridge : public AnimationListener {
+    public:
+        AnimationListenerBridge(JNIEnv* env, jobject finishListener, jint id) {
+            mFinishListener = env->NewGlobalRef(finishListener);
+            env->GetJavaVM(&mJvm);
+            mId = id;
+        }
+
+        virtual ~AnimationListenerBridge() {
+            if (mFinishListener) {
+                onAnimationFinished(NULL);
+            }
+        }
+
+        virtual void onAnimationFinished(BaseRenderNodeAnimator*) {
+            LOG_ALWAYS_FATAL_IF(!mFinishListener, "Finished listener twice?");
+            JNIEnv* env = getEnv(mJvm);
+            env->CallStaticVoidMethod(
+                    gVectorDrawableAnimatorClassInfo.clazz,
+                    gVectorDrawableAnimatorClassInfo.callOnFinished,
+                    mFinishListener, mId);
+            releaseJavaObject();
+        }
+
+    private:
+        void releaseJavaObject() {
+            JNIEnv* env = getEnv(mJvm);
+            env->DeleteGlobalRef(mFinishListener);
+            mFinishListener = NULL;
+        }
+
+        JavaVM* mJvm;
+        jobject mFinishListener;
+        jint mId;
+    };
+    return new AnimationListenerBridge(env, finishListener, id);
+}
+
+static void addAnimator(JNIEnv*, jobject, jlong animatorSetPtr, jlong propertyHolderPtr,
+        jlong interpolatorPtr, jlong startDelay, jlong duration, jint repeatCount,
+        jint repeatMode) {
+    PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
+    PropertyValuesHolder* holder = reinterpret_cast<PropertyValuesHolder*>(propertyHolderPtr);
+    Interpolator* interpolator = reinterpret_cast<Interpolator*>(interpolatorPtr);
+    RepeatMode mode = static_cast<RepeatMode>(repeatMode);
+    set->addPropertyAnimator(holder, interpolator, startDelay, duration, repeatCount, mode);
+}
+
+static jlong createAnimatorSet(JNIEnv*, jobject) {
+    PropertyValuesAnimatorSet* animatorSet = new PropertyValuesAnimatorSet();
+    return reinterpret_cast<jlong>(animatorSet);
+}
+
+static void setVectorDrawableTarget(JNIEnv*, jobject,jlong animatorPtr, jlong vectorDrawablePtr) {
+    VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(vectorDrawablePtr);
+    PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorPtr);
+    set->setVectorDrawable(tree);
+}
+
+static jlong createGroupPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId,
+        jfloat startValue, jfloat endValue) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(nativePtr);
+    GroupPropertyValuesHolder* newHolder = new GroupPropertyValuesHolder(group, propertyId,
+            startValue, endValue);
+    return reinterpret_cast<jlong>(newHolder);
+}
+
+static jlong createPathDataPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jlong startValuePtr,
+        jlong endValuePtr) {
+    VectorDrawable::Path* path = reinterpret_cast<VectorDrawable::Path*>(nativePtr);
+    PathData* startData = reinterpret_cast<PathData*>(startValuePtr);
+    PathData* endData = reinterpret_cast<PathData*>(endValuePtr);
+    PathDataPropertyValuesHolder* newHolder = new PathDataPropertyValuesHolder(path,
+            startData, endData);
+    return reinterpret_cast<jlong>(newHolder);
+}
+
+static jlong createPathColorPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId,
+        int startValue, jint endValue) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(nativePtr);
+    FullPathColorPropertyValuesHolder* newHolder = new FullPathColorPropertyValuesHolder(fullPath,
+            propertyId, startValue, endValue);
+    return reinterpret_cast<jlong>(newHolder);
+}
+
+static jlong createPathPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId,
+        float startValue, jfloat endValue) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(nativePtr);
+    FullPathPropertyValuesHolder* newHolder = new FullPathPropertyValuesHolder(fullPath,
+            propertyId, startValue, endValue);
+    return reinterpret_cast<jlong>(newHolder);
+}
+
+static jlong createRootAlphaPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jfloat startValue,
+        float endValue) {
+    VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(nativePtr);
+    RootAlphaPropertyValuesHolder* newHolder = new RootAlphaPropertyValuesHolder(tree,
+            startValue, endValue);
+    return reinterpret_cast<jlong>(newHolder);
+}
+static void setFloatPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr,
+        jfloatArray srcData, jint length) {
+    jfloat* propertyData = env->GetFloatArrayElements(srcData, nullptr);
+    PropertyValuesHolderImpl<float>* holder =
+            reinterpret_cast<PropertyValuesHolderImpl<float>*>(propertyHolderPtr);
+    holder->setPropertyDataSource(propertyData, length);
+    env->ReleaseFloatArrayElements(srcData, propertyData, JNI_ABORT);
+}
+
+static void setIntPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr,
+        jintArray srcData, jint length) {
+    jint* propertyData = env->GetIntArrayElements(srcData, nullptr);
+    PropertyValuesHolderImpl<int>* holder =
+            reinterpret_cast<PropertyValuesHolderImpl<int>*>(propertyHolderPtr);
+    holder->setPropertyDataSource(propertyData, length);
+    env->ReleaseIntArrayElements(srcData, propertyData, JNI_ABORT);
+}
+
+static void start(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener, jint id) {
+    PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
+    AnimationListener* listener = createAnimationListener(env, finishListener, id);
+    set->start(listener);
+}
+
+static void reverse(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener, jint id) {
+    PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
+    AnimationListener* listener = createAnimationListener(env, finishListener, id);
+    set->reverse(listener);
+}
+
+static void end(JNIEnv*, jobject, jlong animatorSetPtr) {
+    PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
+    set->end();
+}
+
+static void reset(JNIEnv*, jobject, jlong animatorSetPtr) {
+    PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
+    set->reset();
+}
+
+static const JNINativeMethod gMethods[] = {
+    {"nCreateAnimatorSet", "()J", (void*)createAnimatorSet},
+    {"nSetVectorDrawableTarget", "(JJ)V", (void*)setVectorDrawableTarget},
+    {"nAddAnimator", "(JJJJJII)V", (void*)addAnimator},
+    {"nSetPropertyHolderData", "(J[FI)V", (void*)setFloatPropertyHolderData},
+    {"nSetPropertyHolderData", "(J[II)V", (void*)setIntPropertyHolderData},
+    {"nStart", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)start},
+    {"nReverse", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)reverse},
+
+    // ------------- @FastNative -------------------
+
+    {"nCreateGroupPropertyHolder", "(JIFF)J", (void*)createGroupPropertyHolder},
+    {"nCreatePathDataPropertyHolder", "(JJJ)J", (void*)createPathDataPropertyHolder},
+    {"nCreatePathColorPropertyHolder", "(JIII)J", (void*)createPathColorPropertyHolder},
+    {"nCreatePathPropertyHolder", "(JIFF)J", (void*)createPathPropertyHolder},
+    {"nCreateRootAlphaPropertyHolder", "(JFF)J", (void*)createRootAlphaPropertyHolder},
+    {"nEnd", "(J)V", (void*)end},
+    {"nReset", "(J)V", (void*)reset},
+};
+
+const char* const kClassPathName = "android/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT";
+int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env) {
+    gVectorDrawableAnimatorClassInfo.clazz = FindClassOrDie(env, kClassPathName);
+    gVectorDrawableAnimatorClassInfo.clazz = MakeGlobalRefOrDie(env,
+            gVectorDrawableAnimatorClassInfo.clazz);
+
+    gVectorDrawableAnimatorClassInfo.callOnFinished = GetStaticMethodIDOrDie(
+            env, gVectorDrawableAnimatorClassInfo.clazz, "callOnFinished",
+            "(Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V");
+    return RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedVectorDrawable",
+            gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp
new file mode 100644
index 0000000..58a2379
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2015 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 "GraphicsJNI.h"
+#include "jni.h"
+#include "core_jni_helpers.h"
+
+#include "PathParser.h"
+#include "VectorDrawable.h"
+
+#include <hwui/Paint.h>
+
+namespace android {
+using namespace uirenderer;
+using namespace uirenderer::VectorDrawable;
+
+/**
+ * VectorDrawable's pre-draw construction.
+ */
+static jlong createTree(JNIEnv*, jobject, jlong groupPtr) {
+    VectorDrawable::Group* rootGroup = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    VectorDrawable::Tree* tree = new VectorDrawable::Tree(rootGroup);
+    return reinterpret_cast<jlong>(tree);
+}
+
+static jlong createTreeFromCopy(JNIEnv*, jobject, jlong treePtr, jlong groupPtr) {
+    VectorDrawable::Group* rootGroup = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    VectorDrawable::Tree* treeToCopy = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+    VectorDrawable::Tree* tree = new VectorDrawable::Tree(treeToCopy, rootGroup);
+    return reinterpret_cast<jlong>(tree);
+}
+
+static jlong createEmptyFullPath(JNIEnv*, jobject) {
+    VectorDrawable::FullPath* newPath = new VectorDrawable::FullPath();
+    return reinterpret_cast<jlong>(newPath);
+}
+
+static jlong createFullPath(JNIEnv*, jobject, jlong srcFullPathPtr) {
+    VectorDrawable::FullPath* srcFullPath =
+            reinterpret_cast<VectorDrawable::FullPath*>(srcFullPathPtr);
+    VectorDrawable::FullPath* newPath = new VectorDrawable::FullPath(*srcFullPath);
+    return reinterpret_cast<jlong>(newPath);
+}
+
+static jlong createEmptyClipPath(JNIEnv*, jobject) {
+    VectorDrawable::ClipPath* newPath = new VectorDrawable::ClipPath();
+    return reinterpret_cast<jlong>(newPath);
+}
+
+static jlong createClipPath(JNIEnv*, jobject, jlong srcClipPathPtr) {
+    VectorDrawable::ClipPath* srcClipPath =
+            reinterpret_cast<VectorDrawable::ClipPath*>(srcClipPathPtr);
+    VectorDrawable::ClipPath* newPath = new VectorDrawable::ClipPath(*srcClipPath);
+    return reinterpret_cast<jlong>(newPath);
+}
+
+static jlong createEmptyGroup(JNIEnv*, jobject) {
+    VectorDrawable::Group* newGroup = new VectorDrawable::Group();
+    return reinterpret_cast<jlong>(newGroup);
+}
+
+static jlong createGroup(JNIEnv*, jobject, jlong srcGroupPtr) {
+    VectorDrawable::Group* srcGroup = reinterpret_cast<VectorDrawable::Group*>(srcGroupPtr);
+    VectorDrawable::Group* newGroup = new VectorDrawable::Group(*srcGroup);
+    return reinterpret_cast<jlong>(newGroup);
+}
+
+static void setNodeName(JNIEnv* env, jobject, jlong nodePtr, jstring nameStr) {
+    VectorDrawable::Node* node = reinterpret_cast<VectorDrawable::Node*>(nodePtr);
+    const char* nodeName = env->GetStringUTFChars(nameStr, NULL);
+    node->setName(nodeName);
+    env->ReleaseStringUTFChars(nameStr, nodeName);
+}
+
+static void addChild(JNIEnv*, jobject, jlong groupPtr, jlong childPtr) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    VectorDrawable::Node* child = reinterpret_cast<VectorDrawable::Node*>(childPtr);
+    group->addChild(child);
+}
+
+static void setAllowCaching(JNIEnv*, jobject, jlong treePtr, jboolean allowCaching) {
+    VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+    tree->setAllowCaching(allowCaching);
+}
+
+static void setAntiAlias(JNIEnv*, jobject, jlong treePtr, jboolean aa) {
+    VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+    tree->setAntiAlias(aa);
+}
+
+/**
+ * Draw
+ */
+static int draw(JNIEnv* env, jobject, jlong treePtr, jlong canvasPtr,
+        jlong colorFilterPtr, jobject jrect, jboolean needsMirroring, jboolean canReuseCache) {
+    VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    SkRect rect;
+    GraphicsJNI::jrect_to_rect(env, jrect, &rect);
+    SkColorFilter* colorFilter = reinterpret_cast<SkColorFilter*>(colorFilterPtr);
+    return tree->draw(canvas, colorFilter, rect, needsMirroring, canReuseCache);
+}
+
+/**
+ * Setters and getters for updating staging properties that can happen both pre-draw and post draw.
+ */
+static void setTreeViewportSize(JNIEnv*, jobject, jlong treePtr,
+        jfloat viewportWidth, jfloat viewportHeight) {
+    VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+    tree->mutateStagingProperties()->setViewportSize(viewportWidth, viewportHeight);
+}
+
+static jboolean setRootAlpha(JNIEnv*, jobject, jlong treePtr, jfloat alpha) {
+    VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+    return tree->mutateStagingProperties()->setRootAlpha(alpha);
+}
+
+static jfloat getRootAlpha(JNIEnv*, jobject, jlong treePtr) {
+    VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+    return tree->stagingProperties().getRootAlpha();
+}
+
+static void updateFullPathPropertiesAndStrokeStyles(JNIEnv*, jobject, jlong fullPathPtr,
+        jfloat strokeWidth, jint strokeColor, jfloat strokeAlpha, jint fillColor, jfloat fillAlpha,
+        jfloat trimPathStart, jfloat trimPathEnd, jfloat trimPathOffset, jfloat strokeMiterLimit,
+        jint strokeLineCap, jint strokeLineJoin, jint fillType) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    fullPath->mutateStagingProperties()->updateProperties(strokeWidth, strokeColor, strokeAlpha,
+            fillColor, fillAlpha, trimPathStart, trimPathEnd, trimPathOffset, strokeMiterLimit,
+            strokeLineCap, strokeLineJoin, fillType);
+}
+
+static void updateFullPathFillGradient(JNIEnv*, jobject, jlong pathPtr, jlong fillGradientPtr) {
+    VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr);
+    SkShader* fillShader = reinterpret_cast<SkShader*>(fillGradientPtr);
+    path->mutateStagingProperties()->setFillGradient(fillShader);
+}
+
+static void updateFullPathStrokeGradient(JNIEnv*, jobject, jlong pathPtr, jlong strokeGradientPtr) {
+    VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr);
+    SkShader* strokeShader = reinterpret_cast<SkShader*>(strokeGradientPtr);
+    path->mutateStagingProperties()->setStrokeGradient(strokeShader);
+}
+
+static jboolean getFullPathProperties(JNIEnv* env, jobject, jlong fullPathPtr,
+        jbyteArray outProperties, jint length) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    int8_t pathProperties[length];
+    bool success = fullPath->stagingProperties()->copyProperties(pathProperties, length);
+    env->SetByteArrayRegion(outProperties, 0, length, reinterpret_cast<int8_t*>(&pathProperties));
+    return success;
+}
+
+static jboolean getGroupProperties(JNIEnv* env, jobject, jlong groupPtr,
+        jfloatArray outProperties, jint length) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    float groupProperties[length];
+    bool success = group->stagingProperties()->copyProperties(groupProperties, length);
+    env->SetFloatArrayRegion(outProperties, 0, length, reinterpret_cast<float*>(&groupProperties));
+    return success;
+}
+
+static void updateGroupProperties(JNIEnv*, jobject, jlong groupPtr, jfloat rotate, jfloat pivotX,
+        jfloat pivotY, jfloat scaleX, jfloat scaleY, jfloat translateX, jfloat translateY) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    group->mutateStagingProperties()->updateProperties(rotate, pivotX, pivotY, scaleX, scaleY,
+            translateX, translateY);
+}
+
+static void setPathString(JNIEnv* env, jobject, jlong pathPtr, jstring inputStr,
+        jint stringLength) {
+    VectorDrawable::Path* path = reinterpret_cast<VectorDrawable::Path*>(pathPtr);
+    const char* pathString = env->GetStringUTFChars(inputStr, NULL);
+
+    PathParser::ParseResult result;
+    PathData data;
+    PathParser::getPathDataFromAsciiString(&data, &result, pathString, stringLength);
+    if (result.failureOccurred) {
+        doThrowIAE(env, result.failureMessage.c_str());
+    }
+    path->mutateStagingProperties()->setData(data);
+    env->ReleaseStringUTFChars(inputStr, pathString);
+}
+
+/**
+ * Setters and getters that should only be called from animation thread for animation purpose.
+ */
+static jfloat getRotation(JNIEnv*, jobject, jlong groupPtr) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    return group->stagingProperties()->getRotation();
+}
+
+static void setRotation(JNIEnv*, jobject, jlong groupPtr, jfloat rotation) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    group->mutateStagingProperties()->setRotation(rotation);
+}
+
+static jfloat getPivotX(JNIEnv*, jobject, jlong groupPtr) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    return group->stagingProperties()->getPivotX();
+}
+
+static void setPivotX(JNIEnv*, jobject, jlong groupPtr, jfloat pivotX) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    group->mutateStagingProperties()->setPivotX(pivotX);
+}
+
+static jfloat getPivotY(JNIEnv*, jobject, jlong groupPtr) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    return group->stagingProperties()->getPivotY();
+}
+
+static void setPivotY(JNIEnv*, jobject, jlong groupPtr, jfloat pivotY) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    group->mutateStagingProperties()->setPivotY(pivotY);
+}
+
+static jfloat getScaleX(JNIEnv*, jobject, jlong groupPtr) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    return group->stagingProperties()->getScaleX();
+}
+
+static void setScaleX(JNIEnv*, jobject, jlong groupPtr, jfloat scaleX) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    group->mutateStagingProperties()->setScaleX(scaleX);
+}
+
+static jfloat getScaleY(JNIEnv*, jobject, jlong groupPtr) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    return group->stagingProperties()->getScaleY();
+}
+
+static void setScaleY(JNIEnv*, jobject, jlong groupPtr, jfloat scaleY) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    group->mutateStagingProperties()->setScaleY(scaleY);
+}
+
+static jfloat getTranslateX(JNIEnv*, jobject, jlong groupPtr) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    return group->stagingProperties()->getTranslateX();
+}
+
+static void setTranslateX(JNIEnv*, jobject, jlong groupPtr, jfloat translateX) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    group->mutateStagingProperties()->setTranslateX(translateX);
+}
+
+static jfloat getTranslateY(JNIEnv*, jobject, jlong groupPtr) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    return group->stagingProperties()->getTranslateY();
+}
+
+static void setTranslateY(JNIEnv*, jobject, jlong groupPtr, jfloat translateY) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+    group->mutateStagingProperties()->setTranslateY(translateY);
+}
+
+static void setPathData(JNIEnv*, jobject, jlong pathPtr, jlong pathDataPtr) {
+    VectorDrawable::Path* path = reinterpret_cast<VectorDrawable::Path*>(pathPtr);
+    PathData* pathData = reinterpret_cast<PathData*>(pathDataPtr);
+    path->mutateStagingProperties()->setData(*pathData);
+}
+
+static jfloat getStrokeWidth(JNIEnv*, jobject, jlong fullPathPtr) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    return fullPath->stagingProperties()->getStrokeWidth();
+}
+
+static void setStrokeWidth(JNIEnv*, jobject, jlong fullPathPtr, jfloat strokeWidth) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    fullPath->mutateStagingProperties()->setStrokeWidth(strokeWidth);
+}
+
+static jint getStrokeColor(JNIEnv*, jobject, jlong fullPathPtr) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    return fullPath->stagingProperties()->getStrokeColor();
+}
+
+static void setStrokeColor(JNIEnv*, jobject, jlong fullPathPtr, jint strokeColor) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    fullPath->mutateStagingProperties()->setStrokeColor(strokeColor);
+}
+
+static jfloat getStrokeAlpha(JNIEnv*, jobject, jlong fullPathPtr) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    return fullPath->stagingProperties()->getStrokeAlpha();
+}
+
+static void setStrokeAlpha(JNIEnv*, jobject, jlong fullPathPtr, jfloat strokeAlpha) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    fullPath->mutateStagingProperties()->setStrokeAlpha(strokeAlpha);
+}
+
+static jint getFillColor(JNIEnv*, jobject, jlong fullPathPtr) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    return fullPath->stagingProperties()->getFillColor();
+}
+
+static void setFillColor(JNIEnv*, jobject, jlong fullPathPtr, jint fillColor) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    fullPath->mutateStagingProperties()->setFillColor(fillColor);
+}
+
+static jfloat getFillAlpha(JNIEnv*, jobject, jlong fullPathPtr) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    return fullPath->stagingProperties()->getFillAlpha();
+}
+
+static void setFillAlpha(JNIEnv*, jobject, jlong fullPathPtr, jfloat fillAlpha) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    fullPath->mutateStagingProperties()->setFillAlpha(fillAlpha);
+}
+
+static jfloat getTrimPathStart(JNIEnv*, jobject, jlong fullPathPtr) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    return fullPath->stagingProperties()->getTrimPathStart();
+}
+
+static void setTrimPathStart(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathStart) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    fullPath->mutateStagingProperties()->setTrimPathStart(trimPathStart);
+}
+
+static jfloat getTrimPathEnd(JNIEnv*, jobject, jlong fullPathPtr) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    return fullPath->stagingProperties()->getTrimPathEnd();
+}
+
+static void setTrimPathEnd(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathEnd) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    fullPath->mutateStagingProperties()->setTrimPathEnd(trimPathEnd);
+}
+
+static jfloat getTrimPathOffset(JNIEnv*, jobject, jlong fullPathPtr) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    return fullPath->stagingProperties()->getTrimPathOffset();
+}
+
+static void setTrimPathOffset(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathOffset) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+    fullPath->mutateStagingProperties()->setTrimPathOffset(trimPathOffset);
+}
+
+static const JNINativeMethod gMethods[] = {
+        {"nDraw", "(JJJLandroid/graphics/Rect;ZZ)I", (void*)draw},
+        {"nGetFullPathProperties", "(J[BI)Z", (void*)getFullPathProperties},
+        {"nGetGroupProperties", "(J[FI)Z", (void*)getGroupProperties},
+        {"nSetPathString", "(JLjava/lang/String;I)V", (void*)setPathString},
+        {"nSetName", "(JLjava/lang/String;)V", (void*)setNodeName},
+
+        // ------------- @FastNative ----------------
+
+        {"nCreateTree", "(J)J", (void*)createTree},
+        {"nCreateTreeFromCopy", "(JJ)J", (void*)createTreeFromCopy},
+        {"nSetRendererViewportSize", "(JFF)V", (void*)setTreeViewportSize},
+        {"nSetRootAlpha", "(JF)Z", (void*)setRootAlpha},
+        {"nGetRootAlpha", "(J)F", (void*)getRootAlpha},
+        {"nSetAntiAlias", "(JZ)V", (void*)setAntiAlias},
+        {"nSetAllowCaching", "(JZ)V", (void*)setAllowCaching},
+
+        {"nCreateFullPath", "()J", (void*)createEmptyFullPath},
+        {"nCreateFullPath", "(J)J", (void*)createFullPath},
+        {"nUpdateFullPathProperties", "(JFIFIFFFFFIII)V", (void*)updateFullPathPropertiesAndStrokeStyles},
+        {"nUpdateFullPathFillGradient", "(JJ)V", (void*)updateFullPathFillGradient},
+        {"nUpdateFullPathStrokeGradient", "(JJ)V", (void*)updateFullPathStrokeGradient},
+
+        {"nCreateClipPath", "()J", (void*)createEmptyClipPath},
+        {"nCreateClipPath", "(J)J", (void*)createClipPath},
+        {"nCreateGroup", "()J", (void*)createEmptyGroup},
+        {"nCreateGroup", "(J)J", (void*)createGroup},
+        {"nUpdateGroupProperties", "(JFFFFFFF)V", (void*)updateGroupProperties},
+
+        {"nAddChild", "(JJ)V", (void*)addChild},
+        {"nGetRotation", "(J)F", (void*)getRotation},
+        {"nSetRotation", "(JF)V", (void*)setRotation},
+        {"nGetPivotX", "(J)F", (void*)getPivotX},
+        {"nSetPivotX", "(JF)V", (void*)setPivotX},
+        {"nGetPivotY", "(J)F", (void*)getPivotY},
+        {"nSetPivotY", "(JF)V", (void*)setPivotY},
+        {"nGetScaleX", "(J)F", (void*)getScaleX},
+        {"nSetScaleX", "(JF)V", (void*)setScaleX},
+        {"nGetScaleY", "(J)F", (void*)getScaleY},
+        {"nSetScaleY", "(JF)V", (void*)setScaleY},
+        {"nGetTranslateX", "(J)F", (void*)getTranslateX},
+        {"nSetTranslateX", "(JF)V", (void*)setTranslateX},
+        {"nGetTranslateY", "(J)F", (void*)getTranslateY},
+        {"nSetTranslateY", "(JF)V", (void*)setTranslateY},
+
+        {"nSetPathData", "(JJ)V", (void*)setPathData},
+        {"nGetStrokeWidth", "(J)F", (void*)getStrokeWidth},
+        {"nSetStrokeWidth", "(JF)V", (void*)setStrokeWidth},
+        {"nGetStrokeColor", "(J)I", (void*)getStrokeColor},
+        {"nSetStrokeColor", "(JI)V", (void*)setStrokeColor},
+        {"nGetStrokeAlpha", "(J)F", (void*)getStrokeAlpha},
+        {"nSetStrokeAlpha", "(JF)V", (void*)setStrokeAlpha},
+        {"nGetFillColor", "(J)I", (void*)getFillColor},
+        {"nSetFillColor", "(JI)V", (void*)setFillColor},
+        {"nGetFillAlpha", "(J)F", (void*)getFillAlpha},
+        {"nSetFillAlpha", "(JF)V", (void*)setFillAlpha},
+        {"nGetTrimPathStart", "(J)F", (void*)getTrimPathStart},
+        {"nSetTrimPathStart", "(JF)V", (void*)setTrimPathStart},
+        {"nGetTrimPathEnd", "(J)F", (void*)getTrimPathEnd},
+        {"nSetTrimPathEnd", "(JF)V", (void*)setTrimPathEnd},
+        {"nGetTrimPathOffset", "(J)F", (void*)getTrimPathOffset},
+        {"nSetTrimPathOffset", "(JF)V", (void*)setTrimPathOffset},
+};
+
+int register_android_graphics_drawable_VectorDrawable(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/drawable/VectorDrawable", gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/libs/hwui/jni/android_nio_utils.cpp b/libs/hwui/jni/android_nio_utils.cpp
new file mode 100644
index 0000000..1e6d49e
--- /dev/null
+++ b/libs/hwui/jni/android_nio_utils.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 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 "android_nio_utils.h"
+
+#include "core_jni_helpers.h"
+
+namespace android {
+
+AutoBufferPointer::AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit)
+        : fEnv(env), fCommit(commit) {
+    jlong pointer = jniGetNioBufferPointer(fEnv, nioBuffer);
+    if (pointer != 0L) {
+        // Buffer is backed by a direct buffer.
+        fArray = nullptr;
+        fElements = nullptr;
+        fPointer = reinterpret_cast<void*>(pointer);
+    } else {
+        // Buffer is backed by a managed array.
+        jint byteOffset = jniGetNioBufferBaseArrayOffset(fEnv, nioBuffer);
+        fArray = jniGetNioBufferBaseArray(fEnv, nioBuffer);
+        fElements = fEnv->GetPrimitiveArrayCritical(fArray, /* isCopy= */ nullptr);
+        fPointer = reinterpret_cast<void*>(reinterpret_cast<char*>(fElements) + byteOffset);
+    }
+}
+
+AutoBufferPointer::~AutoBufferPointer() {
+    if (nullptr != fArray) {
+        fEnv->ReleasePrimitiveArrayCritical(fArray, fElements, fCommit ? 0 : JNI_ABORT);
+    }
+}
+
+}  // namespace android
diff --git a/libs/hwui/jni/android_nio_utils.h b/libs/hwui/jni/android_nio_utils.h
new file mode 100644
index 0000000..4aaa0a7
--- /dev/null
+++ b/libs/hwui/jni/android_nio_utils.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 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 _ANDROID_NIO_UTILS_H_
+#define _ANDROID_NIO_UTILS_H_
+
+#include <nativehelper/JNIHelp.h>
+
+namespace android {
+
+/**
+ * Class providing scoped access to the memory backing a java.nio.Buffer instance.
+ *
+ * Instances of this class should only be allocated on the stack as heap allocation is not
+ * supported.
+ *
+ * Instances of this class do not create any global references for performance reasons.
+ */
+class AutoBufferPointer final {
+public:
+    /** Constructor for an AutoBufferPointer instance.
+     *
+     * @param env          The current JNI env
+     * @param nioBuffer    Instance of a java.nio.Buffer whose memory will be accessed.
+     * @param commit       JNI_TRUE if the underlying memory will be updated and should be
+     *                     copied back to the managed heap. JNI_FALSE if the data will
+     *                     not be modified or the modifications may be discarded.
+     *
+     * The commit parameter is only applicable if the buffer is backed by a managed heap
+     * array and the runtime had to provide a copy of the data rather than the original data.
+     */
+    AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit);
+
+    /** Destructor for an AutoBufferPointer instance.
+     *
+     * Releases critical managed heap array pointer if acquired.
+     */
+    ~AutoBufferPointer();
+
+    /**
+     * Returns a pointer to the current position of the buffer provided to the constructor.  This
+     * pointer is only valid whilst the AutoBufferPointer instance remains in scope.
+     */
+    void* pointer() const { return fPointer; }
+
+private:
+    JNIEnv* const fEnv;
+    void* fPointer;   // Pointer to current buffer position when constructed.
+    void* fElements;  // Pointer to array element 0 (null if buffer is direct, may be
+                      // within fArray or point to a copy of the array).
+    jarray fArray;    // Pointer to array on managed heap.
+    const jboolean fCommit;  // Flag to commit data to source (when fElements is a copy of fArray).
+
+    // Unsupported constructors and operators.
+    AutoBufferPointer() = delete;
+    AutoBufferPointer(AutoBufferPointer&) = delete;
+    AutoBufferPointer& operator=(AutoBufferPointer&) = delete;
+    static void* operator new(size_t);
+    static void* operator new[](size_t);
+    static void* operator new(size_t, void*);
+    static void* operator new[](size_t, void*);
+    static void operator delete(void*, size_t);
+    static void operator delete[](void*, size_t);
+};
+
+}   /* namespace android */
+
+#endif  // _ANDROID_NIO_UTILS_H_
diff --git a/libs/hwui/jni/android_util_PathParser.cpp b/libs/hwui/jni/android_util_PathParser.cpp
new file mode 100644
index 0000000..10efb95
--- /dev/null
+++ b/libs/hwui/jni/android_util_PathParser.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 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 "jni.h"
+#include "GraphicsJNI.h"
+
+#include <PathParser.h>
+#include <SkPath.h>
+#include <utils/VectorDrawableUtils.h>
+
+#include <android/log.h>
+#include "core_jni_helpers.h"
+
+namespace android {
+
+using namespace uirenderer;
+
+static void parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring inputPathStr,
+        jint strLength) {
+    const char* pathString = env->GetStringUTFChars(inputPathStr, NULL);
+    SkPath* skPath = reinterpret_cast<SkPath*>(skPathHandle);
+
+    PathParser::ParseResult result;
+    PathParser::parseAsciiStringForSkPath(skPath, &result, pathString, strLength);
+    env->ReleaseStringUTFChars(inputPathStr, pathString);
+    if (result.failureOccurred) {
+        doThrowIAE(env, result.failureMessage.c_str());
+    }
+}
+
+static long createEmptyPathData(JNIEnv*, jobject) {
+    PathData* pathData = new PathData();
+    return reinterpret_cast<jlong>(pathData);
+}
+
+static long createPathData(JNIEnv*, jobject, jlong pathDataPtr) {
+    PathData* pathData = reinterpret_cast<PathData*>(pathDataPtr);
+    PathData* newPathData = new PathData(*pathData);
+    return reinterpret_cast<jlong>(newPathData);
+}
+
+static long createPathDataFromStringPath(JNIEnv* env, jobject, jstring inputStr, jint strLength) {
+    const char* pathString = env->GetStringUTFChars(inputStr, NULL);
+    PathData* pathData = new PathData();
+    PathParser::ParseResult result;
+    PathParser::getPathDataFromAsciiString(pathData, &result, pathString, strLength);
+    env->ReleaseStringUTFChars(inputStr, pathString);
+    if (!result.failureOccurred) {
+        return reinterpret_cast<jlong>(pathData);
+    } else {
+        delete pathData;
+        doThrowIAE(env, result.failureMessage.c_str());
+        return NULL;
+    }
+}
+
+static bool interpolatePathData(JNIEnv*, jobject, jlong outPathDataPtr, jlong fromPathDataPtr,
+        jlong toPathDataPtr, jfloat fraction) {
+    PathData* outPathData = reinterpret_cast<PathData*>(outPathDataPtr);
+    PathData* fromPathData = reinterpret_cast<PathData*>(fromPathDataPtr);
+    PathData* toPathData = reinterpret_cast<PathData*>(toPathDataPtr);
+    return VectorDrawableUtils::interpolatePathData(outPathData, *fromPathData,
+            *toPathData, fraction);
+}
+
+static void deletePathData(JNIEnv*, jobject, jlong pathDataHandle) {
+    PathData* pathData = reinterpret_cast<PathData*>(pathDataHandle);
+    delete pathData;
+}
+
+static bool canMorphPathData(JNIEnv*, jobject, jlong fromPathDataPtr, jlong toPathDataPtr) {
+    PathData* fromPathData = reinterpret_cast<PathData*>(fromPathDataPtr);
+    PathData* toPathData = reinterpret_cast<PathData*>(toPathDataPtr);
+    return VectorDrawableUtils::canMorph(*fromPathData, *toPathData);
+}
+
+static void setPathData(JNIEnv*, jobject, jlong outPathDataPtr, jlong fromPathDataPtr) {
+    PathData* fromPathData = reinterpret_cast<PathData*>(fromPathDataPtr);
+    PathData* outPathData = reinterpret_cast<PathData*>(outPathDataPtr);
+    *outPathData = *fromPathData;
+}
+
+static void setSkPathFromPathData(JNIEnv*, jobject, jlong outPathPtr, jlong pathDataPtr) {
+    PathData* pathData = reinterpret_cast<PathData*>(pathDataPtr);
+    SkPath* skPath = reinterpret_cast<SkPath*>(outPathPtr);
+    VectorDrawableUtils::verbsToPath(skPath, *pathData);
+}
+
+static const JNINativeMethod gMethods[] = {
+    {"nParseStringForPath", "(JLjava/lang/String;I)V", (void*)parseStringForPath},
+    {"nCreatePathDataFromString", "(Ljava/lang/String;I)J", (void*)createPathDataFromStringPath},
+
+    // ---------------- @FastNative -----------------
+
+    {"nCreateEmptyPathData", "()J", (void*)createEmptyPathData},
+    {"nCreatePathData", "(J)J", (void*)createPathData},
+    {"nInterpolatePathData", "(JJJF)Z", (void*)interpolatePathData},
+    {"nFinalize", "(J)V", (void*)deletePathData},
+    {"nCanMorph", "(JJ)Z", (void*)canMorphPathData},
+    {"nSetPathData", "(JJ)V", (void*)setPathData},
+    {"nCreatePathFromPathData", "(JJ)V", (void*)setSkPathFromPathData},
+};
+
+int register_android_util_PathParser(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/util/PathParser", gMethods, NELEM(gMethods));
+}
+};
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
new file mode 100644
index 0000000..bfb9bae
--- /dev/null
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "Minikin"
+
+#include <nativehelper/JNIHelp.h>
+#include <core_jni_helpers.h>
+
+#include "SkData.h"
+#include "SkFontMgr.h"
+#include "SkRefCnt.h"
+#include "SkTypeface.h"
+#include "GraphicsJNI.h"
+#include <nativehelper/ScopedUtfChars.h>
+#include <android_runtime/AndroidRuntime.h>
+#include "Utils.h"
+#include "FontUtils.h"
+
+#include <hwui/MinikinSkia.h>
+#include <hwui/Typeface.h>
+#include <minikin/FontFamily.h>
+#include <ui/FatVector.h>
+
+#include <memory>
+
+namespace android {
+
+struct NativeFontBuilder {
+    std::vector<minikin::FontVariation> axes;
+};
+
+static inline NativeFontBuilder* toBuilder(jlong ptr) {
+    return reinterpret_cast<NativeFontBuilder*>(ptr);
+}
+
+static void releaseFont(jlong font) {
+    delete reinterpret_cast<FontWrapper*>(font);
+}
+
+static void release_global_ref(const void* /*data*/, void* context) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    if (env == nullptr) {
+        JavaVMAttachArgs args;
+        args.version = JNI_VERSION_1_4;
+        args.name = "release_font_data";
+        args.group = nullptr;
+        jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args);
+        if (result != JNI_OK) {
+            ALOGE("failed to attach to thread to release global ref.");
+            return;
+        }
+    }
+
+    jobject obj = reinterpret_cast<jobject>(context);
+    env->DeleteGlobalRef(obj);
+}
+
+// Regular JNI
+static jlong Font_Builder_initBuilder(JNIEnv*, jobject) {
+    return reinterpret_cast<jlong>(new NativeFontBuilder());
+}
+
+// Critical Native
+static void Font_Builder_addAxis(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jint tag, jfloat value) {
+    toBuilder(builderPtr)->axes.emplace_back(static_cast<minikin::AxisTag>(tag), value);
+}
+
+// Regular JNI
+static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jobject buffer,
+        jstring filePath, jint weight, jboolean italic, jint ttcIndex) {
+    NPE_CHECK_RETURN_ZERO(env, buffer);
+    std::unique_ptr<NativeFontBuilder> builder(toBuilder(builderPtr));
+    const void* fontPtr = env->GetDirectBufferAddress(buffer);
+    if (fontPtr == nullptr) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "Not a direct buffer");
+        return 0;
+    }
+    jlong fontSize = env->GetDirectBufferCapacity(buffer);
+    if (fontSize <= 0) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "buffer size must not be zero or negative");
+        return 0;
+    }
+    ScopedUtfChars fontPath(env, filePath);
+    jobject fontRef = MakeGlobalRefOrDie(env, buffer);
+    sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
+            release_global_ref, reinterpret_cast<void*>(fontRef)));
+
+    FatVector<SkFontArguments::Axis, 2> skiaAxes;
+    for (const auto& axis : builder->axes) {
+        skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
+    }
+
+    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));
+
+    SkFontArguments params;
+    params.setCollectionIndex(ttcIndex);
+    params.setAxes(skiaAxes.data(), skiaAxes.size());
+
+    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+    sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params));
+    if (face == nullptr) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "Failed to create internal object. maybe invalid font data.");
+        return 0;
+    }
+    std::shared_ptr<minikin::MinikinFont> minikinFont =
+            std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize,
+                                              std::string_view(fontPath.c_str(), fontPath.size()),
+                                              ttcIndex, builder->axes);
+    minikin::Font font = minikin::Font::Builder(minikinFont).setWeight(weight)
+                    .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build();
+    return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
+}
+
+// Critical Native
+static jlong Font_Builder_getReleaseNativeFont(CRITICAL_JNI_PARAMS) {
+    return reinterpret_cast<jlong>(releaseFont);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gFontBuilderMethods[] = {
+    { "nInitBuilder", "()J", (void*) Font_Builder_initBuilder },
+    { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
+    { "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build },
+    { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont },
+};
+
+int register_android_graphics_fonts_Font(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods,
+            NELEM(gFontBuilderMethods));
+}
+
+}
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
new file mode 100644
index 0000000..b0d10c3
--- /dev/null
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "Minikin"
+
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <core_jni_helpers.h>
+
+#include "FontUtils.h"
+
+#include <minikin/FontFamily.h>
+#include <minikin/LocaleList.h>
+
+#include <memory>
+
+namespace android {
+
+struct NativeFamilyBuilder {
+    std::vector<minikin::Font> fonts;
+};
+
+static inline NativeFamilyBuilder* toBuilder(jlong ptr) {
+    return reinterpret_cast<NativeFamilyBuilder*>(ptr);
+}
+
+static inline FontWrapper* toFontWrapper(jlong ptr) {
+    return reinterpret_cast<FontWrapper*>(ptr);
+}
+
+static void releaseFontFamily(jlong family) {
+    delete reinterpret_cast<FontFamilyWrapper*>(family);
+}
+
+// Regular JNI
+static jlong FontFamily_Builder_initBuilder(JNIEnv*, jobject) {
+    return reinterpret_cast<jlong>(new NativeFamilyBuilder());
+}
+
+// Critical Native
+static void FontFamily_Builder_addFont(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jlong fontPtr) {
+    toBuilder(builderPtr)->fonts.push_back(toFontWrapper(fontPtr)->font);
+}
+
+// Regular JNI
+static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr,
+            jstring langTags, jint variant, jboolean isCustomFallback) {
+    std::unique_ptr<NativeFamilyBuilder> builder(toBuilder(builderPtr));
+    uint32_t localeId;
+    if (langTags == nullptr) {
+        localeId = minikin::registerLocaleList("");
+    } else {
+        ScopedUtfChars str(env, langTags);
+        localeId = minikin::registerLocaleList(str.c_str());
+    }
+    std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
+            localeId, static_cast<minikin::FamilyVariant>(variant), std::move(builder->fonts),
+            isCustomFallback);
+    if (family->getCoverage().length() == 0) {
+        // No coverage means minikin rejected given font for some reasons.
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "Failed to create internal object. maybe invalid font data");
+        return 0;
+    }
+    return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
+}
+
+// CriticalNative
+static jlong FontFamily_Builder_GetReleaseFunc(CRITICAL_JNI_PARAMS) {
+    return reinterpret_cast<jlong>(releaseFontFamily);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gFontFamilyBuilderMethods[] = {
+    { "nInitBuilder", "()J", (void*) FontFamily_Builder_initBuilder },
+    { "nAddFont", "(JJ)V", (void*) FontFamily_Builder_addFont },
+    { "nBuild", "(JLjava/lang/String;IZ)J", (void*) FontFamily_Builder_build },
+
+    { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
+};
+
+int register_android_graphics_fonts_FontFamily(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily$Builder",
+            gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods));
+}
+
+}
diff --git a/libs/hwui/jni/pdf/PdfDocument.cpp b/libs/hwui/jni/pdf/PdfDocument.cpp
new file mode 100644
index 0000000..5f67d30
--- /dev/null
+++ b/libs/hwui/jni/pdf/PdfDocument.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2013 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 "jni.h"
+#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
+#include <vector>
+
+#include "CreateJavaOutputStreamAdaptor.h"
+
+#include "SkPDFDocument.h"
+#include "SkPicture.h"
+#include "SkPictureRecorder.h"
+#include "SkRect.h"
+#include "SkStream.h"
+
+#include <hwui/Canvas.h>
+
+namespace android {
+
+struct PageRecord {
+
+    PageRecord(int width, int height, const SkRect& contentRect)
+            : mPictureRecorder(new SkPictureRecorder())
+            , mPicture(NULL)
+            , mWidth(width)
+            , mHeight(height) {
+        mContentRect = contentRect;
+    }
+
+    ~PageRecord() {
+        delete mPictureRecorder;
+        if (NULL != mPicture) {
+            mPicture->unref();
+        }
+    }
+
+    SkPictureRecorder* mPictureRecorder;
+    SkPicture* mPicture;
+    const int mWidth;
+    const int mHeight;
+    SkRect mContentRect;
+};
+
+class PdfDocument {
+public:
+    PdfDocument() {
+        mCurrentPage = NULL;
+    }
+
+    SkCanvas* startPage(int width, int height,
+            int contentLeft, int contentTop, int contentRight, int contentBottom) {
+        assert(mCurrentPage == NULL);
+
+        SkRect contentRect = SkRect::MakeLTRB(
+                contentLeft, contentTop, contentRight, contentBottom);
+        PageRecord* page = new PageRecord(width, height, contentRect);
+        mPages.push_back(page);
+        mCurrentPage = page;
+
+        SkCanvas* canvas = page->mPictureRecorder->beginRecording(
+                SkRect::MakeWH(contentRect.width(), contentRect.height()));
+
+        return canvas;
+    }
+
+    void finishPage() {
+        assert(mCurrentPage != NULL);
+        assert(mCurrentPage->mPictureRecorder != NULL);
+        assert(mCurrentPage->mPicture == NULL);
+        mCurrentPage->mPicture = mCurrentPage->mPictureRecorder->finishRecordingAsPicture().release();
+        delete mCurrentPage->mPictureRecorder;
+        mCurrentPage->mPictureRecorder = NULL;
+        mCurrentPage = NULL;
+    }
+
+    void write(SkWStream* stream) {
+        sk_sp<SkDocument> document = SkPDF::MakeDocument(stream);
+        for (unsigned i = 0; i < mPages.size(); i++) {
+            PageRecord* page =  mPages[i];
+
+            SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight,
+                    &(page->mContentRect));
+            canvas->drawPicture(page->mPicture);
+
+            document->endPage();
+        }
+        document->close();
+    }
+
+    void close() {
+        assert(NULL == mCurrentPage);
+        for (unsigned i = 0; i < mPages.size(); i++) {
+            delete mPages[i];
+        }
+    }
+
+private:
+    ~PdfDocument() {
+        close();
+    }
+
+    std::vector<PageRecord*> mPages;
+    PageRecord* mCurrentPage;
+};
+
+static jlong nativeCreateDocument(JNIEnv* env, jobject thiz) {
+    return reinterpret_cast<jlong>(new PdfDocument());
+}
+
+static jlong nativeStartPage(JNIEnv* env, jobject thiz, jlong documentPtr,
+        jint pageWidth, jint pageHeight,
+        jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
+    PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
+    SkCanvas* canvas = document->startPage(pageWidth, pageHeight,
+            contentLeft, contentTop, contentRight, contentBottom);
+    return reinterpret_cast<jlong>(Canvas::create_canvas(canvas));
+}
+
+static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) {
+    PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
+    document->finishPage();
+}
+
+static void nativeWriteTo(JNIEnv* env, jobject thiz, jlong documentPtr, jobject out,
+        jbyteArray chunk) {
+    PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
+    SkWStream* skWStream = CreateJavaOutputStreamAdaptor(env, out, chunk);
+    document->write(skWStream);
+    delete skWStream;
+}
+
+static void nativeClose(JNIEnv* env, jobject thiz, jlong documentPtr) {
+    PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
+    document->close();
+}
+
+static const JNINativeMethod gPdfDocument_Methods[] = {
+    {"nativeCreateDocument", "()J", (void*) nativeCreateDocument},
+    {"nativeStartPage", "(JIIIIII)J", (void*) nativeStartPage},
+    {"nativeFinishPage", "(J)V", (void*) nativeFinishPage},
+    {"nativeWriteTo", "(JLjava/io/OutputStream;[B)V", (void*) nativeWriteTo},
+    {"nativeClose", "(J)V", (void*) nativeClose}
+};
+
+int register_android_graphics_pdf_PdfDocument(JNIEnv* env) {
+    return RegisterMethodsOrDie(
+            env, "android/graphics/pdf/PdfDocument", gPdfDocument_Methods,
+            NELEM(gPdfDocument_Methods));
+}
+
+};
diff --git a/libs/hwui/jni/pdf/PdfEditor.cpp b/libs/hwui/jni/pdf/PdfEditor.cpp
new file mode 100644
index 0000000..10c3026
--- /dev/null
+++ b/libs/hwui/jni/pdf/PdfEditor.cpp
@@ -0,0 +1,308 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "PdfEditor"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <log/log.h>
+#include <utils/Log.h>
+
+#include "PdfUtils.h"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
+#include "fpdfview.h"
+#include "fpdf_edit.h"
+#include "fpdf_save.h"
+#include "fpdf_transformpage.h"
+#pragma GCC diagnostic pop
+
+#include "SkMatrix.h"
+
+#include <core_jni_helpers.h>
+
+namespace android {
+
+enum PageBox {PAGE_BOX_MEDIA, PAGE_BOX_CROP};
+
+static struct {
+    jfieldID x;
+    jfieldID y;
+} gPointClassInfo;
+
+static struct {
+    jfieldID left;
+    jfieldID top;
+    jfieldID right;
+    jfieldID bottom;
+} gRectClassInfo;
+
+static jint nativeRemovePage(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex) {
+    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
+
+    FPDFPage_Delete(document, pageIndex);
+    return FPDF_GetPageCount(document);
+}
+
+struct PdfToFdWriter : FPDF_FILEWRITE {
+    int dstFd;
+};
+
+static bool writeAllBytes(const int fd, const void* buffer, const size_t byteCount) {
+    char* writeBuffer = static_cast<char*>(const_cast<void*>(buffer));
+    size_t remainingBytes = byteCount;
+    while (remainingBytes > 0) {
+        ssize_t writtenByteCount = write(fd, writeBuffer, remainingBytes);
+        if (writtenByteCount == -1) {
+            if (errno == EINTR) {
+                continue;
+            }
+            ALOGE("Error writing to buffer: %d", errno);
+            return false;
+        }
+        remainingBytes -= writtenByteCount;
+        writeBuffer += writtenByteCount;
+    }
+    return true;
+}
+
+static int writeBlock(FPDF_FILEWRITE* owner, const void* buffer, unsigned long size) {
+    const PdfToFdWriter* writer = reinterpret_cast<PdfToFdWriter*>(owner);
+    const bool success = writeAllBytes(writer->dstFd, buffer, size);
+    if (!success) {
+        ALOGE("Cannot write to file descriptor. Error:%d", errno);
+        return 0;
+    }
+    return 1;
+}
+
+static void nativeWrite(JNIEnv* env, jclass thiz, jlong documentPtr, jint fd) {
+    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
+    PdfToFdWriter writer;
+    writer.dstFd = fd;
+    writer.WriteBlock = &writeBlock;
+    const bool success = FPDF_SaveAsCopy(document, &writer, FPDF_NO_INCREMENTAL);
+    if (!success) {
+        jniThrowExceptionFmt(env, "java/io/IOException",
+                "cannot write to fd. Error: %d", errno);
+    }
+}
+
+static void nativeSetTransformAndClip(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
+        jlong transformPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom) {
+    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
+
+    FPDF_PAGE* page = (FPDF_PAGE*) FPDF_LoadPage(document, pageIndex);
+    if (!page) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                "cannot open page");
+        return;
+    }
+
+    double width = 0;
+    double height = 0;
+
+    const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height);
+    if (!result) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                    "cannot get page size");
+        return;
+    }
+
+    // PDF's coordinate system origin is left-bottom while in graphics it
+    // is the top-left. So, translate the PDF coordinates to ours.
+    SkMatrix reflectOnX = SkMatrix::MakeScale(1, -1);
+    SkMatrix moveUp = SkMatrix::MakeTrans(0, FPDF_GetPageHeight(page));
+    SkMatrix coordinateChange = SkMatrix::Concat(moveUp, reflectOnX);
+
+    // Apply the transformation what was created in our coordinates.
+    SkMatrix matrix = SkMatrix::Concat(*reinterpret_cast<SkMatrix*>(transformPtr),
+            coordinateChange);
+
+    // Translate the result back to PDF coordinates.
+    matrix.setConcat(coordinateChange, matrix);
+
+    SkScalar transformValues[6];
+    if (!matrix.asAffine(transformValues)) {
+        FPDF_ClosePage(page);
+
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                "transform matrix has perspective. Only affine matrices are allowed.");
+        return;
+    }
+
+    FS_MATRIX transform = {transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY],
+                           transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY],
+                           transformValues[SkMatrix::kATransX],
+                           transformValues[SkMatrix::kATransY]};
+
+    FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom};
+
+    FPDFPage_TransFormWithClip(page, &transform, &clip);
+
+    FPDF_ClosePage(page);
+}
+
+static void nativeGetPageSize(JNIEnv* env, jclass thiz, jlong documentPtr,
+        jint pageIndex, jobject outSize) {
+    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
+
+    FPDF_PAGE page = FPDF_LoadPage(document, pageIndex);
+    if (!page) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                "cannot open page");
+        return;
+    }
+
+    double width = 0;
+    double height = 0;
+
+    const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height);
+    if (!result) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                    "cannot get page size");
+        return;
+    }
+
+    env->SetIntField(outSize, gPointClassInfo.x, width);
+    env->SetIntField(outSize, gPointClassInfo.y, height);
+
+    FPDF_ClosePage(page);
+}
+
+static bool nativeGetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
+        PageBox pageBox, jobject outBox) {
+    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
+
+    FPDF_PAGE page = FPDF_LoadPage(document, pageIndex);
+    if (!page) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                "cannot open page");
+        return false;
+    }
+
+    float left;
+    float top;
+    float right;
+    float bottom;
+
+    const FPDF_BOOL success = (pageBox == PAGE_BOX_MEDIA)
+        ? FPDFPage_GetMediaBox(page, &left, &top, &right, &bottom)
+        : FPDFPage_GetCropBox(page, &left, &top, &right, &bottom);
+
+    FPDF_ClosePage(page);
+
+    if (!success) {
+        return false;
+    }
+
+    env->SetIntField(outBox, gRectClassInfo.left, (int) left);
+    env->SetIntField(outBox, gRectClassInfo.top, (int) top);
+    env->SetIntField(outBox, gRectClassInfo.right, (int) right);
+    env->SetIntField(outBox, gRectClassInfo.bottom, (int) bottom);
+
+    return true;
+}
+
+static jboolean nativeGetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
+        jobject outMediaBox) {
+    const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA,
+            outMediaBox);
+    return success ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean nativeGetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
+        jobject outMediaBox) {
+    const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP,
+         outMediaBox);
+    return success ? JNI_TRUE : JNI_FALSE;
+}
+
+static void nativeSetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
+        PageBox pageBox, jobject box) {
+    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
+
+    FPDF_PAGE page = FPDF_LoadPage(document, pageIndex);
+    if (!page) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                "cannot open page");
+        return;
+    }
+
+    const int left = env->GetIntField(box, gRectClassInfo.left);
+    const int top = env->GetIntField(box, gRectClassInfo.top);
+    const int right = env->GetIntField(box, gRectClassInfo.right);
+    const int bottom = env->GetIntField(box, gRectClassInfo.bottom);
+
+    if (pageBox == PAGE_BOX_MEDIA) {
+        FPDFPage_SetMediaBox(page, left, top, right, bottom);
+    } else {
+        FPDFPage_SetCropBox(page, left, top, right, bottom);
+    }
+
+    FPDF_ClosePage(page);
+}
+
+static void nativeSetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
+        jobject mediaBox) {
+    nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, mediaBox);
+}
+
+static void nativeSetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex,
+        jobject mediaBox) {
+    nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, mediaBox);
+}
+
+static const JNINativeMethod gPdfEditor_Methods[] = {
+    {"nativeOpen", "(IJ)J", (void*) nativeOpen},
+    {"nativeClose", "(J)V", (void*) nativeClose},
+    {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount},
+    {"nativeRemovePage", "(JI)I", (void*) nativeRemovePage},
+    {"nativeWrite", "(JI)V", (void*) nativeWrite},
+    {"nativeSetTransformAndClip", "(JIJIIII)V", (void*) nativeSetTransformAndClip},
+    {"nativeGetPageSize", "(JILandroid/graphics/Point;)V", (void*) nativeGetPageSize},
+    {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting},
+    {"nativeGetPageMediaBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageMediaBox},
+    {"nativeSetPageMediaBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageMediaBox},
+    {"nativeGetPageCropBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageCropBox},
+    {"nativeSetPageCropBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageCropBox}
+};
+
+int register_android_graphics_pdf_PdfEditor(JNIEnv* env) {
+    const int result = RegisterMethodsOrDie(
+            env, "android/graphics/pdf/PdfEditor", gPdfEditor_Methods,
+            NELEM(gPdfEditor_Methods));
+
+    jclass pointClass = FindClassOrDie(env, "android/graphics/Point");
+    gPointClassInfo.x = GetFieldIDOrDie(env, pointClass, "x", "I");
+    gPointClassInfo.y = GetFieldIDOrDie(env, pointClass, "y", "I");
+
+    jclass rectClass = FindClassOrDie(env, "android/graphics/Rect");
+    gRectClassInfo.left = GetFieldIDOrDie(env, rectClass, "left", "I");
+    gRectClassInfo.top = GetFieldIDOrDie(env, rectClass, "top", "I");
+    gRectClassInfo.right = GetFieldIDOrDie(env, rectClass, "right", "I");
+    gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClass, "bottom", "I");
+
+    return result;
+};
+
+};
diff --git a/libs/hwui/jni/pdf/PdfRenderer.cpp b/libs/hwui/jni/pdf/PdfRenderer.cpp
new file mode 100644
index 0000000..761830b
--- /dev/null
+++ b/libs/hwui/jni/pdf/PdfRenderer.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+#include "PdfUtils.h"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include "GraphicsJNI.h"
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+#include "fpdfview.h"
+
+#include "core_jni_helpers.h"
+#include <vector>
+#include <utils/Log.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android {
+
+static const int RENDER_MODE_FOR_DISPLAY = 1;
+static const int RENDER_MODE_FOR_PRINT = 2;
+
+static struct {
+    jfieldID x;
+    jfieldID y;
+} gPointClassInfo;
+
+static jlong nativeOpenPageAndGetSize(JNIEnv* env, jclass thiz, jlong documentPtr,
+        jint pageIndex, jobject outSize) {
+    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
+
+    FPDF_PAGE page = FPDF_LoadPage(document, pageIndex);
+    if (!page) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                "cannot load page");
+        return -1;
+    }
+
+    double width = 0;
+    double height = 0;
+
+    int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height);
+    if (!result) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                    "cannot get page size");
+        return -1;
+    }
+
+    env->SetIntField(outSize, gPointClassInfo.x, width);
+    env->SetIntField(outSize, gPointClassInfo.y, height);
+
+    return reinterpret_cast<jlong>(page);
+}
+
+static void nativeClosePage(JNIEnv* env, jclass thiz, jlong pagePtr) {
+    FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr);
+    FPDF_ClosePage(page);
+}
+
+static void nativeRenderPage(JNIEnv* env, jclass thiz, jlong documentPtr, jlong pagePtr,
+        jlong bitmapPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom,
+        jlong transformPtr, jint renderMode) {
+    FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr);
+
+    SkBitmap skBitmap;
+    bitmap::toBitmap(bitmapPtr).getSkBitmap(&skBitmap);
+
+    const int stride = skBitmap.width() * 4;
+
+    FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(skBitmap.width(), skBitmap.height(),
+            FPDFBitmap_BGRA, skBitmap.getPixels(), stride);
+
+    int renderFlags = FPDF_REVERSE_BYTE_ORDER;
+    if (renderMode == RENDER_MODE_FOR_DISPLAY) {
+        renderFlags |= FPDF_LCD_TEXT;
+    } else if (renderMode == RENDER_MODE_FOR_PRINT) {
+        renderFlags |= FPDF_PRINTING;
+    }
+
+    SkMatrix matrix = *reinterpret_cast<SkMatrix*>(transformPtr);
+    SkScalar transformValues[6];
+    if (!matrix.asAffine(transformValues)) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                "transform matrix has perspective. Only affine matrices are allowed.");
+        return;
+    }
+
+    FS_MATRIX transform = {transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY],
+                           transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY],
+                           transformValues[SkMatrix::kATransX],
+                           transformValues[SkMatrix::kATransY]};
+
+    FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom};
+
+    FPDF_RenderPageBitmapWithMatrix(bitmap, page, &transform, &clip, renderFlags);
+
+    skBitmap.notifyPixelsChanged();
+}
+
+static const JNINativeMethod gPdfRenderer_Methods[] = {
+    {"nativeCreate", "(IJ)J", (void*) nativeOpen},
+    {"nativeClose", "(J)V", (void*) nativeClose},
+    {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount},
+    {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting},
+    {"nativeRenderPage", "(JJJIIIIJI)V", (void*) nativeRenderPage},
+    {"nativeOpenPageAndGetSize", "(JILandroid/graphics/Point;)J", (void*) nativeOpenPageAndGetSize},
+    {"nativeClosePage", "(J)V", (void*) nativeClosePage}
+};
+
+int register_android_graphics_pdf_PdfRenderer(JNIEnv* env) {
+    int result = RegisterMethodsOrDie(
+            env, "android/graphics/pdf/PdfRenderer", gPdfRenderer_Methods,
+            NELEM(gPdfRenderer_Methods));
+
+    jclass clazz = FindClassOrDie(env, "android/graphics/Point");
+    gPointClassInfo.x = GetFieldIDOrDie(env, clazz, "x", "I");
+    gPointClassInfo.y = GetFieldIDOrDie(env, clazz, "y", "I");
+
+    return result;
+};
+
+};
diff --git a/libs/hwui/jni/pdf/PdfUtils.cpp b/libs/hwui/jni/pdf/PdfUtils.cpp
new file mode 100644
index 0000000..36355eb
--- /dev/null
+++ b/libs/hwui/jni/pdf/PdfUtils.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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 "PdfUtils.h"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+
+#include "fpdfview.h"
+
+#define LOG_TAG "PdfUtils"
+#include <utils/Log.h>
+
+namespace android {
+
+static int sUnmatchedPdfiumInitRequestCount = 0;
+
+int getBlock(void* param, unsigned long position, unsigned char* outBuffer,
+        unsigned long size) {
+    const int fd = reinterpret_cast<intptr_t>(param);
+    const int readCount = pread(fd, outBuffer, size, position);
+    if (readCount < 0) {
+        ALOGE("Cannot read from file descriptor. Error:%d", errno);
+        return 0;
+    }
+    return 1;
+}
+
+// Check if the last pdfium command failed and if so, forward the error to java via an exception. If
+// this function returns true an exception is pending.
+bool forwardPdfiumError(JNIEnv* env) {
+    long error = FPDF_GetLastError();
+    switch (error) {
+        case FPDF_ERR_SUCCESS:
+            return false;
+        case FPDF_ERR_FILE:
+            jniThrowException(env, "java/io/IOException", "file not found or cannot be opened");
+            break;
+        case FPDF_ERR_FORMAT:
+            jniThrowException(env, "java/io/IOException", "file not in PDF format or corrupted");
+            break;
+        case FPDF_ERR_PASSWORD:
+            jniThrowException(env, "java/lang/SecurityException",
+                    "password required or incorrect password");
+            break;
+        case FPDF_ERR_SECURITY:
+            jniThrowException(env, "java/lang/SecurityException", "unsupported security scheme");
+            break;
+        case FPDF_ERR_PAGE:
+            jniThrowException(env, "java/io/IOException", "page not found or content error");
+            break;
+#ifdef PDF_ENABLE_XFA
+        case FPDF_ERR_XFALOAD:
+            jniThrowException(env, "java/lang/Exception", "load XFA error");
+            break;
+        case FPDF_ERR_XFALAYOUT:
+            jniThrowException(env, "java/lang/Exception", "layout XFA error");
+            break;
+#endif  // PDF_ENABLE_XFA
+        case FPDF_ERR_UNKNOWN:
+        default:
+            jniThrowExceptionFmt(env, "java/lang/Exception", "unknown error %d", error);
+    }
+
+    return true;
+}
+
+static void initializeLibraryIfNeeded(JNIEnv* env) {
+    if (sUnmatchedPdfiumInitRequestCount == 0) {
+        FPDF_InitLibrary();
+    }
+
+    sUnmatchedPdfiumInitRequestCount++;
+}
+
+static void destroyLibraryIfNeeded(JNIEnv* env, bool handleError) {
+    if (sUnmatchedPdfiumInitRequestCount == 1) {
+        FPDF_DestroyLibrary();
+    }
+
+    sUnmatchedPdfiumInitRequestCount--;
+}
+
+jlong nativeOpen(JNIEnv* env, jclass thiz, jint fd, jlong size) {
+    initializeLibraryIfNeeded(env);
+
+    FPDF_FILEACCESS loader;
+    loader.m_FileLen = size;
+    loader.m_Param = reinterpret_cast<void*>(intptr_t(fd));
+    loader.m_GetBlock = &getBlock;
+
+    FPDF_DOCUMENT document = FPDF_LoadCustomDocument(&loader, NULL);
+    if (!document) {
+        forwardPdfiumError(env);
+        destroyLibraryIfNeeded(env, false);
+        return -1;
+    }
+
+    return reinterpret_cast<jlong>(document);
+}
+
+void nativeClose(JNIEnv* env, jclass thiz, jlong documentPtr) {
+    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
+    FPDF_CloseDocument(document);
+
+    destroyLibraryIfNeeded(env, true);
+}
+
+jint nativeGetPageCount(JNIEnv* env, jclass thiz, jlong documentPtr) {
+    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
+
+    return FPDF_GetPageCount(document);
+}
+
+jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr) {
+    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
+    FPDF_BOOL printScaling = FPDF_VIEWERREF_GetPrintScaling(document);
+
+    return printScaling ? JNI_TRUE : JNI_FALSE;
+}
+
+};
diff --git a/libs/hwui/jni/pdf/PdfUtils.h b/libs/hwui/jni/pdf/PdfUtils.h
new file mode 100644
index 0000000..6532738
--- /dev/null
+++ b/libs/hwui/jni/pdf/PdfUtils.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef PDF_UTILS_H_
+#define PDF_UTILS_H_
+
+#include "jni.h"
+
+namespace android {
+
+int getBlock(void* param, unsigned long position, unsigned char* outBuffer,
+        unsigned long size);
+
+jlong nativeOpen(JNIEnv* env, jclass thiz, jint fd, jlong size);
+void nativeClose(JNIEnv* env, jclass thiz, jlong documentPtr);
+
+jint nativeGetPageCount(JNIEnv* env, jclass thiz, jlong documentPtr);
+jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr);
+
+};
+
+#endif /* PDF_UTILS_H_ */
diff --git a/libs/hwui/jni/text/LineBreaker.cpp b/libs/hwui/jni/text/LineBreaker.cpp
new file mode 100644
index 0000000..8dae655
--- /dev/null
+++ b/libs/hwui/jni/text/LineBreaker.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "LineBreaker"
+
+#include "utils/misc.h"
+#include "utils/Log.h"
+#include <nativehelper/ScopedStringChars.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
+#include "scoped_nullable_primitive_array.h"
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <algorithm>
+
+#include "SkPaint.h"
+#include "SkTypeface.h"
+#include <hwui/MinikinSkia.h>
+#include <hwui/MinikinUtils.h>
+#include <hwui/Paint.h>
+#include <minikin/FontCollection.h>
+#include <minikin/AndroidLineBreakerHelper.h>
+#include <minikin/MinikinFont.h>
+
+namespace android {
+
+static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) {
+    if (javaArray == nullptr) {
+         return std::vector<float>();
+    } else {
+        ScopedIntArrayRO intArr(env, javaArray);
+        return std::vector<float>(intArr.get(), intArr.get() + intArr.size());
+    }
+}
+
+static inline minikin::android::StaticLayoutNative* toNative(jlong ptr) {
+    return reinterpret_cast<minikin::android::StaticLayoutNative*>(ptr);
+}
+
+// set text and set a number of parameters for creating a layout (width, tabstops, strategy,
+// hyphenFrequency)
+static jlong nInit(JNIEnv* env, jclass /* unused */,
+        jint breakStrategy, jint hyphenationFrequency, jboolean isJustified, jintArray indents) {
+    return reinterpret_cast<jlong>(new minikin::android::StaticLayoutNative(
+            static_cast<minikin::BreakStrategy>(breakStrategy),
+            static_cast<minikin::HyphenationFrequency>(hyphenationFrequency),
+            isJustified,
+            jintArrayToFloatVector(env, indents)));
+}
+
+static void nFinish(jlong nativePtr) {
+    delete toNative(nativePtr);
+}
+
+// CriticalNative
+static jlong nGetReleaseFunc(CRITICAL_JNI_PARAMS) {
+    return reinterpret_cast<jlong>(nFinish);
+}
+
+static jlong nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
+        // Inputs
+        jcharArray javaText,
+        jlong measuredTextPtr,
+        jint length,
+        jfloat firstWidth,
+        jint firstWidthLineCount,
+        jfloat restWidth,
+        jfloatArray variableTabStops,
+        jfloat defaultTabStop,
+        jint indentsOffset) {
+    minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
+
+    ScopedCharArrayRO text(env, javaText);
+    ScopedNullableFloatArrayRO tabStops(env, variableTabStops);
+
+    minikin::U16StringPiece u16Text(text.get(), length);
+    minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr);
+
+    std::unique_ptr<minikin::LineBreakResult> result =
+          std::make_unique<minikin::LineBreakResult>(builder->computeBreaks(
+                u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset,
+                tabStops.get(), tabStops.size(), defaultTabStop));
+    return reinterpret_cast<jlong>(result.release());
+}
+
+static jint nGetLineCount(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+    return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints.size();
+}
+
+static jint nGetLineBreakOffset(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+    return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints[i];
+}
+
+static jfloat nGetLineWidth(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+    return reinterpret_cast<minikin::LineBreakResult*>(ptr)->widths[i];
+}
+
+static jfloat nGetLineAscent(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+    return reinterpret_cast<minikin::LineBreakResult*>(ptr)->ascents[i];
+}
+
+static jfloat nGetLineDescent(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+    return reinterpret_cast<minikin::LineBreakResult*>(ptr)->descents[i];
+}
+
+static jint nGetLineFlag(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+    return reinterpret_cast<minikin::LineBreakResult*>(ptr)->flags[i];
+}
+
+static void nReleaseResult(jlong ptr) {
+    delete reinterpret_cast<minikin::LineBreakResult*>(ptr);
+}
+
+static jlong nGetReleaseResultFunc(CRITICAL_JNI_PARAMS) {
+    return reinterpret_cast<jlong>(nReleaseResult);
+}
+
+static const JNINativeMethod gMethods[] = {
+    // Fast Natives
+    {"nInit", "("
+        "I"  // breakStrategy
+        "I"  // hyphenationFrequency
+        "Z"  // isJustified
+        "[I"  // indents
+        ")J", (void*) nInit},
+
+    // Critical Natives
+    {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc},
+
+    // Regular JNI
+    {"nComputeLineBreaks", "("
+        "J"  // nativePtr
+        "[C"  // text
+        "J"  // MeasuredParagraph ptr.
+        "I"  // length
+        "F"  // firstWidth
+        "I"  // firstWidthLineCount
+        "F"  // restWidth
+        "[F"  // variableTabStops
+        "F"  // defaultTabStop
+        "I"  // indentsOffset
+        ")J", (void*) nComputeLineBreaks},
+
+    // Result accessors, CriticalNatives
+    {"nGetLineCount", "(J)I", (void*)nGetLineCount},
+    {"nGetLineBreakOffset", "(JI)I", (void*)nGetLineBreakOffset},
+    {"nGetLineWidth", "(JI)F", (void*)nGetLineWidth},
+    {"nGetLineAscent", "(JI)F", (void*)nGetLineAscent},
+    {"nGetLineDescent", "(JI)F", (void*)nGetLineDescent},
+    {"nGetLineFlag", "(JI)I", (void*)nGetLineFlag},
+    {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc},
+};
+
+int register_android_graphics_text_LineBreaker(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/text/LineBreaker", gMethods,
+                                NELEM(gMethods));
+}
+
+}
diff --git a/libs/hwui/jni/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp
new file mode 100644
index 0000000..3b5ecbc
--- /dev/null
+++ b/libs/hwui/jni/text/MeasuredText.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MeasuredText"
+
+#include "GraphicsJNI.h"
+#include "utils/misc.h"
+#include "utils/Log.h"
+#include <nativehelper/ScopedStringChars.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <algorithm>
+
+#include "SkPaint.h"
+#include "SkTypeface.h"
+#include <hwui/MinikinSkia.h>
+#include <hwui/MinikinUtils.h>
+#include <hwui/Paint.h>
+#include <minikin/FontCollection.h>
+#include <minikin/AndroidLineBreakerHelper.h>
+#include <minikin/MinikinFont.h>
+
+namespace android {
+
+static inline minikin::MeasuredTextBuilder* toBuilder(jlong ptr) {
+    return reinterpret_cast<minikin::MeasuredTextBuilder*>(ptr);
+}
+
+static inline Paint* toPaint(jlong ptr) {
+    return reinterpret_cast<Paint*>(ptr);
+}
+
+static inline minikin::MeasuredText* toMeasuredParagraph(jlong ptr) {
+    return reinterpret_cast<minikin::MeasuredText*>(ptr);
+}
+
+template<typename Ptr> static inline jlong toJLong(Ptr ptr) {
+    return reinterpret_cast<jlong>(ptr);
+}
+
+static void releaseMeasuredParagraph(jlong measuredTextPtr) {
+    delete toMeasuredParagraph(measuredTextPtr);
+}
+
+// Regular JNI
+static jlong nInitBuilder(CRITICAL_JNI_PARAMS) {
+    return toJLong(new minikin::MeasuredTextBuilder());
+}
+
+// Regular JNI
+static void nAddStyleRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr,
+                         jlong paintPtr, jint start, jint end, jboolean isRtl) {
+    Paint* paint = toPaint(paintPtr);
+    const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
+    minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface);
+    toBuilder(builderPtr)->addStyleRun(start, end, std::move(minikinPaint), isRtl);
+}
+
+// Regular JNI
+static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr,
+                               jlong paintPtr, jint start, jint end, jfloat width) {
+    toBuilder(builderPtr)->addReplacementRun(start, end, width,
+                                             toPaint(paintPtr)->getMinikinLocaleListId());
+}
+
+// Regular JNI
+static jlong nBuildMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr,
+                                jlong hintPtr, jcharArray javaText, jboolean computeHyphenation,
+                                jboolean computeLayout) {
+    ScopedCharArrayRO text(env, javaText);
+    const minikin::U16StringPiece textBuffer(text.get(), text.size());
+
+    // Pass the ownership to Java.
+    return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation, computeLayout,
+                                                toMeasuredParagraph(hintPtr)).release());
+}
+
+// Regular JNI
+static void nFreeBuilder(JNIEnv* env, jclass /* unused */, jlong builderPtr) {
+    delete toBuilder(builderPtr);
+}
+
+// CriticalNative
+static jfloat nGetWidth(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint start, jint end) {
+    minikin::MeasuredText* mt = toMeasuredParagraph(ptr);
+    float r = 0.0f;
+    for (int i = start; i < end; ++i) {
+        r += mt->widths[i];
+    }
+    return r;
+}
+
+static jfloat nGetCharWidthAt(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint offset) {
+    return toMeasuredParagraph(ptr)->widths[offset];
+}
+
+// Regular JNI
+static void nGetBounds(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jint start, jint end,
+                       jobject bounds) {
+    ScopedCharArrayRO text(env, javaText);
+    const minikin::U16StringPiece textBuffer(text.get(), text.size());
+    const minikin::Range range(start, end);
+
+    minikin::MinikinRect rect = toMeasuredParagraph(ptr)->getBounds(textBuffer, range);
+
+    SkRect r;
+    r.fLeft = rect.mLeft;
+    r.fTop = rect.mTop;
+    r.fRight = rect.mRight;
+    r.fBottom = rect.mBottom;
+
+    SkIRect ir;
+    r.roundOut(&ir);
+    GraphicsJNI::irect_to_jrect(ir, env, bounds);
+}
+
+// CriticalNative
+static jlong nGetReleaseFunc(CRITICAL_JNI_PARAMS) {
+    return toJLong(&releaseMeasuredParagraph);
+}
+
+static jint nGetMemoryUsage(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+    return static_cast<jint>(toMeasuredParagraph(ptr)->getMemoryUsage());
+}
+
+static const JNINativeMethod gMTBuilderMethods[] = {
+    // MeasuredParagraphBuilder native functions.
+    {"nInitBuilder", "()J", (void*) nInitBuilder},
+    {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
+    {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
+    {"nBuildMeasuredText", "(JJ[CZZ)J", (void*) nBuildMeasuredText},
+    {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
+};
+
+static const JNINativeMethod gMTMethods[] = {
+    // MeasuredParagraph native functions.
+    {"nGetWidth", "(JII)F", (void*) nGetWidth},  // Critical Natives
+    {"nGetBounds", "(J[CIILandroid/graphics/Rect;)V", (void*) nGetBounds},  // Regular JNI
+    {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc},  // Critical Natives
+    {"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage},  // Critical Native
+    {"nGetCharWidthAt", "(JI)F", (void*) nGetCharWidthAt},  // Critical Native
+};
+
+int register_android_graphics_text_MeasuredText(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/text/MeasuredText",
+            gMTMethods, NELEM(gMTMethods))
+        + RegisterMethodsOrDie(env, "android/graphics/text/MeasuredText$Builder",
+            gMTBuilderMethods, NELEM(gMTBuilderMethods));
+}
+
+}