Merge QP1A.180823.001

Change-Id: Ia2d6fa811b3c57185c1975db603d59e6eaef73fc
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index fc625bb..843c146 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -74,6 +74,7 @@
 const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
 const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
 const char* AssetManager::PRODUCT_OVERLAY_DIR = "/product/overlay";
+const char* AssetManager::PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay";
 const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme";
 const char* AssetManager::TARGET_PACKAGE_NAME = "android";
 const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index 08da731..cdb87bc 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -61,6 +61,7 @@
     static const char* IDMAP_BIN;
     static const char* OVERLAY_DIR;
     static const char* PRODUCT_OVERLAY_DIR;
+    static const char* PRODUCT_SERVICES_OVERLAY_DIR;
     /*
      * If OVERLAY_THEME_DIR_PROPERTY is set, search for runtime resource overlay
      * APKs in OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 59760ab..83e90b6 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -72,7 +72,6 @@
         "libft2",
         "libminikin",
         "libandroidfw",
-        "libRScpp",
     ],
     static_libs: [
         "libEGL_blobCache",
@@ -230,7 +229,6 @@
         "RenderProperties.cpp",
         "ResourceCache.cpp",
         "SkiaCanvas.cpp",
-        "SkiaCanvasProxy.cpp",
         "Snapshot.cpp",
         "Texture.cpp",
         "VectorDrawable.cpp",
@@ -243,7 +241,6 @@
     },
 
     export_include_dirs: ["."],
-    export_shared_lib_headers: ["libRScpp"],
 }
 
 cc_library {
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index bac7a4d..1b15dbd 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -15,32 +15,38 @@
  */
 
 #include "CanvasTransform.h"
+#include "utils/Color.h"
 #include "Properties.h"
 
+#include <ui/ColorSpace.h>
 #include <SkColorFilter.h>
 #include <SkPaint.h>
-#include <log/log.h>
+
+#include <algorithm>
+#include <cmath>
 
 namespace android::uirenderer {
 
 static SkColor makeLight(SkColor color) {
-    SkScalar hsv[3];
-    SkColorToHSV(color, hsv);
-    if (hsv[1] > .2f) return color;
-    // hsv[1] *= .85f;
-    // hsv[2] = std::min(1.0f, std::max(hsv[2], 1 - hsv[2]) * 1.3f);
-    hsv[2] = std::max(hsv[2], 1.1f - hsv[2]);
-    return SkHSVToColor(SkColorGetA(color), hsv);
+    Lab lab = sRGBToLab(color);
+    float invertedL = std::min(110 - lab.L, 100.0f);
+    if (invertedL > lab.L) {
+        lab.L = invertedL;
+        return LabToSRGB(lab, SkColorGetA(color));
+    } else {
+        return color;
+    }
 }
 
 static SkColor makeDark(SkColor color) {
-    SkScalar hsv[3];
-    SkColorToHSV(color, hsv);
-    if (hsv[1] > .2f) return color;
-    // hsv[1] *= .85f;
-    // hsv[2] = std::max(0.0f, std::min(hsv[2], 1 - hsv[2]) * .7f);
-    hsv[2] = std::min(hsv[2], 1.1f - hsv[2]);
-    return SkHSVToColor(SkColorGetA(color), hsv);
+    Lab lab = sRGBToLab(color);
+    float invertedL = std::min(110 - lab.L, 100.0f);
+    if (invertedL < lab.L) {
+        lab.L = invertedL;
+        return LabToSRGB(lab, SkColorGetA(color));
+    } else {
+        return color;
+    }
 }
 
 static SkColor transformColor(ColorTransform transform, SkColor color) {
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 6408ce6..ab80d3d 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -286,7 +286,7 @@
         eglDestroySyncKHR(display, fence);
     }
 
-    return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
+    return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap)));
 }
 
 };  // namespace android::uirenderer
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
deleted file mode 100644
index fc009d8..0000000
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * 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 "SkiaCanvasProxy.h"
-
-#include <memory>
-
-#include <log/log.h>
-
-#include <SkLatticeIter.h>
-#include <SkPaint.h>
-#include <SkPatchUtils.h>
-#include <SkPath.h>
-#include <SkPixelRef.h>
-#include <SkRRect.h>
-#include <SkRSXform.h>
-#include <SkRect.h>
-#include <SkSurface.h>
-#include <SkTextBlobRunIterator.h>
-#include <SkVertices.h>
-#include "hwui/Bitmap.h"
-
-namespace android {
-namespace uirenderer {
-
-SkiaCanvasProxy::SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls)
-        : INHERITED(canvas->width(), canvas->height())
-        , mCanvas(canvas)
-        , mFilterHwuiCalls(filterHwuiCalls) {}
-
-void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) {
-    mCanvas->drawPaint(paint);
-}
-
-void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPoint pts[],
-                                   const SkPaint& paint) {
-    if (!pts || count == 0) {
-        return;
-    }
-
-    // convert the SkPoints into floats
-    static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats");
-    const size_t floatCount = count << 1;
-    const float* floatArray = &pts[0].fX;
-
-    switch (pointMode) {
-        case kPoints_PointMode: {
-            mCanvas->drawPoints(floatArray, floatCount, paint);
-            break;
-        }
-        case kLines_PointMode: {
-            mCanvas->drawLines(floatArray, floatCount, paint);
-            break;
-        }
-        case kPolygon_PointMode: {
-            SkPaint strokedPaint(paint);
-            strokedPaint.setStyle(SkPaint::kStroke_Style);
-
-            SkPath path;
-            for (size_t i = 0; i < count - 1; i++) {
-                path.moveTo(pts[i]);
-                path.lineTo(pts[i + 1]);
-                this->drawPath(path, strokedPaint);
-                path.rewind();
-            }
-            break;
-        }
-        default:
-            LOG_ALWAYS_FATAL("Unknown point type");
-    }
-}
-
-void SkiaCanvasProxy::onDrawOval(const SkRect& rect, const SkPaint& paint) {
-    mCanvas->drawOval(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
-}
-
-void SkiaCanvasProxy::onDrawRect(const SkRect& rect, const SkPaint& paint) {
-    mCanvas->drawRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
-}
-
-void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint) {
-    if (!roundRect.isComplex()) {
-        const SkRect& rect = roundRect.rect();
-        SkVector radii = roundRect.getSimpleRadii();
-        mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, radii.fX, radii.fY,
-                               paint);
-    } else {
-        SkPath path;
-        path.addRRect(roundRect);
-        mCanvas->drawPath(path, paint);
-    }
-}
-
-void SkiaCanvasProxy::onDrawArc(const SkRect& rect, SkScalar startAngle, SkScalar sweepAngle,
-                                bool useCenter, const SkPaint& paint) {
-    mCanvas->drawArc(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, startAngle, sweepAngle,
-                     useCenter, paint);
-}
-
-void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) {
-    mCanvas->drawPath(path, paint);
-}
-
-void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
-                                   const SkPaint* paint) {
-    sk_sp<Bitmap> hwuiBitmap = Bitmap::createFrom(bitmap.info(), *bitmap.pixelRef());
-    // HWUI doesn't support extractSubset(), so convert any subsetted bitmap into
-    // a drawBitmapRect(); pass through an un-subsetted bitmap.
-    if (hwuiBitmap && bitmap.dimensions() != hwuiBitmap->info().dimensions()) {
-        SkIPoint origin = bitmap.pixelRefOrigin();
-        mCanvas->drawBitmap(
-                *hwuiBitmap, origin.fX, origin.fY, origin.fX + bitmap.dimensions().width(),
-                origin.fY + bitmap.dimensions().height(), left, top,
-                left + bitmap.dimensions().width(), top + bitmap.dimensions().height(), paint);
-    } else {
-        mCanvas->drawBitmap(*hwuiBitmap, left, top, paint);
-    }
-}
-
-void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& skBitmap, const SkRect* srcPtr,
-                                       const SkRect& dst, const SkPaint* paint, SrcRectConstraint) {
-    SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(skBitmap.width(), skBitmap.height());
-    // TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src?
-    Bitmap* bitmap = reinterpret_cast<Bitmap*>(skBitmap.pixelRef());
-    mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft, dst.fTop,
-                        dst.fRight, dst.fBottom, paint);
-}
-
-void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
-                                       const SkRect& dst, const SkPaint*) {
-    // TODO make nine-patch drawing a method on Canvas.h
-    SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported");
-}
-
-void SkiaCanvasProxy::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
-                                  const SkPaint* paint) {
-    SkBitmap skiaBitmap;
-    SkPixmap pixmap;
-    if (image->peekPixels(&pixmap) && skiaBitmap.installPixels(pixmap)) {
-        onDrawBitmap(skiaBitmap, left, top, paint);
-    }
-}
-
-void SkiaCanvasProxy::onDrawImageRect(const SkImage* image, const SkRect* srcPtr, const SkRect& dst,
-                                      const SkPaint* paint, SrcRectConstraint constraint) {
-    SkBitmap skiaBitmap;
-    SkPixmap pixmap;
-    if (image->peekPixels(&pixmap) && skiaBitmap.installPixels(pixmap)) {
-        sk_sp<Bitmap> bitmap = Bitmap::createFrom(skiaBitmap.info(), *skiaBitmap.pixelRef());
-        SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(image->width(), image->height());
-        mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft,
-                            dst.fTop, dst.fRight, dst.fBottom, paint);
-    }
-}
-
-void SkiaCanvasProxy::onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
-                                      const SkPaint*) {
-    SkDEBUGFAIL("SkiaCanvasProxy::onDrawImageNine is not yet supported");
-}
-
-void SkiaCanvasProxy::onDrawImageLattice(const SkImage* image, const Lattice& lattice,
-                                         const SkRect& dst, const SkPaint* paint) {
-    SkLatticeIter iter(lattice, dst);
-    SkRect srcR, dstR;
-    while (iter.next(&srcR, &dstR)) {
-        onDrawImageRect(image, &srcR, dstR, paint, SkCanvas::kFast_SrcRectConstraint);
-    }
-}
-
-void SkiaCanvasProxy::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
-                                           const SkPaint& paint) {
-    if (mFilterHwuiCalls) {
-        return;
-    }
-    mCanvas->drawVertices(vertices, bmode, paint);
-}
-
-sk_sp<SkSurface> SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
-    SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported");
-    return NULL;
-}
-
-void SkiaCanvasProxy::willSave() {
-    mCanvas->save(android::SaveFlags::MatrixClip);
-}
-
-static inline SaveFlags::Flags saveFlags(SkCanvas::SaveLayerFlags layerFlags) {
-    SaveFlags::Flags saveFlags = 0;
-
-    if (!(layerFlags & SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag)) {
-        saveFlags |= SaveFlags::ClipToLayer;
-    }
-
-    return saveFlags;
-}
-
-SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(
-        const SaveLayerRec& saveLayerRec) {
-    SkRect rect;
-    if (saveLayerRec.fBounds) {
-        rect = *saveLayerRec.fBounds;
-    } else if (!mCanvas->getClipBounds(&rect)) {
-        rect = SkRect::MakeEmpty();
-    }
-    mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, saveLayerRec.fPaint,
-                       saveFlags(saveLayerRec.fSaveLayerFlags));
-    return SkCanvas::kNoLayer_SaveLayerStrategy;
-}
-
-void SkiaCanvasProxy::willRestore() {
-    mCanvas->restore();
-}
-
-void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) {
-    mCanvas->concat(matrix);
-}
-
-void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
-    mCanvas->setMatrix(matrix);
-}
-
-void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
-                                   const SkPaint& paint) {
-    SkPath path;
-    path.addRRect(outer);
-    path.addRRect(inner);
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    this->drawPath(path, paint);
-}
-
-/**
- * Utility class that converts the incoming text & paint from the given encoding
- * into glyphIDs.
- */
-class GlyphIDConverter {
-public:
-    GlyphIDConverter(const void* text, size_t byteLength, const SkPaint& origPaint) {
-        paint = origPaint;
-        if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
-            glyphIDs = (uint16_t*)text;
-            count = byteLength >> 1;
-        } else {
-            // ensure space for one glyph per ID given UTF8 encoding.
-            storage.reset(new uint16_t[byteLength]);
-            glyphIDs = storage.get();
-            count = paint.textToGlyphs(text, byteLength, storage.get());
-            paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-        }
-    }
-
-    SkPaint paint;
-    uint16_t* glyphIDs;
-    int count;
-
-private:
-    std::unique_ptr<uint16_t[]> storage;
-};
-
-void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
-                                 const SkPaint& origPaint) {
-    // convert to glyphIDs if necessary
-    GlyphIDConverter glyphs(text, byteLength, origPaint);
-
-    // compute the glyph positions
-    std::unique_ptr<SkScalar[]> glyphWidths(new SkScalar[glyphs.count]);
-    glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get());
-
-    // compute conservative bounds
-    // NOTE: We could call the faster paint.getFontBounds for a less accurate,
-    //       but even more conservative bounds if this  is too slow.
-    SkRect bounds;
-    glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
-
-    // adjust for non-left alignment
-    if (glyphs.paint.getTextAlign() != SkPaint::kLeft_Align) {
-        SkScalar stop = 0;
-        for (int i = 0; i < glyphs.count; i++) {
-            stop += glyphWidths[i];
-        }
-        if (glyphs.paint.getTextAlign() == SkPaint::kCenter_Align) {
-            stop = SkScalarHalf(stop);
-        }
-        if (glyphs.paint.isVerticalText()) {
-            y -= stop;
-        } else {
-            x -= stop;
-        }
-    }
-
-    // setup the first glyph position and adjust bounds if needed
-    int xBaseline = 0;
-    int yBaseline = 0;
-    if (mCanvas->drawTextAbsolutePos()) {
-        bounds.offset(x, y);
-        xBaseline = x;
-        yBaseline = y;
-    }
-
-    static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats");
-    auto glyphFunc = [&](uint16_t* text, float* positions) {
-        memcpy(text, glyphs.glyphIDs, glyphs.count * sizeof(uint16_t));
-        size_t posIndex = 0;
-        // setup the first glyph position
-        positions[posIndex++] = xBaseline;
-        positions[posIndex++] = yBaseline;
-        // setup the remaining glyph positions
-        if (glyphs.paint.isVerticalText()) {
-            float yPosition = yBaseline;
-            for (int i = 1; i < glyphs.count; i++) {
-                positions[posIndex++] = xBaseline;
-                yPosition += glyphWidths[i - 1];
-                positions[posIndex++] = yPosition;
-            }
-        } else {
-            float xPosition = xBaseline;
-            for (int i = 1; i < glyphs.count; i++) {
-                xPosition += glyphWidths[i - 1];
-                positions[posIndex++] = xPosition;
-                positions[posIndex++] = yBaseline;
-            }
-        }
-    };
-    mCanvas->drawGlyphs(glyphFunc, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop,
-                        bounds.fRight, bounds.fBottom, 0);
-}
-
-void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
-                                    const SkPaint& origPaint) {
-    // convert to glyphIDs if necessary
-    GlyphIDConverter glyphs(text, byteLength, origPaint);
-
-    // convert to relative positions if necessary
-    int x, y;
-    if (mCanvas->drawTextAbsolutePos()) {
-        x = 0;
-        y = 0;
-    } else {
-        x = pos[0].fX;
-        y = pos[0].fY;
-    }
-
-    // Compute conservative bounds.  If the content has already been processed
-    // by Minikin then it had already computed these bounds.  Unfortunately,
-    // there is no way to capture those bounds as part of the Skia drawPosText
-    // API so we need to do that computation again here.
-    SkRect bounds = SkRect::MakeEmpty();
-    for (int i = 0; i < glyphs.count; i++) {
-        SkRect glyphBounds = SkRect::MakeEmpty();
-        glyphs.paint.measureText(&glyphs.glyphIDs[i], sizeof(uint16_t), &glyphBounds);
-        glyphBounds.offset(pos[i].fX, pos[i].fY);
-        bounds.join(glyphBounds);
-    }
-
-    static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats");
-    auto glyphFunc = [&](uint16_t* text, float* positions) {
-        memcpy(text, glyphs.glyphIDs, glyphs.count * sizeof(uint16_t));
-        if (mCanvas->drawTextAbsolutePos()) {
-            memcpy(positions, pos, 2 * glyphs.count * sizeof(float));
-        } else {
-            for (int i = 0, posIndex = 0; i < glyphs.count; i++) {
-                positions[posIndex++] = pos[i].fX - x;
-                positions[posIndex++] = pos[i].fY - y;
-            }
-        }
-    };
-    mCanvas->drawGlyphs(glyphFunc, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop,
-                        bounds.fRight, bounds.fBottom, 0);
-}
-
-void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
-                                     SkScalar constY, const SkPaint& paint) {
-    const size_t pointCount = byteLength >> 1;
-    std::unique_ptr<SkPoint[]> pts(new SkPoint[pointCount]);
-    for (size_t i = 0; i < pointCount; i++) {
-        pts[i].set(xpos[i], constY);
-    }
-    this->onDrawPosText(text, byteLength, pts.get(), paint);
-}
-
-void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
-                                       const SkMatrix* matrix, const SkPaint& origPaint) {
-    SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextOnPath is not supported");
-}
-
-void SkiaCanvasProxy::onDrawTextRSXform(const void* text, size_t byteLength,
-                                        const SkRSXform xform[], const SkRect* cullRect,
-                                        const SkPaint& paint) {
-    GlyphIDConverter glyphs(text, byteLength, paint);  // Just get count
-    SkMatrix localM, currM, origM;
-    mCanvas->getMatrix(&currM);
-    origM = currM;
-    for (int i = 0; i < glyphs.count; i++) {
-        localM.setRSXform(*xform++);
-        currM.setConcat(origM, localM);
-        mCanvas->setMatrix(currM);
-        this->onDrawText((char*)text + (byteLength / glyphs.count * i), byteLength / glyphs.count,
-                         0, 0, paint);
-    }
-    mCanvas->setMatrix(origM);
-}
-
-void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                                     const SkPaint& paint) {
-    SkPaint runPaint = paint;
-
-    SkTextBlobRunIterator it(blob);
-    for (; !it.done(); it.next()) {
-        size_t textLen = it.glyphCount() * sizeof(uint16_t);
-        const SkPoint& offset = it.offset();
-        // applyFontToPaint() always overwrites the exact same attributes,
-        // so it is safe to not re-seed the paint for this reason.
-        it.applyFontToPaint(&runPaint);
-
-        switch (it.positioning()) {
-            case SkTextBlob::kDefault_Positioning:
-                this->drawText(it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
-                break;
-            case SkTextBlob::kHorizontal_Positioning: {
-                std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
-                for (size_t i = 0; i < it.glyphCount(); i++) {
-                    pts[i].set(x + offset.x() + it.pos()[i], y + offset.y());
-                }
-                this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
-                break;
-            }
-            case SkTextBlob::kFull_Positioning: {
-                std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
-                for (size_t i = 0; i < it.glyphCount(); i++) {
-                    const size_t xIndex = i * 2;
-                    const size_t yIndex = xIndex + 1;
-                    pts[i].set(x + offset.x() + it.pos()[xIndex],
-                               y + offset.y() + it.pos()[yIndex]);
-                }
-                this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
-                break;
-            }
-            default:
-                SK_ABORT("unhandled positioning mode");
-        }
-    }
-}
-
-void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
-                                  const SkPoint texCoords[4], SkBlendMode bmode,
-                                  const SkPaint& paint) {
-    if (mFilterHwuiCalls) {
-        return;
-    }
-    SkMatrix matrix;
-    mCanvas->getMatrix(&matrix);
-    SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix);
-
-    mCanvas->drawVertices(
-            SkPatchUtils::MakeVertices(cubics, colors, texCoords, lod.width(), lod.height()).get(),
-            bmode, paint);
-}
-
-void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle) {
-    mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op);
-}
-
-void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkClipOp op, ClipEdgeStyle) {
-    SkPath path;
-    path.addRRect(roundRect);
-    mCanvas->clipPath(&path, op);
-}
-
-void SkiaCanvasProxy::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle) {
-    mCanvas->clipPath(&path, op);
-}
-
-};  // namespace uirenderer
-};  // namespace android
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
deleted file mode 100644
index 360d5a0..0000000
--- a/libs/hwui/SkiaCanvasProxy.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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 SkiaCanvasProxy_DEFINED
-#define SkiaCanvasProxy_DEFINED
-
-#include <SkCanvas.h>
-#include <cutils/compiler.h>
-
-#include "hwui/Canvas.h"
-
-namespace android {
-namespace uirenderer {
-
-/**
- * This class serves as a proxy between Skia's SkCanvas and Android Framework's
- * Canvas.  The class does not maintain any draw-related state and will pass
- * through most requests directly to the Canvas provided in the constructor.
- *
- * Upon construction it is expected that the provided Canvas has already been
- * prepared for recording and will continue to be in the recording state while
- * this proxy class is being used.
- *
- * If filterHwuiCalls is true, the proxy silently ignores away draw calls that
- * aren't supported by HWUI.
- */
-class ANDROID_API SkiaCanvasProxy : public SkCanvas {
-public:
-    explicit SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls = false);
-    virtual ~SkiaCanvasProxy() {}
-
-protected:
-    virtual sk_sp<SkSurface> onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
-
-    virtual void willSave() override;
-    virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
-    virtual void willRestore() override;
-
-    virtual void didConcat(const SkMatrix&) override;
-    virtual void didSetMatrix(const SkMatrix&) override;
-
-    virtual void onDrawPaint(const SkPaint& paint) override;
-    virtual void onDrawPoints(PointMode, size_t count, const SkPoint pts[],
-                              const SkPaint&) override;
-    virtual void onDrawOval(const SkRect&, const SkPaint&) override;
-    virtual void onDrawRect(const SkRect&, const SkPaint&) override;
-    virtual void onDrawRRect(const SkRRect&, const SkPaint&) override;
-    virtual void onDrawPath(const SkPath& path, const SkPaint&) override;
-    virtual void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
-                           const SkPaint&) override;
-    virtual void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
-                              const SkPaint*) override;
-    virtual void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst,
-                                  const SkPaint* paint, SrcRectConstraint) override;
-    virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
-                                  const SkPaint*) override;
-    virtual void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*);
-    virtual void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
-                                 SrcRectConstraint);
-    virtual void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
-                                 const SkPaint*);
-    virtual void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
-                                    const SkPaint*);
-    virtual void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override;
-
-    virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
-
-    virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
-                            const SkPaint&) override;
-    virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
-                               const SkPaint&) override;
-    virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
-                                SkScalar constY, const SkPaint&) override;
-    virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
-                                  const SkMatrix* matrix, const SkPaint&) override;
-    virtual void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
-                                   const SkRect* cullRect, const SkPaint& paint);
-    virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                                const SkPaint& paint) override;
-
-    virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
-                             const SkPoint texCoords[4], SkBlendMode,
-                             const SkPaint& paint) override;
-
-    virtual void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
-    virtual void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
-    virtual void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
-
-private:
-    Canvas* mCanvas;
-    bool mFilterHwuiCalls;
-
-    typedef SkCanvas INHERITED;
-};
-
-};  // namespace uirenderer
-};  // namespace android
-
-#endif  // SkiaCanvasProxy_DEFINED
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 402fbad..9f82d0f 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -474,14 +474,13 @@
 // Update the given paint with alpha and color filter. Return nullptr if no color filter is
 // specified and root alpha is 1. Otherwise, return updated paint.
 SkPaint* Tree::updatePaint(SkPaint* outPaint, TreeProperties* prop) {
-    if (prop->getRootAlpha() == 1.0f && prop->getColorFilter() == nullptr) {
-        return nullptr;
-    } else {
+    // HWUI always draws VD with bilinear filtering.
+    outPaint->setFilterQuality(kLow_SkFilterQuality);
+    if (prop->getRootAlpha() < 1.0f || prop->getColorFilter() != nullptr) {
         outPaint->setColorFilter(sk_ref_sp(prop->getColorFilter()));
-        outPaint->setFilterQuality(kLow_SkFilterQuality);
         outPaint->setAlpha(prop->getRootAlpha() * 255);
-        return outPaint;
     }
+    return outPaint;
 }
 
 Bitmap& Tree::getBitmapUpdateIfDirty() {
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 007961a..8d4e7e0 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -21,7 +21,8 @@
 
 #include <SkPicture.h>
 #include <SkRefCnt.h>
-#include <SkTLazy.h>
+
+#include <optional>
 
 namespace android {
 
@@ -126,13 +127,13 @@
 
 // Only called on the RenderThread.
 void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
-    SkTLazy<SkPaint> lazyPaint;
+    std::optional<SkPaint> lazyPaint;
     SkAutoCanvasRestore acr(canvas, false);
     if (mProperties.mAlpha != SK_AlphaOPAQUE || mProperties.mColorFilter.get()) {
-        lazyPaint.init();
-        lazyPaint.get()->setAlpha(mProperties.mAlpha);
-        lazyPaint.get()->setColorFilter(mProperties.mColorFilter);
-        lazyPaint.get()->setFilterQuality(kLow_SkFilterQuality);
+        lazyPaint.emplace();
+        lazyPaint->setAlpha(mProperties.mAlpha);
+        lazyPaint->setColorFilter(mProperties.mColorFilter);
+        lazyPaint->setFilterQuality(kLow_SkFilterQuality);
     }
     if (mProperties.mMirrored) {
         canvas->save();
@@ -147,8 +148,8 @@
     if (drawDirectly) {
         // The image is not animating, and never was. Draw directly from
         // mSkAnimatedImage.
-        if (lazyPaint.isValid()) {
-            canvas->saveLayer(mSkAnimatedImage->getBounds(), lazyPaint.get());
+        if (lazyPaint) {
+            canvas->saveLayer(mSkAnimatedImage->getBounds(), &*lazyPaint);
         }
 
         std::unique_lock lock{mImageLock};
@@ -193,7 +194,7 @@
     if (!drawDirectly) {
         // No other thread will modify mCurrentSnap so this should be safe to
         // use without locking.
-        canvas->drawPicture(mSnapshot.mPic, nullptr, lazyPaint.getMaybeNull());
+        canvas->drawPicture(mSnapshot.mPic, nullptr, lazyPaint ? &*lazyPaint : nullptr);
     }
 
     if (finalFrame) {
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index a401b3f..7a8d026 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -17,6 +17,7 @@
 
 #include "Caches.h"
 #include "HardwareBitmapUploader.h"
+#include "Properties.h"
 #include "renderthread/RenderProxy.h"
 #include "utils/Color.h"
 
@@ -34,6 +35,7 @@
 #include <SkToSRGBColorFilter.h>
 
 #include <limits>
+#include <SkHighContrastFilter.h>
 
 namespace android {
 
@@ -195,11 +197,13 @@
     mPixelStorage.ashmem.size = mappedSize;
 }
 
-Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info)
+Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette)
         : SkPixelRef(info.width(), info.height(), nullptr,
                      bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride())
         , mInfo(validateAlpha(info))
-        , mPixelStorageType(PixelStorageType::Hardware) {
+        , mPixelStorageType(PixelStorageType::Hardware)
+        , mPalette(palette)
+        , mPaletteGenerationId(getGenerationID()) {
     mPixelStorage.hardware.buffer = buffer;
     buffer->incStrong(buffer);
     setImmutable();  // HW bitmaps are always immutable
@@ -326,7 +330,106 @@
     if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) {
         *outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace());
     }
+
+    // TODO: Move this to the canvas (or other?) layer where we have the target lightness
+    // mode and can selectively do the right thing.
+    if (palette() != BitmapPalette::Unknown && uirenderer::Properties::forceDarkMode) {
+        SkHighContrastConfig config;
+        config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
+        *outputColorFilter = SkHighContrastFilter::Make(config)->makeComposed(*outputColorFilter);
+    }
     return image;
 }
 
+class MinMaxAverage {
+public:
+
+    void add(float sample) {
+        if (mCount == 0) {
+            mMin = sample;
+            mMax = sample;
+        } else {
+            mMin = std::min(mMin, sample);
+            mMax = std::max(mMax, sample);
+        }
+        mTotal += sample;
+        mCount++;
+    }
+
+    float average() {
+        return mTotal / mCount;
+    }
+
+    float min() {
+        return mMin;
+    }
+
+    float max() {
+        return mMax;
+    }
+
+    float delta() {
+        return mMax - mMin;
+    }
+
+private:
+    float mMin = 0.0f;
+    float mMax = 0.0f;
+    float mTotal = 0.0f;
+    int mCount = 0;
+};
+
+BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes) {
+    ATRACE_CALL();
+
+    SkPixmap pixmap{info, addr, rowBytes};
+
+    // TODO: This calculation of converting to HSV & tracking min/max is probably overkill
+    // Experiment with something simpler since we just want to figure out if it's "color-ful"
+    // and then the average perceptual lightness.
+
+    MinMaxAverage hue, saturation, value;
+    int sampledCount = 0;
+
+    // Sample a grid of 100 pixels to get an overall estimation of the colors in play
+    const int x_step = std::max(1, pixmap.width() / 10);
+    const int y_step = std::max(1, pixmap.height() / 10);
+    for (int x = 0; x < pixmap.width(); x += x_step) {
+        for (int y = 0; y < pixmap.height(); y += y_step) {
+            SkColor color = pixmap.getColor(x, y);
+            if (!info.isOpaque() && SkColorGetA(color) < 75) {
+                continue;
+            }
+
+            sampledCount++;
+            float hsv[3];
+            SkColorToHSV(color, hsv);
+            hue.add(hsv[0]);
+            saturation.add(hsv[1]);
+            value.add(hsv[2]);
+        }
+    }
+
+    // TODO: Tune the coverage threshold
+    if (sampledCount < 5) {
+        ALOGV("Not enough samples, only found %d for image sized %dx%d, format = %d, alpha = %d",
+              sampledCount, info.width(), info.height(), (int) info.colorType(), (int) info.alphaType());
+        return BitmapPalette::Unknown;
+    }
+
+    ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = %f]",
+          sampledCount,
+          hue.min(), hue.max(), hue.average(),
+          saturation.min(), saturation.max(), saturation.average());
+
+    if (hue.delta() <= 20 && saturation.delta() <= .1f) {
+        if (value.average() >= .5f) {
+            return BitmapPalette::Light;
+        } else {
+            return BitmapPalette::Dark;
+        }
+    }
+    return BitmapPalette::Unknown;
+}
+
 }  // namespace android
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index dbd4456..d268042 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -34,6 +34,12 @@
     Hardware,
 };
 
+enum class BitmapPalette {
+    Unknown,
+    Light,
+    Dark,
+};
+
 namespace uirenderer {
 namespace renderthread {
 class RenderThread;
@@ -63,7 +69,7 @@
     Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
            size_t rowBytes);
     Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
-    Bitmap(GraphicBuffer* buffer, const SkImageInfo& info);
+    Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette = BitmapPalette::Unknown);
 
     int rowBytesAsPixels() const { return rowBytes() >> mInfo.shiftPerPixel(); }
 
@@ -103,6 +109,20 @@
      */
     sk_sp<SkImage> makeImage(sk_sp<SkColorFilter>* outputColorFilter);
 
+    static BitmapPalette computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes);
+
+    static BitmapPalette computePalette(const SkBitmap& bitmap) {
+        return computePalette(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes());
+    }
+
+    BitmapPalette palette() {
+        if (!isHardware() && mPaletteGenerationId != getGenerationID()) {
+            mPalette = computePalette(info(), pixels(), rowBytes());
+            mPaletteGenerationId = getGenerationID();
+        }
+        return mPalette;
+    }
+
 private:
     virtual ~Bitmap();
     void* getStorage() const;
@@ -111,6 +131,9 @@
 
     const PixelStorageType mPixelStorageType;
 
+    BitmapPalette mPalette = BitmapPalette::Unknown;
+    uint32_t mPaletteGenerationId = -1;
+
     bool mHasHardwareMipMap = false;
 
     union {
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 5d380a6..b9af7de2 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -74,7 +74,6 @@
 }  // namespace SaveFlags
 
 namespace uirenderer {
-class SkiaCanvasProxy;
 namespace VectorDrawable {
 class Tree;
 };
@@ -305,7 +304,6 @@
 
     friend class DrawTextFunctor;
     friend class DrawTextOnPathFunctor;
-    friend class uirenderer::SkiaCanvasProxy;
 };
 
 };  // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 85fdc103..ea14d11 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -21,6 +21,8 @@
 #include "SkiaPipeline.h"
 #include "utils/TraceUtils.h"
 
+#include <optional>
+
 namespace android {
 namespace uirenderer {
 namespace skiapipeline {
@@ -171,9 +173,9 @@
 
 protected:
     bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type t) const override {
-        SkTLazy<SkPaint> defaultPaint;
+        std::optional<SkPaint> defaultPaint;
         if (!*paint) {
-            paint->init(*defaultPaint.init());
+            paint->init(defaultPaint.emplace());
         }
         paint->writable()->setAlpha((uint8_t)(*paint)->getAlpha() * mAlpha);
         return true;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 46e39aa..b9748af 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -257,9 +257,8 @@
     SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
     PaintCoW filteredPaint(paint);
-    // Besides kNone, the other three SkFilterQualities are treated the same. And Android's
-    // Java API only supports kLow and kNone anyway.
-    if (!filteredPaint || filteredPaint->getFilterQuality() == kNone_SkFilterQuality) {
+    // HWUI always draws 9-patches with bilinear filtering, regardless of what is set in the Paint.
+    if (!filteredPaint || filteredPaint->getFilterQuality() != kLow_SkFilterQuality) {
         filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
     }
     sk_sp<SkColorFilter> colorFilter;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 6eca8d2..e3807e6 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -85,6 +85,11 @@
             [ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); });
 }
 
+void RenderProxy::allocateBuffers(const sp<Surface>& surface) {
+    mRenderThread.queue().post(
+            [ surf = surface ]() mutable { surf->allocateBuffers(); });
+}
+
 void RenderProxy::updateSurface(const sp<Surface>& surface) {
     mRenderThread.queue().post(
             [ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); });
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 5668484..c2964a4 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -70,6 +70,7 @@
     ANDROID_API void setName(const char* name);
 
     ANDROID_API void initialize(const sp<Surface>& surface);
+    ANDROID_API void allocateBuffers(const sp<Surface>& surface);
     ANDROID_API void updateSurface(const sp<Surface>& surface);
     ANDROID_API bool pauseSurface(const sp<Surface>& surface);
     ANDROID_API void setStopped(bool stopped);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 3eaf43b..c1284ec 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -189,6 +189,7 @@
     LOG_ALWAYS_FATAL_IF(!glInterface.get());
 
     GrContextOptions options;
+    options.fPreferExternalImagesOverES3 = true;
     options.fDisableDistanceFieldPaths = true;
     cacheManager().configureContext(&options);
     sk_sp<GrContext> grContext(GrContext::MakeGL(std::move(glInterface), options));
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index a19edae..1517f57 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -25,6 +25,8 @@
 #include <GrBackendSurface.h>
 #include <GrContext.h>
 #include <GrTypes.h>
+#include <GrTypes.h>
+#include <vk/GrVkExtensions.h>
 #include <vk/GrVkTypes.h>
 
 namespace android {
@@ -62,7 +64,7 @@
     mInstance = VK_NULL_HANDLE;
 }
 
-bool VulkanManager::setupDevice(VkPhysicalDeviceFeatures& deviceFeatures) {
+bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFeatures2& features) {
     VkResult err;
 
     constexpr VkApplicationInfo app_info = {
@@ -128,7 +130,7 @@
     GET_INST_PROC(DestroyInstance);
     GET_INST_PROC(EnumeratePhysicalDevices);
     GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
-    GET_INST_PROC(GetPhysicalDeviceFeatures);
+    GET_INST_PROC(GetPhysicalDeviceFeatures2);
     GET_INST_PROC(CreateDevice);
     GET_INST_PROC(EnumerateDeviceExtensionProperties);
     GET_INST_PROC(CreateAndroidSurfaceKHR);
@@ -217,11 +219,38 @@
         }
     }
 
-    // query to get the physical device properties
-    mGetPhysicalDeviceFeatures(mPhysicalDevice, &deviceFeatures);
+    auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
+        if (device != VK_NULL_HANDLE) {
+            return vkGetDeviceProcAddr(device, proc_name);
+        }
+        return vkGetInstanceProcAddr(instance, proc_name);
+    };
+    grExtensions.init(getProc, mInstance, mPhysicalDevice, instanceExtensions.size(),
+            instanceExtensions.data(), deviceExtensions.size(), deviceExtensions.data());
+
+    memset(&features, 0, sizeof(VkPhysicalDeviceFeatures2));
+    features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+    features.pNext = nullptr;
+
+    // Setup all extension feature structs we may want to use.
+    void** tailPNext = &features.pNext;
+
+    if (grExtensions.hasExtension(VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME, 2)) {
+        VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT* blend;
+        blend = (VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT*) malloc(
+                sizeof(VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT));
+        LOG_ALWAYS_FATAL_IF(!blend);
+        blend->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT;
+        blend->pNext = nullptr;
+        *tailPNext = blend;
+        tailPNext = &blend->pNext;
+    }
+
+    // query to get the physical device features
+    mGetPhysicalDeviceFeatures2(mPhysicalDevice, &features);
     // this looks like it would slow things down,
     // and we can't depend on it on all platforms
-    deviceFeatures.robustBufferAccess = VK_FALSE;
+    features.features.robustBufferAccess = VK_FALSE;
 
     float queuePriorities[1] = { 0.0 };
 
@@ -247,7 +276,7 @@
 
     const VkDeviceCreateInfo deviceInfo = {
         VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,    // sType
-        nullptr,                                 // pNext
+        &features,                               // pNext
         0,                                       // VkDeviceCreateFlags
         queueInfoCount,                          // queueCreateInfoCount
         queueInfo,                               // pQueueCreateInfos
@@ -255,7 +284,7 @@
         nullptr,                                 // ppEnabledLayerNames
         (uint32_t) deviceExtensions.size(),      // extensionCount
         deviceExtensions.data(),                 // ppEnabledExtensionNames
-        &deviceFeatures                          // ppEnabledFeatures
+        nullptr,                                 // ppEnabledFeatures
     };
 
     err = mCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice);
@@ -294,33 +323,39 @@
     return true;
 }
 
+static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) {
+    // All Vulkan structs that could be part of the features chain will start with the
+    // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader
+    // so we can get access to the pNext for the next struct.
+    struct CommonVulkanHeader {
+        VkStructureType sType;
+        void*           pNext;
+    };
+
+    void* pNext = features.pNext;
+    while (pNext) {
+        void* current = pNext;
+        pNext = static_cast<CommonVulkanHeader*>(current)->pNext;
+        free(current);
+    }
+}
+
 void VulkanManager::initialize() {
     if (mDevice != VK_NULL_HANDLE) {
         return;
     }
 
-    std::vector<const char*> instanceExtensions;
-    std::vector<const char*> deviceExtensions;
-    VkPhysicalDeviceFeatures deviceFeatures;
-    LOG_ALWAYS_FATAL_IF(!this->setupDevice(deviceFeatures));
+    GET_PROC(EnumerateInstanceVersion);
+    uint32_t instanceVersion = 0;
+    LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion));
+    LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0));
+
+    GrVkExtensions extensions;
+    VkPhysicalDeviceFeatures2 features;
+    LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, features));
 
     mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
 
-    uint32_t extensionFlags = kKHR_surface_GrVkExtensionFlag |
-                              kKHR_android_surface_GrVkExtensionFlag |
-                              kKHR_swapchain_GrVkExtensionFlag;
-
-    uint32_t featureFlags = 0;
-    if (deviceFeatures.geometryShader) {
-        featureFlags |= kGeometryShader_GrVkFeatureFlag;
-    }
-    if (deviceFeatures.dualSrcBlend) {
-        featureFlags |= kDualSrcBlend_GrVkFeatureFlag;
-    }
-    if (deviceFeatures.sampleRateShading) {
-        featureFlags |= kSampleRateShading_GrVkFeatureFlag;
-    }
-
     auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
         if (device != VK_NULL_HANDLE) {
             return vkGetDeviceProcAddr(device, proc_name);
@@ -334,11 +369,10 @@
     backendContext.fDevice = mDevice;
     backendContext.fQueue = mGraphicsQueue;
     backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
-    backendContext.fMinAPIVersion = VK_MAKE_VERSION(1, 0, 0);
-    backendContext.fExtensions = extensionFlags;
-    backendContext.fFeatures = featureFlags;
+    backendContext.fInstanceVersion = instanceVersion;
+    backendContext.fVkExtensions = &extensions;
+    backendContext.fDeviceFeatures2 = &features;
     backendContext.fGetProc = std::move(getProc);
-    backendContext.fOwnsInstanceAndDevice = false;
 
     // create the command pool for the command buffers
     if (VK_NULL_HANDLE == mCommandPool) {
@@ -361,6 +395,9 @@
     sk_sp<GrContext> grContext(GrContext::MakeVulkan(backendContext, options));
     LOG_ALWAYS_FATAL_IF(!grContext.get());
     mRenderThread.setGrContext(grContext);
+
+    free_features_extensions_structs(features);
+
     DeviceInfo::initialize(mRenderThread.getGrContext()->maxRenderTargetSize());
 
     if (Properties::enablePartialUpdates && Properties::useBufferAge) {
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 9a72706..5524c39 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -17,10 +17,15 @@
 #ifndef VULKANMANAGER_H
 #define VULKANMANAGER_H
 
+#if !defined(VK_USE_PLATFORM_ANDROID_KHR)
+#  define VK_USE_PLATFORM_ANDROID_KHR
+#endif
+#include <vulkan/vulkan.h>
+
 #include <SkSurface.h>
 #include <vk/GrVkBackendContext.h>
 
-#include <vulkan/vulkan.h>
+class GrVkExtensions;
 
 namespace android {
 namespace uirenderer {
@@ -113,7 +118,7 @@
 
     // Sets up the VkInstance and VkDevice objects. Also fills out the passed in
     // VkPhysicalDeviceFeatures struct.
-    bool setupDevice(VkPhysicalDeviceFeatures& deviceFeatures);
+    bool setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&);
 
     void destroyBuffers(VulkanSurface* surface);
 
@@ -153,13 +158,14 @@
     VkPtr<PFN_vkCreateSharedSwapchainsKHR> mCreateSharedSwapchainsKHR;
 
     // Instance Functions
+    VkPtr<PFN_vkEnumerateInstanceVersion> mEnumerateInstanceVersion;
     VkPtr<PFN_vkEnumerateInstanceExtensionProperties> mEnumerateInstanceExtensionProperties;
     VkPtr<PFN_vkCreateInstance> mCreateInstance;
 
     VkPtr<PFN_vkDestroyInstance> mDestroyInstance;
     VkPtr<PFN_vkEnumeratePhysicalDevices> mEnumeratePhysicalDevices;
     VkPtr<PFN_vkGetPhysicalDeviceQueueFamilyProperties> mGetPhysicalDeviceQueueFamilyProperties;
-    VkPtr<PFN_vkGetPhysicalDeviceFeatures> mGetPhysicalDeviceFeatures;
+    VkPtr<PFN_vkGetPhysicalDeviceFeatures2> mGetPhysicalDeviceFeatures2;
     VkPtr<PFN_vkCreateDevice> mCreateDevice;
     VkPtr<PFN_vkEnumerateDeviceExtensionProperties> mEnumerateDeviceExtensionProperties;
 
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 75740e8..a3e7859 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -16,8 +16,10 @@
 
 #include "Color.h"
 
-
 #include <utils/Log.h>
+#include <ui/ColorSpace.h>
+
+#include <algorithm>
 #include <cmath>
 
 namespace android {
@@ -107,5 +109,97 @@
     }
 }
 
+template<typename T>
+static constexpr T clamp(T x, T min, T max) {
+    return x < min ? min : x > max ? max : x;
+}
+
+//static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
+static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
+static const mat3 BRADFORD = mat3{
+        float3{ 0.8951f, -0.7502f,  0.0389f},
+        float3{ 0.2664f,  1.7135f, -0.0685f},
+        float3{-0.1614f,  0.0367f,  1.0296f}
+};
+
+static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
+    float3 srcLMS = matrix * srcWhitePoint;
+    float3 dstLMS = matrix * dstWhitePoint;
+    return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
+}
+
+namespace LabColorSpace {
+
+static constexpr float A = 216.0f / 24389.0f;
+static constexpr float B = 841.0f / 108.0f;
+static constexpr float C = 4.0f / 29.0f;
+static constexpr float D = 6.0f / 29.0f;
+
+float3 toXyz(const Lab& lab) {
+    float3 v { lab.L, lab.a, lab.b };
+    v[0] = clamp(v[0], 0.0f, 100.0f);
+    v[1] = clamp(v[1], -128.0f, 128.0f);
+    v[2] = clamp(v[2], -128.0f, 128.0f);
+
+    float fy = (v[0] + 16.0f) / 116.0f;
+    float fx = fy + (v[1] * 0.002f);
+    float fz = fy - (v[2] * 0.005f);
+    float X = fx > D ? fx * fx * fx : (1.0f / B) * (fx - C);
+    float Y = fy > D ? fy * fy * fy : (1.0f / B) * (fy - C);
+    float Z = fz > D ? fz * fz * fz : (1.0f / B) * (fz - C);
+
+    v[0] = X * ILLUMINANT_D50_XYZ[0];
+    v[1] = Y * ILLUMINANT_D50_XYZ[1];
+    v[2] = Z * ILLUMINANT_D50_XYZ[2];
+
+    return v;
+}
+
+Lab fromXyz(const float3& v) {
+    float X = v[0] / ILLUMINANT_D50_XYZ[0];
+    float Y = v[1] / ILLUMINANT_D50_XYZ[1];
+    float Z = v[2] / ILLUMINANT_D50_XYZ[2];
+
+    float fx = X > A ? pow(X, 1.0f / 3.0f) : B * X + C;
+    float fy = Y > A ? pow(Y, 1.0f / 3.0f) : B * Y + C;
+    float fz = Z > A ? pow(Z, 1.0f / 3.0f) : B * Z + C;
+
+    float L = 116.0f * fy - 16.0f;
+    float a = 500.0f * (fx - fy);
+    float b = 200.0f * (fy - fz);
+
+    return Lab {
+            clamp(L, 0.0f, 100.0f),
+            clamp(a, -128.0f, 128.0f),
+            clamp(b, -128.0f, 128.0f)
+    };
+}
+
+};
+
+Lab sRGBToLab(SkColor color) {
+    auto colorSpace = ColorSpace::sRGB();
+    float3 rgb;
+    rgb.r = SkColorGetR(color) / 255.0f;
+    rgb.g = SkColorGetG(color) / 255.0f;
+    rgb.b = SkColorGetB(color) / 255.0f;
+    float3 xyz = colorSpace.rgbToXYZ(rgb);
+    float3 srcXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
+    xyz = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * xyz;
+    return LabColorSpace::fromXyz(xyz);
+}
+
+SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) {
+    auto colorSpace = ColorSpace::sRGB();
+    float3 xyz = LabColorSpace::toXyz(lab);
+    float3 dstXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
+    xyz = adaptation(BRADFORD, ILLUMINANT_D50_XYZ, dstXYZ) * xyz;
+    float3 rgb = colorSpace.xyzToRGB(xyz);
+    return SkColorSetARGB(alpha,
+            static_cast<uint8_t>(rgb.r * 255),
+            static_cast<uint8_t>(rgb.g * 255),
+            static_cast<uint8_t>(rgb.b * 255));
+}
+
 };  // namespace uirenderer
 };  // namespace android
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 2bec1f5..3c13a54 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -114,6 +114,16 @@
 bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
 
 sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
+
+struct Lab {
+    float L;
+    float a;
+    float b;
+};
+
+Lab sRGBToLab(SkColor color);
+SkColor LabToSRGB(const Lab& lab, SkAlpha alpha);
+
 } /* namespace uirenderer */
 } /* namespace android */