Merge "Use a wrapper around ANW to allow updating Surface" into main
diff --git a/libs/android_display_backend/crosvm_android_display_client.cpp b/libs/android_display_backend/crosvm_android_display_client.cpp
index 0557127..6e4a793 100644
--- a/libs/android_display_backend/crosvm_android_display_client.cpp
+++ b/libs/android_display_backend/crosvm_android_display_client.cpp
@@ -30,47 +30,179 @@
 
 namespace {
 
+class SinkANativeWindow_Buffer {
+public:
+    SinkANativeWindow_Buffer() = default;
+    virtual ~SinkANativeWindow_Buffer() = default;
+
+    bool configure(uint32_t width, uint32_t height, int format) {
+        if (format != HAL_PIXEL_FORMAT_BGRA_8888) {
+            return false;
+        }
+
+        mBufferBits.resize(width * height * 4);
+        mBuffer = ANativeWindow_Buffer{
+                .width = static_cast<int32_t>(width),
+                .height = static_cast<int32_t>(height),
+                .stride = static_cast<int32_t>(width),
+                .format = format,
+                .bits = mBufferBits.data(),
+        };
+        return true;
+    }
+
+    operator ANativeWindow_Buffer&() { return mBuffer; }
+
+private:
+    ANativeWindow_Buffer mBuffer;
+    std::vector<uint8_t> mBufferBits;
+};
+
+// Wrapper which contains the latest available Surface/ANativeWindow
+// from the DisplayService, if available. A Surface/ANativeWindow may
+// not always be available if, for example, the VmLauncherApp on the
+// other end of the DisplayService is not in the foreground / is paused.
+class AndroidDisplaySurface {
+public:
+    AndroidDisplaySurface() = default;
+    virtual ~AndroidDisplaySurface() = default;
+
+    void setSurface(Surface* surface) {
+        {
+            std::lock_guard lk(mSurfaceMutex);
+            mNativeSurface = std::make_unique<Surface>(surface->release());
+            mNativeSurfaceNeedsConfiguring = true;
+        }
+
+        mNativeSurfaceReady.notify_one();
+    }
+
+    void removeSurface() {
+        {
+            std::lock_guard lk(mSurfaceMutex);
+            mNativeSurface = nullptr;
+        }
+        mNativeSurfaceReady.notify_one();
+    }
+
+    Surface* getSurface() {
+        std::unique_lock lk(mSurfaceMutex);
+        return mNativeSurface.get();
+    }
+
+    void configure(uint32_t width, uint32_t height) {
+        std::unique_lock lk(mSurfaceMutex);
+
+        mRequestedSurfaceDimensions = Rect{
+                .width = width,
+                .height = height,
+        };
+
+        mSinkBuffer.configure(width, height, kFormat);
+    }
+
+    void waitForNativeSurface() {
+        std::unique_lock lk(mSurfaceMutex);
+        mNativeSurfaceReady.wait(lk, [this] { return mNativeSurface != nullptr; });
+    }
+
+    int lock(ANativeWindow_Buffer* out_buffer) {
+        std::unique_lock lk(mSurfaceMutex);
+
+        Surface* surface = mNativeSurface.get();
+        if (surface == nullptr) {
+            // Surface not currently available but not necessarily an error
+            // if, for example, the VmLauncherApp is not in the foreground.
+            *out_buffer = mSinkBuffer;
+            return 0;
+        }
+
+        ANativeWindow* anw = surface->get();
+        if (anw == nullptr) {
+            return -1;
+        }
+
+        if (mNativeSurfaceNeedsConfiguring) {
+            if (!mRequestedSurfaceDimensions) {
+                return -1;
+            }
+            const auto& dims = *mRequestedSurfaceDimensions;
+
+            // Ensure locked buffers have our desired format.
+            if (ANativeWindow_setBuffersGeometry(anw, dims.width, dims.height, kFormat) != 0) {
+                return -1;
+            }
+
+            mNativeSurfaceNeedsConfiguring = false;
+        }
+
+        return ANativeWindow_lock(anw, out_buffer, nullptr);
+    }
+
+    int unlockAndPost() {
+        std::unique_lock lk(mSurfaceMutex);
+
+        Surface* surface = mNativeSurface.get();
+        if (surface == nullptr) {
+            // Surface not currently available but not necessarily an error
+            // if, for example, the VmLauncherApp is not in the foreground.
+            return 0;
+        }
+
+        ANativeWindow* anw = surface->get();
+        if (anw == nullptr) {
+            return -1;
+        }
+
+        return ANativeWindow_unlockAndPost(anw);
+    }
+
+private:
+    // Note: crosvm always uses BGRA8888 or BGRX8888. See devices/src/virtio/gpu/mod.rs in
+    // crosvm where the SetScanoutBlob command is handled. Let's use BGRA not BGRX with a hope
+    // that we will need alpha blending for the cursor surface.
+    static constexpr const int kFormat = HAL_PIXEL_FORMAT_BGRA_8888;
+
+    std::mutex mSurfaceMutex;
+    std::unique_ptr<Surface> mNativeSurface;
+    std::condition_variable mNativeSurfaceReady;
+    bool mNativeSurfaceNeedsConfiguring = true;
+
+    SinkANativeWindow_Buffer mSinkBuffer;
+
+    struct Rect {
+        uint32_t width = 0;
+        uint32_t height = 0;
+    };
+    std::optional<Rect> mRequestedSurfaceDimensions;
+};
+
 class DisplayService : public BnCrosvmAndroidDisplayService {
 public:
     DisplayService() = default;
     virtual ~DisplayService() = default;
 
     ndk::ScopedAStatus setSurface(Surface* surface, bool forCursor) override {
-        {
-            std::lock_guard lk(mSurfaceReadyMutex);
-            if (forCursor) {
-                mCursorSurface = std::make_unique<Surface>(surface->release());
-            } else {
-                mSurface = std::make_unique<Surface>(surface->release());
-            }
+        if (forCursor) {
+            mCursor.setSurface(surface);
+        } else {
+            mScanout.setSurface(surface);
         }
-        mSurfaceReady.notify_all();
         return ::ndk::ScopedAStatus::ok();
     }
 
     ndk::ScopedAStatus removeSurface(bool forCursor) override {
-        {
-            std::lock_guard lk(mSurfaceReadyMutex);
-            if (forCursor) {
-                mCursorSurface = nullptr;
-            } else {
-                mSurface = nullptr;
-            }
+        if (forCursor) {
+            mCursor.removeSurface();
+        } else {
+            mScanout.removeSurface();
         }
-        mSurfaceReady.notify_all();
         return ::ndk::ScopedAStatus::ok();
     }
 
-    Surface* getSurface(bool forCursor) {
-        std::unique_lock lk(mSurfaceReadyMutex);
-        if (forCursor) {
-            mSurfaceReady.wait(lk, [this] { return mCursorSurface != nullptr; });
-            return mCursorSurface.get();
-        } else {
-            mSurfaceReady.wait(lk, [this] { return mSurface != nullptr; });
-            return mSurface.get();
-        }
-    }
+    AndroidDisplaySurface* getCursorSurface() { return &mCursor; }
+    AndroidDisplaySurface* getScanoutSurface() { return &mScanout; }
+
     ndk::ScopedFileDescriptor& getCursorStream() { return mCursorStream; }
     ndk::ScopedAStatus setCursorStream(const ndk::ScopedFileDescriptor& in_stream) {
         mCursorStream = ndk::ScopedFileDescriptor(dup(in_stream.get()));
@@ -78,10 +210,8 @@
     }
 
 private:
-    std::condition_variable mSurfaceReady;
-    std::mutex mSurfaceReadyMutex;
-    std::unique_ptr<Surface> mSurface;
-    std::unique_ptr<Surface> mCursorSurface;
+    AndroidDisplaySurface mScanout;
+    AndroidDisplaySurface mCursor;
     ndk::ScopedFileDescriptor mCursorStream;
 };
 
@@ -149,25 +279,29 @@
     delete ctx;
 }
 
-extern "C" ANativeWindow* create_android_surface(struct AndroidDisplayContext* ctx, uint32_t width,
-                                                 uint32_t height, bool for_cursor) {
+extern "C" AndroidDisplaySurface* create_android_surface(struct AndroidDisplayContext* ctx,
+                                                         uint32_t width, uint32_t height,
+                                                         bool forCursor) {
     if (ctx->disp_service == nullptr) {
         ctx->errorf("Display service was not created");
         return nullptr;
     }
-    // Note: crosvm always uses BGRA8888 or BGRX8888. See devices/src/virtio/gpu/mod.rs in crosvm
-    // where the SetScanoutBlob command is handled. Let's use BGRA not BGRX with a hope that we will
-    // need alpha blending for the cursor surface.
-    int format = HAL_PIXEL_FORMAT_BGRA_8888;
-    ANativeWindow* surface = ctx->disp_service->getSurface(for_cursor)->get(); // this can block
-    if (ANativeWindow_setBuffersGeometry(surface, width, height, format) != 0) {
-        ctx->errorf("Failed to set buffer gemoetry");
+
+    AndroidDisplaySurface* displaySurface = forCursor ? ctx->disp_service->getCursorSurface()
+                                                      : ctx->disp_service->getScanoutSurface();
+    if (displaySurface == nullptr) {
+        ctx->errorf("AndroidDisplaySurface was not created");
         return nullptr;
     }
+
+    displaySurface->configure(width, height);
+
+    displaySurface->waitForNativeSurface(); // this can block
+
     // TODO(b/332785161): if we know that surface can get destroyed dynamically while VM is running,
     // consider calling ANativeWindow_acquire here and _release in destroy_android_surface, so that
     // crosvm doesn't hold a dangling pointer.
-    return surface;
+    return displaySurface;
 }
 
 extern "C" void destroy_android_surface(struct AndroidDisplayContext*, ANativeWindow*) {
@@ -175,16 +309,23 @@
 }
 
 extern "C" bool get_android_surface_buffer(struct AndroidDisplayContext* ctx,
-                                           ANativeWindow* surface,
+                                           AndroidDisplaySurface* surface,
                                            ANativeWindow_Buffer* out_buffer) {
     if (out_buffer == nullptr) {
         ctx->errorf("out_buffer is null");
         return false;
     }
-    if (ANativeWindow_lock(surface, out_buffer, nullptr) != 0) {
+
+    if (surface == nullptr) {
+        ctx->errorf("Invalid AndroidDisplaySurface provided");
+        return false;
+    }
+
+    if (surface->lock(out_buffer) != 0) {
         ctx->errorf("Failed to lock buffer");
         return false;
     }
+
     return true;
 }
 
@@ -204,9 +345,14 @@
 }
 
 extern "C" void post_android_surface_buffer(struct AndroidDisplayContext* ctx,
-                                            ANativeWindow* surface) {
-    if (ANativeWindow_unlockAndPost(surface) != 0) {
-        ctx->errorf("Failed to unlock and post surface.");
+                                            AndroidDisplaySurface* surface) {
+    if (surface == nullptr) {
+        ctx->errorf("Invalid AndroidDisplaySurface provided");
+        return;
+    }
+
+    if (surface->unlockAndPost() != 0) {
+        ctx->errorf("Failed to unlock and post AndroidDisplaySurface.");
         return;
     }
 }