Allow PixelCopy for a window from any View

Also make it actually async, and allow the bitmap
to be auto-allocated

Bug: 195673633
Test: PixelCopyTest CTS suite

Change-Id: Ie872f20c809eaaeb8dc32f3ec6347f21a9a7bc1a
diff --git a/libs/hwui/CopyRequest.h b/libs/hwui/CopyRequest.h
new file mode 100644
index 0000000..5fbd5f9
--- /dev/null
+++ b/libs/hwui/CopyRequest.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Rect.h"
+#include "hwui/Bitmap.h"
+
+namespace android::uirenderer {
+
+// Keep in sync with PixelCopy.java codes
+enum class CopyResult {
+    Success = 0,
+    UnknownError = 1,
+    Timeout = 2,
+    SourceEmpty = 3,
+    SourceInvalid = 4,
+    DestinationInvalid = 5,
+};
+
+struct CopyRequest {
+    Rect srcRect;
+    CopyRequest(Rect srcRect) : srcRect(srcRect) {}
+    virtual ~CopyRequest() {}
+    virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) = 0;
+    virtual void onCopyFinished(CopyResult result) = 0;
+};
+
+}  // namespace android::uirenderer
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 1191b92..02c2e67 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -49,8 +49,7 @@
 
 #define ARECT_ARGS(r) float((r).left), float((r).top), float((r).right), float((r).bottom)
 
-CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRect,
-                                     SkBitmap* bitmap) {
+void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request) {
     ATRACE_CALL();
     // Setup the source
     AHardwareBuffer* rawSourceBuffer;
@@ -63,30 +62,33 @@
     // Really this shouldn't ever happen, but better safe than sorry.
     if (err == UNKNOWN_TRANSACTION) {
         ALOGW("Readback failed to ANativeWindow_getLastQueuedBuffer2 - who are we talking to?");
-        return copySurfaceIntoLegacy(window, inSrcRect, bitmap);
+        return request->onCopyFinished(CopyResult::SourceInvalid);
     }
     ALOGV("Using new path, cropRect=" RECT_STRING ", transform=%x", ARECT_ARGS(cropRect),
           windowTransform);
 
     if (err != NO_ERROR) {
         ALOGW("Failed to get last queued buffer, error = %d", err);
-        return CopyResult::UnknownError;
+        return request->onCopyFinished(CopyResult::SourceInvalid);
     }
     if (rawSourceBuffer == nullptr) {
         ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
-        return CopyResult::SourceEmpty;
+        return request->onCopyFinished(CopyResult::SourceEmpty);
     }
     UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
     AHardwareBuffer_Desc description;
     AHardwareBuffer_describe(sourceBuffer.get(), &description);
     if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
         ALOGW("Surface is protected, unable to copy from it");
-        return CopyResult::SourceInvalid;
+        return request->onCopyFinished(CopyResult::SourceInvalid);
     }
 
-    if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
-        ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
-        return CopyResult::Timeout;
+    {
+        ATRACE_NAME("sync_wait");
+        if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
+            ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+            return request->onCopyFinished(CopyResult::Timeout);
+        }
     }
 
     sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
@@ -95,12 +97,12 @@
             SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
 
     if (!image.get()) {
-        return CopyResult::UnknownError;
+        return request->onCopyFinished(CopyResult::UnknownError);
     }
 
     sk_sp<GrDirectContext> grContext = mRenderThread.requireGrContext();
 
-    SkRect srcRect = inSrcRect.toSkRect();
+    SkRect srcRect = request->srcRect.toSkRect();
 
     SkRect imageSrcRect = SkRect::MakeIWH(description.width, description.height);
     SkISize imageWH = SkISize::Make(description.width, description.height);
@@ -148,10 +150,12 @@
         ALOGV("intersecting " RECT_STRING " with " RECT_STRING, SK_RECT_ARGS(srcRect),
               SK_RECT_ARGS(textureRect));
         if (!srcRect.intersect(textureRect)) {
-            return CopyResult::UnknownError;
+            return request->onCopyFinished(CopyResult::UnknownError);
         }
     }
 
+    SkBitmap skBitmap = request->getDestinationBitmap(srcRect.width(), srcRect.height());
+    SkBitmap* bitmap = &skBitmap;
     sk_sp<SkSurface> tmpSurface =
             SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
                                         bitmap->info(), 0, kTopLeft_GrSurfaceOrigin, nullptr);
@@ -164,7 +168,7 @@
                                                  tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr);
         if (!tmpSurface.get()) {
             ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap");
-            return CopyResult::UnknownError;
+            return request->onCopyFinished(CopyResult::UnknownError);
         }
     }
 
@@ -235,52 +239,13 @@
             !tmpBitmap.tryAllocPixels(tmpInfo) || !tmpSurface->readPixels(tmpBitmap, 0, 0) ||
             !tmpBitmap.readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
             ALOGW("Unable to convert content into the provided bitmap");
-            return CopyResult::UnknownError;
+            return request->onCopyFinished(CopyResult::UnknownError);
         }
     }
 
     bitmap->notifyPixelsChanged();
 
-    return CopyResult::Success;
-}
-
-CopyResult Readback::copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect,
-                                           SkBitmap* bitmap) {
-    // Setup the source
-    AHardwareBuffer* rawSourceBuffer;
-    int rawSourceFence;
-    Matrix4 texTransform;
-    status_t err = ANativeWindow_getLastQueuedBuffer(window, &rawSourceBuffer, &rawSourceFence,
-                                                     texTransform.data);
-    base::unique_fd sourceFence(rawSourceFence);
-    texTransform.invalidateType();
-    if (err != NO_ERROR) {
-        ALOGW("Failed to get last queued buffer, error = %d", err);
-        return CopyResult::UnknownError;
-    }
-    if (rawSourceBuffer == nullptr) {
-        ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
-        return CopyResult::SourceEmpty;
-    }
-
-    UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
-    AHardwareBuffer_Desc description;
-    AHardwareBuffer_describe(sourceBuffer.get(), &description);
-    if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
-        ALOGW("Surface is protected, unable to copy from it");
-        return CopyResult::SourceInvalid;
-    }
-
-    if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
-        ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
-        return CopyResult::Timeout;
-    }
-
-    sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
-            static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
-    sk_sp<SkImage> image =
-            SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
-    return copyImageInto(image, srcRect, bitmap);
+    return request->onCopyFinished(CopyResult::Success);
 }
 
 CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
@@ -318,14 +283,14 @@
 CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect,
                                    SkBitmap* bitmap) {
     ATRACE_CALL();
+    if (!image.get()) {
+        return CopyResult::UnknownError;
+    }
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
         mRenderThread.requireGlContext();
     } else {
         mRenderThread.requireVkContext();
     }
-    if (!image.get()) {
-        return CopyResult::UnknownError;
-    }
     int imgWidth = image->width();
     int imgHeight = image->height();
     sk_sp<GrDirectContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index aa6e43c..a092d47 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -16,12 +16,13 @@
 
 #pragma once
 
+#include <SkRefCnt.h>
+
+#include "CopyRequest.h"
 #include "Matrix.h"
 #include "Rect.h"
 #include "renderthread/RenderThread.h"
 
-#include <SkRefCnt.h>
-
 class SkBitmap;
 class SkImage;
 struct SkRect;
@@ -35,23 +36,13 @@
 class DeferredLayerUpdater;
 class Layer;
 
-// Keep in sync with PixelCopy.java codes
-enum class CopyResult {
-    Success = 0,
-    UnknownError = 1,
-    Timeout = 2,
-    SourceEmpty = 3,
-    SourceInvalid = 4,
-    DestinationInvalid = 5,
-};
-
 class Readback {
 public:
     explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
     /**
      * Copies the surface's most recently queued buffer into the provided bitmap.
      */
-    CopyResult copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
+    void copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request);
 
     CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
     CopyResult copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap);
@@ -59,7 +50,6 @@
     CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
 
 private:
-    CopyResult copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
     CopyResult copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect, SkBitmap* bitmap);
 
     bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 55b1f23..4f281fc 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -88,6 +88,11 @@
     jmethodID onFrameComplete;
 } gFrameCompleteCallback;
 
+struct {
+    jmethodID onCopyFinished;
+    jmethodID getDestinationBitmap;
+} gCopyRequest;
+
 static JNIEnv* getenv(JavaVM* vm) {
     JNIEnv* env;
     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -672,15 +677,41 @@
     }
 }
 
-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);
+class CopyRequestAdapter : public CopyRequest {
+public:
+    CopyRequestAdapter(JavaVM* vm, jobject jCopyRequest, Rect srcRect)
+            : CopyRequest(srcRect), mRefHolder(vm, jCopyRequest) {}
+
+    virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
+        JNIEnv* env = getenv(mRefHolder.vm());
+        jlong bitmapPtr = env->CallLongMethod(
+                mRefHolder.object(), gCopyRequest.getDestinationBitmap, srcWidth, srcHeight);
+        SkBitmap bitmap;
+        bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
+        return bitmap;
+    }
+
+    virtual void onCopyFinished(CopyResult result) override {
+        JNIEnv* env = getenv(mRefHolder.vm());
+        env->CallVoidMethod(mRefHolder.object(), gCopyRequest.onCopyFinished,
+                            static_cast<jint>(result));
+    }
+
+private:
+    JGlobalRefHolder mRefHolder;
+};
+
+static void android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, jobject clazz,
+                                                          jobject jsurface, jint left, jint top,
+                                                          jint right, jint bottom,
+                                                          jobject jCopyRequest) {
+    JavaVM* vm = nullptr;
+    LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+    auto copyRequest = std::make_shared<CopyRequestAdapter>(vm, env->NewGlobalRef(jCopyRequest),
+                                                            Rect(left, top, right, bottom));
     ANativeWindow* window = fromSurface(env, jsurface);
-    jint result = RenderProxy::copySurfaceInto(window, left, top, right, bottom, &bitmap);
+    RenderProxy::copySurfaceInto(window, std::move(copyRequest));
     ANativeWindow_release(window);
-    return result;
 }
 
 class ContextFactory : public IContextFactory {
@@ -969,7 +1000,8 @@
          (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",
+        {"nCopySurfaceInto",
+         "(Landroid/view/Surface;IIIILandroid/graphics/HardwareRenderer$CopyRequest;)V",
          (void*)android_view_ThreadedRenderer_copySurfaceInto},
         {"nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;",
          (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode},
@@ -1042,6 +1074,11 @@
     gFrameCompleteCallback.onFrameComplete =
             GetMethodIDOrDie(env, frameCompleteClass, "onFrameComplete", "()V");
 
+    jclass copyRequest = FindClassOrDie(env, "android/graphics/HardwareRenderer$CopyRequest");
+    gCopyRequest.onCopyFinished = GetMethodIDOrDie(env, copyRequest, "onCopyFinished", "(I)V");
+    gCopyRequest.getDestinationBitmap =
+            GetMethodIDOrDie(env, copyRequest, "getDestinationBitmap", "(II)J");
+
     void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
     fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface");
     LOG_ALWAYS_FATAL_IF(fromSurface == nullptr,
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b2ba15c..40a0bac 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -364,12 +364,13 @@
     mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); });
 }
 
-int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom,
-                                 SkBitmap* bitmap) {
+void RenderProxy::copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request) {
     auto& thread = RenderThread::getInstance();
-    return static_cast<int>(thread.queue().runSync([&]() -> auto {
-        return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap);
-    }));
+    ANativeWindow_acquire(window);
+    thread.queue().post([&thread, window, request = std::move(request)] {
+        thread.readback().copySurfaceInto(window, request);
+        ANativeWindow_release(window);
+    });
 }
 
 void RenderProxy::prepareToDraw(Bitmap& bitmap) {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index bbfeeac..2a99a73 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -19,13 +19,14 @@
 
 #include <SkRefCnt.h>
 #include <android/native_window.h>
-#include <cutils/compiler.h>
 #include <android/surface_control.h>
+#include <cutils/compiler.h>
 #include <utils/Functor.h>
 
 #include "../FrameMetricsObserver.h"
 #include "../IContextFactory.h"
 #include "ColorMode.h"
+#include "CopyRequest.h"
 #include "DrawFrameTask.h"
 #include "SwapBehavior.h"
 #include "hwui/Bitmap.h"
@@ -137,8 +138,7 @@
     void removeFrameMetricsObserver(FrameMetricsObserver* observer);
     void setForceDark(bool enable);
 
-    static int copySurfaceInto(ANativeWindow* window, int left, int top, int right,
-                                           int bottom, SkBitmap* bitmap);
+    static void copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request);
     static void prepareToDraw(Bitmap& bitmap);
 
     static int copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
index 070a339..13a4381 100644
--- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
@@ -25,17 +25,51 @@
 
 class MagnifierAnimation;
 
+using Rect = android::uirenderer::Rect;
+
 static TestScene::Registrar _Magnifier(TestScene::Info{
         "magnifier", "A sample magnifier using Readback",
         TestScene::simpleCreateScene<MagnifierAnimation>});
 
+class BlockingCopyRequest : public CopyRequest {
+    sk_sp<Bitmap> mDestination;
+    std::mutex mLock;
+    std::condition_variable mCondVar;
+    CopyResult mResult;
+
+public:
+    BlockingCopyRequest(::Rect rect, sk_sp<Bitmap> bitmap)
+            : CopyRequest(rect), mDestination(bitmap) {}
+
+    virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
+        SkBitmap bitmap;
+        mDestination->getSkBitmap(&bitmap);
+        return bitmap;
+    }
+
+    virtual void onCopyFinished(CopyResult result) override {
+        std::unique_lock _lock{mLock};
+        mResult = result;
+        mCondVar.notify_all();
+    }
+
+    CopyResult waitForResult() {
+        std::unique_lock _lock{mLock};
+        mCondVar.wait(_lock);
+        return mResult;
+    }
+};
+
 class MagnifierAnimation : public TestScene {
 public:
     sp<RenderNode> card;
     sp<RenderNode> zoomImageView;
+    sk_sp<Bitmap> magnifier;
+    std::shared_ptr<BlockingCopyRequest> copyRequest;
 
     void createContent(int width, int height, Canvas& canvas) override {
         magnifier = TestUtils::createBitmap(200, 100);
+        setupCopyRequest();
         SkBitmap temp;
         magnifier->getSkBitmap(&temp);
         temp.eraseColor(Color::White);
@@ -65,19 +99,20 @@
         canvas.enableZ(false);
     }
 
+    void setupCopyRequest() {
+        constexpr int x = 90;
+        constexpr int y = 325;
+        copyRequest = std::make_shared<BlockingCopyRequest>(
+                ::Rect(x, y, x + magnifier->width(), y + magnifier->height()), magnifier);
+    }
+
     void doFrame(int frameNr) override {
         int curFrame = frameNr % 150;
         card->mutateStagingProperties().setTranslationX(curFrame);
         card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
         if (renderTarget) {
-            SkBitmap temp;
-            magnifier->getSkBitmap(&temp);
-            constexpr int x = 90;
-            constexpr int y = 325;
-            RenderProxy::copySurfaceInto(renderTarget.get(), x, y, x + magnifier->width(),
-                                         y + magnifier->height(), &temp);
+            RenderProxy::copySurfaceInto(renderTarget.get(), copyRequest);
+            copyRequest->waitForResult();
         }
     }
-
-    sk_sp<Bitmap> magnifier;
 };
diff --git a/libs/hwui/thread/WorkQueue.h b/libs/hwui/thread/WorkQueue.h
index 46b8bc0..f2751d2 100644
--- a/libs/hwui/thread/WorkQueue.h
+++ b/libs/hwui/thread/WorkQueue.h
@@ -57,7 +57,7 @@
 
 public:
     WorkQueue(std::function<void()>&& wakeFunc, std::mutex& lock)
-            : mWakeFunc(move(wakeFunc)), mLock(lock) {}
+            : mWakeFunc(std::move(wakeFunc)), mLock(lock) {}
 
     void process() {
         auto now = clock::now();