Make RutabagaLayer shared between "guest impls"

... so that "guest gralloc", "guest egl", "guest vulkan", etc are
all using the same underlying emulation layer (RutabagaLayer).

This moves Gralloc and ANativeWindow into platform as these should
be hidden from GfxstreamEnd2EndTests.

Note: we still want to static-ify a lot of the guest libraries.

Bug: b/323902846
Test: cd hardware/google/gfxstream && mma
Test: cd hardware/google/gfxstream &&
      meson setup \
          -Ddefault_library=static \
          -Dgfxstream-build=both \
          build
      meson compile -C build
Test: GfxstreamEnd2EndTests
Test: cvd start --gpu_mode=gfxstream
Change-Id: If929cecf353587d78cf100d456e7d3b5f141e4f0
diff --git a/guest/OpenglSystemCommon/Android.bp b/guest/OpenglSystemCommon/Android.bp
index 0cf3aaa..3cb00d6 100644
--- a/guest/OpenglSystemCommon/Android.bp
+++ b/guest/OpenglSystemCommon/Android.bp
@@ -19,50 +19,6 @@
     default_applicable_licenses: ["hardware_google_gfxstream_license"],
 }
 
-cc_library_static {
-    name: "libgfxstream_guest_gralloc",
-    vendor: true,
-    host_supported: true,
-    defaults: [
-        "libgfxstream_guest_cc_defaults",
-    ],
-    header_libs: [
-        "libdrm_headers",
-        "libgfxstream_guest_graphics_headers",
-        "libgfxstream_guest_iostream",
-        "libgralloc_cb.ranchu",
-        "minigbm_headers",
-    ],
-    export_header_lib_headers: [
-        "libgfxstream_guest_graphics_headers",
-        "libgfxstream_guest_iostream",
-        "libgralloc_cb.ranchu",
-    ],
-    shared_libs: [
-        "lib_renderControl_enc",
-    ],
-    export_shared_lib_headers: [
-        "lib_renderControl_enc",
-    ],
-    export_include_dirs: [
-        ".",
-    ],
-    target: {
-        android: {
-            shared_libs: [
-                "libnativewindow",
-            ],
-            export_shared_lib_headers: [
-                "libnativewindow",
-            ],
-            srcs: [
-                "GrallocGoldfish.cpp",
-                "GrallocMinigbm.cpp",
-            ],
-        },
-    },
-}
-
 cc_library_headers {
     name: "libgfxstream_guest_sync",
     export_include_dirs: [
@@ -70,6 +26,24 @@
     ],
 }
 
+cc_library_headers {
+    name: "libOpenglSystemCommonHeaders",
+    host_supported: true,
+    vendor: true,
+    defaults: [
+        "libgfxstream_guest_cc_defaults",
+    ],
+    export_include_dirs: [
+        ".",
+    ],
+    header_libs: [
+        "libgfxstream_guest_rendercontrol_encoder_headers",
+    ],
+    export_header_lib_headers: [
+        "libgfxstream_guest_rendercontrol_encoder_headers",
+    ],
+}
+
 cc_defaults {
     name: "libOpenglSystemCommonDefaults",
     host_supported: true,
@@ -81,15 +55,12 @@
         "gfxstream_vulkan_headers",
         "libgfxstream_guest_graphics_headers",
         "libgfxstream_guest_iostream",
-        "libgralloc_cb.ranchu",
         "libhardware_headers",
         "libnativebase_headers",
-        "minigbm_headers",
     ],
     export_header_lib_headers: [
         "libgfxstream_guest_graphics_headers",
         "libgfxstream_guest_iostream",
-        "libgralloc_cb.ranchu",
         "libhardware_headers",
         "libnativebase_headers",
     ],
@@ -112,10 +83,6 @@
         "libGoldfishAddressSpace",
         "libringbuffer",
         "libqemupipe.ranchu",
-        "libgfxstream_guest_gralloc",
-    ],
-    export_static_lib_headers: [
-        "libgfxstream_guest_gralloc",
     ],
     cflags: [
         "-Wno-unused-variable",
@@ -140,18 +107,6 @@
             export_header_lib_headers: [
                 "gfxstream_opengl_headers",
             ],
-            shared_libs: [
-                "libnativewindow",
-                "libsync",
-            ],
-            srcs: [
-                "ANativeWindowAndroid.cpp",
-            ],
-        },
-        host: {
-            srcs: [
-                "TestingAndroidWsi.cpp",
-            ],
         },
     },
 }
@@ -165,12 +120,22 @@
         "libandroidemu",
     ],
     static_libs: [
+        "libgfxstream_guest_android",
         "libvulkan_enc",
         "libplatform",
     ],
     export_static_lib_headers: [
+        "libgfxstream_guest_android",
         "libplatform",
     ],
+    target: {
+        android: {
+            shared_libs: [
+                "libnativewindow",
+                "libsync",
+            ],
+        }
+    },
 }
 
 cc_library_shared {
@@ -179,14 +144,16 @@
         "libOpenglSystemCommonDefaults",
     ],
     shared_libs: [
-        "libgfxstream_backend",
-        "libplatform_rutabaga",
         "libandroidemu",
+        "libplatform_rutabaga_server",
     ],
     static_libs: [
+        "libgfxstream_guest_android_with_host",
         "libgfxstream_guest_vulkan_encoder_with_host",
+        "libplatform_rutabaga",
     ],
-    export_shared_lib_headers: [
+    export_static_lib_headers: [
+        "libgfxstream_guest_android_with_host",
         "libplatform_rutabaga",
     ],
     target: {
@@ -209,9 +176,11 @@
     ],
     static_libs: [
         "libandroidemu_static",
+        "libgfxstream_guest_android",
         "libplatform",
     ],
     export_static_lib_headers: [
+        "libgfxstream_guest_android",
         "libplatform",
     ],
 }
@@ -225,13 +194,15 @@
         "vulkan_enc_headers",
     ],
     shared_libs: [
-        "libgfxstream_backend",
-        "libplatform_rutabaga",
+        "libplatform_rutabaga_server",
     ],
     static_libs: [
         "libandroidemu_static",
+        "libgfxstream_guest_android_with_host",
+        "libplatform_rutabaga",
     ],
-    export_shared_lib_headers: [
+    export_static_lib_headers: [
+        "libgfxstream_guest_android_with_host",
         "libplatform_rutabaga",
     ],
     target: {
diff --git a/guest/OpenglSystemCommon/HostConnection.cpp b/guest/OpenglSystemCommon/HostConnection.cpp
index 59b2385..b82cab0 100644
--- a/guest/OpenglSystemCommon/HostConnection.cpp
+++ b/guest/OpenglSystemCommon/HostConnection.cpp
@@ -15,12 +15,7 @@
 */
 #include "HostConnection.h"
 
-#if defined(__ANDROID__)
-#include "ANativeWindowAndroid.h"
-#endif
 #include "GoldfishAddressSpaceStream.h"
-#include "GrallocGoldfish.h"
-#include "GrallocMinigbm.h"
 #include "VirtioGpuAddressSpaceStream.h"
 #include "aemu/base/AndroidHealthMonitor.h"
 #include "aemu/base/AndroidHealthMonitorConsumerBasic.h"
@@ -45,8 +40,6 @@
 using gfxstream::guest::HealthMonitor;
 using gfxstream::guest::HealthMonitorConsumerBasic;
 using gfxstream::guest::IOStream;
-using gfxstream::GoldfishGralloc;
-using gfxstream::MinigbmGralloc;
 
 #ifdef GOLDFISH_NO_GL
 struct gl_client_context_t {
@@ -178,29 +171,6 @@
     return value;
 }
 
-static GrallocType getGrallocTypeFromProperty() {
-    std::string value;
-
-#if defined(__ANDROID__)
-    value = android::base::GetProperty("ro.hardware.gralloc", "");
-#endif
-
-    if (value.empty()) {
-        return GRALLOC_TYPE_RANCHU;
-    }
-    if (value == "minigbm") {
-        return GRALLOC_TYPE_MINIGBM;
-    }
-    if (value == "ranchu") {
-        return GRALLOC_TYPE_RANCHU;
-    }
-    return GRALLOC_TYPE_RANCHU;
-}
-
-#if defined(__ANDROID__)
-static GoldfishGralloc m_goldfishGralloc;
-#endif
-
 HostConnection::HostConnection()
     : exitUncleanly(false),
       m_checksumHelper(),
@@ -216,10 +186,6 @@
         (void)m_rcEnc->rcGetRendererVersion(m_rcEnc.get());
     }
 
-    if (m_grallocType == GRALLOC_TYPE_MINIGBM) {
-        delete m_grallocHelper;
-    }
-
     if (m_vkEnc) {
         m_vkEnc->decRef();
     }
@@ -252,10 +218,7 @@
             ALOGE("Fatal: HOST_CONNECTION_ADDRESS_SPACE not supported on this host.");
             abort();
 #endif
-            con->m_grallocType = GRALLOC_TYPE_RANCHU;
-#if defined(__ANDROID__)
-            con->m_grallocHelper = &m_goldfishGralloc;
-#endif
+
             break;
         }
 #if !defined(__Fuchsia__)
@@ -269,11 +232,7 @@
                 ALOGE("Failed to connect to host (QemuPipeStream)\n");
                 return nullptr;
             }
-            con->m_grallocType = GRALLOC_TYPE_RANCHU;
             con->m_stream = stream;
-#if defined(__ANDROID__)
-            con->m_grallocHelper = &m_goldfishGralloc;
-#endif
             break;
         }
 #endif
@@ -287,26 +246,9 @@
                 ALOGE("Failed to connect to host (VirtioGpu)\n");
                 return nullptr;
             }
-            con->m_grallocType = getGrallocTypeFromProperty();
             auto rendernodeFd = stream->getRendernodeFd();
             con->m_stream = stream;
             con->m_rendernodeFd = rendernodeFd;
-#if defined(__ANDROID__)
-            switch (con->m_grallocType) {
-                case GRALLOC_TYPE_RANCHU:
-                    con->m_grallocHelper = &m_goldfishGralloc;
-                    break;
-                case GRALLOC_TYPE_MINIGBM: {
-                    MinigbmGralloc* m = new MinigbmGralloc;
-                    m->setFd(rendernodeFd);
-                    con->m_grallocHelper = m;
-                    break;
-                }
-                default:
-                    ALOGE("Fatal: Unknown gralloc type 0x%x\n", con->m_grallocType);
-                    abort();
-            }
-#endif
             break;
         }
         case HOST_CONNECTION_VIRTIO_GPU_ADDRESS_SPACE: {
@@ -320,36 +262,28 @@
                 ALOGE("Failed to create virtgpu AddressSpaceStream\n");
                 return nullptr;
             }
-            con->m_grallocType = getGrallocTypeFromProperty();
             con->m_stream = stream;
             con->m_rendernodeFd = deviceHandle;
-#if defined(__ANDROID__)
-            switch (con->m_grallocType) {
-                case GRALLOC_TYPE_RANCHU:
-                    con->m_grallocHelper = &m_goldfishGralloc;
-                    break;
-                case GRALLOC_TYPE_MINIGBM: {
-                    MinigbmGralloc* m = new gfxstream::MinigbmGralloc;
-                    m->setFd(deviceHandle);
-                    con->m_grallocHelper = m;
-                    break;
-                }
-                default:
-                    ALOGE("Fatal: Unknown gralloc type 0x%x\n", con->m_grallocType);
-                    abort();
-            }
-#endif
             break;
         }
         default:
             break;
     }
 
-#if defined(__ANDROID__)
-    con->m_anwHelper = new gfxstream::ANativeWindowHelperAndroid();
-#else
-    // Host builds are expected to set an ANW helper for testing.
+#if defined(ANDROID)
+    con->m_grallocHelper.reset(gfxstream::createPlatformGralloc(con->m_rendernodeFd));
+    if (!con->m_grallocHelper) {
+        ALOGE("Failed to create platform Gralloc!");
+        abort();
+    }
+
+    con->m_anwHelper.reset(gfxstream::createPlatformANativeWindowHelper());
+    if (!con->m_anwHelper) {
+        ALOGE("Failed to create platform ANativeWindowHelper!");
+        abort();
+    }
 #endif
+
     con->m_syncHelper.reset(gfxstream::createPlatformSyncHelper());
 
     // send zero 'clientFlags' to the host.
diff --git a/guest/OpenglSystemCommon/HostConnection.h b/guest/OpenglSystemCommon/HostConnection.h
index 740f207..228df4e 100644
--- a/guest/OpenglSystemCommon/HostConnection.h
+++ b/guest/OpenglSystemCommon/HostConnection.h
@@ -16,22 +16,24 @@
 #ifndef __COMMON_HOST_CONNECTION_H
 #define __COMMON_HOST_CONNECTION_H
 
-#include "ANativeWindow.h"
+#if defined(ANDROID)
+#include "gfxstream/guest/ANativeWindow.h"
+#include "gfxstream/guest/Gralloc.h"
+#endif
+
+#include <cstring>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+
 #include "EmulatorFeatureInfo.h"
-#include "Gralloc.h"
 #include "Sync.h"
 #include "VirtGpu.h"
 #include "gfxstream/guest/ChecksumCalculator.h"
 #include "gfxstream/guest/IOStream.h"
 #include "renderControl_enc.h"
 
-#include <mutex>
-
-#include <memory>
-#include <optional>
-#include <cstring>
-#include <string>
-
 class GLEncoder;
 struct gl_client_context_t;
 class GL2Encoder;
@@ -134,14 +136,11 @@
 
     gfxstream::guest::ChecksumCalculator *checksumHelper() { return &m_checksumHelper; }
 
-    gfxstream::Gralloc* grallocHelper() { return m_grallocHelper; }
-    void setGrallocHelperForTesting(gfxstream::Gralloc* gralloc) { m_grallocHelper = gralloc; }
-
+#if defined(ANDROID)
+    gfxstream::ANativeWindowHelper* anwHelper() { return m_anwHelper.get(); }
+    gfxstream::Gralloc* grallocHelper() { return m_grallocHelper.get(); }
+#endif
     gfxstream::SyncHelper* syncHelper() { return m_syncHelper.get(); }
-    void setSyncHelperForTesting(gfxstream::SyncHelper* sync) { m_syncHelper.reset(sync); }
-
-    gfxstream::ANativeWindowHelper* anwHelper() { return m_anwHelper; }
-    void setANativeWindowHelperForTesting(gfxstream::ANativeWindowHelper* anw) { m_anwHelper = anw; }
 
     void flush() {
         if (m_stream) {
@@ -205,27 +204,28 @@
  GLint queryVersion(ExtendedRCEncoderContext* rcEnc);
 
 private:
-    HostConnectionType m_connectionType;
-    GrallocType m_grallocType;
+ HostConnectionType m_connectionType;
 
-    // intrusively refcounted
-    gfxstream::guest::IOStream* m_stream = nullptr;
+ // intrusively refcounted
+ gfxstream::guest::IOStream* m_stream = nullptr;
 
-    std::unique_ptr<GLEncoder> m_glEnc;
-    std::unique_ptr<GL2Encoder> m_gl2Enc;
+ std::unique_ptr<GLEncoder> m_glEnc;
+ std::unique_ptr<GL2Encoder> m_gl2Enc;
 
-    // intrusively refcounted
-    gfxstream::vk::VkEncoder* m_vkEnc = nullptr;
-    std::unique_ptr<ExtendedRCEncoderContext> m_rcEnc;
+ // intrusively refcounted
+ gfxstream::vk::VkEncoder* m_vkEnc = nullptr;
+ std::unique_ptr<ExtendedRCEncoderContext> m_rcEnc;
 
-    gfxstream::guest::ChecksumCalculator m_checksumHelper;
-    gfxstream::ANativeWindowHelper* m_anwHelper = nullptr;
-    gfxstream::Gralloc* m_grallocHelper = nullptr;
-    std::unique_ptr<gfxstream::SyncHelper> m_syncHelper;
-    std::string m_hostExtensions;
-    bool m_noHostError;
-    mutable std::mutex m_lock;
-    int m_rendernodeFd;
+ gfxstream::guest::ChecksumCalculator m_checksumHelper;
+#if defined(ANDROID)
+ std::unique_ptr<gfxstream::ANativeWindowHelper> m_anwHelper;
+ std::unique_ptr<gfxstream::Gralloc> m_grallocHelper;
+#endif
+ std::unique_ptr<gfxstream::SyncHelper> m_syncHelper;
+ std::string m_hostExtensions;
+ bool m_noHostError;
+ mutable std::mutex m_lock;
+ int m_rendernodeFd;
 };
 
 #endif
diff --git a/guest/OpenglSystemCommon/TestingAndroidWsi.cpp b/guest/OpenglSystemCommon/TestingAndroidWsi.cpp
deleted file mode 100644
index b62ff18..0000000
--- a/guest/OpenglSystemCommon/TestingAndroidWsi.cpp
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright 2023 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#include <iostream>
-#include <cutils/log.h>
-
-#include "drm_fourcc.h"
-#include "TestingAndroidWsi.h"
-
-namespace gfxstream {
-
-static constexpr int numFds = 0;
-static constexpr int numInts = 1;
-
-std::optional<uint32_t> GlFormatToDrmFormat(uint32_t glFormat) {
-    switch (glFormat) {
-        case kGlRGB:
-            return DRM_FORMAT_BGR888;
-        case kGlRGB565:
-            return DRM_FORMAT_BGR565;
-        case kGlRGBA:
-            return DRM_FORMAT_ABGR8888;
-    }
-    return std::nullopt;
-}
-
-std::optional<uint32_t> DrmToVirglFormat(uint32_t drmFormat) {
-    switch (drmFormat) {
-        case DRM_FORMAT_ABGR8888:
-            return VIRGL_FORMAT_B8G8R8A8_UNORM;
-        case DRM_FORMAT_BGR888:
-            return VIRGL_FORMAT_R8G8B8_UNORM;
-        case DRM_FORMAT_BGR565:
-            return VIRGL_FORMAT_B5G6R5_UNORM;
-    }
-    return std::nullopt;
-}
-
-TestingAHardwareBuffer::TestingAHardwareBuffer(
-        uint32_t width,
-        uint32_t height,
-        VirtGpuBlobPtr resource)
-    : mWidth(width),
-      mHeight(height),
-      mResource(resource) {
-    mHandle = native_handle_create(numFds, numInts);
-    mHandle->data[0] = mResource->getResourceHandle();
-}
-
-TestingAHardwareBuffer::~TestingAHardwareBuffer() {
-    native_handle_close(mHandle);
-}
-
-uint32_t TestingAHardwareBuffer::getResourceId() const {
-    return mResource->getResourceHandle();
-}
-
-uint32_t TestingAHardwareBuffer::getWidth() const {
-    return mWidth;
-}
-
-uint32_t TestingAHardwareBuffer::getHeight() const {
-    return mHeight;
-}
-
-int TestingAHardwareBuffer::getAndroidFormat() const {
-    return /*AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM=*/1;
-}
-
-uint32_t TestingAHardwareBuffer::getDrmFormat() const {
-    return DRM_FORMAT_ABGR8888;
-}
-
-AHardwareBuffer* TestingAHardwareBuffer::asAHardwareBuffer() {
-    return reinterpret_cast<AHardwareBuffer*>(this);
-}
-
-buffer_handle_t TestingAHardwareBuffer::asBufferHandle() {
-    return reinterpret_cast<buffer_handle_t>(mHandle);
-}
-
-EGLClientBuffer TestingAHardwareBuffer::asEglClientBuffer() {
-    return reinterpret_cast<EGLClientBuffer>(this);
-}
-
-TestingVirtGpuGralloc::TestingVirtGpuGralloc() {}
-
-uint32_t TestingVirtGpuGralloc::createColorBuffer(
-        void*,
-        int width,
-        int height,
-        uint32_t glFormat) {
-    auto drmFormat = GlFormatToDrmFormat(glFormat);
-    if (!drmFormat) {
-        ALOGE("Unhandled format");
-    }
-
-    auto ahb = allocate(width, height, *drmFormat);
-
-    uint32_t hostHandle = ahb->getResourceId();
-    mAllocatedColorBuffers.emplace(hostHandle, std::move(ahb));
-    return hostHandle;
-}
-
-int TestingVirtGpuGralloc::allocate(
-        uint32_t width,
-        uint32_t height,
-        uint32_t format,
-        uint64_t usage,
-        AHardwareBuffer** outputAhb) {
-    (void)width;
-    (void)height;
-    (void)format;
-    (void)usage;
-    (void)outputAhb;
-
-    // TODO: support export flow?
-    ALOGE("Unimplemented");
-
-    return 0;
-}
-
-std::unique_ptr<TestingAHardwareBuffer> TestingVirtGpuGralloc::allocate(
-        uint32_t width,
-        uint32_t height,
-        uint32_t format) {
-    ALOGE("Allocating AHB w:%u, h:%u, format %u", width, height, format);
-
-    auto device = VirtGpuDevice::getInstance();
-    if (!device) {
-        ALOGE("Failed to allocate: no virtio gpu device.");
-        return nullptr;
-    }
-
-    auto virglFormat = DrmToVirglFormat(format);
-    if (!virglFormat) {
-        std::cout << "Unhandled DRM format:" << format;
-        return nullptr;
-    }
-
-    auto resource = device->createVirglBlob(width, height, *virglFormat);
-    if (!resource) {
-        ALOGE("Failed to allocate: failed to create virtio resource.");
-        return nullptr;
-    }
-
-    resource->wait();
-
-    return std::make_unique<TestingAHardwareBuffer>(width, height, std::move(resource));
-}
-
-void TestingVirtGpuGralloc::acquire(AHardwareBuffer* ahb) {
-    // TODO
-}
-
-void TestingVirtGpuGralloc::release(AHardwareBuffer* ahb) {
-    // TODO
-}
-
-uint32_t TestingVirtGpuGralloc::getHostHandle(const native_handle_t* handle) {
-    const auto* ahb = reinterpret_cast<const TestingAHardwareBuffer*>(handle);
-    return ahb->getResourceId();
-}
-
-uint32_t TestingVirtGpuGralloc::getHostHandle(const AHardwareBuffer* handle) {
-    const auto* ahb = reinterpret_cast<const TestingAHardwareBuffer*>(handle);
-    return ahb->getResourceId();
-}
-
-int TestingVirtGpuGralloc::getFormat(const native_handle_t* handle) {
-    const auto* ahb = reinterpret_cast<const TestingAHardwareBuffer*>(handle);
-    return ahb->getAndroidFormat();
-}
-
-int TestingVirtGpuGralloc::getFormat(const AHardwareBuffer* handle) {
-    const auto* ahb = reinterpret_cast<const TestingAHardwareBuffer*>(handle);
-    return ahb->getAndroidFormat();
-}
-
-uint32_t TestingVirtGpuGralloc::getFormatDrmFourcc(const AHardwareBuffer* handle) {
-    const auto* ahb = reinterpret_cast<const TestingAHardwareBuffer*>(handle);
-    return ahb->getDrmFormat();
-}
-
-size_t TestingVirtGpuGralloc::getAllocatedSize(const native_handle_t*) {
-    ALOGE("Unimplemented.");
-    return 0;
-}
-
-size_t TestingVirtGpuGralloc::getAllocatedSize(const AHardwareBuffer*) {
-    ALOGE("Unimplemented.");
-    return 0;
-}
-
-TestingANativeWindow::TestingANativeWindow(
-        uint32_t width,
-        uint32_t height,
-        uint32_t format,
-        std::vector<std::unique_ptr<TestingAHardwareBuffer>> buffers)
-    : mWidth(width),
-      mHeight(height),
-      mFormat(format),
-      mBuffers(std::move(buffers)) {
-    for (auto& buffer : mBuffers) {
-        mBufferQueue.push_back(QueuedAHB{
-            .ahb = buffer.get(),
-            .fence = -1,
-        });
-    }
-}
-
-EGLNativeWindowType TestingANativeWindow::asEglNativeWindowType() {
-    return reinterpret_cast<EGLNativeWindowType>(this);
-}
-
-uint32_t TestingANativeWindow::getWidth() const {
-    return mWidth;
-}
-
-uint32_t TestingANativeWindow::getHeight() const {
-    return mHeight;
-}
-
-int TestingANativeWindow::getFormat() const {
-    return mFormat;
-}
-
-int TestingANativeWindow::queueBuffer(EGLClientBuffer buffer, int fence) {
-    auto ahb = reinterpret_cast<TestingAHardwareBuffer*>(buffer);
-
-    mBufferQueue.push_back(QueuedAHB{
-        .ahb = ahb,
-        .fence = fence,
-    });
-
-    return 0;
-}
-
-int TestingANativeWindow::dequeueBuffer(EGLClientBuffer* buffer, int* fence) {
-    auto queuedAhb = mBufferQueue.front();
-    mBufferQueue.pop_front();
-
-    *buffer = queuedAhb.ahb->asEglClientBuffer();
-    *fence = queuedAhb.fence;
-    return 0;
-}
-
-int TestingANativeWindow::cancelBuffer(EGLClientBuffer buffer) {
-    auto ahb = reinterpret_cast<TestingAHardwareBuffer*>(buffer);
-
-    mBufferQueue.push_back(QueuedAHB{
-        .ahb = ahb,
-        .fence = -1,
-    });
-
-    return 0;
-}
-
-bool TestingVirtGpuANativeWindowHelper::isValid(EGLNativeWindowType window) {
-    // TODO: maybe a registry of valid TestingANativeWindow-s?
-    return true;
-}
-
-bool TestingVirtGpuANativeWindowHelper::isValid(EGLClientBuffer buffer) {
-    // TODO: maybe a registry of valid TestingAHardwareBuffer-s?
-    return true;
-}
-
-void TestingVirtGpuANativeWindowHelper::acquire(EGLNativeWindowType window) {
-    // TODO: maybe a registry of valid TestingANativeWindow-s?
-}
-
-void TestingVirtGpuANativeWindowHelper::release(EGLNativeWindowType window) {
-    // TODO: maybe a registry of valid TestingANativeWindow-s?
-}
-
-void TestingVirtGpuANativeWindowHelper::acquire(EGLClientBuffer buffer) {
-    // TODO: maybe a registry of valid TestingAHardwareBuffer-s?
-}
-
-void TestingVirtGpuANativeWindowHelper::release(EGLClientBuffer buffer) {
-    // TODO: maybe a registry of valid TestingAHardwareBuffer-s?
-}
-
-int TestingVirtGpuANativeWindowHelper::getConsumerUsage(EGLNativeWindowType window, int* usage) {
-    (void)window;
-    (void)usage;
-    return 0;
-}
-void TestingVirtGpuANativeWindowHelper::setUsage(EGLNativeWindowType window, int usage) {
-    (void)window;
-    (void)usage;
-}
-
-int TestingVirtGpuANativeWindowHelper::getWidth(EGLNativeWindowType window) {
-    auto anw = reinterpret_cast<TestingANativeWindow*>(window);
-    return anw->getWidth();
-}
-
-int TestingVirtGpuANativeWindowHelper::getHeight(EGLNativeWindowType window) {
-    auto anw = reinterpret_cast<TestingANativeWindow*>(window);
-    return anw->getHeight();
-}
-
-int TestingVirtGpuANativeWindowHelper::getWidth(EGLClientBuffer buffer) {
-    auto ahb = reinterpret_cast<TestingAHardwareBuffer*>(buffer);
-    return ahb->getWidth();
-}
-
-int TestingVirtGpuANativeWindowHelper::getHeight(EGLClientBuffer buffer) {
-    auto ahb = reinterpret_cast<TestingAHardwareBuffer*>(buffer);
-    return ahb->getHeight();
-}
-
-int TestingVirtGpuANativeWindowHelper::getFormat(EGLClientBuffer buffer, Gralloc* helper) {
-    auto ahb = reinterpret_cast<TestingAHardwareBuffer*>(buffer);
-    return ahb->getAndroidFormat();
-}
-
-void TestingVirtGpuANativeWindowHelper::setSwapInterval(EGLNativeWindowType window, int interval) {
-    ALOGE("Unimplemented");
-}
-
-int TestingVirtGpuANativeWindowHelper::queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer, int fence) {
-    auto anw = reinterpret_cast<TestingANativeWindow*>(window);
-    return anw->queueBuffer(buffer, fence);
-}
-
-int TestingVirtGpuANativeWindowHelper::dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer, int* fence) {
-    auto anw = reinterpret_cast<TestingANativeWindow*>(window);
-    return anw->dequeueBuffer(buffer, fence);
-}
-
-int TestingVirtGpuANativeWindowHelper::cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) {
-    auto anw = reinterpret_cast<TestingANativeWindow*>(window);
-    return anw->cancelBuffer(buffer);
-}
-
-int TestingVirtGpuANativeWindowHelper::getHostHandle(EGLClientBuffer buffer, Gralloc*) {
-    auto ahb = reinterpret_cast<TestingAHardwareBuffer*>(buffer);
-    return ahb->getResourceId();
-}
-
-}  // namespace gfxstream
diff --git a/guest/OpenglSystemCommon/TestingAndroidWsi.h b/guest/OpenglSystemCommon/TestingAndroidWsi.h
deleted file mode 100644
index b18455e..0000000
--- a/guest/OpenglSystemCommon/TestingAndroidWsi.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright 2023 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#include <optional>
-#include <queue>
-#include <unordered_map>
-#include <vector>
-
-#include "VirtGpu.h"
-#include "ANativeWindow.h"
-
-namespace gfxstream {
-
-class TestingAHardwareBuffer {
-  public:
-    TestingAHardwareBuffer(uint32_t width,
-                           uint32_t height,
-                           VirtGpuBlobPtr resource);
-
-    ~TestingAHardwareBuffer();
-
-    uint32_t getResourceId() const;
-
-    uint32_t getWidth() const;
-
-    uint32_t getHeight() const;
-
-    int getAndroidFormat() const;
-
-    uint32_t getDrmFormat() const;
-
-    AHardwareBuffer* asAHardwareBuffer();
-
-    buffer_handle_t asBufferHandle();
-
-    EGLClientBuffer asEglClientBuffer();
-
-  private:
-    uint32_t mWidth;
-    uint32_t mHeight;
-    VirtGpuBlobPtr mResource;
-    native_handle_t *mHandle = nullptr;
-};
-
-class TestingVirtGpuGralloc : public Gralloc {
-   public:
-    TestingVirtGpuGralloc();
-
-    uint32_t createColorBuffer(void*,
-                               int width,
-                               int height,
-                               uint32_t glFormat) override;
-
-    int allocate(uint32_t width,
-                 uint32_t height,
-                 uint32_t format,
-                 uint64_t usage,
-                 AHardwareBuffer** outputAhb) override;
-
-    std::unique_ptr<TestingAHardwareBuffer> allocate(uint32_t width,
-                                                     uint32_t height,
-                                                     uint32_t format);
-
-    void acquire(AHardwareBuffer* ahb) override;
-    void release(AHardwareBuffer* ahb) override;
-
-    uint32_t getHostHandle(const native_handle_t* handle) override;
-    uint32_t getHostHandle(const AHardwareBuffer* handle) override;
-
-    int getFormat(const native_handle_t* handle) override;
-    int getFormat(const AHardwareBuffer* handle) override;
-
-    uint32_t getFormatDrmFourcc(const AHardwareBuffer* handle) override;
-
-    size_t getAllocatedSize(const native_handle_t*) override;
-    size_t getAllocatedSize(const AHardwareBuffer*) override;
-
-  private:
-    std::unordered_map<uint32_t, std::unique_ptr<TestingAHardwareBuffer>> mAllocatedColorBuffers;
-};
-
-class TestingANativeWindow {
-  public:
-    TestingANativeWindow(uint32_t width,
-                         uint32_t height,
-                         uint32_t format,
-                         std::vector<std::unique_ptr<TestingAHardwareBuffer>> buffers);
-
-    EGLNativeWindowType asEglNativeWindowType();
-
-    uint32_t getWidth() const;
-
-    uint32_t getHeight() const;
-
-    int getFormat() const;
-
-    int queueBuffer(EGLClientBuffer buffer, int fence);
-    int dequeueBuffer(EGLClientBuffer* buffer, int* fence);
-    int cancelBuffer(EGLClientBuffer buffer);
-
-  private:
-    uint32_t mWidth;
-    uint32_t mHeight;
-    uint32_t mFormat;
-    std::vector<std::unique_ptr<TestingAHardwareBuffer>> mBuffers;
-
-    struct QueuedAHB {
-        TestingAHardwareBuffer* ahb;
-        int fence = -1;
-    };
-    std::deque<QueuedAHB> mBufferQueue;
-};
-
-class TestingVirtGpuANativeWindowHelper : public ANativeWindowHelper {
-  public:
-    bool isValid(EGLNativeWindowType window) override;
-    bool isValid(EGLClientBuffer buffer) override;
-
-    void acquire(EGLNativeWindowType window) override;
-    void release(EGLNativeWindowType window) override;
-
-    void acquire(EGLClientBuffer buffer) override;
-    void release(EGLClientBuffer buffer) override;
-
-    int getConsumerUsage(EGLNativeWindowType window, int* usage) override;
-    void setUsage(EGLNativeWindowType window, int usage) override;
-
-    int getWidth(EGLNativeWindowType window) override;
-    int getHeight(EGLNativeWindowType window) override;
-
-    int getWidth(EGLClientBuffer buffer) override;
-    int getHeight(EGLClientBuffer buffer) override;
-
-    int getFormat(EGLClientBuffer buffer, Gralloc* helper) override;
-
-    void setSwapInterval(EGLNativeWindowType window, int interval) override;
-
-    int queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer, int fence) override;
-    int dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer, int* fence) override;
-    int cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) override;
-
-    int getHostHandle(EGLClientBuffer buffer, Gralloc*) override;
-};
-
-}  // namespace gfxstream
diff --git a/guest/OpenglSystemCommon/ANativeWindowAndroid.cpp b/guest/android/ANativeWindowAndroid.cpp
similarity index 95%
rename from guest/OpenglSystemCommon/ANativeWindowAndroid.cpp
rename to guest/android/ANativeWindowAndroid.cpp
index 1edae70..6ab4fa6 100644
--- a/guest/OpenglSystemCommon/ANativeWindowAndroid.cpp
+++ b/guest/android/ANativeWindowAndroid.cpp
@@ -68,7 +68,6 @@
 #endif  // defined(__ANDROID__)
 }
 
-
 void ANativeWindowHelperAndroid::acquire(EGLClientBuffer buffer) {
 #if defined(__ANDROID__)
     auto* anwb = reinterpret_cast<ANativeWindowBuffer*>(buffer);
@@ -169,7 +168,8 @@
 #endif  // defined(__ANDROID__)
 }
 
-int ANativeWindowHelperAndroid::queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer, int fence) {
+int ANativeWindowHelperAndroid::queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer,
+                                            int fence) {
 #if defined(__ANDROID__)
     auto* anw = reinterpret_cast<ANativeWindow*>(window);
     auto* anb = reinterpret_cast<ANativeWindowBuffer*>(buffer);
@@ -182,7 +182,8 @@
 #endif  // defined(__ANDROID__)
 }
 
-int ANativeWindowHelperAndroid::dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer, int* fence) {
+int ANativeWindowHelperAndroid::dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer,
+                                              int* fence) {
 #if defined(__ANDROID__)
     auto* anw = reinterpret_cast<ANativeWindow*>(window);
     auto* anb = reinterpret_cast<ANativeWindowBuffer**>(buffer);
@@ -218,4 +219,8 @@
 #endif  // defined(__ANDROID__)
 }
 
+ANativeWindowHelper* createPlatformANativeWindowHelper() {
+    return new ANativeWindowHelperAndroid();
+}
+
 }  // namespace gfxstream
\ No newline at end of file
diff --git a/guest/OpenglSystemCommon/ANativeWindowAndroid.h b/guest/android/ANativeWindowAndroid.h
similarity index 96%
rename from guest/OpenglSystemCommon/ANativeWindowAndroid.h
rename to guest/android/ANativeWindowAndroid.h
index 97eba6a..bac57c3 100644
--- a/guest/OpenglSystemCommon/ANativeWindowAndroid.h
+++ b/guest/android/ANativeWindowAndroid.h
@@ -17,12 +17,12 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include "ANativeWindow.h"
+#include "gfxstream/guest/ANativeWindow.h"
 
 namespace gfxstream {
 
 class ANativeWindowHelperAndroid : public ANativeWindowHelper {
-  public:
+   public:
     ANativeWindowHelperAndroid() = default;
 
     bool isValid(EGLNativeWindowType window);
diff --git a/guest/android/ANativeWindowEmulated.cpp b/guest/android/ANativeWindowEmulated.cpp
new file mode 100644
index 0000000..d3e83c3
--- /dev/null
+++ b/guest/android/ANativeWindowEmulated.cpp
@@ -0,0 +1,199 @@
+// Copyright 2024 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 "ANativeWindowEmulated.h"
+
+#include <log/log.h>
+
+#include "drm_fourcc.h"
+
+namespace gfxstream {
+
+EmulatedANativeWindow::EmulatedANativeWindow(
+    uint32_t width, uint32_t height, uint32_t format,
+    std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> buffers)
+    : mRefCount(1), mWidth(width), mHeight(height), mFormat(format), mBuffers(std::move(buffers)) {
+    for (auto& buffer : mBuffers) {
+        mBufferQueue.push_back(QueuedAHB{
+            .ahb = buffer.get(),
+            .fence = -1,
+        });
+    }
+}
+
+EGLNativeWindowType EmulatedANativeWindow::asEglNativeWindowType() {
+    return reinterpret_cast<EGLNativeWindowType>(this);
+}
+
+uint32_t EmulatedANativeWindow::getWidth() const { return mWidth; }
+
+uint32_t EmulatedANativeWindow::getHeight() const { return mHeight; }
+
+int EmulatedANativeWindow::getFormat() const { return mFormat; }
+
+int EmulatedANativeWindow::queueBuffer(EGLClientBuffer buffer, int fence) {
+    auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
+
+    mBufferQueue.push_back(QueuedAHB{
+        .ahb = ahb,
+        .fence = fence,
+    });
+
+    return 0;
+}
+
+int EmulatedANativeWindow::dequeueBuffer(EGLClientBuffer* buffer, int* fence) {
+    auto queuedAhb = mBufferQueue.front();
+    mBufferQueue.pop_front();
+
+    *buffer = queuedAhb.ahb->asEglClientBuffer();
+    *fence = queuedAhb.fence;
+    return 0;
+}
+
+int EmulatedANativeWindow::cancelBuffer(EGLClientBuffer buffer) {
+    auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
+
+    mBufferQueue.push_back(QueuedAHB{
+        .ahb = ahb,
+        .fence = -1,
+    });
+
+    return 0;
+}
+
+void EmulatedANativeWindow::acquire() { ++mRefCount; }
+
+void EmulatedANativeWindow::release() {
+    --mRefCount;
+    if (mRefCount == 0) {
+        delete this;
+    }
+}
+
+bool EmulatedANativeWindowHelper::isValid(EGLNativeWindowType window) {
+    // TODO: maybe a registry of valid EmulatedANativeWindow-s?
+    (void)window;
+    return true;
+}
+
+bool EmulatedANativeWindowHelper::isValid(EGLClientBuffer buffer) {
+    // TODO: maybe a registry of valid EmulatedAHardwareBuffer-s?
+    (void)buffer;
+    return true;
+}
+
+void EmulatedANativeWindowHelper::acquire(EGLNativeWindowType window) {
+    auto* anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    anw->acquire();
+}
+
+void EmulatedANativeWindowHelper::release(EGLNativeWindowType window) {
+    auto* anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    anw->release();
+}
+
+void EmulatedANativeWindowHelper::acquire(EGLClientBuffer buffer) {
+    // TODO: maybe a registry of valid EmulatedAHardwareBuffer-s?
+    (void)buffer;
+}
+
+void EmulatedANativeWindowHelper::release(EGLClientBuffer buffer) { (void)buffer; }
+
+int EmulatedANativeWindowHelper::getConsumerUsage(EGLNativeWindowType window, int* usage) {
+    (void)window;
+    (void)usage;
+    return 0;
+}
+void EmulatedANativeWindowHelper::setUsage(EGLNativeWindowType window, int usage) {
+    (void)window;
+    (void)usage;
+}
+
+int EmulatedANativeWindowHelper::getWidth(EGLNativeWindowType window) {
+    auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    return anw->getWidth();
+}
+
+int EmulatedANativeWindowHelper::getHeight(EGLNativeWindowType window) {
+    auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    return anw->getHeight();
+}
+
+int EmulatedANativeWindowHelper::getWidth(EGLClientBuffer buffer) {
+    auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
+    return ahb->getWidth();
+}
+
+int EmulatedANativeWindowHelper::getHeight(EGLClientBuffer buffer) {
+    auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
+    return ahb->getHeight();
+}
+
+int EmulatedANativeWindowHelper::getFormat(EGLClientBuffer buffer, Gralloc* helper) {
+    (void)helper;
+
+    auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
+    return ahb->getAndroidFormat();
+}
+
+void EmulatedANativeWindowHelper::setSwapInterval(EGLNativeWindowType window, int interval) {
+    ALOGE("Unimplemented");
+    (void)window;
+    (void)interval;
+}
+
+int EmulatedANativeWindowHelper::queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer,
+                                             int fence) {
+    auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    return anw->queueBuffer(buffer, fence);
+}
+
+int EmulatedANativeWindowHelper::dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer,
+                                               int* fence) {
+    auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    return anw->dequeueBuffer(buffer, fence);
+}
+
+int EmulatedANativeWindowHelper::cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) {
+    auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
+    return anw->cancelBuffer(buffer);
+}
+
+int EmulatedANativeWindowHelper::getHostHandle(EGLClientBuffer buffer, Gralloc*) {
+    auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
+    return ahb->getResourceId();
+}
+
+EGLNativeWindowType EmulatedANativeWindowHelper::createNativeWindowForTesting(Gralloc* gralloc,
+                                                                              uint32_t width,
+                                                                              uint32_t height) {
+    std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> buffers;
+    for (int i = 0; i < 3; i++) {
+        AHardwareBuffer* ahb = nullptr;
+        if (gralloc->allocate(width, height, DRM_FORMAT_ABGR8888, -1, &ahb) != 0) {
+            ALOGE("Failed to allocate gralloc buffer.");
+            return nullptr;
+        }
+        buffers.emplace_back(reinterpret_cast<EmulatedAHardwareBuffer*>(ahb));
+    }
+    return reinterpret_cast<EGLNativeWindowType>(
+        new EmulatedANativeWindow(width, height, DRM_FORMAT_ABGR8888, std::move(buffers)));
+}
+
+ANativeWindowHelper* createPlatformANativeWindowHelper() {
+    return new EmulatedANativeWindowHelper();
+}
+
+}  // namespace gfxstream
diff --git a/guest/android/ANativeWindowEmulated.h b/guest/android/ANativeWindowEmulated.h
new file mode 100644
index 0000000..8794e51
--- /dev/null
+++ b/guest/android/ANativeWindowEmulated.h
@@ -0,0 +1,95 @@
+// Copyright 2024 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 <cstdint>
+#include <memory>
+#include <optional>
+#include <queue>
+#include <unordered_map>
+#include <vector>
+
+#include "gfxstream/guest/ANativeWindow.h"
+#include "GrallocEmulated.h"
+
+namespace gfxstream {
+
+class EmulatedANativeWindow {
+   public:
+    EmulatedANativeWindow(uint32_t width, uint32_t height, uint32_t format,
+                          std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> buffers);
+
+    EGLNativeWindowType asEglNativeWindowType();
+
+    uint32_t getWidth() const;
+
+    uint32_t getHeight() const;
+
+    int getFormat() const;
+
+    int queueBuffer(EGLClientBuffer buffer, int fence);
+    int dequeueBuffer(EGLClientBuffer* buffer, int* fence);
+    int cancelBuffer(EGLClientBuffer buffer);
+
+    void acquire();
+    void release();
+
+   private:
+    uint32_t mRefCount;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint32_t mFormat;
+    std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> mBuffers;
+
+    struct QueuedAHB {
+        EmulatedAHardwareBuffer* ahb;
+        int fence = -1;
+    };
+    std::deque<QueuedAHB> mBufferQueue;
+};
+
+class EmulatedANativeWindowHelper : public ANativeWindowHelper {
+   public:
+    bool isValid(EGLNativeWindowType window) override;
+    bool isValid(EGLClientBuffer buffer) override;
+
+    void acquire(EGLNativeWindowType window) override;
+    void release(EGLNativeWindowType window) override;
+
+    void acquire(EGLClientBuffer buffer) override;
+    void release(EGLClientBuffer buffer) override;
+
+    int getConsumerUsage(EGLNativeWindowType window, int* usage) override;
+    void setUsage(EGLNativeWindowType window, int usage) override;
+
+    int getWidth(EGLNativeWindowType window) override;
+    int getHeight(EGLNativeWindowType window) override;
+
+    int getWidth(EGLClientBuffer buffer) override;
+    int getHeight(EGLClientBuffer buffer) override;
+
+    int getFormat(EGLClientBuffer buffer, Gralloc* helper) override;
+
+    void setSwapInterval(EGLNativeWindowType window, int interval) override;
+
+    int queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer, int fence) override;
+    int dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer, int* fence) override;
+    int cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) override;
+
+    int getHostHandle(EGLClientBuffer buffer, Gralloc*) override;
+
+    EGLNativeWindowType createNativeWindowForTesting(Gralloc* gralloc, uint32_t width,
+                                                     uint32_t height) override;
+};
+
+}  // namespace gfxstream
diff --git a/guest/android/ANativeWindowStub.cpp b/guest/android/ANativeWindowStub.cpp
new file mode 100644
index 0000000..4d613b9
--- /dev/null
+++ b/guest/android/ANativeWindowStub.cpp
@@ -0,0 +1,21 @@
+// Copyright 2024 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 expresso or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gfxstream/guest/ANativeWindow.h"
+
+namespace gfxstream {
+
+ANativeWindowHelper* createPlatformANativeWindowHelper() { return nullptr; }
+
+}  // namespace gfxstream
\ No newline at end of file
diff --git a/guest/android/Android.bp b/guest/android/Android.bp
new file mode 100644
index 0000000..4846597
--- /dev/null
+++ b/guest/android/Android.bp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["hardware_google_gfxstream_license"],
+}
+
+cc_defaults {
+    name: "libgfxstream_guest_android_defaults",
+    defaults: [
+        "libgfxstream_guest_cc_defaults",
+    ],
+    host_supported: true,
+    vendor: true,
+    header_libs: [
+        "gfxstream_opengl_headers",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libdrm",
+        "liblog",
+        "libutils",
+    ],
+    export_shared_lib_headers: [
+        "libdrm",
+    ],
+    export_include_dirs: [
+        "include"
+    ],
+    local_include_dirs: [
+        ".",
+        "include",
+    ],
+}
+
+cc_library_static {
+    name: "libgfxstream_guest_android",
+    defaults: [
+        "libgfxstream_guest_android_defaults",
+    ],
+    static_libs: [
+        "libplatform",
+    ],
+    target: {
+        android: {
+            header_libs: [
+                "libnativebase_headers",
+                "libgralloc_cb.ranchu",
+                "minigbm_headers",
+            ],
+            shared_libs: [
+                "lib_renderControl_enc",
+                "libnativewindow",
+            ],
+            srcs: [
+                "ANativeWindowAndroid.cpp",
+                "Gralloc.cpp",
+                "GrallocGoldfish.cpp",
+                "GrallocMinigbm.cpp",
+            ],
+        },
+        host: {
+            srcs: [
+                "ANativeWindowStub.cpp",
+                "GrallocStub.cpp",
+            ],
+        },
+    },
+}
+
+cc_library_static {
+    name: "libgfxstream_guest_android_with_host",
+    defaults: [
+        "libgfxstream_guest_android_defaults",
+    ],
+    static_libs: [
+        "libplatform_rutabaga",
+    ],
+    target: {
+        host: {
+            compile_multilib: "64",
+        },
+        android: {
+            compile_multilib: "64",
+        },
+    },
+    srcs: [
+        "ANativeWindowEmulated.cpp",
+        "GrallocEmulated.cpp",
+    ],
+}
\ No newline at end of file
diff --git a/guest/android/Gralloc.cpp b/guest/android/Gralloc.cpp
new file mode 100644
index 0000000..500519d
--- /dev/null
+++ b/guest/android/Gralloc.cpp
@@ -0,0 +1,39 @@
+// Copyright 2024 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 "gfxstream/guest/Gralloc.h"
+
+#if defined(__ANDROID__)
+
+#include <string>
+
+#include "GrallocGoldfish.h"
+#include "GrallocMinigbm.h"
+#include "android-base/properties.h"
+
+namespace gfxstream {
+
+Gralloc* createPlatformGralloc(int deviceFd) {
+    const std::string value = android::base::GetProperty("ro.hardware.gralloc", "");
+    if (value == "minigbm") {
+        auto gralloc = new MinigbmGralloc();
+        gralloc->setFd(deviceFd);
+        return gralloc;
+    }
+    return new GoldfishGralloc();
+}
+
+}  // namespace gfxstream
+
+#endif
\ No newline at end of file
diff --git a/guest/android/GrallocEmulated.cpp b/guest/android/GrallocEmulated.cpp
new file mode 100644
index 0000000..e6031b1
--- /dev/null
+++ b/guest/android/GrallocEmulated.cpp
@@ -0,0 +1,204 @@
+// Copyright 2024 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 "GrallocEmulated.h"
+
+#include <cutils/log.h>
+
+#include <optional>
+
+#include "drm_fourcc.h"
+
+namespace gfxstream {
+namespace {
+
+static constexpr int numFds = 0;
+static constexpr int numInts = 1;
+
+std::optional<uint32_t> GlFormatToDrmFormat(uint32_t glFormat) {
+    switch (glFormat) {
+        case kGlRGB:
+            return DRM_FORMAT_BGR888;
+        case kGlRGB565:
+            return DRM_FORMAT_BGR565;
+        case kGlRGBA:
+            return DRM_FORMAT_ABGR8888;
+    }
+    return std::nullopt;
+}
+
+std::optional<uint32_t> DrmToVirglFormat(uint32_t drmFormat) {
+    switch (drmFormat) {
+        case DRM_FORMAT_ABGR8888:
+            return VIRGL_FORMAT_B8G8R8A8_UNORM;
+        case DRM_FORMAT_BGR888:
+            return VIRGL_FORMAT_R8G8B8_UNORM;
+        case DRM_FORMAT_BGR565:
+            return VIRGL_FORMAT_B5G6R5_UNORM;
+    }
+    return std::nullopt;
+}
+
+}  // namespace
+
+EmulatedAHardwareBuffer::EmulatedAHardwareBuffer(uint32_t width, uint32_t height,
+                                                 VirtGpuBlobPtr resource)
+    : mRefCount(1), mWidth(width), mHeight(height), mResource(resource) {}
+
+EmulatedAHardwareBuffer::~EmulatedAHardwareBuffer() {}
+
+uint32_t EmulatedAHardwareBuffer::getResourceId() const { return mResource->getResourceHandle(); }
+
+uint32_t EmulatedAHardwareBuffer::getWidth() const { return mWidth; }
+
+uint32_t EmulatedAHardwareBuffer::getHeight() const { return mHeight; }
+
+int EmulatedAHardwareBuffer::getAndroidFormat() const {
+    return /*AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM=*/1;
+}
+
+uint32_t EmulatedAHardwareBuffer::getDrmFormat() const { return DRM_FORMAT_ABGR8888; }
+
+AHardwareBuffer* EmulatedAHardwareBuffer::asAHardwareBuffer() {
+    return reinterpret_cast<AHardwareBuffer*>(this);
+}
+
+buffer_handle_t EmulatedAHardwareBuffer::asBufferHandle() {
+    return reinterpret_cast<buffer_handle_t>(this);
+}
+
+EGLClientBuffer EmulatedAHardwareBuffer::asEglClientBuffer() {
+    return reinterpret_cast<EGLClientBuffer>(this);
+}
+
+void EmulatedAHardwareBuffer::acquire() { ++mRefCount; }
+
+void EmulatedAHardwareBuffer::release() {
+    --mRefCount;
+    if (mRefCount == 0) {
+        delete this;
+    }
+}
+
+EmulatedGralloc::EmulatedGralloc() {}
+
+uint32_t EmulatedGralloc::createColorBuffer(void*, int width, int height, uint32_t glFormat) {
+    auto drmFormat = GlFormatToDrmFormat(glFormat);
+    if (!drmFormat) {
+        ALOGE("Unhandled format");
+    }
+
+    auto ahb = allocate(width, height, *drmFormat);
+
+    EmulatedAHardwareBuffer* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
+
+    mOwned.emplace_back(rahb);
+
+    return rahb->getResourceId();
+}
+
+int EmulatedGralloc::allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
+                              AHardwareBuffer** outputAhb) {
+    (void)usage;
+    *outputAhb = allocate(width, height, format);
+    return 0;
+}
+
+AHardwareBuffer* EmulatedGralloc::allocate(uint32_t width, uint32_t height, uint32_t format) {
+    ALOGE("Allocating AHB w:%u, h:%u, format %u", width, height, format);
+
+    auto device = VirtGpuDevice::getInstance();
+    if (!device) {
+        ALOGE("Failed to allocate: no virtio gpu device.");
+        return nullptr;
+    }
+
+    auto virglFormat = DrmToVirglFormat(format);
+    if (!virglFormat) {
+        ALOGE("Unhandled DRM format:%u", format);
+        return nullptr;
+    }
+
+    auto resource = device->createVirglBlob(width, height, *virglFormat);
+    if (!resource) {
+        ALOGE("Failed to allocate: failed to create virtio resource.");
+        return nullptr;
+    }
+
+    resource->wait();
+
+    return reinterpret_cast<AHardwareBuffer*>(
+        new EmulatedAHardwareBuffer(width, height, std::move(resource)));
+}
+
+void EmulatedGralloc::acquire(AHardwareBuffer* ahb) {
+    auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
+    rahb->acquire();
+}
+
+void EmulatedGralloc::release(AHardwareBuffer* ahb) {
+    auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
+    rahb->release();
+}
+
+uint32_t EmulatedGralloc::getHostHandle(const native_handle_t* handle) {
+    const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
+    return ahb->getResourceId();
+}
+
+uint32_t EmulatedGralloc::getHostHandle(const AHardwareBuffer* handle) {
+    const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
+    return ahb->getResourceId();
+}
+
+const native_handle_t* EmulatedGralloc::getNativeHandle(const AHardwareBuffer* ahb) {
+    return reinterpret_cast<const native_handle_t*>(ahb);
+}
+
+int EmulatedGralloc::getFormat(const native_handle_t* handle) {
+    const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
+    return ahb->getAndroidFormat();
+}
+
+int EmulatedGralloc::getFormat(const AHardwareBuffer* handle) {
+    const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
+    return ahb->getAndroidFormat();
+}
+
+uint32_t EmulatedGralloc::getFormatDrmFourcc(const AHardwareBuffer* handle) {
+    const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
+    return ahb->getDrmFormat();
+}
+
+size_t EmulatedGralloc::getAllocatedSize(const native_handle_t*) {
+    ALOGE("Unimplemented.");
+    return 0;
+}
+
+size_t EmulatedGralloc::getAllocatedSize(const AHardwareBuffer*) {
+    ALOGE("Unimplemented.");
+    return 0;
+}
+
+int EmulatedGralloc::getId(const AHardwareBuffer* ahb, uint64_t* id) {
+    const auto* rahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(ahb);
+    *id = rahb->getResourceId();
+    return 0;
+}
+
+Gralloc* createPlatformGralloc(int /*deviceFd*/) {
+    return new EmulatedGralloc();
+}
+
+}  // namespace gfxstream
diff --git a/guest/android/GrallocEmulated.h b/guest/android/GrallocEmulated.h
new file mode 100644
index 0000000..8a48fef
--- /dev/null
+++ b/guest/android/GrallocEmulated.h
@@ -0,0 +1,91 @@
+// Copyright 2024 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 <cstdint>
+#include <memory>
+#include <vector>
+
+#include "gfxstream/guest/Gralloc.h"
+#include "VirtGpu.h"
+
+namespace gfxstream {
+
+using EGLClientBuffer = void*;
+
+class EmulatedAHardwareBuffer {
+   public:
+    EmulatedAHardwareBuffer(uint32_t width, uint32_t height, VirtGpuBlobPtr resource);
+
+    ~EmulatedAHardwareBuffer();
+
+    uint32_t getResourceId() const;
+
+    uint32_t getWidth() const;
+
+    uint32_t getHeight() const;
+
+    int getAndroidFormat() const;
+
+    uint32_t getDrmFormat() const;
+
+    AHardwareBuffer* asAHardwareBuffer();
+
+    buffer_handle_t asBufferHandle();
+
+    EGLClientBuffer asEglClientBuffer();
+
+    void acquire();
+    void release();
+
+   private:
+    uint32_t mRefCount;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    VirtGpuBlobPtr mResource;
+};
+
+class EmulatedGralloc : public Gralloc {
+   public:
+    EmulatedGralloc();
+
+    uint32_t createColorBuffer(void*, int width, int height, uint32_t glFormat) override;
+
+    int allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
+                 AHardwareBuffer** outputAhb) override;
+
+    AHardwareBuffer* allocate(uint32_t width, uint32_t height, uint32_t format);
+
+    void acquire(AHardwareBuffer* ahb) override;
+    void release(AHardwareBuffer* ahb) override;
+
+    uint32_t getHostHandle(const native_handle_t* handle) override;
+    uint32_t getHostHandle(const AHardwareBuffer* handle) override;
+
+    const native_handle_t* getNativeHandle(const AHardwareBuffer* ahb) override;
+
+    int getFormat(const native_handle_t* handle) override;
+    int getFormat(const AHardwareBuffer* handle) override;
+
+    uint32_t getFormatDrmFourcc(const AHardwareBuffer* handle) override;
+
+    size_t getAllocatedSize(const native_handle_t*) override;
+    size_t getAllocatedSize(const AHardwareBuffer*) override;
+
+    int getId(const AHardwareBuffer* ahb, uint64_t* id) override;
+
+   private:
+    std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> mOwned;
+};
+
+}  // namespace gfxstream
diff --git a/guest/OpenglSystemCommon/GrallocGoldfish.cpp b/guest/android/GrallocGoldfish.cpp
similarity index 75%
rename from guest/OpenglSystemCommon/GrallocGoldfish.cpp
rename to guest/android/GrallocGoldfish.cpp
index b32562a..f07abde 100644
--- a/guest/OpenglSystemCommon/GrallocGoldfish.cpp
+++ b/guest/android/GrallocGoldfish.cpp
@@ -21,18 +21,13 @@
 
 namespace gfxstream {
 
-uint32_t GoldfishGralloc::createColorBuffer(void* rcEnc, int width,
-                                            int height, uint32_t glformat) {
+uint32_t GoldfishGralloc::createColorBuffer(void* rcEnc, int width, int height, uint32_t glformat) {
     auto* rc = reinterpret_cast<renderControl_client_context_t*>(rcEnc);
     return rc->rcCreateColorBuffer(rc, width, height, glformat);
 }
 
-int GoldfishGralloc::allocate(uint32_t width,
-                              uint32_t height,
-                              uint32_t format,
-                              uint64_t usage,
-                              AHardwareBuffer** outputAhb)  {
-
+int GoldfishGralloc::allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
+                              AHardwareBuffer** outputAhb) {
     struct AHardwareBuffer_Desc desc = {
         .width = width,
         .height = height,
@@ -44,13 +39,9 @@
     return AHardwareBuffer_allocate(&desc, outputAhb);
 }
 
-void GoldfishGralloc::acquire(AHardwareBuffer* ahb) {
-    AHardwareBuffer_acquire(ahb);
-}
+void GoldfishGralloc::acquire(AHardwareBuffer* ahb) { AHardwareBuffer_acquire(ahb); }
 
-void GoldfishGralloc::release(AHardwareBuffer* ahb) {
-    AHardwareBuffer_release(ahb);
-}
+void GoldfishGralloc::release(AHardwareBuffer* ahb) { AHardwareBuffer_release(ahb); }
 
 uint32_t GoldfishGralloc::getHostHandle(native_handle_t const* handle) {
     return cb_handle_t::from(handle)->hostHandle;
@@ -61,6 +52,10 @@
     return getHostHandle(handle);
 }
 
+const native_handle_t* GoldfishGralloc::getNativeHandle(const AHardwareBuffer* ahb) {
+    return AHardwareBuffer_getNativeHandle(ahb);
+}
+
 int GoldfishGralloc::getFormat(const native_handle_t* handle) {
     return cb_handle_t::from(handle)->format;
 }
@@ -79,6 +74,16 @@
     return getAllocatedSize(handle);
 }
 
+int GoldfishGralloc::getId(const AHardwareBuffer* ahb, uint64_t* id) {
+#if ANDROID_API_LEVEL >= 31
+    return AHardwareBuffer_getId(ahb, id);
+#else
+    (void)ahb;
+    *id = 0;
+    return 0;
+#endif
+}
+
 bool GoldfishGralloc::treatBlobAsImage() { return true; }
 
 }  // namespace gfxstream
diff --git a/guest/OpenglSystemCommon/GrallocGoldfish.h b/guest/android/GrallocGoldfish.h
similarity index 81%
rename from guest/OpenglSystemCommon/GrallocGoldfish.h
rename to guest/android/GrallocGoldfish.h
index 2d7b9fa..560550f 100644
--- a/guest/OpenglSystemCommon/GrallocGoldfish.h
+++ b/guest/android/GrallocGoldfish.h
@@ -14,19 +14,15 @@
 
 #pragma once
 
-#include "Gralloc.h"
+#include "gfxstream/guest/Gralloc.h"
 
 namespace gfxstream {
 
 class GoldfishGralloc : public Gralloc {
    public:
-    uint32_t createColorBuffer(void* rcEnc, int width, int height,
-                               uint32_t glformat) override;
+    uint32_t createColorBuffer(void* rcEnc, int width, int height, uint32_t glformat) override;
 
-    int allocate(uint32_t width,
-                 uint32_t height,
-                 uint32_t format,
-                 uint64_t usage,
+    int allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
                  AHardwareBuffer** outputAhb) override;
 
     void acquire(AHardwareBuffer* ahb) override;
@@ -35,14 +31,17 @@
     uint32_t getHostHandle(native_handle_t const* handle) override;
     uint32_t getHostHandle(const AHardwareBuffer* handle) override;
 
+    const native_handle_t* getNativeHandle(const AHardwareBuffer* ahb) override;
+
     int getFormat(const native_handle_t* handle) override;
     int getFormat(const AHardwareBuffer* handle) override;
 
     size_t getAllocatedSize(const native_handle_t* handle) override;
     size_t getAllocatedSize(const AHardwareBuffer* handle) override;
 
+    int getId(const AHardwareBuffer* ahb, uint64_t* id) override;
+
     bool treatBlobAsImage() override;
 };
 
-
 }  // namespace gfxstream
diff --git a/guest/OpenglSystemCommon/GrallocMinigbm.cpp b/guest/android/GrallocMinigbm.cpp
similarity index 90%
rename from guest/OpenglSystemCommon/GrallocMinigbm.cpp
rename to guest/android/GrallocMinigbm.cpp
index a4057c8..a3daf43 100644
--- a/guest/OpenglSystemCommon/GrallocMinigbm.cpp
+++ b/guest/android/GrallocMinigbm.cpp
@@ -14,16 +14,16 @@
 
 #include "GrallocMinigbm.h"
 
-#include <cinttypes>
-#include <cstring>
-#include <stdlib.h>
-
 #include <cros_gralloc/cros_gralloc_handle.h>
 #include <errno.h>
 #include <log/log.h>
+#include <stdlib.h>
 #include <sys/user.h>
-#include <xf86drm.h>
 #include <vndk/hardware_buffer.h>
+#include <xf86drm.h>
+
+#include <cinttypes>
+#include <cstring>
 
 #include "virtgpu_drm.h"
 
@@ -34,7 +34,6 @@
 static const size_t kPageSize = getpagesize();
 #endif
 
-
 namespace gfxstream {
 namespace {
 
@@ -61,8 +60,7 @@
         return false;
     }
     struct ManagedDrmGem {
-        ManagedDrmGem(int fd, uint32_t handle)
-            : m_fd(fd), m_prime_handle(handle) {}
+        ManagedDrmGem(int fd, uint32_t handle) : m_fd(fd), m_prime_handle(handle) {}
         ManagedDrmGem(const ManagedDrmGem&) = delete;
         ~ManagedDrmGem() {
             struct drm_gem_close gem_close {
@@ -114,8 +112,7 @@
 
 }  // namespace
 
-uint32_t MinigbmGralloc::createColorBuffer(void*, int width, int height,
-                                           uint32_t glformat) {
+uint32_t MinigbmGralloc::createColorBuffer(void*, int width, int height, uint32_t glformat) {
     // Only supported format for pbuffers in gfxstream should be RGBA8
     const uint32_t kVirglFormatRGBA = 67;  // VIRGL_FORMAT_R8G8B8A8_UNORM;
     uint32_t virtgpu_format = 0;
@@ -162,12 +159,8 @@
     return res_create.res_handle;
 }
 
-int MinigbmGralloc::allocate(uint32_t width,
-                             uint32_t height,
-                             uint32_t format,
-                             uint64_t usage,
+int MinigbmGralloc::allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
                              AHardwareBuffer** outputAhb) {
-
     struct AHardwareBuffer_Desc desc = {
         .width = width,
         .height = height,
@@ -179,13 +172,9 @@
     return AHardwareBuffer_allocate(&desc, outputAhb);
 }
 
-void MinigbmGralloc::acquire(AHardwareBuffer* ahb) {
-    AHardwareBuffer_acquire(ahb);
-}
+void MinigbmGralloc::acquire(AHardwareBuffer* ahb) { AHardwareBuffer_acquire(ahb); }
 
-void MinigbmGralloc::release(AHardwareBuffer* ahb) {
-    AHardwareBuffer_release(ahb);
-}
+void MinigbmGralloc::release(AHardwareBuffer* ahb) { AHardwareBuffer_release(ahb); }
 
 uint32_t MinigbmGralloc::getHostHandle(const native_handle_t* handle) {
     struct drm_virtgpu_resource_info info;
@@ -202,6 +191,10 @@
     return getHostHandle(handle);
 }
 
+const native_handle_t* MinigbmGralloc::getNativeHandle(const AHardwareBuffer* ahb) {
+    return AHardwareBuffer_getNativeHandle(ahb);
+}
+
 int MinigbmGralloc::getFormat(const native_handle_t* handle) {
     return ((cros_gralloc_handle*)handle)->droid_format;
 }
@@ -235,4 +228,14 @@
     return getAllocatedSize(handle);
 }
 
+int MinigbmGralloc::getId(const AHardwareBuffer* ahb, uint64_t* id) {
+#if ANDROID_API_LEVEL >= 31
+    return AHardwareBuffer_getId(ahb, id);
+#else
+    (void)ahb;
+    *id = 0;
+    return 0;
+#endif
+}
+
 }  // namespace gfxstream
diff --git a/guest/OpenglSystemCommon/GrallocMinigbm.h b/guest/android/GrallocMinigbm.h
similarity index 83%
rename from guest/OpenglSystemCommon/GrallocMinigbm.h
rename to guest/android/GrallocMinigbm.h
index d165a03..c02aaeb 100644
--- a/guest/OpenglSystemCommon/GrallocMinigbm.h
+++ b/guest/android/GrallocMinigbm.h
@@ -14,19 +14,15 @@
 
 #pragma once
 
-#include "Gralloc.h"
+#include "gfxstream/guest/Gralloc.h"
 
 namespace gfxstream {
 
 class MinigbmGralloc : public Gralloc {
    public:
-    uint32_t createColorBuffer(void* rcEnc, int width, int height,
-                               uint32_t glformat) override;
+    uint32_t createColorBuffer(void* rcEnc, int width, int height, uint32_t glformat) override;
 
-    int allocate(uint32_t width,
-                 uint32_t height,
-                 uint32_t format,
-                 uint64_t usage,
+    int allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
                  AHardwareBuffer** outputAhb) override;
 
     void acquire(AHardwareBuffer* ahb) override;
@@ -35,6 +31,8 @@
     uint32_t getHostHandle(native_handle_t const* handle) override;
     uint32_t getHostHandle(const AHardwareBuffer* handle) override;
 
+    const native_handle_t* getNativeHandle(const AHardwareBuffer* ahb) override;
+
     int getFormat(const native_handle_t* handle) override;
     int getFormat(const AHardwareBuffer* handle) override;
 
@@ -44,6 +42,8 @@
     size_t getAllocatedSize(const native_handle_t* handle) override;
     size_t getAllocatedSize(const AHardwareBuffer* handle) override;
 
+    int getId(const AHardwareBuffer* ahb, uint64_t* id) override;
+
     void setFd(int fd) { m_fd = fd; }
 
    private:
diff --git a/guest/android/GrallocStub.cpp b/guest/android/GrallocStub.cpp
new file mode 100644
index 0000000..e92faff
--- /dev/null
+++ b/guest/android/GrallocStub.cpp
@@ -0,0 +1,21 @@
+// Copyright 2024 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 expresso or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gfxstream/guest/Gralloc.h"
+
+namespace gfxstream {
+
+Gralloc* createPlatformGralloc(int) { return nullptr; }
+
+}  // namespace gfxstream
\ No newline at end of file
diff --git a/guest/OpenglSystemCommon/ANativeWindow.h b/guest/android/include/gfxstream/guest/ANativeWindow.h
similarity index 79%
rename from guest/OpenglSystemCommon/ANativeWindow.h
rename to guest/android/include/gfxstream/guest/ANativeWindow.h
index a50d9c2..0c59d19 100644
--- a/guest/OpenglSystemCommon/ANativeWindow.h
+++ b/guest/android/include/gfxstream/guest/ANativeWindow.h
@@ -14,23 +14,27 @@
 
 #pragma once
 
+#if defined(ANDROID)
+
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
 #include "Gralloc.h"
 
+using EGLClientBuffer = void*;
+
 namespace gfxstream {
 
 // Abstraction around libnativewindow to support testing.
 class ANativeWindowHelper {
-  public:
+   public:
     virtual ~ANativeWindowHelper() {}
 
     virtual bool isValid(EGLNativeWindowType window) = 0;
     virtual bool isValid(EGLClientBuffer buffer) = 0;
 
     virtual void acquire(EGLNativeWindowType window) = 0;
-    virtual void release(EGLNativeWindowType window)= 0;
+    virtual void release(EGLNativeWindowType window) = 0;
 
     virtual void acquire(EGLClientBuffer buffer) = 0;
     virtual void release(EGLClientBuffer buffer) = 0;
@@ -51,6 +55,16 @@
     virtual int queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer, int fence) = 0;
     virtual int dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer, int* fence) = 0;
     virtual int cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) = 0;
+
+    virtual EGLNativeWindowType createNativeWindowForTesting(Gralloc* /*gralloc*/,
+                                                             uint32_t /*width*/,
+                                                             uint32_t /*height*/) {
+        return (EGLNativeWindowType)0;
+    }
 };
 
+ANativeWindowHelper* createPlatformANativeWindowHelper();
+
 }  // namespace gfxstream
+
+#endif  // defined(ANDROID)
\ No newline at end of file
diff --git a/guest/OpenglSystemCommon/Gralloc.h b/guest/android/include/gfxstream/guest/Gralloc.h
similarity index 83%
rename from guest/OpenglSystemCommon/Gralloc.h
rename to guest/android/include/gfxstream/guest/Gralloc.h
index 1758c00..5eb9139 100644
--- a/guest/OpenglSystemCommon/Gralloc.h
+++ b/guest/android/include/gfxstream/guest/Gralloc.h
@@ -14,10 +14,11 @@
 
 #pragma once
 
-#include <stddef.h>
-#include <stdint.h>
+#if defined(ANDROID)
 
 #include <cutils/native_handle.h>
+#include <stddef.h>
+#include <stdint.h>
 
 typedef struct AHardwareBuffer AHardwareBuffer;
 
@@ -32,18 +33,16 @@
    public:
     virtual ~Gralloc() {}
 
-    virtual uint32_t createColorBuffer(void* rcEnc, int width, int height,
-                                       uint32_t glformat) = 0;
+    virtual uint32_t createColorBuffer(void* rcEnc, int width, int height, uint32_t glformat) = 0;
 
     virtual void acquire(AHardwareBuffer* ahb) = 0;
     virtual void release(AHardwareBuffer* ahb) = 0;
 
-    virtual int allocate(uint32_t width,
-                         uint32_t height,
-                         uint32_t format,
-                         uint64_t usage,
+    virtual int allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
                          AHardwareBuffer** outputAhb) = 0;
 
+    virtual const native_handle_t* getNativeHandle(const AHardwareBuffer* ahb) = 0;
+
     virtual uint32_t getHostHandle(const native_handle_t* handle) = 0;
     virtual uint32_t getHostHandle(const AHardwareBuffer* handle) = 0;
 
@@ -62,7 +61,13 @@
     virtual size_t getAllocatedSize(const native_handle_t* handle) = 0;
     virtual size_t getAllocatedSize(const AHardwareBuffer* handle) = 0;
 
+    virtual int getId(const AHardwareBuffer* ahb, uint64_t* id) = 0;
+
     virtual bool treatBlobAsImage() { return false; }
 };
 
+Gralloc* createPlatformGralloc(int deviceFd = -1);
+
 }  // namespace gfxstream
+
+#endif  // defined(ANDROID)
\ No newline at end of file
diff --git a/guest/egl/Android.bp b/guest/egl/Android.bp
index 7a1819b..3b07122 100644
--- a/guest/egl/Android.bp
+++ b/guest/egl/Android.bp
@@ -89,8 +89,6 @@
     ],
     shared_libs: [
         "libOpenglSystemCommonWithHost",
-        "libplatform_rutabaga",
-        "libgfxstream_backend",
     ],
     target: {
         host: {
diff --git a/guest/platform/Android.bp b/guest/platform/Android.bp
index 9ecc24e..e356124 100644
--- a/guest/platform/Android.bp
+++ b/guest/platform/Android.bp
@@ -19,10 +19,19 @@
     default_applicable_licenses: ["hardware_google_gfxstream_license"],
 }
 
+cc_library_headers {
+    name: "libplatform_headers",
+    host_supported: true,
+    vendor_available: true,
+    export_include_dirs: [
+        "include"
+    ],
+}
+
 cc_library_static {
     name: "libplatform",
     host_supported: true,
-    vendor_available: true,
+    vendor: true,
     srcs: [
         "VirtGpu.cpp",
         "linux/LinuxVirtGpuBlob.cpp",
@@ -56,17 +65,46 @@
             shared_libs: [
                 "libsync",
             ],
-        }
-    }
+        },
+    },
 }
 
 cc_library_shared {
+    name: "libplatform_rutabaga_server",
+    host_supported: true,
+    vendor_available: true,
+    srcs: [
+        "rutabaga/RutabagaLayer.cpp",
+    ],
+    header_libs: [
+        "libplatform_headers",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libgfxstream_backend",
+        "liblog",
+        "libutils",
+    ],
+    export_include_dirs: [
+        // TODO: restrict to just RutabagaLayer.h
+        "rutabaga/include",
+    ],
+    target: {
+        host: {
+            compile_multilib: "64",
+        },
+        android: {
+            compile_multilib: "64",
+        },
+    },
+}
+
+cc_library_static {
     name: "libplatform_rutabaga",
     host_supported: true,
     vendor_available: true,
     srcs: [
         "VirtGpu.cpp",
-        "rutabaga/RutabagaLayer.cpp",
         "rutabaga/RutabagaVirtGpuBlob.cpp",
         "rutabaga/RutabagaVirtGpuBlobMapping.cpp",
         "rutabaga/RutabagaVirtGpuDevice.cpp",
@@ -75,7 +113,7 @@
     shared_libs: [
         "libcutils",
         "libdrm",
-        "libgfxstream_backend",
+        "libplatform_rutabaga_server",
         "liblog",
         "libutils",
     ],
diff --git a/guest/platform/rutabaga/RutabagaLayer.cpp b/guest/platform/rutabaga/RutabagaLayer.cpp
index 5017a9a..fb225f9 100644
--- a/guest/platform/rutabaga/RutabagaLayer.cpp
+++ b/guest/platform/rutabaga/RutabagaLayer.cpp
@@ -929,55 +929,62 @@
     }
 }
 
-namespace {
-
-EmulatedVirtioGpu* sInstance = nullptr;
-
-}  // namespace
-
 EmulatedVirtioGpu::EmulatedVirtioGpu()
     : mImpl{std::make_unique<EmulatedVirtioGpu::EmulatedVirtioGpuImpl>()} {}
 
+namespace {
+
+static std::mutex sInstanceMutex;
+static std::weak_ptr<EmulatedVirtioGpu> sInstance;
+
+}  // namespace
+
 /*static*/
-EmulatedVirtioGpu& EmulatedVirtioGpu::Get() {
-    if (sInstance == nullptr) {
-        sInstance = new EmulatedVirtioGpu();
+std::shared_ptr<EmulatedVirtioGpu> EmulatedVirtioGpu::Get() {
+    std::lock_guard<std::mutex> lock(sInstanceMutex);
 
-        bool withGl = false;
-        bool withVk = true;
-        bool withVkSnapshots = false;
+    std::shared_ptr<EmulatedVirtioGpu> instance = sInstance.lock();
+    if (instance != nullptr) {
+        return instance;
+    }
 
-        struct Option {
-            std::string env;
-            bool* val;
-        };
-        const std::vector<Option> options = {
-            {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_GL", &withGl},
-            {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK", &withVk},
-            {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK_SNAPSHOTS", &withVkSnapshots},
-        };
-        for (const Option option : options) {
-            const char* val = std::getenv(option.env.c_str());
-            if (val != nullptr && (val[0] == 'Y' || val[0] == 'y')) {
-                *option.val = true;
-            }
-        }
+    instance = std::shared_ptr<EmulatedVirtioGpu>(new EmulatedVirtioGpu());
 
-        ALOGE("Initializing withGl:%d withVk:%d withVkSnapshots:%d", withGl, withVk,
-              withVkSnapshots);
-        if (!sInstance->Init(withGl, withVk, withVkSnapshots)) {
-            ALOGE("Failed to initialize EmulatedVirtioGpu.");
+    bool withGl = false;
+    bool withVk = true;
+    bool withVkSnapshots = false;
+
+    struct Option {
+        std::string env;
+        bool* val;
+    };
+    const std::vector<Option> options = {
+        {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_GL", &withGl},
+        {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK", &withVk},
+        {"GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK_SNAPSHOTS", &withVkSnapshots},
+    };
+    for (const Option option : options) {
+        const char* val = std::getenv(option.env.c_str());
+        if (val != nullptr && (val[0] == 'Y' || val[0] == 'y')) {
+            *option.val = true;
         }
     }
-    return *sInstance;
+
+    ALOGE("Initializing withGl:%d withVk:%d withVkSnapshots:%d", withGl, withVk, withVkSnapshots);
+    if (!instance->Init(withGl, withVk, withVkSnapshots)) {
+        ALOGE("Failed to initialize EmulatedVirtioGpu.");
+        return nullptr;
+    }
+    ALOGE("Successfully initialized EmulatedVirtioGpu.");
+    sInstance = instance;
+    return instance;
 }
 
 /*static*/
-void EmulatedVirtioGpu::Reset() {
-    if (sInstance != nullptr) {
-        delete sInstance;
-        sInstance = nullptr;
-    }
+uint32_t EmulatedVirtioGpu::GetNumActiveUsers() {
+    std::lock_guard<std::mutex> lock(sInstanceMutex);
+    std::shared_ptr<EmulatedVirtioGpu> instance = sInstance.lock();
+    return instance.use_count();
 }
 
 bool EmulatedVirtioGpu::Init(bool withGl, bool withVk, bool withVkSnapshots) {
@@ -1031,6 +1038,6 @@
 
 void EmulatedVirtioGpu::SignalEmulatedFence(int fenceId) { mImpl->SignalEmulatedFence(fenceId); }
 
-void ResetEmulatedVirtioGpu() { EmulatedVirtioGpu::Reset(); }
+bool GetNumActiveEmulatedVirtioGpuUsers() { return EmulatedVirtioGpu::GetNumActiveUsers(); }
 
 }  // namespace gfxstream
diff --git a/guest/platform/rutabaga/RutabagaLayer.h b/guest/platform/rutabaga/RutabagaLayer.h
index 057b2f7..7c6443b 100644
--- a/guest/platform/rutabaga/RutabagaLayer.h
+++ b/guest/platform/rutabaga/RutabagaLayer.h
@@ -19,6 +19,8 @@
 #include <memory>
 #include <optional>
 
+// TODO: switch to virtgpu_drm.h to avoid circular dep between
+// libplatform_rutabaga and libplatform_rutabaga_server.
 #include "VirtGpu.h"
 
 namespace gfxstream {
@@ -28,47 +30,38 @@
 // host server via rutabaga.
 class EmulatedVirtioGpu {
   public:
-    static EmulatedVirtioGpu& Get();
-    static void Reset();
+   static std::shared_ptr<EmulatedVirtioGpu> Get();
+   static uint32_t GetNumActiveUsers();
 
-    bool Init(bool withGl, bool withVk, bool withVkSnapshots);
+   bool Init(bool withGl, bool withVk, bool withVkSnapshots);
 
-    VirtGpuCaps GetCaps(VirtGpuCapset capset);
+   VirtGpuCaps GetCaps(VirtGpuCapset capset);
 
-    std::optional<uint32_t> CreateContext();
-    void DestroyContext(uint32_t contextId);
+   std::optional<uint32_t> CreateContext();
+   void DestroyContext(uint32_t contextId);
 
-    std::optional<uint32_t> CreateBlob(uint32_t contextId,
-                                       const struct VirtGpuCreateBlob& params);
-    std::optional<uint32_t> CreateVirglBlob(uint32_t contextId,
-                                            uint32_t width,
-                                            uint32_t height,
-                                            uint32_t virglFormat);
+   std::optional<uint32_t> CreateBlob(uint32_t contextId, const struct VirtGpuCreateBlob& params);
+   std::optional<uint32_t> CreateVirglBlob(uint32_t contextId, uint32_t width, uint32_t height,
+                                           uint32_t virglFormat);
 
-    void DestroyResource(uint32_t contextId,
-                         uint32_t resourceId);
+   void DestroyResource(uint32_t contextId, uint32_t resourceId);
 
-    uint8_t* Map(uint32_t resourceId);
-    void Unmap(uint32_t resourceId);
+   uint8_t* Map(uint32_t resourceId);
+   void Unmap(uint32_t resourceId);
 
-    int ExecBuffer(uint32_t contextId,
-                   struct VirtGpuExecBuffer& execbuffer,
-                   std::optional<uint32_t> blobResourceId);
+   int ExecBuffer(uint32_t contextId, struct VirtGpuExecBuffer& execbuffer,
+                  std::optional<uint32_t> blobResourceId);
 
-    int Wait(uint32_t resourceId);
+   int Wait(uint32_t resourceId);
 
-    int TransferFromHost(uint32_t contextId,
-                         uint32_t resourceId,
-                         uint32_t transferOffset,
-                         uint32_t transferSize);
-    int TransferToHost(uint32_t contextId,
-                       uint32_t resourceId,
-                       uint32_t transferOffset,
-                       uint32_t transferSize);
+   int TransferFromHost(uint32_t contextId, uint32_t resourceId, uint32_t transferOffset,
+                        uint32_t transferSize);
+   int TransferToHost(uint32_t contextId, uint32_t resourceId, uint32_t transferOffset,
+                      uint32_t transferSize);
 
-    void SignalEmulatedFence(int fenceId);
+   void SignalEmulatedFence(int fenceId);
 
-    int WaitOnEmulatedFence(int fenceAsFileDescriptor, int timeoutMilliseconds);
+   int WaitOnEmulatedFence(int fenceAsFileDescriptor, int timeoutMilliseconds);
 
   private:
     EmulatedVirtioGpu();
diff --git a/guest/platform/rutabaga/RutabagaVirtGpu.h b/guest/platform/rutabaga/RutabagaVirtGpu.h
index 8cf07f5..64568ab 100644
--- a/guest/platform/rutabaga/RutabagaVirtGpu.h
+++ b/guest/platform/rutabaga/RutabagaVirtGpu.h
@@ -28,14 +28,16 @@
 
 class RutabagaVirtGpuBlobMapping : public VirtGpuBlobMapping {
   public:
-    RutabagaVirtGpuBlobMapping(VirtGpuBlobPtr blob, uint8_t* mapped);
-    ~RutabagaVirtGpuBlobMapping();
+   RutabagaVirtGpuBlobMapping(std::shared_ptr<EmulatedVirtioGpu> emulation, VirtGpuBlobPtr blob,
+                              uint8_t* mapped);
+   ~RutabagaVirtGpuBlobMapping();
 
-    uint8_t* asRawPtr(void) override;
+   uint8_t* asRawPtr(void) override;
 
   private:
-    VirtGpuBlobPtr mBlob;
-    uint8_t* mMapped = nullptr;
+   const std::shared_ptr<EmulatedVirtioGpu> mEmulation;
+   const VirtGpuBlobPtr mBlob;
+   uint8_t* mMapped = nullptr;
 };
 
 class RutabagaVirtGpuResource : public std::enable_shared_from_this<RutabagaVirtGpuResource>, public VirtGpuBlob {
@@ -61,11 +63,10 @@
         kPipe,
     };
 
-    RutabagaVirtGpuResource(uint32_t resourceId,
-                            ResourceType resourceType,
-                            uint32_t contextId);
+    RutabagaVirtGpuResource(std::shared_ptr<EmulatedVirtioGpu> emulation, uint32_t resourceId,
+                            ResourceType resourceType, uint32_t contextId);
 
-
+    const std::shared_ptr<EmulatedVirtioGpu> mEmulation;
     const uint32_t mContextId;
     const uint32_t mResourceId;
     const ResourceType mResourceType;
@@ -73,27 +74,29 @@
 
 class RutabagaVirtGpuDevice : public std::enable_shared_from_this<RutabagaVirtGpuDevice>, public VirtGpuDevice {
   public:
-    RutabagaVirtGpuDevice(uint32_t contextId, VirtGpuCapset capset);
-    ~RutabagaVirtGpuDevice();
+   RutabagaVirtGpuDevice(std::shared_ptr<EmulatedVirtioGpu> emulation, uint32_t contextId,
+                         VirtGpuCapset capset);
+   ~RutabagaVirtGpuDevice();
 
-    int64_t getDeviceHandle() override;
+   int64_t getDeviceHandle() override;
 
-    VirtGpuCaps getCaps() override;
+   VirtGpuCaps getCaps() override;
 
-    VirtGpuBlobPtr createBlob(const struct VirtGpuCreateBlob& blobCreate) override;
+   VirtGpuBlobPtr createBlob(const struct VirtGpuCreateBlob& blobCreate) override;
 
-    VirtGpuBlobPtr createVirglBlob(uint32_t width, uint32_t height, uint32_t virglFormat) override;
+   VirtGpuBlobPtr createVirglBlob(uint32_t width, uint32_t height, uint32_t virglFormat) override;
 
-    VirtGpuBlobPtr importBlob(const struct VirtGpuExternalHandle& handle) override;
+   VirtGpuBlobPtr importBlob(const struct VirtGpuExternalHandle& handle) override;
 
-    int execBuffer(struct VirtGpuExecBuffer& execbuffer, const VirtGpuBlob* blob) override;
+   int execBuffer(struct VirtGpuExecBuffer& execbuffer, const VirtGpuBlob* blob) override;
 
-   private:
-    const uint32_t mContextId;
-    const VirtGpuCapset mCapset;
+  private:
+   const std::shared_ptr<EmulatedVirtioGpu> mEmulation;
+   const uint32_t mContextId;
+   const VirtGpuCapset mCapset;
 
-    friend class RutabagaVirtGpuResource;
-    uint32_t GetContextId() const { return mContextId; }
+   friend class RutabagaVirtGpuResource;
+   uint32_t GetContextId() const { return mContextId; }
 };
 
 }  // namespace gfxstream
diff --git a/guest/platform/rutabaga/RutabagaVirtGpuBlob.cpp b/guest/platform/rutabaga/RutabagaVirtGpuBlob.cpp
index 0b7db44..6f97ffb 100644
--- a/guest/platform/rutabaga/RutabagaVirtGpuBlob.cpp
+++ b/guest/platform/rutabaga/RutabagaVirtGpuBlob.cpp
@@ -21,17 +21,21 @@
 
 namespace gfxstream {
 
-RutabagaVirtGpuResource::RutabagaVirtGpuResource(uint32_t resourceId, ResourceType resourceType,
+RutabagaVirtGpuResource::RutabagaVirtGpuResource(std::shared_ptr<EmulatedVirtioGpu> emulation,
+                                                 uint32_t resourceId, ResourceType resourceType,
                                                  uint32_t contextId)
-    : mContextId(contextId), mResourceId(resourceId), mResourceType(resourceType) {}
+    : mEmulation(emulation),
+      mContextId(contextId),
+      mResourceId(resourceId),
+      mResourceType(resourceType) {}
 
 RutabagaVirtGpuResource::~RutabagaVirtGpuResource() {
-    EmulatedVirtioGpu::Get().DestroyResource(mContextId, mResourceId);
+    mEmulation->DestroyResource(mContextId, mResourceId);
 }
 
 VirtGpuBlobMappingPtr RutabagaVirtGpuResource::createMapping(void) {
-    uint8_t* mapped = EmulatedVirtioGpu::Get().Map(mResourceId);
-    return std::make_shared<RutabagaVirtGpuBlobMapping>(shared_from_this(), mapped);
+    uint8_t* mapped = mEmulation->Map(mResourceId);
+    return std::make_shared<RutabagaVirtGpuBlobMapping>(mEmulation, shared_from_this(), mapped);
 }
 
 uint32_t RutabagaVirtGpuResource::getResourceHandle() const { return mResourceId; }
@@ -56,7 +60,7 @@
     return -1;
 }
 
-int RutabagaVirtGpuResource::wait() { return EmulatedVirtioGpu::Get().Wait(mResourceId); }
+int RutabagaVirtGpuResource::wait() { return mEmulation->Wait(mResourceId); }
 
 int RutabagaVirtGpuResource::transferFromHost(uint32_t offset, uint32_t size) {
     if (mResourceType != ResourceType::kPipe) {
@@ -64,7 +68,7 @@
         return -1;
     }
 
-    return EmulatedVirtioGpu::Get().TransferFromHost(mContextId, mResourceId, offset, size);
+    return mEmulation->TransferFromHost(mContextId, mResourceId, offset, size);
 }
 
 int RutabagaVirtGpuResource::transferToHost(uint32_t offset, uint32_t size) {
@@ -73,7 +77,7 @@
         return -1;
     }
 
-    return EmulatedVirtioGpu::Get().TransferToHost(mContextId, mResourceId, offset, size);
+    return mEmulation->TransferToHost(mContextId, mResourceId, offset, size);
 }
 
 }  // namespace gfxstream
diff --git a/guest/platform/rutabaga/RutabagaVirtGpuBlobMapping.cpp b/guest/platform/rutabaga/RutabagaVirtGpuBlobMapping.cpp
index 4f82f7c..750ddee 100644
--- a/guest/platform/rutabaga/RutabagaVirtGpuBlobMapping.cpp
+++ b/guest/platform/rutabaga/RutabagaVirtGpuBlobMapping.cpp
@@ -19,11 +19,12 @@
 
 namespace gfxstream {
 
-RutabagaVirtGpuBlobMapping::RutabagaVirtGpuBlobMapping(VirtGpuBlobPtr blob, uint8_t* mapped)
-    : mBlob(blob), mMapped(mapped) {}
+RutabagaVirtGpuBlobMapping::RutabagaVirtGpuBlobMapping(std::shared_ptr<EmulatedVirtioGpu> emulation,
+                                                       VirtGpuBlobPtr blob, uint8_t* mapped)
+    : mEmulation(emulation), mBlob(blob), mMapped(mapped) {}
 
 RutabagaVirtGpuBlobMapping::~RutabagaVirtGpuBlobMapping(void) {
-    EmulatedVirtioGpu::Get().Unmap(mBlob->getResourceHandle());
+    mEmulation->Unmap(mBlob->getResourceHandle());
 }
 
 uint8_t* RutabagaVirtGpuBlobMapping::asRawPtr(void) { return mMapped; }
diff --git a/guest/platform/rutabaga/RutabagaVirtGpuDevice.cpp b/guest/platform/rutabaga/RutabagaVirtGpuDevice.cpp
index 999ac2f..615e81e 100644
--- a/guest/platform/rutabaga/RutabagaVirtGpuDevice.cpp
+++ b/guest/platform/rutabaga/RutabagaVirtGpuDevice.cpp
@@ -21,38 +21,35 @@
 
 namespace gfxstream {
 
-RutabagaVirtGpuDevice::RutabagaVirtGpuDevice(uint32_t contextId, VirtGpuCapset capset)
-    : VirtGpuDevice(capset), mContextId(contextId), mCapset(capset) {}
+RutabagaVirtGpuDevice::RutabagaVirtGpuDevice(std::shared_ptr<EmulatedVirtioGpu> emulation,
+                                             uint32_t contextId, VirtGpuCapset capset)
+    : VirtGpuDevice(capset), mEmulation(emulation), mContextId(contextId), mCapset(capset) {}
 
-RutabagaVirtGpuDevice::~RutabagaVirtGpuDevice() {
-    EmulatedVirtioGpu::Get().DestroyContext(mContextId);
-    EmulatedVirtioGpu::Reset();
-}
+RutabagaVirtGpuDevice::~RutabagaVirtGpuDevice() { mEmulation->DestroyContext(mContextId); }
 
 int64_t RutabagaVirtGpuDevice::getDeviceHandle() { return -1; }
 
-VirtGpuCaps RutabagaVirtGpuDevice::getCaps() { return EmulatedVirtioGpu::Get().GetCaps(mCapset); }
+VirtGpuCaps RutabagaVirtGpuDevice::getCaps() { return mEmulation->GetCaps(mCapset); }
 
 VirtGpuBlobPtr RutabagaVirtGpuDevice::createBlob(const struct VirtGpuCreateBlob& blobCreate) {
-    const auto resourceIdOpt = EmulatedVirtioGpu::Get().CreateBlob(mContextId, blobCreate);
+    const auto resourceIdOpt = mEmulation->CreateBlob(mContextId, blobCreate);
     if (!resourceIdOpt) {
         return nullptr;
     }
 
     return VirtGpuBlobPtr(new RutabagaVirtGpuResource(
-        *resourceIdOpt, RutabagaVirtGpuResource::ResourceType::kBlob, mContextId));
+        mEmulation, *resourceIdOpt, RutabagaVirtGpuResource::ResourceType::kBlob, mContextId));
 }
 
 VirtGpuBlobPtr RutabagaVirtGpuDevice::createVirglBlob(uint32_t width, uint32_t height,
                                                       uint32_t virglFormat) {
-    const auto resourceIdOpt =
-        EmulatedVirtioGpu::Get().CreateVirglBlob(mContextId, width, height, virglFormat);
+    const auto resourceIdOpt = mEmulation->CreateVirglBlob(mContextId, width, height, virglFormat);
     if (!resourceIdOpt) {
         return nullptr;
     }
 
     return VirtGpuBlobPtr(new RutabagaVirtGpuResource(
-        *resourceIdOpt, RutabagaVirtGpuResource::ResourceType::kPipe, mContextId));
+        mEmulation, *resourceIdOpt, RutabagaVirtGpuResource::ResourceType::kPipe, mContextId));
 }
 
 int RutabagaVirtGpuDevice::execBuffer(struct VirtGpuExecBuffer& execbuffer,
@@ -61,7 +58,7 @@
     if (blob) {
         blobResourceId = blob->getResourceHandle();
     }
-    return EmulatedVirtioGpu::Get().ExecBuffer(mContextId, execbuffer, blobResourceId);
+    return mEmulation->ExecBuffer(mContextId, execbuffer, blobResourceId);
 }
 
 VirtGpuBlobPtr RutabagaVirtGpuDevice::importBlob(const struct VirtGpuExternalHandle&) {
@@ -72,10 +69,16 @@
 }  // namespace gfxstream
 
 VirtGpuDevice* createPlatformVirtGpuDevice(enum VirtGpuCapset capset, int) {
-    const auto contextIdOp = gfxstream::EmulatedVirtioGpu::Get().CreateContext();
+    std::shared_ptr<gfxstream::EmulatedVirtioGpu> emulation = gfxstream::EmulatedVirtioGpu::Get();
+    if (!emulation) {
+        ALOGE("Failed to create RutabagaVirtGpuDevice: failed to get emulation layer.");
+        return nullptr;
+    }
+
+    const auto contextIdOp = emulation->CreateContext();
     if (!contextIdOp) {
         ALOGE("Failed to create RutabagaVirtGpuDevice: failed to create context.");
         return nullptr;
     }
-    return new gfxstream::RutabagaVirtGpuDevice(*contextIdOp, capset);
+    return new gfxstream::RutabagaVirtGpuDevice(emulation, *contextIdOp, capset);
 }
diff --git a/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.cpp b/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.cpp
index 4ddaee2..2f97ea8 100644
--- a/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.cpp
+++ b/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.cpp
@@ -16,14 +16,21 @@
 
 #include "RutabagaVirtGpuSyncHelper.h"
 
+#include <log/log.h>
+
 #include "RutabagaLayer.h"
 
 namespace gfxstream {
 
 RutabagaVirtGpuSyncHelper::RutabagaVirtGpuSyncHelper() {}
 
+bool RutabagaVirtGpuSyncHelper::Init() {
+    mEmulation = EmulatedVirtioGpu::Get();
+    return mEmulation != nullptr;
+}
+
 int RutabagaVirtGpuSyncHelper::wait(int syncFd, int timeoutMilliseconds) {
-    return EmulatedVirtioGpu::Get().WaitOnEmulatedFence(syncFd, timeoutMilliseconds);
+    return mEmulation->WaitOnEmulatedFence(syncFd, timeoutMilliseconds);
 }
 
 int RutabagaVirtGpuSyncHelper::dup(int syncFd) {
@@ -33,6 +40,13 @@
 
 int RutabagaVirtGpuSyncHelper::close(int) { return 0; }
 
-SyncHelper* createPlatformSyncHelper() { return new RutabagaVirtGpuSyncHelper(); }
+SyncHelper* createPlatformSyncHelper() {
+    RutabagaVirtGpuSyncHelper* sync = new RutabagaVirtGpuSyncHelper();
+    if (!sync->Init()) {
+        ALOGE("Failed to initialize RutabagaVirtGpuSyncHelper: Failed to get emulation layer.");
+        return nullptr;
+    }
+    return sync;
+}
 
 }  // namespace gfxstream
diff --git a/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.h b/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.h
index c163eb3..6b50e71 100644
--- a/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.h
+++ b/guest/platform/rutabaga/RutabagaVirtGpuSyncHelper.h
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#include <memory>
+
+#include "RutabagaLayer.h"
 #include "Sync.h"
 
 namespace gfxstream {
@@ -22,11 +25,16 @@
   public:
     RutabagaVirtGpuSyncHelper();
 
+    bool Init();
+
     int wait(int syncFd, int timeoutMilliseconds) override;
 
     int dup(int syncFd) override;
 
     int close(int) override;
+
+   private:
+    std::shared_ptr<EmulatedVirtioGpu> mEmulation;
 };
 
 SyncHelper* createPlatformSyncHelper();
diff --git a/guest/platform/rutabaga/include/gfxstream/RutabagaLayerTestUtils.h b/guest/platform/rutabaga/include/gfxstream/RutabagaLayerTestUtils.h
index 4817a9c..8f4536d 100644
--- a/guest/platform/rutabaga/include/gfxstream/RutabagaLayerTestUtils.h
+++ b/guest/platform/rutabaga/include/gfxstream/RutabagaLayerTestUtils.h
@@ -18,6 +18,6 @@
 
 namespace gfxstream {
 
-void ResetEmulatedVirtioGpu();
+uint32_t GetNumActiveEmulatedVirtioGpuUsers();
 
 }  // namespace gfxstream
diff --git a/guest/renderControl_enc/Android.bp b/guest/renderControl_enc/Android.bp
index 2ffb2b4..5f346a3 100644
--- a/guest/renderControl_enc/Android.bp
+++ b/guest/renderControl_enc/Android.bp
@@ -19,6 +19,18 @@
     default_applicable_licenses: ["hardware_google_gfxstream_license"],
 }
 
+cc_library_headers {
+    name: "libgfxstream_guest_rendercontrol_encoder_headers",
+    host_supported: true,
+    vendor_available: true,
+    defaults: [
+        "libgfxstream_guest_cc_defaults",
+    ],
+    export_include_dirs: [
+        ".",
+    ],
+}
+
 cc_library_shared {
     name: "lib_renderControl_enc",
     host_supported: true,
@@ -32,6 +44,7 @@
     ],
     export_header_lib_headers: [
         "libgfxstream_guest_graphics_headers",
+        "libgfxstream_guest_iostream",
     ],
     shared_libs: [
         "libandroidemu",
diff --git a/guest/vulkan/Android.bp b/guest/vulkan/Android.bp
index 4db968f..03b4e96 100644
--- a/guest/vulkan/Android.bp
+++ b/guest/vulkan/Android.bp
@@ -47,7 +47,6 @@
         "libmesa_util_gfxstream",
         "libmesa_util_format_gfxstream",
         "libGoldfishAddressSpace",
-        "libgfxstream_guest_gralloc",
         "libringbuffer",
         "libqemupipe.ranchu",
         "libarect",
@@ -114,6 +113,7 @@
     ],
     static_libs: [
         "libplatform",
+        "libgfxstream_guest_android",
         "libgfxstream_guest_system_common",
     ],
 }
@@ -124,11 +124,12 @@
         "libgfxstream_guest_vulkan_defaults",
     ],
     shared_libs: [
-        "libplatform_rutabaga",
-        "libgfxstream_backend",
+        "libplatform_rutabaga_server",
     ],
     static_libs: [
+        "libgfxstream_guest_android_with_host",
         "libgfxstream_guest_system_common_with_host",
+        "libplatform_rutabaga",
     ],
     cflags: [
         "-DEND2END_TESTS",
diff --git a/guest/vulkan_enc/Android.bp b/guest/vulkan_enc/Android.bp
index f129878..a672423 100644
--- a/guest/vulkan_enc/Android.bp
+++ b/guest/vulkan_enc/Android.bp
@@ -113,6 +113,7 @@
     ],
     header_libs: [
         "gfxstream_vulkan_headers",
+        "libOpenglSystemCommonHeaders",
         "libgfxstream_guest_iostream",
         "libnativewindow_headers",
         "mesa_common_headers_gfxstream",
@@ -133,9 +134,9 @@
         "libdrm",
         "libandroidemu_static",
         "libGoldfishAddressSpace",
-        "libgfxstream_guest_gralloc",
         "libmesa_vulkan_util_gfxstream",
         "libmesa_vulkan_runtime_gfxstream",
+        "libgfxstream_guest_android",
         "libgfxstream_guest_vulkan_entrypoints",
         "libmesa_util_gfxstream",
         "libmesa_util_format_gfxstream",
@@ -196,9 +197,11 @@
     defaults: [
         "libgfxstream_guest_vulkan_encoder_defaults",
     ],
-    shared_libs: [
+    static_libs: [
         "libplatform_rutabaga",
-        "libgfxstream_backend",
+    ],
+    export_static_lib_headers: [
+        "libplatform_rutabaga",
     ],
     target: {
         host: {
diff --git a/guest/vulkan_enc/AndroidHardwareBuffer.cpp b/guest/vulkan_enc/AndroidHardwareBuffer.cpp
index 0cb7794..c5984b3 100644
--- a/guest/vulkan_enc/AndroidHardwareBuffer.cpp
+++ b/guest/vulkan_enc/AndroidHardwareBuffer.cpp
@@ -20,8 +20,9 @@
 #endif
 
 #include <assert.h>
+#include <log/log.h>
 
-#include "../OpenglSystemCommon/HostConnection.h"
+#include "gfxstream/guest/Gralloc.h"
 #include "vk_format_info.h"
 #include "vk_util.h"
 
diff --git a/guest/vulkan_enc/AndroidHardwareBuffer.h b/guest/vulkan_enc/AndroidHardwareBuffer.h
index 0389f09..5b2bef5 100644
--- a/guest/vulkan_enc/AndroidHardwareBuffer.h
+++ b/guest/vulkan_enc/AndroidHardwareBuffer.h
@@ -16,7 +16,7 @@
 
 #include <vulkan/vulkan.h>
 
-#include "../OpenglSystemCommon/Gralloc.h"
+#include "gfxstream/guest/Gralloc.h"
 #include "HostVisibleMemoryVirtualization.h"
 
 // Structure similar to
diff --git a/guest/vulkan_enc/ResourceTracker.cpp b/guest/vulkan_enc/ResourceTracker.cpp
index be6fb36..7a568fd 100644
--- a/guest/vulkan_enc/ResourceTracker.cpp
+++ b/guest/vulkan_enc/ResourceTracker.cpp
@@ -702,8 +702,9 @@
 
 uint64_t getAHardwareBufferId(AHardwareBuffer* ahw) {
     uint64_t id = 0;
-#if defined(__ANDROID__) && ANDROID_API_LEVEL >= 31
-    AHardwareBuffer_getId(ahw, &id);
+#if defined(ANDROID)
+    auto* gralloc = ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->grallocHelper();
+    gralloc->getId(ahw, &id);
 #else
     (void)ahw;
 #endif
@@ -5938,13 +5939,7 @@
 
     auto* gralloc = ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->grallocHelper();
     const native_handle_t* nativeHandle = (const native_handle_t*)inputNativeInfo->handle;
-
-#if defined(END2END_TESTS)
-    // This is valid since the testing backend creates the handle and we know the layout.
-    *(uint32_t*)(outputNativeInfo->handle) = (uint32_t)nativeHandle->data[0];
-#else
     *(uint32_t*)(outputNativeInfo->handle) = gralloc->getHostHandle(nativeHandle);
-#endif
 }
 
 void ResourceTracker::unwrap_VkBindImageMemorySwapchainInfoKHR(
@@ -6499,6 +6494,7 @@
     VkExternalBufferProperties* pExternalBufferProperties) {
     VkEncoder* enc = (VkEncoder*)context;
 
+#if defined(ANDROID)
     // Older versions of Goldfish's Gralloc did not support allocating AHARDWAREBUFFER_FORMAT_BLOB
     // with GPU usage (b/299520213).
     if (ResourceTracker::threadingCallbacks.hostConnectionGetFunc()
@@ -6511,6 +6507,7 @@
         pExternalBufferProperties->externalMemoryProperties.compatibleHandleTypes = 0;
         return;
     }
+#endif
 
     uint32_t supportedHandleType = 0;
 #ifdef VK_USE_PLATFORM_FUCHSIA
diff --git a/guest/vulkan_enc/func_table.cpp b/guest/vulkan_enc/func_table.cpp
index f3632ca..4dcfcc4 100644
--- a/guest/vulkan_enc/func_table.cpp
+++ b/guest/vulkan_enc/func_table.cpp
@@ -34,7 +34,6 @@
 
 #include <cstring>
 
-#include "../OpenglSystemCommon/HostConnection.h"
 #include "ResourceTracker.h"
 #include "VkEncoder.h"
 #include "gfxstream_vk_entrypoints.h"
diff --git a/guest/vulkan_enc/vk_format_info.h b/guest/vulkan_enc/vk_format_info.h
index 2d5d84e..9c5422c 100644
--- a/guest/vulkan_enc/vk_format_info.h
+++ b/guest/vulkan_enc/vk_format_info.h
@@ -35,6 +35,10 @@
     HAL_PIXEL_FORMAT_YV12 = 842094169,
 };
 #endif
+
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
+#endif
 #include <vndk/hardware_buffer.h>
 #include <vulkan/vulkan.h>