gralloc4: adds gralloc4 support am: 1476c7ea85 am: 06937a2a57

Change-Id: I5187e4cab429cf54e397e6b8f1b559da2820bf2a
diff --git a/Android.bp b/Android.bp
index 63a50f7..02e8e5d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2,17 +2,7 @@
 // found in the LICENSE file.
 
 cc_defaults {
-    name: "gralloc.minigbm_intel_defaults",
-    cflags: ["-DDRV_I915"],
-}
-
-cc_defaults {
-    name: "gralloc.minigbm_meson_defaults",
-    cflags: ["-DDRV_MESON"],
-}
-
-cc_defaults {
-    name: "gralloc.minigbm_defaults",
+    name: "minigbm_defaults",
 
     srcs: [
         "amdgpu.c",
@@ -34,11 +24,6 @@
         "vc4.c",
         "vgem.c",
         "virtio_gpu.c",
-
-        "cros_gralloc/cros_gralloc_buffer.cc",
-        "cros_gralloc/cros_gralloc_driver.cc",
-        "cros_gralloc/cros_gralloc_helpers.cc",
-        "cros_gralloc/gralloc0/gralloc0.cc",
     ],
 
     cflags: [
@@ -53,61 +38,50 @@
     ],
     cppflags: ["-std=c++14"],
 
-    // The preferred path for vendor HALs is /vendor/lib/hw
     vendor: true,
-    relative_install_path: "hw",
 
     header_libs: [
         "libhardware_headers",
         "libnativebase_headers",
+        "libnativewindow_headers",
+        "libsystem_headers",
+    ],
+
+    export_header_lib_headers: [
+        "libhardware_headers",
+        "libnativebase_headers",
+        "libnativewindow_headers",
         "libsystem_headers",
     ],
 
     shared_libs: [
         "libcutils",
         "libdrm",
-
         "libnativewindow",
         "libsync",
         "liblog",
     ],
 
     static_libs: ["libarect"],
+
+    export_static_lib_headers: ["libarect"],
 }
 
-cc_library_shared {
-    name: "gralloc.minigbm",
-    defaults: ["gralloc.minigbm_defaults"],
+cc_defaults {
+    name: "minigbm_cros_gralloc_defaults",
+
+    defaults: ["minigbm_defaults"],
+
+    srcs: [
+        "cros_gralloc/cros_gralloc_buffer.cc",
+        "cros_gralloc/cros_gralloc_helpers.cc",
+        "cros_gralloc/cros_gralloc_driver.cc",
+    ]
 }
 
-cc_library_shared {
-    name: "gralloc.minigbm_intel",
-    defaults: [
-        "gralloc.minigbm_defaults",
-        "gralloc.minigbm_intel_defaults",
-    ],
-    enabled: false,
-    arch: {
-        x86: {
-            enabled: true,
-        },
-        x86_64: {
-            enabled: true,
-        },
-    },
-}
-
-cc_library_shared {
-    name: "gralloc.minigbm_meson",
-    defaults: [
-        "gralloc.minigbm_defaults",
-        "gralloc.minigbm_meson_defaults",
-    ],
-}
-
-cc_library_shared {
+cc_library_static {
     name: "libminigbm",
-    defaults: ["gralloc.minigbm_defaults"],
+    defaults: ["minigbm_defaults"],
     shared_libs: ["liblog"],
     static_libs: ["libdrm"],
 
@@ -118,3 +92,41 @@
 
     export_include_dirs: ["."],
 }
+
+cc_library_static {
+    name: "libminigbm_cros_gralloc",
+    defaults: ["minigbm_cros_gralloc_defaults"],
+    shared_libs: ["liblog"],
+    static_libs: ["libdrm"],
+
+    export_include_dirs: ["."],
+}
+
+cc_library_shared {
+    name: "gralloc.minigbm",
+    defaults: ["minigbm_cros_gralloc_defaults"],
+    srcs: ["cros_gralloc/gralloc0/gralloc0.cc"],
+}
+
+cc_library_shared {
+    name: "gralloc.minigbm_intel",
+    defaults: ["minigbm_cros_gralloc_defaults"],
+    enabled: false,
+    arch: {
+        x86: {
+            enabled: true,
+        },
+        x86_64: {
+            enabled: true,
+        },
+    },
+    cflags: ["-DDRV_I915"],
+    srcs: ["cros_gralloc/gralloc0/gralloc0.cc"],
+}
+
+cc_library_shared {
+    name: "gralloc.minigbm_meson",
+    defaults: ["minigbm_cros_gralloc_defaults"],
+    cflags: ["-DDRV_MESON"],
+    srcs: ["cros_gralloc/gralloc0/gralloc0.cc"],
+}
\ No newline at end of file
diff --git a/cros_gralloc/cros_gralloc_buffer.cc b/cros_gralloc/cros_gralloc_buffer.cc
index 1066edc..2982505 100644
--- a/cros_gralloc/cros_gralloc_buffer.cc
+++ b/cros_gralloc/cros_gralloc_buffer.cc
@@ -10,8 +10,11 @@
 #include <sys/mman.h>
 
 cros_gralloc_buffer::cros_gralloc_buffer(uint32_t id, struct bo *acquire_bo,
-					 struct cros_gralloc_handle *acquire_handle)
-    : id_(id), bo_(acquire_bo), hnd_(acquire_handle), refcount_(1), lockcount_(0)
+					 struct cros_gralloc_handle *acquire_handle,
+					 int32_t reserved_region_fd, uint64_t reserved_region_size)
+    : id_(id), bo_(acquire_bo), hnd_(acquire_handle), refcount_(1), lockcount_(0),
+      reserved_region_fd_(reserved_region_fd), reserved_region_size_(reserved_region_size),
+      reserved_region_addr_(nullptr)
 {
 	assert(bo_);
 	num_planes_ = drv_bo_get_num_planes(bo_);
@@ -26,6 +29,9 @@
 		native_handle_close(&hnd_->base);
 		delete hnd_;
 	}
+	if (reserved_region_addr_) {
+		munmap(reserved_region_addr_, reserved_region_size_);
+	}
 }
 
 uint32_t cros_gralloc_buffer::get_id() const
@@ -114,3 +120,52 @@
 {
 	return drv_resource_info(bo_, strides, offsets);
 }
+
+int32_t cros_gralloc_buffer::invalidate()
+{
+	if (lockcount_ <= 0) {
+		drv_log("Buffer was not locked.\n");
+		return -EINVAL;
+	}
+
+	if (lock_data_[0]) {
+		return drv_bo_invalidate(bo_, lock_data_[0]);
+	}
+
+	return 0;
+}
+
+int32_t cros_gralloc_buffer::flush()
+{
+	if (lockcount_ <= 0) {
+		drv_log("Buffer was not locked.\n");
+		return -EINVAL;
+	}
+
+	if (lock_data_[0]) {
+		return drv_bo_flush(bo_, lock_data_[0]);
+	}
+
+	return 0;
+}
+
+int32_t cros_gralloc_buffer::get_reserved_region(void **addr, uint64_t *size)
+{
+	if (reserved_region_fd_ <= 0) {
+		drv_log("Buffer does not have reserved region.\n");
+		return -EINVAL;
+	}
+
+	if (!reserved_region_addr_) {
+		reserved_region_addr_ = mmap(nullptr, reserved_region_size_, PROT_WRITE | PROT_READ,
+					     MAP_SHARED, reserved_region_fd_, 0);
+		if (reserved_region_addr_ == MAP_FAILED) {
+			drv_log("Failed to mmap reserved region: %s.\n", strerror(errno));
+			return -errno;
+		}
+	}
+
+	*addr = reserved_region_addr_;
+	*size = reserved_region_size_;
+	return 0;
+}
diff --git a/cros_gralloc/cros_gralloc_buffer.h b/cros_gralloc/cros_gralloc_buffer.h
index ebd72ec..8634882 100644
--- a/cros_gralloc/cros_gralloc_buffer.h
+++ b/cros_gralloc/cros_gralloc_buffer.h
@@ -14,7 +14,8 @@
 {
       public:
 	cros_gralloc_buffer(uint32_t id, struct bo *acquire_bo,
-			    struct cros_gralloc_handle *acquire_handle);
+			    struct cros_gralloc_handle *acquire_handle, int32_t reserved_region_fd,
+			    uint64_t reserved_region_size);
 	~cros_gralloc_buffer();
 
 	uint32_t get_id() const;
@@ -28,12 +29,19 @@
 	int32_t unlock();
 	int32_t resource_info(uint32_t strides[DRV_MAX_PLANES], uint32_t offsets[DRV_MAX_PLANES]);
 
+	int32_t invalidate();
+	int32_t flush();
+
+	int32_t get_reserved_region(void **reserved_region_addr, uint64_t *reserved_region_size);
+
       private:
 	cros_gralloc_buffer(cros_gralloc_buffer const &);
 	cros_gralloc_buffer operator=(cros_gralloc_buffer const &);
 
 	uint32_t id_;
 	struct bo *bo_;
+
+	/* Note: this will be nullptr for imported/retained buffers. */
 	struct cros_gralloc_handle *hnd_;
 
 	int32_t refcount_;
@@ -41,6 +49,11 @@
 	uint32_t num_planes_;
 
 	struct mapping *lock_data_[DRV_MAX_PLANES];
+
+	/* Optional additional shared memory region attached to some gralloc4 buffers. */
+	int32_t reserved_region_fd_;
+	uint64_t reserved_region_size_;
+	void *reserved_region_addr_;
 };
 
 #endif
diff --git a/cros_gralloc/cros_gralloc_driver.cc b/cros_gralloc/cros_gralloc_driver.cc
index 62b43d4..e324bce 100644
--- a/cros_gralloc/cros_gralloc_driver.cc
+++ b/cros_gralloc/cros_gralloc_driver.cc
@@ -5,12 +5,16 @@
  */
 
 #include "cros_gralloc_driver.h"
-#include "../util.h"
 
 #include <cstdlib>
 #include <fcntl.h>
+#include <sys/mman.h>
 #include <xf86drm.h>
 
+#include "../drv_priv.h"
+#include "../helpers.h"
+#include "../util.h"
+
 cros_gralloc_driver::cros_gralloc_driver() : drv_(nullptr)
 {
 }
@@ -90,15 +94,38 @@
 	return (combo != nullptr);
 }
 
+int32_t create_reserved_region(const std::string &buffer_name, uint64_t reserved_region_size)
+{
+	int32_t reserved_region_fd;
+	std::string reserved_region_name = buffer_name + " reserved region";
+
+	reserved_region_fd = memfd_create(reserved_region_name.c_str(), FD_CLOEXEC);
+	if (reserved_region_fd == -1) {
+		drv_log("Failed to create reserved region fd: %s.\n", strerror(errno));
+		return -errno;
+	}
+
+	if (ftruncate(reserved_region_fd, reserved_region_size)) {
+		drv_log("Failed to set reserved region size: %s.\n", strerror(errno));
+		return -errno;
+	}
+
+	return reserved_region_fd;
+}
+
 int32_t cros_gralloc_driver::allocate(const struct cros_gralloc_buffer_descriptor *descriptor,
 				      buffer_handle_t *out_handle)
 {
 	uint32_t id;
-	uint64_t mod;
 	size_t num_planes;
+	size_t num_fds;
+	size_t num_ints;
+	size_t num_bytes;
 	uint32_t resolved_format;
 	uint32_t bytes_per_pixel;
 	uint64_t use_flags;
+	int32_t reserved_region_fd;
+	char *name;
 
 	struct bo *bo;
 	struct cros_gralloc_handle *hnd;
@@ -140,41 +167,72 @@
 		return -EINVAL;
 	}
 
-	hnd = new cros_gralloc_handle();
 	num_planes = drv_bo_get_num_planes(bo);
+	num_fds = num_planes;
 
+	if (descriptor->reserved_region_size > 0) {
+		reserved_region_fd =
+		    create_reserved_region(descriptor->name, descriptor->reserved_region_size);
+		if (reserved_region_fd < 0) {
+			drv_bo_destroy(bo);
+			return reserved_region_fd;
+		}
+		num_fds += 1;
+	} else {
+		reserved_region_fd = -1;
+	}
+
+	num_bytes = sizeof(struct cros_gralloc_handle);
+	num_bytes += (descriptor->name.size() + 1);
+	/*
+	 * Ensure that the total number of bytes is a multiple of sizeof(int) as
+	 * native_handle_clone() copies data based on hnd->base.numInts.
+	 */
+	num_bytes = ALIGN(num_bytes, sizeof(int));
+	num_ints = num_bytes - sizeof(native_handle_t) - num_fds;
+	/*
+	 * Malloc is used as handles are ultimetly destroyed via free in
+	 * native_handle_delete().
+	 */
+	hnd = static_cast<struct cros_gralloc_handle *>(malloc(num_bytes));
 	hnd->base.version = sizeof(hnd->base);
-	hnd->base.numFds = num_planes;
-	hnd->base.numInts = handle_data_size - num_planes;
-
+	hnd->base.numFds = num_fds;
+	hnd->base.numInts = num_ints;
+	hnd->num_planes = num_planes;
 	for (size_t plane = 0; plane < num_planes; plane++) {
 		hnd->fds[plane] = drv_bo_get_plane_fd(bo, plane);
 		hnd->strides[plane] = drv_bo_get_plane_stride(bo, plane);
 		hnd->offsets[plane] = drv_bo_get_plane_offset(bo, plane);
-
-		mod = drv_bo_get_plane_format_modifier(bo, plane);
-		hnd->format_modifiers[2 * plane] = static_cast<uint32_t>(mod >> 32);
-		hnd->format_modifiers[2 * plane + 1] = static_cast<uint32_t>(mod);
+		hnd->sizes[plane] = drv_bo_get_plane_size(bo, plane);
 	}
-
+	hnd->fds[hnd->num_planes] = reserved_region_fd;
+	hnd->reserved_region_size = descriptor->reserved_region_size;
+	static std::atomic<uint32_t> next_buffer_id{ 1 };
+	hnd->id = next_buffer_id++;
 	hnd->width = drv_bo_get_width(bo);
 	hnd->height = drv_bo_get_height(bo);
 	hnd->format = drv_bo_get_format(bo);
-	hnd->use_flags[0] = static_cast<uint32_t>(descriptor->use_flags >> 32);
-	hnd->use_flags[1] = static_cast<uint32_t>(descriptor->use_flags);
+	hnd->format_modifier = drv_bo_get_plane_format_modifier(bo, 0);
+	hnd->use_flags = descriptor->use_flags;
 	bytes_per_pixel = drv_bytes_per_pixel_from_format(hnd->format, 0);
 	hnd->pixel_stride = DIV_ROUND_UP(hnd->strides[0], bytes_per_pixel);
 	hnd->magic = cros_gralloc_magic;
 	hnd->droid_format = descriptor->droid_format;
-	hnd->usage = descriptor->producer_usage;
+	hnd->usage = descriptor->droid_usage;
+	hnd->total_size = descriptor->reserved_region_size + bo->meta.total_size;
+	hnd->name_offset = handle_data_size;
+
+	name = (char *)(&hnd->base.data[hnd->name_offset]);
+	snprintf(name, descriptor->name.size() + 1, "%s", descriptor->name.c_str());
 
 	id = drv_bo_get_plane_handle(bo, 0).u32;
-	auto buffer = new cros_gralloc_buffer(id, bo, hnd);
+	auto buffer = new cros_gralloc_buffer(id, bo, hnd, hnd->fds[hnd->num_planes],
+					      hnd->reserved_region_size);
 
 	std::lock_guard<std::mutex> lock(mutex_);
 	buffers_.emplace(id, buffer);
 	handles_.emplace(hnd, std::make_pair(buffer, 1));
-	*out_handle = &hnd->base;
+	*out_handle = reinterpret_cast<buffer_handle_t>(hnd);
 	return 0;
 }
 
@@ -208,18 +266,16 @@
 		struct bo *bo;
 		struct drv_import_fd_data data;
 		data.format = hnd->format;
+
 		data.width = hnd->width;
 		data.height = hnd->height;
-		data.use_flags = static_cast<uint64_t>(hnd->use_flags[0]) << 32;
-		data.use_flags |= hnd->use_flags[1];
+		data.use_flags = hnd->use_flags;
 
 		memcpy(data.fds, hnd->fds, sizeof(data.fds));
 		memcpy(data.strides, hnd->strides, sizeof(data.strides));
 		memcpy(data.offsets, hnd->offsets, sizeof(data.offsets));
 		for (uint32_t plane = 0; plane < DRV_MAX_PLANES; plane++) {
-			data.format_modifiers[plane] =
-			    static_cast<uint64_t>(hnd->format_modifiers[2 * plane]) << 32;
-			data.format_modifiers[plane] |= hnd->format_modifiers[2 * plane + 1];
+			data.format_modifiers[plane] = hnd->format_modifier;
 		}
 
 		bo = drv_bo_import(drv_, &data);
@@ -228,7 +284,8 @@
 
 		id = drv_bo_get_plane_handle(bo, 0).u32;
 
-		buffer = new cros_gralloc_buffer(id, bo, nullptr);
+		buffer = new cros_gralloc_buffer(id, bo, nullptr, hnd->fds[hnd->num_planes],
+						 hnd->reserved_region_size);
 		buffers_.emplace(id, buffer);
 	}
 
@@ -264,10 +321,10 @@
 }
 
 int32_t cros_gralloc_driver::lock(buffer_handle_t handle, int32_t acquire_fence,
-				  const struct rectangle *rect, uint32_t map_flags,
-				  uint8_t *addr[DRV_MAX_PLANES])
+				  bool close_acquire_fence, const struct rectangle *rect,
+				  uint32_t map_flags, uint8_t *addr[DRV_MAX_PLANES])
 {
-	int32_t ret = cros_gralloc_sync_wait(acquire_fence);
+	int32_t ret = cros_gralloc_sync_wait(acquire_fence, close_acquire_fence);
 	if (ret)
 		return ret;
 
@@ -313,6 +370,51 @@
 	return buffer->unlock();
 }
 
+int32_t cros_gralloc_driver::invalidate(buffer_handle_t handle)
+{
+	std::lock_guard<std::mutex> lock(mutex_);
+
+	auto hnd = cros_gralloc_convert_handle(handle);
+	if (!hnd) {
+		drv_log("Invalid handle.\n");
+		return -EINVAL;
+	}
+
+	auto buffer = get_buffer(hnd);
+	if (!buffer) {
+		drv_log("Invalid Reference.\n");
+		return -EINVAL;
+	}
+
+	return buffer->invalidate();
+}
+
+int32_t cros_gralloc_driver::flush(buffer_handle_t handle, int32_t *release_fence)
+{
+	std::lock_guard<std::mutex> lock(mutex_);
+
+	auto hnd = cros_gralloc_convert_handle(handle);
+	if (!hnd) {
+		drv_log("Invalid handle.\n");
+		return -EINVAL;
+	}
+
+	auto buffer = get_buffer(hnd);
+	if (!buffer) {
+		drv_log("Invalid Reference.\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * From the ANativeWindow::dequeueBuffer documentation:
+	 *
+	 * "A value of -1 indicates that the caller may access the buffer immediately without
+	 * waiting on a fence."
+	 */
+	*release_fence = -1;
+	return buffer->flush();
+}
+
 int32_t cros_gralloc_driver::get_backing_store(buffer_handle_t handle, uint64_t *out_store)
 {
 	std::lock_guard<std::mutex> lock(mutex_);
@@ -353,6 +455,32 @@
 	return buffer->resource_info(strides, offsets);
 }
 
+int32_t cros_gralloc_driver::get_reserved_region(buffer_handle_t handle,
+						 void **reserved_region_addr,
+						 uint64_t *reserved_region_size)
+{
+	std::lock_guard<std::mutex> lock(mutex_);
+
+	auto hnd = cros_gralloc_convert_handle(handle);
+	if (!hnd) {
+		drv_log("Invalid handle.\n");
+		return -EINVAL;
+	}
+
+	auto buffer = get_buffer(hnd);
+	if (!buffer) {
+		drv_log("Invalid Reference.\n");
+		return -EINVAL;
+	}
+
+	return buffer->get_reserved_region(reserved_region_addr, reserved_region_size);
+}
+
+uint32_t cros_gralloc_driver::get_resolved_drm_format(uint32_t drm_format, uint64_t usage)
+{
+	return drv_resolve_format(drv_, drm_format, usage);
+}
+
 cros_gralloc_buffer *cros_gralloc_driver::get_buffer(cros_gralloc_handle_t hnd)
 {
 	/* Assumes driver mutex is held. */
@@ -361,3 +489,13 @@
 
 	return nullptr;
 }
+
+void cros_gralloc_driver::for_each_handle(
+    const std::function<void(cros_gralloc_handle_t)> &function)
+{
+	std::lock_guard<std::mutex> lock(mutex_);
+
+	for (const auto &pair : handles_) {
+		function(pair.first);
+	}
+}
\ No newline at end of file
diff --git a/cros_gralloc/cros_gralloc_driver.h b/cros_gralloc/cros_gralloc_driver.h
index f051277..d444ecd 100644
--- a/cros_gralloc/cros_gralloc_driver.h
+++ b/cros_gralloc/cros_gralloc_driver.h
@@ -9,6 +9,7 @@
 
 #include "cros_gralloc_buffer.h"
 
+#include <functional>
 #include <mutex>
 #include <unordered_map>
 
@@ -26,14 +27,25 @@
 	int32_t retain(buffer_handle_t handle);
 	int32_t release(buffer_handle_t handle);
 
-	int32_t lock(buffer_handle_t handle, int32_t acquire_fence, const struct rectangle *rect,
-		     uint32_t map_flags, uint8_t *addr[DRV_MAX_PLANES]);
+	int32_t lock(buffer_handle_t handle, int32_t acquire_fence, bool close_acquire_fence,
+		     const struct rectangle *rect, uint32_t map_flags,
+		     uint8_t *addr[DRV_MAX_PLANES]);
 	int32_t unlock(buffer_handle_t handle, int32_t *release_fence);
 
+	int32_t invalidate(buffer_handle_t handle);
+	int32_t flush(buffer_handle_t handle, int32_t *release_fence);
+
 	int32_t get_backing_store(buffer_handle_t handle, uint64_t *out_store);
 	int32_t resource_info(buffer_handle_t handle, uint32_t strides[DRV_MAX_PLANES],
 			      uint32_t offsets[DRV_MAX_PLANES]);
 
+	int32_t get_reserved_region(buffer_handle_t handle, void **reserved_region_addr,
+				    uint64_t *reserved_region_size);
+
+	uint32_t get_resolved_drm_format(uint32_t drm_format, uint64_t usage);
+
+	void for_each_handle(const std::function<void(cros_gralloc_handle_t)> &function);
+
       private:
 	cros_gralloc_driver(cros_gralloc_driver const &);
 	cros_gralloc_driver operator=(cros_gralloc_driver const &);
diff --git a/cros_gralloc/cros_gralloc_handle.h b/cros_gralloc/cros_gralloc_handle.h
index cd3edfe..d2e1607 100644
--- a/cros_gralloc/cros_gralloc_handle.h
+++ b/cros_gralloc/cros_gralloc_handle.h
@@ -11,27 +11,40 @@
 #include <cutils/native_handle.h>
 
 #define DRV_MAX_PLANES 4
-
-/*
- * Only use 32-bit integers in the handle. This guarantees that the handle is
- * densely packed (i.e, the compiler does not insert any padding).
- */
+#define DRV_MAX_FDS (DRV_MAX_PLANES + 1)
 
 struct cros_gralloc_handle {
 	native_handle_t base;
-	int32_t fds[DRV_MAX_PLANES];
+	/*
+	 * File descriptors must immediately follow the native_handle_t base and used file
+	 * descriptors must be packed at the beginning of this array to work with
+	 * native_handle_clone().
+	 *
+	 * This field contains 'num_planes' plane file descriptors followed by an optional metadata
+	 * reserved region file descriptor if 'reserved_region_size' is greater than zero.
+	 */
+	int32_t fds[DRV_MAX_FDS];
 	uint32_t strides[DRV_MAX_PLANES];
 	uint32_t offsets[DRV_MAX_PLANES];
-	uint32_t format_modifiers[2 * DRV_MAX_PLANES];
+	uint32_t sizes[DRV_MAX_PLANES];
+	uint32_t id;
 	uint32_t width;
 	uint32_t height;
-	uint32_t format;       /* DRM format */
-	uint32_t use_flags[2]; /* Buffer creation flags */
+	uint32_t format; /* DRM format */
+	uint64_t format_modifier;
+	uint64_t use_flags; /* Buffer creation flags */
 	uint32_t magic;
 	uint32_t pixel_stride;
 	int32_t droid_format;
 	int32_t usage; /* Android usage. */
-};
+	uint32_t num_planes;
+	uint64_t reserved_region_size;
+	uint64_t total_size; /* Total allocation size */
+	/*
+	 * Name is a null terminated char array located at handle->base.data[handle->name_offset].
+	 */
+	uint32_t name_offset;
+} __attribute__((packed));
 
 typedef const struct cros_gralloc_handle *cros_gralloc_handle_t;
 
diff --git a/cros_gralloc/cros_gralloc_helpers.cc b/cros_gralloc/cros_gralloc_helpers.cc
index 73e59cb..1e05150 100644
--- a/cros_gralloc/cros_gralloc_helpers.cc
+++ b/cros_gralloc/cros_gralloc_helpers.cc
@@ -20,6 +20,8 @@
 		return DRM_FORMAT_ARGB8888;
 	case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
 		return DRM_FORMAT_FLEX_IMPLEMENTATION_DEFINED;
+	case HAL_PIXEL_FORMAT_RAW16:
+		return DRM_FORMAT_R16;
 	case HAL_PIXEL_FORMAT_RGB_565:
 		return DRM_FORMAT_RGB565;
 	case HAL_PIXEL_FORMAT_RGB_888:
@@ -59,29 +61,31 @@
 	return hnd;
 }
 
-int32_t cros_gralloc_sync_wait(int32_t acquire_fence)
+int32_t cros_gralloc_sync_wait(int32_t fence, bool close_fence)
 {
-	if (acquire_fence < 0)
+	if (fence < 0)
 		return 0;
 
 	/*
 	 * Wait initially for 1000 ms, and then wait indefinitely. The SYNC_IOC_WAIT
 	 * documentation states the caller waits indefinitely on the fence if timeout < 0.
 	 */
-	int err = sync_wait(acquire_fence, 1000);
+	int err = sync_wait(fence, 1000);
 	if (err < 0) {
 		drv_log("Timed out on sync wait, err = %s\n", strerror(errno));
-		err = sync_wait(acquire_fence, -1);
+		err = sync_wait(fence, -1);
 		if (err < 0) {
 			drv_log("sync wait error = %s\n", strerror(errno));
 			return -errno;
 		}
 	}
 
-	err = close(acquire_fence);
-	if (err) {
-		drv_log("Unable to close fence fd, err = %s\n", strerror(errno));
-		return -errno;
+	if (close_fence) {
+		err = close(fence);
+		if (err) {
+			drv_log("Unable to close fence fd, err = %s\n", strerror(errno));
+			return -errno;
+		}
 	}
 
 	return 0;
diff --git a/cros_gralloc/cros_gralloc_helpers.h b/cros_gralloc/cros_gralloc_helpers.h
index a55eebc..36f86ef 100644
--- a/cros_gralloc/cros_gralloc_helpers.h
+++ b/cros_gralloc/cros_gralloc_helpers.h
@@ -22,6 +22,6 @@
 
 cros_gralloc_handle_t cros_gralloc_convert_handle(buffer_handle_t handle);
 
-int32_t cros_gralloc_sync_wait(int32_t acquire_fence);
+int32_t cros_gralloc_sync_wait(int32_t fence, bool close_fence);
 
 #endif
diff --git a/cros_gralloc/cros_gralloc_types.h b/cros_gralloc/cros_gralloc_types.h
index 1fa81de..22f58e2 100644
--- a/cros_gralloc/cros_gralloc_types.h
+++ b/cros_gralloc/cros_gralloc_types.h
@@ -7,14 +7,17 @@
 #ifndef CROS_GRALLOC_TYPES_H
 #define CROS_GRALLOC_TYPES_H
 
+#include <string>
+
 struct cros_gralloc_buffer_descriptor {
 	uint32_t width;
 	uint32_t height;
-	uint32_t consumer_usage;
-	uint32_t producer_usage;
-	uint32_t droid_format;
+	int32_t droid_format;
+	int32_t droid_usage;
 	uint32_t drm_format;
 	uint64_t use_flags;
+	uint64_t reserved_region_size;
+	std::string name;
 };
 
 #endif
diff --git a/cros_gralloc/gralloc0/gralloc0.cc b/cros_gralloc/gralloc0/gralloc0.cc
index a70498a..170dae9 100644
--- a/cros_gralloc/gralloc0/gralloc0.cc
+++ b/cros_gralloc/gralloc0/gralloc0.cc
@@ -119,9 +119,10 @@
 	descriptor.width = w;
 	descriptor.height = h;
 	descriptor.droid_format = format;
-	descriptor.producer_usage = descriptor.consumer_usage = usage;
+	descriptor.droid_usage = usage;
 	descriptor.drm_format = cros_gralloc_convert_format(format);
 	descriptor.use_flags = gralloc0_convert_usage(usage);
+	descriptor.reserved_region_size = 0;
 
 	supported = mod->driver->is_supported(&descriptor);
 	if (!supported && (usage & GRALLOC_USAGE_HW_COMPOSER)) {
@@ -248,7 +249,7 @@
 	if (ret)
 		return ret;
 
-	ret = cros_gralloc_sync_wait(fence_fd);
+	ret = cros_gralloc_sync_wait(fence_fd, /*close_acquire_fence=*/true);
 	if (ret)
 		return ret;
 
@@ -359,7 +360,7 @@
 	assert(h >= 0);
 
 	map_flags = gralloc0_convert_map_usage(usage);
-	ret = mod->driver->lock(handle, fence_fd, &rect, map_flags, addr);
+	ret = mod->driver->lock(handle, fence_fd, true, &rect, map_flags, addr);
 	*vaddr = addr[0];
 	return ret;
 }
@@ -404,7 +405,7 @@
 	assert(h >= 0);
 
 	map_flags = gralloc0_convert_map_usage(usage);
-	ret = mod->driver->lock(handle, fence_fd, &rect, map_flags, addr);
+	ret = mod->driver->lock(handle, fence_fd, true, &rect, map_flags, addr);
 	if (ret)
 		return ret;
 
diff --git a/cros_gralloc/gralloc0/tests/gralloctest.c b/cros_gralloc/gralloc0/tests/gralloctest.c
index 8dfcd0b..f663cd0 100644
--- a/cros_gralloc/gralloc0/tests/gralloctest.c
+++ b/cros_gralloc/gralloc0/tests/gralloctest.c
@@ -42,10 +42,11 @@
 	} while (0)
 
 /* Private API enumeration -- see <gralloc_drm.h> */
-enum { GRALLOC_DRM_GET_STRIDE,
-       GRALLOC_DRM_GET_FORMAT,
-       GRALLOC_DRM_GET_DIMENSIONS,
-       GRALLOC_DRM_GET_BACKING_STORE,
+enum {
+	GRALLOC_DRM_GET_STRIDE,
+	GRALLOC_DRM_GET_FORMAT,
+	GRALLOC_DRM_GET_DIMENSIONS,
+	GRALLOC_DRM_GET_BACKING_STORE,
 };
 
 struct gralloctest_context {
diff --git a/cros_gralloc/gralloc4/.clang-format b/cros_gralloc/gralloc4/.clang-format
new file mode 100644
index 0000000..b310cc1
--- /dev/null
+++ b/cros_gralloc/gralloc4/.clang-format
@@ -0,0 +1,19 @@
+# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# This directory is formatted to match the format of the interfaces implemented.
+
+BasedOnStyle: Google
+Standard: Cpp11
+AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IncludeBlocks: Preserve
+IndentWidth: 4
+ContinuationIndentWidth: 8
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
\ No newline at end of file
diff --git a/cros_gralloc/gralloc4/Android.bp b/cros_gralloc/gralloc4/Android.bp
new file mode 100644
index 0000000..a0a8622
--- /dev/null
+++ b/cros_gralloc/gralloc4/Android.bp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_binary {
+    name: "android.hardware.graphics.allocator@4.0-service.minigbm",
+    relative_install_path: "hw",
+    vendor: true,
+    init_rc: ["android.hardware.graphics.allocator@4.0-service.minigbm.rc"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "android.hardware.graphics.allocator@4.0",
+        "android.hardware.graphics.mapper@4.0",
+        "libbase",
+        "libcutils",
+        "libgralloctypes",
+        "libhidlbase",
+        "liblog",
+        "libsync",
+        "libutils",
+    ],
+
+    static_libs: [
+        "libdrm",
+        "libminigbm_cros_gralloc",
+    ],
+
+    srcs: [
+        "CrosGralloc4Allocator.cc",
+        "CrosGralloc4AllocatorService.cc",
+        "CrosGralloc4Utils.cc",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.graphics.mapper@4.0-impl.minigbm",
+    relative_install_path: "hw",
+    vendor: true,
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "android.hardware.graphics.mapper@4.0",
+        "libbase",
+        "libcutils",
+        "libgralloctypes",
+        "libhidlbase",
+        "liblog",
+        "libsync",
+        "libutils",
+    ],
+
+    static_libs: [
+        "libdrm",
+        "libminigbm_cros_gralloc",
+    ],
+
+    srcs: [
+        "CrosGralloc4Mapper.cc",
+        "CrosGralloc4Utils.cc",
+    ],
+}
diff --git a/cros_gralloc/gralloc4/CrosGralloc4Allocator.cc b/cros_gralloc/gralloc4/CrosGralloc4Allocator.cc
new file mode 100644
index 0000000..4fb7845
--- /dev/null
+++ b/cros_gralloc/gralloc4/CrosGralloc4Allocator.cc
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "cros_gralloc/gralloc4/CrosGralloc4Allocator.h"
+
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+#include <gralloctypes/Gralloc4.h>
+
+#include "cros_gralloc/cros_gralloc_helpers.h"
+#include "cros_gralloc/gralloc4/CrosGralloc4Utils.h"
+
+using android::hardware::hidl_handle;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::graphics::common::V1_2::BufferUsage;
+using android::hardware::graphics::common::V1_2::PixelFormat;
+using android::hardware::graphics::mapper::V4_0::Error;
+
+using BufferDescriptorInfo =
+        android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo;
+
+CrosGralloc4Allocator::CrosGralloc4Allocator() : mDriver(std::make_unique<cros_gralloc_driver>()) {
+    if (mDriver->init()) {
+        drv_log("Failed to initialize driver.\n");
+        mDriver = nullptr;
+    }
+}
+
+Error CrosGralloc4Allocator::allocate(const BufferDescriptorInfo& descriptor, uint32_t* outStride,
+                                      hidl_handle* outHandle) {
+    if (!mDriver) {
+        drv_log("Failed to allocate. Driver is uninitialized.\n");
+        return Error::NO_RESOURCES;
+    }
+
+    if (!outStride || !outHandle) {
+        return Error::NO_RESOURCES;
+    }
+
+    struct cros_gralloc_buffer_descriptor crosDescriptor;
+    if (convertToCrosDescriptor(descriptor, &crosDescriptor)) {
+        return Error::UNSUPPORTED;
+    }
+
+    bool supported = mDriver->is_supported(&crosDescriptor);
+    if (!supported && (descriptor.usage & BufferUsage::COMPOSER_OVERLAY)) {
+        crosDescriptor.use_flags &= ~BO_USE_SCANOUT;
+        supported = mDriver->is_supported(&crosDescriptor);
+    }
+
+    if (!supported) {
+        std::string drmFormatString = getDrmFormatString(crosDescriptor.drm_format);
+        std::string pixelFormatString = getPixelFormatString(descriptor.format);
+        std::string usageString = getUsageString(descriptor.usage);
+        drv_log("Unsupported combination -- pixel format: %s, drm format:%s, usage: %s\n",
+                pixelFormatString.c_str(), drmFormatString.c_str(), usageString.c_str());
+        return Error::UNSUPPORTED;
+    }
+
+    buffer_handle_t handle;
+    int ret = mDriver->allocate(&crosDescriptor, &handle);
+    if (ret) {
+        return Error::NO_RESOURCES;
+    }
+
+    cros_gralloc_handle_t crosHandle = cros_gralloc_convert_handle(handle);
+    if (!crosHandle) {
+        return Error::NO_RESOURCES;
+    }
+
+    *outHandle = handle;
+    *outStride = crosHandle->pixel_stride;
+
+    return Error::NONE;
+}
+
+Return<void> CrosGralloc4Allocator::allocate(const hidl_vec<uint8_t>& descriptor, uint32_t count,
+                                             allocate_cb hidlCb) {
+    hidl_vec<hidl_handle> handles;
+
+    if (!mDriver) {
+        drv_log("Failed to allocate. Driver is uninitialized.\n");
+        hidlCb(Error::NO_RESOURCES, 0, handles);
+        return Void();
+    }
+
+    BufferDescriptorInfo description;
+
+    int ret = android::gralloc4::decodeBufferDescriptorInfo(descriptor, &description);
+    if (ret) {
+        drv_log("Failed to allocate. Failed to decode buffer descriptor: %d.\n", ret);
+        hidlCb(Error::BAD_DESCRIPTOR, 0, handles);
+        return Void();
+    }
+
+    handles.resize(count);
+
+    uint32_t stride = 0;
+    for (int i = 0; i < handles.size(); i++) {
+        Error err = allocate(description, &stride, &(handles[i]));
+        if (err != Error::NONE) {
+            for (int j = 0; j < i; j++) {
+                mDriver->release(handles[j].getNativeHandle());
+            }
+            handles.resize(0);
+            hidlCb(err, 0, handles);
+            return Void();
+        }
+    }
+
+    hidlCb(Error::NONE, stride, handles);
+
+    for (const hidl_handle& handle : handles) {
+        mDriver->release(handle.getNativeHandle());
+    }
+
+    return Void();
+}
diff --git a/cros_gralloc/gralloc4/CrosGralloc4Allocator.h b/cros_gralloc/gralloc4/CrosGralloc4Allocator.h
new file mode 100644
index 0000000..21ad7ad
--- /dev/null
+++ b/cros_gralloc/gralloc4/CrosGralloc4Allocator.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <android/hardware/graphics/allocator/4.0/IAllocator.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+
+#include "cros_gralloc/cros_gralloc_driver.h"
+
+class CrosGralloc4Allocator : public android::hardware::graphics::allocator::V4_0::IAllocator {
+  public:
+    CrosGralloc4Allocator();
+
+    android::hardware::Return<void> allocate(const android::hardware::hidl_vec<uint8_t>& descriptor,
+                                             uint32_t count, allocate_cb hidl_cb) override;
+
+  private:
+    android::hardware::graphics::mapper::V4_0::Error allocate(
+            const android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo&
+                    description,
+            uint32_t* outStride, android::hardware::hidl_handle* outHandle);
+
+    std::unique_ptr<cros_gralloc_driver> mDriver;
+};
diff --git a/cros_gralloc/gralloc4/CrosGralloc4AllocatorService.cc b/cros_gralloc/gralloc4/CrosGralloc4AllocatorService.cc
new file mode 100644
index 0000000..5b79860
--- /dev/null
+++ b/cros_gralloc/gralloc4/CrosGralloc4AllocatorService.cc
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#define LOG_TAG "AllocatorService"
+
+#include <hidl/LegacySupport.h>
+
+#include "cros_gralloc/gralloc4/CrosGralloc4Allocator.h"
+
+using android::sp;
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::graphics::allocator::V4_0::IAllocator;
+
+int main(int, char**) {
+    sp<IAllocator> allocator = new CrosGralloc4Allocator();
+    configureRpcThreadpool(4, true /* callerWillJoin */);
+    if (allocator->registerAsService() != android::NO_ERROR) {
+        ALOGE("failed to register graphics IAllocator 4.0 service");
+        return -EINVAL;
+    }
+
+    ALOGI("graphics IAllocator 4.0 service is initialized");
+    android::hardware::joinRpcThreadpool();
+    ALOGI("graphics IAllocator 4.0 service is terminating");
+    return 0;
+}
diff --git a/cros_gralloc/gralloc4/CrosGralloc4Mapper.cc b/cros_gralloc/gralloc4/CrosGralloc4Mapper.cc
new file mode 100644
index 0000000..47e24ac
--- /dev/null
+++ b/cros_gralloc/gralloc4/CrosGralloc4Mapper.cc
@@ -0,0 +1,1004 @@
+/*
+ * Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "cros_gralloc/gralloc4/CrosGralloc4Mapper.h"
+
+#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <aidl/android/hardware/graphics/common/Rect.h>
+#include <cutils/native_handle.h>
+#include <gralloctypes/Gralloc4.h>
+
+#include "cros_gralloc/gralloc4/CrosGralloc4Utils.h"
+#include "helpers.h"
+
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::Rect;
+using android::hardware::hidl_handle;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::graphics::common::V1_2::BufferUsage;
+using android::hardware::graphics::common::V1_2::PixelFormat;
+using android::hardware::graphics::mapper::V4_0::Error;
+using android::hardware::graphics::mapper::V4_0::IMapper;
+
+CrosGralloc4Mapper::CrosGralloc4Mapper() : mDriver(std::make_unique<cros_gralloc_driver>()) {
+    if (mDriver->init()) {
+        drv_log("Failed to initialize driver.\n");
+        mDriver = nullptr;
+    }
+}
+
+Return<void> CrosGralloc4Mapper::createDescriptor(const BufferDescriptorInfo& description,
+                                                  createDescriptor_cb hidlCb) {
+    hidl_vec<uint8_t> descriptor;
+
+    if (description.width == 0) {
+        drv_log("Failed to createDescriptor. Bad width: %d.\n", description.width);
+        hidlCb(Error::BAD_VALUE, descriptor);
+        return Void();
+    }
+
+    if (description.height == 0) {
+        drv_log("Failed to createDescriptor. Bad height: %d.\n", description.height);
+        hidlCb(Error::BAD_VALUE, descriptor);
+        return Void();
+    }
+
+    if (description.layerCount == 0) {
+        drv_log("Failed to createDescriptor. Bad layer count: %d.\n", description.layerCount);
+        hidlCb(Error::BAD_VALUE, descriptor);
+        return Void();
+    }
+
+    int ret = android::gralloc4::encodeBufferDescriptorInfo(description, &descriptor);
+    if (ret) {
+        drv_log("Failed to createDescriptor. Failed to encode: %d.\n", ret);
+        hidlCb(Error::BAD_VALUE, descriptor);
+        return Void();
+    }
+
+    hidlCb(Error::NONE, descriptor);
+    return Void();
+}
+
+Return<void> CrosGralloc4Mapper::importBuffer(const hidl_handle& handle, importBuffer_cb hidlCb) {
+    if (!mDriver) {
+        drv_log("Failed to import buffer. Driver is uninitialized.\n");
+        hidlCb(Error::NO_RESOURCES, nullptr);
+        return Void();
+    }
+
+    const native_handle_t* bufferHandle = handle.getNativeHandle();
+    if (!bufferHandle || bufferHandle->numFds == 0) {
+        drv_log("Failed to importBuffer. Bad handle.\n");
+        hidlCb(Error::BAD_BUFFER, nullptr);
+        return Void();
+    }
+
+    native_handle_t* importedBufferHandle = native_handle_clone(bufferHandle);
+    if (!importedBufferHandle) {
+        drv_log("Failed to importBuffer. Handle clone failed.\n");
+        hidlCb(Error::NO_RESOURCES, nullptr);
+        return Void();
+    }
+
+    int ret = mDriver->retain(importedBufferHandle);
+    if (ret) {
+        native_handle_close(importedBufferHandle);
+        native_handle_delete(importedBufferHandle);
+        hidlCb(Error::NO_RESOURCES, nullptr);
+        return Void();
+    }
+
+    hidlCb(Error::NONE, importedBufferHandle);
+    return Void();
+}
+
+Return<Error> CrosGralloc4Mapper::freeBuffer(void* rawHandle) {
+    if (!mDriver) {
+        drv_log("Failed to freeBuffer. Driver is uninitialized.\n");
+        return Error::NO_RESOURCES;
+    }
+
+    native_handle_t* bufferHandle = reinterpret_cast<native_handle_t*>(rawHandle);
+    if (!bufferHandle) {
+        drv_log("Failed to freeBuffer. Empty handle.\n");
+        return Error::BAD_BUFFER;
+    }
+
+    int ret = mDriver->release(bufferHandle);
+    if (ret) {
+        return Error::BAD_BUFFER;
+    }
+
+    native_handle_close(bufferHandle);
+    native_handle_delete(bufferHandle);
+    return Error::NONE;
+}
+
+Return<Error> CrosGralloc4Mapper::validateBufferSize(void* rawHandle,
+                                                     const BufferDescriptorInfo& descriptor,
+                                                     uint32_t stride) {
+    if (!mDriver) {
+        drv_log("Failed to validateBufferSize. Driver is uninitialized.\n");
+        return Error::NO_RESOURCES;
+    }
+
+    native_handle_t* bufferHandle = reinterpret_cast<native_handle_t*>(rawHandle);
+    if (!bufferHandle) {
+        drv_log("Failed to validateBufferSize. Empty handle.\n");
+        return Error::BAD_BUFFER;
+    }
+
+    cros_gralloc_handle_t crosHandle = cros_gralloc_convert_handle(bufferHandle);
+    if (!crosHandle) {
+        drv_log("Failed to validateBufferSize. Invalid handle.\n");
+        return Error::BAD_BUFFER;
+    }
+
+    PixelFormat crosHandleFormat = static_cast<PixelFormat>(crosHandle->droid_format);
+    if (descriptor.format != crosHandleFormat) {
+        drv_log("Failed to validateBufferSize. Format mismatch.\n");
+        return Error::BAD_BUFFER;
+    }
+
+    if (descriptor.width != crosHandle->width) {
+        drv_log("Failed to validateBufferSize. Width mismatch (%d vs %d).\n", descriptor.width,
+                crosHandle->width);
+        return Error::BAD_VALUE;
+    }
+
+    if (descriptor.height != crosHandle->height) {
+        drv_log("Failed to validateBufferSize. Height mismatch (%d vs %d).\n", descriptor.height,
+                crosHandle->height);
+        return Error::BAD_VALUE;
+    }
+
+    if (stride != crosHandle->pixel_stride) {
+        drv_log("Failed to validateBufferSize. Stride mismatch (%d vs %d).\n", stride,
+                crosHandle->pixel_stride);
+        return Error::BAD_VALUE;
+    }
+
+    return Error::NONE;
+}
+
+Return<void> CrosGralloc4Mapper::getTransportSize(void* rawHandle, getTransportSize_cb hidlCb) {
+    if (!mDriver) {
+        drv_log("Failed to getTransportSize. Driver is uninitialized.\n");
+        hidlCb(Error::BAD_BUFFER, 0, 0);
+        return Void();
+    }
+
+    native_handle_t* bufferHandle = reinterpret_cast<native_handle_t*>(rawHandle);
+    if (!bufferHandle) {
+        drv_log("Failed to getTransportSize. Bad handle.\n");
+        hidlCb(Error::BAD_BUFFER, 0, 0);
+        return Void();
+    }
+
+    // No local process data is currently stored on the native handle.
+    hidlCb(Error::NONE, bufferHandle->numFds, bufferHandle->numInts);
+    return Void();
+}
+
+Return<void> CrosGralloc4Mapper::lock(void* rawBuffer, uint64_t cpuUsage, const Rect& region,
+                                      const hidl_handle& acquireFence, lock_cb hidlCb) {
+    if (!mDriver) {
+        drv_log("Failed to lock. Driver is uninitialized.\n");
+        hidlCb(Error::NO_RESOURCES, nullptr);
+        return Void();
+    }
+
+    buffer_handle_t bufferHandle = reinterpret_cast<buffer_handle_t>(rawBuffer);
+    if (!bufferHandle) {
+        drv_log("Failed to lock. Empty handle.\n");
+        hidlCb(Error::BAD_BUFFER, nullptr);
+        return Void();
+    }
+
+    if (cpuUsage == 0) {
+        drv_log("Failed to lock. Bad cpu usage: %" PRIu64 ".\n", cpuUsage);
+        hidlCb(Error::BAD_VALUE, nullptr);
+        return Void();
+    }
+
+    uint32_t mapUsage = 0;
+    int ret = convertToMapUsage(cpuUsage, &mapUsage);
+    if (ret) {
+        drv_log("Failed to lock. Convert usage failed.\n");
+        hidlCb(Error::BAD_VALUE, nullptr);
+        return Void();
+    }
+
+    cros_gralloc_handle_t crosHandle = cros_gralloc_convert_handle(bufferHandle);
+    if (crosHandle == nullptr) {
+        drv_log("Failed to lock. Invalid handle.\n");
+        hidlCb(Error::BAD_VALUE, nullptr);
+        return Void();
+    }
+
+    if (region.left < 0) {
+        drv_log("Failed to lock. Invalid region: negative left value %d.\n", region.left);
+        hidlCb(Error::BAD_VALUE, nullptr);
+        return Void();
+    }
+
+    if (region.top < 0) {
+        drv_log("Failed to lock. Invalid region: negative top value %d.\n", region.top);
+        hidlCb(Error::BAD_VALUE, nullptr);
+        return Void();
+    }
+
+    if (region.width < 0) {
+        drv_log("Failed to lock. Invalid region: negative width value %d.\n", region.width);
+        hidlCb(Error::BAD_VALUE, nullptr);
+        return Void();
+    }
+
+    if (region.height < 0) {
+        drv_log("Failed to lock. Invalid region: negative height value %d.\n", region.height);
+        hidlCb(Error::BAD_VALUE, nullptr);
+        return Void();
+    }
+
+    if (region.width > crosHandle->width) {
+        drv_log("Failed to lock. Invalid region: width greater than buffer width (%d vs %d).\n",
+                region.width, crosHandle->width);
+        hidlCb(Error::BAD_VALUE, nullptr);
+        return Void();
+    }
+
+    if (region.height > crosHandle->height) {
+        drv_log("Failed to lock. Invalid region: height greater than buffer height (%d vs %d).\n",
+                region.height, crosHandle->height);
+        hidlCb(Error::BAD_VALUE, nullptr);
+        return Void();
+    }
+
+    struct rectangle rect = {static_cast<uint32_t>(region.left), static_cast<uint32_t>(region.top),
+                             static_cast<uint32_t>(region.width),
+                             static_cast<uint32_t>(region.height)};
+
+    // An access region of all zeros means the entire buffer.
+    if (rect.x == 0 && rect.y == 0 && rect.width == 0 && rect.height == 0) {
+        rect.width = crosHandle->width;
+        rect.height = crosHandle->height;
+    }
+
+    int acquireFenceFd = -1;
+    ret = convertToFenceFd(acquireFence, &acquireFenceFd);
+    if (ret) {
+        drv_log("Failed to lock. Bad acquire fence.\n");
+        hidlCb(Error::BAD_VALUE, nullptr);
+        return Void();
+    }
+
+    uint8_t* addr[DRV_MAX_PLANES];
+    ret = mDriver->lock(bufferHandle, acquireFenceFd, /*close_acquire_fence=*/false, &rect,
+                        mapUsage, addr);
+    if (ret) {
+        hidlCb(Error::BAD_VALUE, nullptr);
+        return Void();
+    }
+
+    hidlCb(Error::NONE, addr[0]);
+    return Void();
+}
+
+Return<void> CrosGralloc4Mapper::unlock(void* rawHandle, unlock_cb hidlCb) {
+    if (!mDriver) {
+        drv_log("Failed to unlock. Driver is uninitialized.\n");
+        hidlCb(Error::BAD_BUFFER, nullptr);
+        return Void();
+    }
+
+    buffer_handle_t bufferHandle = reinterpret_cast<buffer_handle_t>(rawHandle);
+    if (!bufferHandle) {
+        drv_log("Failed to unlock. Empty handle.\n");
+        hidlCb(Error::BAD_BUFFER, nullptr);
+        return Void();
+    }
+
+    int releaseFenceFd = -1;
+    int ret = mDriver->unlock(bufferHandle, &releaseFenceFd);
+    if (ret) {
+        drv_log("Failed to unlock.\n");
+        hidlCb(Error::BAD_BUFFER, nullptr);
+        return Void();
+    }
+
+    hidl_handle releaseFenceHandle;
+    ret = convertToFenceHandle(releaseFenceFd, &releaseFenceHandle);
+    if (ret) {
+        drv_log("Failed to unlock. Failed to convert release fence to handle.\n");
+        hidlCb(Error::BAD_BUFFER, nullptr);
+        return Void();
+    }
+
+    hidlCb(Error::NONE, releaseFenceHandle);
+    return Void();
+}
+
+Return<void> CrosGralloc4Mapper::flushLockedBuffer(void* rawHandle, flushLockedBuffer_cb hidlCb) {
+    if (!mDriver) {
+        drv_log("Failed to flushLockedBuffer. Driver is uninitialized.\n");
+        hidlCb(Error::NO_RESOURCES, nullptr);
+        return Void();
+    }
+
+    buffer_handle_t bufferHandle = reinterpret_cast<buffer_handle_t>(rawHandle);
+    if (!bufferHandle) {
+        drv_log("Failed to flushLockedBuffer. Empty handle.\n");
+        hidlCb(Error::BAD_BUFFER, nullptr);
+        return Void();
+    }
+
+    int releaseFenceFd = -1;
+    int ret = mDriver->flush(bufferHandle, &releaseFenceFd);
+    if (ret) {
+        drv_log("Failed to flushLockedBuffer. Flush failed.\n");
+        hidlCb(Error::BAD_BUFFER, nullptr);
+        return Void();
+    }
+
+    hidl_handle releaseFenceHandle;
+    ret = convertToFenceHandle(releaseFenceFd, &releaseFenceHandle);
+    if (ret) {
+        drv_log("Failed to flushLockedBuffer. Failed to convert release fence to handle.\n");
+        hidlCb(Error::BAD_BUFFER, nullptr);
+        return Void();
+    }
+
+    hidlCb(Error::NONE, releaseFenceHandle);
+    return Void();
+}
+
+Return<Error> CrosGralloc4Mapper::rereadLockedBuffer(void* rawHandle) {
+    if (!mDriver) {
+        drv_log("Failed to rereadLockedBuffer. Driver is uninitialized.\n");
+        return Error::NO_RESOURCES;
+    }
+
+    buffer_handle_t bufferHandle = reinterpret_cast<buffer_handle_t>(rawHandle);
+    if (!bufferHandle) {
+        drv_log("Failed to rereadLockedBuffer. Empty handle.\n");
+        return Error::BAD_BUFFER;
+    }
+
+    int ret = mDriver->invalidate(bufferHandle);
+    if (ret) {
+        drv_log("Failed to rereadLockedBuffer. Failed to invalidate.\n");
+        return Error::BAD_BUFFER;
+    }
+
+    return Error::NONE;
+}
+
+Return<void> CrosGralloc4Mapper::isSupported(const BufferDescriptorInfo& descriptor,
+                                             isSupported_cb hidlCb) {
+    if (!mDriver) {
+        drv_log("Failed to isSupported. Driver is uninitialized.\n");
+        hidlCb(Error::BAD_VALUE, false);
+        return Void();
+    }
+
+    struct cros_gralloc_buffer_descriptor crosDescriptor;
+    if (convertToCrosDescriptor(descriptor, &crosDescriptor)) {
+        hidlCb(Error::NONE, false);
+        return Void();
+    }
+
+    bool supported = mDriver->is_supported(&crosDescriptor);
+    if (!supported) {
+        crosDescriptor.use_flags &= ~BO_USE_SCANOUT;
+        supported = mDriver->is_supported(&crosDescriptor);
+    }
+
+    hidlCb(Error::NONE, supported);
+    return Void();
+}
+
+Return<void> CrosGralloc4Mapper::get(void* rawHandle, const MetadataType& metadataType,
+                                     get_cb hidlCb) {
+    hidl_vec<uint8_t> encodedMetadata;
+
+    if (!mDriver) {
+        drv_log("Failed to get. Driver is uninitialized.\n");
+        hidlCb(Error::NO_RESOURCES, encodedMetadata);
+        return Void();
+    }
+
+    buffer_handle_t bufferHandle = reinterpret_cast<buffer_handle_t>(rawHandle);
+    if (!bufferHandle) {
+        drv_log("Failed to get. Empty handle.\n");
+        hidlCb(Error::BAD_BUFFER, encodedMetadata);
+        return Void();
+    }
+
+    cros_gralloc_handle_t crosHandle = cros_gralloc_convert_handle(bufferHandle);
+    if (!crosHandle) {
+        drv_log("Failed to get. Invalid handle.\n");
+        hidlCb(Error::BAD_BUFFER, encodedMetadata);
+        return Void();
+    }
+
+    get(crosHandle, metadataType, hidlCb);
+    return Void();
+}
+
+Return<void> CrosGralloc4Mapper::get(cros_gralloc_handle_t crosHandle,
+                                     const MetadataType& metadataType, get_cb hidlCb) {
+    hidl_vec<uint8_t> encodedMetadata;
+
+    if (!mDriver) {
+        drv_log("Failed to get. Driver is uninitialized.\n");
+        hidlCb(Error::NO_RESOURCES, encodedMetadata);
+        return Void();
+    }
+
+    if (!crosHandle) {
+        drv_log("Failed to get. Invalid handle.\n");
+        hidlCb(Error::BAD_BUFFER, encodedMetadata);
+        return Void();
+    }
+
+    android::status_t status = android::NO_ERROR;
+    if (metadataType == android::gralloc4::MetadataType_BufferId) {
+        status = android::gralloc4::encodeBufferId(crosHandle->id, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Name) {
+        const char* name = (const char*)(&crosHandle->base.data[crosHandle->name_offset]);
+        status = android::gralloc4::encodeName(name, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Width) {
+        status = android::gralloc4::encodeWidth(crosHandle->width, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Height) {
+        status = android::gralloc4::encodeHeight(crosHandle->height, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_LayerCount) {
+        status = android::gralloc4::encodeLayerCount(1, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_PixelFormatRequested) {
+        PixelFormat pixelFormat = static_cast<PixelFormat>(crosHandle->droid_format);
+        status = android::gralloc4::encodePixelFormatRequested(pixelFormat, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_PixelFormatFourCC) {
+        status = android::gralloc4::encodePixelFormatFourCC(crosHandle->format, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_PixelFormatModifier) {
+        status = android::gralloc4::encodePixelFormatModifier(crosHandle->format_modifier,
+                                                              &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Usage) {
+        uint64_t usage = static_cast<uint64_t>(crosHandle->usage);
+        status = android::gralloc4::encodeUsage(usage, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_AllocationSize) {
+        status = android::gralloc4::encodeAllocationSize(crosHandle->total_size, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_ProtectedContent) {
+        uint64_t hasProtectedContent = crosHandle->usage & BufferUsage::PROTECTED ? 1 : 0;
+        status = android::gralloc4::encodeProtectedContent(hasProtectedContent, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Compression) {
+        status = android::gralloc4::encodeCompression(android::gralloc4::Compression_None,
+                                                      &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Interlaced) {
+        status = android::gralloc4::encodeInterlaced(android::gralloc4::Interlaced_None,
+                                                     &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_ChromaSiting) {
+        status = android::gralloc4::encodeChromaSiting(android::gralloc4::ChromaSiting_None,
+                                                       &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_PlaneLayouts) {
+        std::vector<PlaneLayout> planeLayouts;
+        getPlaneLayouts(crosHandle->format, &planeLayouts);
+
+        for (size_t plane = 0; plane < planeLayouts.size(); plane++) {
+            PlaneLayout& planeLayout = planeLayouts[plane];
+            planeLayout.offsetInBytes = crosHandle->offsets[plane];
+            planeLayout.strideInBytes = crosHandle->strides[plane];
+            planeLayout.totalSizeInBytes = crosHandle->sizes[plane];
+            planeLayout.widthInSamples = crosHandle->width;
+            planeLayout.heightInSamples = crosHandle->height;
+        }
+
+        status = android::gralloc4::encodePlaneLayouts(planeLayouts, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Crop) {
+        std::vector<aidl::android::hardware::graphics::common::Rect> crops;
+        for (size_t plane = 0; plane < crosHandle->num_planes; plane++) {
+            aidl::android::hardware::graphics::common::Rect crop;
+            crop.left = 0;
+            crop.top = 0;
+            crop.right = crosHandle->width;
+            crop.bottom = crosHandle->height;
+            crops.push_back(crop);
+        }
+
+        status = android::gralloc4::encodeCrop(crops, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Dataspace) {
+        status = android::gralloc4::encodeDataspace(Dataspace::UNKNOWN, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_BlendMode) {
+        status = android::gralloc4::encodeBlendMode(BlendMode::INVALID, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Smpte2086) {
+        status = android::gralloc4::encodeSmpte2086(std::nullopt, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Cta861_3) {
+        status = android::gralloc4::encodeCta861_3(std::nullopt, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Smpte2094_40) {
+        status = android::gralloc4::encodeSmpte2094_40(std::nullopt, &encodedMetadata);
+    } else {
+        hidlCb(Error::UNSUPPORTED, encodedMetadata);
+        return Void();
+    }
+
+    if (status != android::NO_ERROR) {
+        hidlCb(Error::NO_RESOURCES, encodedMetadata);
+        drv_log("Failed to get. Failed to encode metadata.\n");
+        return Void();
+    }
+
+    hidlCb(Error::NONE, encodedMetadata);
+    return Void();
+}
+
+Return<Error> CrosGralloc4Mapper::set(void* rawHandle, const MetadataType& metadataType,
+                                      const hidl_vec<uint8_t>& /*metadata*/) {
+    if (!mDriver) {
+        drv_log("Failed to set. Driver is uninitialized.\n");
+        return Error::NO_RESOURCES;
+    }
+
+    buffer_handle_t bufferHandle = reinterpret_cast<buffer_handle_t>(rawHandle);
+    if (!bufferHandle) {
+        drv_log("Failed to set. Empty handle.\n");
+        return Error::BAD_BUFFER;
+    }
+
+    cros_gralloc_handle_t crosHandle = cros_gralloc_convert_handle(bufferHandle);
+    if (!crosHandle) {
+        drv_log("Failed to set. Invalid handle.\n");
+        return Error::BAD_BUFFER;
+    }
+
+    if (metadataType == android::gralloc4::MetadataType_BufferId) {
+        return Error::BAD_VALUE;
+    } else if (metadataType == android::gralloc4::MetadataType_Name) {
+        return Error::BAD_VALUE;
+    } else if (metadataType == android::gralloc4::MetadataType_Width) {
+        return Error::BAD_VALUE;
+    } else if (metadataType == android::gralloc4::MetadataType_Height) {
+        return Error::BAD_VALUE;
+    } else if (metadataType == android::gralloc4::MetadataType_LayerCount) {
+        return Error::BAD_VALUE;
+    } else if (metadataType == android::gralloc4::MetadataType_PixelFormatRequested) {
+        return Error::BAD_VALUE;
+    } else if (metadataType == android::gralloc4::MetadataType_Usage) {
+        return Error::BAD_VALUE;
+    }
+
+    return Error::UNSUPPORTED;
+}
+
+int CrosGralloc4Mapper::getResolvedDrmFormat(PixelFormat pixelFormat, uint64_t bufferUsage,
+                                             uint32_t* outDrmFormat) {
+    uint32_t drmFormat;
+    if (convertToDrmFormat(pixelFormat, &drmFormat)) {
+        std::string pixelFormatString = getPixelFormatString(pixelFormat);
+        drv_log("Failed to getResolvedDrmFormat. Failed to convert format %s\n",
+                pixelFormatString.c_str());
+        return -1;
+    }
+
+    uint64_t usage;
+    if (convertToBufferUsage(bufferUsage, &usage)) {
+        std::string usageString = getUsageString(bufferUsage);
+        drv_log("Failed to getResolvedDrmFormat. Failed to convert usage %s\n",
+                usageString.c_str());
+        return -1;
+    }
+
+    uint32_t resolvedDrmFormat = mDriver->get_resolved_drm_format(drmFormat, usage);
+    if (resolvedDrmFormat == DRM_FORMAT_INVALID) {
+        std::string drmFormatString = getDrmFormatString(drmFormat);
+        drv_log("Failed to getResolvedDrmFormat. Failed to resolve drm format %s\n",
+                drmFormatString.c_str());
+        return -1;
+    }
+
+    *outDrmFormat = resolvedDrmFormat;
+
+    return 0;
+}
+
+Return<void> CrosGralloc4Mapper::getFromBufferDescriptorInfo(
+        const BufferDescriptorInfo& descriptor, const MetadataType& metadataType,
+        getFromBufferDescriptorInfo_cb hidlCb) {
+    hidl_vec<uint8_t> encodedMetadata;
+
+    if (!mDriver) {
+        drv_log("Failed to getFromBufferDescriptorInfo. Driver is uninitialized.\n");
+        hidlCb(Error::NO_RESOURCES, encodedMetadata);
+        return Void();
+    }
+
+    android::status_t status = android::NO_ERROR;
+    if (metadataType == android::gralloc4::MetadataType_Name) {
+        status = android::gralloc4::encodeName(descriptor.name, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Width) {
+        status = android::gralloc4::encodeWidth(descriptor.width, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Height) {
+        status = android::gralloc4::encodeHeight(descriptor.height, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_LayerCount) {
+        status = android::gralloc4::encodeLayerCount(1, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_PixelFormatRequested) {
+        status = android::gralloc4::encodePixelFormatRequested(descriptor.format, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_PixelFormatFourCC) {
+        uint32_t drmFormat;
+        if (getResolvedDrmFormat(descriptor.format, descriptor.usage, &drmFormat)) {
+            hidlCb(Error::BAD_VALUE, encodedMetadata);
+            return Void();
+        }
+        status = android::gralloc4::encodePixelFormatFourCC(drmFormat, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Usage) {
+        status = android::gralloc4::encodeUsage(descriptor.usage, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_ProtectedContent) {
+        uint64_t hasProtectedContent = descriptor.usage & BufferUsage::PROTECTED ? 1 : 0;
+        status = android::gralloc4::encodeProtectedContent(hasProtectedContent, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Compression) {
+        status = android::gralloc4::encodeCompression(android::gralloc4::Compression_None,
+                                                      &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Interlaced) {
+        status = android::gralloc4::encodeInterlaced(android::gralloc4::Interlaced_None,
+                                                     &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_ChromaSiting) {
+        status = android::gralloc4::encodeChromaSiting(android::gralloc4::ChromaSiting_None,
+                                                       &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Crop) {
+        uint32_t drmFormat;
+        if (getResolvedDrmFormat(descriptor.format, descriptor.usage, &drmFormat)) {
+            hidlCb(Error::BAD_VALUE, encodedMetadata);
+            return Void();
+        }
+
+        size_t numPlanes = drv_num_planes_from_format(drmFormat);
+
+        std::vector<aidl::android::hardware::graphics::common::Rect> crops;
+        for (size_t plane = 0; plane < numPlanes; plane++) {
+            aidl::android::hardware::graphics::common::Rect crop;
+            crop.left = 0;
+            crop.top = 0;
+            crop.right = descriptor.width;
+            crop.bottom = descriptor.height;
+            crops.push_back(crop);
+        }
+        status = android::gralloc4::encodeCrop(crops, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Dataspace) {
+        status = android::gralloc4::encodeDataspace(Dataspace::UNKNOWN, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_BlendMode) {
+        status = android::gralloc4::encodeBlendMode(BlendMode::INVALID, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Smpte2086) {
+        status = android::gralloc4::encodeSmpte2086(std::nullopt, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Cta861_3) {
+        status = android::gralloc4::encodeCta861_3(std::nullopt, &encodedMetadata);
+    } else if (metadataType == android::gralloc4::MetadataType_Smpte2094_40) {
+        status = android::gralloc4::encodeSmpte2094_40(std::nullopt, &encodedMetadata);
+    } else {
+        hidlCb(Error::UNSUPPORTED, encodedMetadata);
+        return Void();
+    }
+
+    if (status != android::NO_ERROR) {
+        hidlCb(Error::NO_RESOURCES, encodedMetadata);
+        return Void();
+    }
+
+    hidlCb(Error::NONE, encodedMetadata);
+    return Void();
+}
+
+Return<void> CrosGralloc4Mapper::listSupportedMetadataTypes(listSupportedMetadataTypes_cb hidlCb) {
+    hidl_vec<MetadataTypeDescription> supported;
+
+    if (!mDriver) {
+        drv_log("Failed to listSupportedMetadataTypes. Driver is uninitialized.\n");
+        hidlCb(Error::NO_RESOURCES, supported);
+        return Void();
+    }
+
+    supported = hidl_vec<IMapper::MetadataTypeDescription>({
+            {
+                    android::gralloc4::MetadataType_BufferId,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_Name,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_Width,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_Height,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_LayerCount,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_PixelFormatRequested,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_PixelFormatFourCC,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_PixelFormatModifier,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_Usage,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_AllocationSize,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_ProtectedContent,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_Compression,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_Interlaced,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_ChromaSiting,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_PlaneLayouts,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_Dataspace,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_BlendMode,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_Smpte2086,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_Cta861_3,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+            {
+                    android::gralloc4::MetadataType_Smpte2094_40,
+                    "",
+                    /*isGettable=*/true,
+                    /*isSettable=*/false,
+            },
+    });
+
+    hidlCb(Error::NONE, supported);
+    return Void();
+}
+
+Return<void> CrosGralloc4Mapper::dumpBuffer(void* rawHandle, dumpBuffer_cb hidlCb) {
+    BufferDump bufferDump;
+
+    if (!mDriver) {
+        drv_log("Failed to dumpBuffer. Driver is uninitialized.\n");
+        hidlCb(Error::NO_RESOURCES, bufferDump);
+        return Void();
+    }
+
+    buffer_handle_t bufferHandle = reinterpret_cast<buffer_handle_t>(rawHandle);
+    if (!bufferHandle) {
+        drv_log("Failed to dumpBuffer. Empty handle.\n");
+        hidlCb(Error::BAD_BUFFER, bufferDump);
+        return Void();
+    }
+
+    cros_gralloc_handle_t crosHandle = cros_gralloc_convert_handle(bufferHandle);
+    if (!crosHandle) {
+        drv_log("Failed to dumpBuffer. Invalid handle.\n");
+        hidlCb(Error::BAD_BUFFER, bufferDump);
+        return Void();
+    }
+
+    return dumpBuffer(crosHandle, hidlCb);
+}
+
+Return<void> CrosGralloc4Mapper::dumpBuffer(cros_gralloc_handle_t crosHandle,
+                                            dumpBuffer_cb hidlCb) {
+    BufferDump bufferDump;
+
+    if (!mDriver) {
+        drv_log("Failed to dumpBuffer. Driver is uninitialized.\n");
+        hidlCb(Error::NO_RESOURCES, bufferDump);
+        return Void();
+    }
+
+    if (!crosHandle) {
+        drv_log("Failed to dumpBuffer. Invalid handle.\n");
+        hidlCb(Error::BAD_BUFFER, bufferDump);
+        return Void();
+    }
+
+    std::vector<MetadataDump> metadataDumps;
+
+    MetadataType metadataType = android::gralloc4::MetadataType_BufferId;
+    auto metadata_get_callback = [&](Error, hidl_vec<uint8_t> metadata) {
+        MetadataDump metadataDump;
+        metadataDump.metadataType = metadataType;
+        metadataDump.metadata = metadata;
+        metadataDumps.push_back(metadataDump);
+    };
+
+    metadataType = android::gralloc4::MetadataType_BufferId;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_Name;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_Width;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_Height;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_LayerCount;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_PixelFormatRequested;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_PixelFormatFourCC;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_PixelFormatModifier;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_Usage;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_AllocationSize;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_ProtectedContent;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_Compression;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_Interlaced;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_ChromaSiting;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_PlaneLayouts;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_Dataspace;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    metadataType = android::gralloc4::MetadataType_BlendMode;
+    get(crosHandle, metadataType, metadata_get_callback);
+
+    bufferDump.metadataDump = metadataDumps;
+    hidlCb(Error::NONE, bufferDump);
+    return Void();
+}
+
+Return<void> CrosGralloc4Mapper::dumpBuffers(dumpBuffers_cb hidlCb) {
+    std::vector<BufferDump> bufferDumps;
+
+    if (!mDriver) {
+        drv_log("Failed to dumpBuffers. Driver is uninitialized.\n");
+        hidlCb(Error::NO_RESOURCES, bufferDumps);
+        return Void();
+    }
+
+    Error error = Error::NONE;
+
+    auto handleCallback = [&](cros_gralloc_handle_t crosHandle) {
+        auto dumpBufferCallback = [&](Error err, BufferDump bufferDump) {
+            error = err;
+            if (error == Error::NONE) {
+                bufferDumps.push_back(bufferDump);
+            }
+        };
+
+        dumpBuffer(crosHandle, dumpBufferCallback);
+    };
+    mDriver->for_each_handle(handleCallback);
+
+    hidlCb(error, bufferDumps);
+    return Void();
+}
+
+Return<void> CrosGralloc4Mapper::getReservedRegion(void* rawHandle, getReservedRegion_cb hidlCb) {
+    if (!mDriver) {
+        drv_log("Failed to getReservedRegion. Driver is uninitialized.\n");
+        hidlCb(Error::NO_RESOURCES, nullptr, 0);
+        return Void();
+    }
+
+    buffer_handle_t bufferHandle = reinterpret_cast<buffer_handle_t>(rawHandle);
+    if (!bufferHandle) {
+        drv_log("Failed to getReservedRegion. Empty handle.\n");
+        hidlCb(Error::BAD_BUFFER, nullptr, 0);
+        return Void();
+    }
+
+    cros_gralloc_handle_t crosHandle = cros_gralloc_convert_handle(bufferHandle);
+    if (!crosHandle) {
+        drv_log("Failed to getReservedRegion. Invalid handle.\n");
+        hidlCb(Error::BAD_BUFFER, nullptr, 0);
+        return Void();
+    }
+
+    void* reservedRegionAddr = nullptr;
+    uint64_t reservedRegionSize = 0;
+    int ret = mDriver->get_reserved_region(bufferHandle, &reservedRegionAddr, &reservedRegionSize);
+    if (ret) {
+        drv_log("Failed to getReservedRegion.\n");
+        hidlCb(Error::BAD_BUFFER, nullptr, 0);
+        return Void();
+    }
+
+    hidlCb(Error::NONE, reservedRegionAddr, reservedRegionSize);
+    return Void();
+}
+
+android::hardware::graphics::mapper::V4_0::IMapper* HIDL_FETCH_IMapper(const char* /*name*/) {
+    return static_cast<android::hardware::graphics::mapper::V4_0::IMapper*>(new CrosGralloc4Mapper);
+}
diff --git a/cros_gralloc/gralloc4/CrosGralloc4Mapper.h b/cros_gralloc/gralloc4/CrosGralloc4Mapper.h
new file mode 100644
index 0000000..b318930
--- /dev/null
+++ b/cros_gralloc/gralloc4/CrosGralloc4Mapper.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+
+#include "cros_gralloc/cros_gralloc_driver.h"
+#include "cros_gralloc/cros_gralloc_handle.h"
+
+class CrosGralloc4Mapper : public android::hardware::graphics::mapper::V4_0::IMapper {
+  public:
+    CrosGralloc4Mapper();
+
+    android::hardware::Return<void> createDescriptor(const BufferDescriptorInfo& description,
+                                                     createDescriptor_cb hidlCb) override;
+
+    android::hardware::Return<void> importBuffer(const android::hardware::hidl_handle& rawHandle,
+                                                 importBuffer_cb hidlCb) override;
+
+    android::hardware::Return<android::hardware::graphics::mapper::V4_0::Error> freeBuffer(
+            void* rawHandle) override;
+
+    android::hardware::Return<android::hardware::graphics::mapper::V4_0::Error> validateBufferSize(
+            void* rawHandle, const BufferDescriptorInfo& descriptor, uint32_t stride) override;
+
+    android::hardware::Return<void> getTransportSize(void* rawHandle,
+                                                     getTransportSize_cb hidlCb) override;
+
+    android::hardware::Return<void> lock(void* rawHandle, uint64_t cpuUsage,
+                                         const Rect& accessRegion,
+                                         const android::hardware::hidl_handle& acquireFence,
+                                         lock_cb hidlCb) override;
+
+    android::hardware::Return<void> unlock(void* rawHandle, unlock_cb hidlCb) override;
+
+    android::hardware::Return<void> flushLockedBuffer(void* rawHandle,
+                                                      flushLockedBuffer_cb hidlCb) override;
+
+    android::hardware::Return<android::hardware::graphics::mapper::V4_0::Error> rereadLockedBuffer(
+            void* rawHandle) override;
+
+    android::hardware::Return<void> isSupported(const BufferDescriptorInfo& descriptor,
+                                                isSupported_cb hidlCb) override;
+
+    android::hardware::Return<void> get(void* rawHandle, const MetadataType& metadataType,
+                                        get_cb hidlCb) override;
+
+    android::hardware::Return<android::hardware::graphics::mapper::V4_0::Error> set(
+            void* rawHandle, const MetadataType& metadataType,
+            const android::hardware::hidl_vec<uint8_t>& metadata) override;
+
+    android::hardware::Return<void> getFromBufferDescriptorInfo(
+            const BufferDescriptorInfo& descriptor, const MetadataType& metadataType,
+            getFromBufferDescriptorInfo_cb hidlCb) override;
+
+    android::hardware::Return<void> listSupportedMetadataTypes(
+            listSupportedMetadataTypes_cb hidlCb) override;
+
+    android::hardware::Return<void> dumpBuffer(void* rawHandle, dumpBuffer_cb hidlCb) override;
+    android::hardware::Return<void> dumpBuffers(dumpBuffers_cb hidlCb) override;
+
+    android::hardware::Return<void> getReservedRegion(void* rawHandle,
+                                                      getReservedRegion_cb hidlCb) override;
+
+  private:
+    android::hardware::Return<void> get(cros_gralloc_handle_t crosHandle,
+                                        const MetadataType& metadataType, get_cb hidlCb);
+
+    android::hardware::Return<void> dumpBuffer(cros_gralloc_handle_t crosHandle,
+                                               dumpBuffer_cb hidlCb);
+
+    int getResolvedDrmFormat(android::hardware::graphics::common::V1_2::PixelFormat pixelFormat,
+                             uint64_t bufferUsage, uint32_t* outDrmFormat);
+
+    std::unique_ptr<cros_gralloc_driver> mDriver;
+};
+
+extern "C" android::hardware::graphics::mapper::V4_0::IMapper* HIDL_FETCH_IMapper(const char* name);
diff --git a/cros_gralloc/gralloc4/CrosGralloc4Utils.cc b/cros_gralloc/gralloc4/CrosGralloc4Utils.cc
new file mode 100644
index 0000000..8931164
--- /dev/null
+++ b/cros_gralloc/gralloc4/CrosGralloc4Utils.cc
@@ -0,0 +1,772 @@
+/*
+ * Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "cros_gralloc/gralloc4/CrosGralloc4Utils.h"
+
+#include <array>
+#include <unordered_map>
+
+#include <aidl/android/hardware/graphics/common/PlaneLayoutComponent.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <cutils/native_handle.h>
+#include <gralloctypes/Gralloc4.h>
+
+#include "cros_gralloc/cros_gralloc_helpers.h"
+
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using android::hardware::hidl_bitfield;
+using android::hardware::hidl_handle;
+using android::hardware::graphics::common::V1_2::BufferUsage;
+using android::hardware::graphics::common::V1_2::PixelFormat;
+
+using BufferDescriptorInfo =
+        android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo;
+
+std::string getDrmFormatString(uint32_t drmFormat) {
+    switch (drmFormat) {
+        case DRM_FORMAT_ABGR1555:
+            return "DRM_FORMAT_ABGR1555";
+        case DRM_FORMAT_ABGR2101010:
+            return "DRM_FORMAT_ABGR2101010";
+        case DRM_FORMAT_ABGR4444:
+            return "DRM_FORMAT_ABGR4444";
+        case DRM_FORMAT_ABGR8888:
+            return "DRM_FORMAT_ABGR8888";
+        case DRM_FORMAT_ARGB1555:
+            return "DRM_FORMAT_ARGB1555";
+        case DRM_FORMAT_ARGB2101010:
+            return "DRM_FORMAT_ARGB2101010";
+        case DRM_FORMAT_ARGB4444:
+            return "DRM_FORMAT_ARGB4444";
+        case DRM_FORMAT_ARGB8888:
+            return "DRM_FORMAT_ARGB8888";
+        case DRM_FORMAT_AYUV:
+            return "DRM_FORMAT_AYUV";
+        case DRM_FORMAT_BGR233:
+            return "DRM_FORMAT_BGR233";
+        case DRM_FORMAT_BGR565:
+            return "DRM_FORMAT_BGR565";
+        case DRM_FORMAT_BGR888:
+            return "DRM_FORMAT_BGR888";
+        case DRM_FORMAT_BGRA1010102:
+            return "DRM_FORMAT_BGRA1010102";
+        case DRM_FORMAT_BGRA4444:
+            return "DRM_FORMAT_BGRA4444";
+        case DRM_FORMAT_BGRA5551:
+            return "DRM_FORMAT_BGRA5551";
+        case DRM_FORMAT_BGRA8888:
+            return "DRM_FORMAT_BGRA8888";
+        case DRM_FORMAT_BGRX1010102:
+            return "DRM_FORMAT_BGRX1010102";
+        case DRM_FORMAT_BGRX4444:
+            return "DRM_FORMAT_BGRX4444";
+        case DRM_FORMAT_BGRX5551:
+            return "DRM_FORMAT_BGRX5551";
+        case DRM_FORMAT_BGRX8888:
+            return "DRM_FORMAT_BGRX8888";
+        case DRM_FORMAT_C8:
+            return "DRM_FORMAT_C8";
+        case DRM_FORMAT_GR88:
+            return "DRM_FORMAT_GR88";
+        case DRM_FORMAT_NV12:
+            return "DRM_FORMAT_NV12";
+        case DRM_FORMAT_NV21:
+            return "DRM_FORMAT_NV21";
+        case DRM_FORMAT_R8:
+            return "DRM_FORMAT_R8";
+        case DRM_FORMAT_RG88:
+            return "DRM_FORMAT_RG88";
+        case DRM_FORMAT_RGB332:
+            return "DRM_FORMAT_RGB332";
+        case DRM_FORMAT_RGB565:
+            return "DRM_FORMAT_RGB565";
+        case DRM_FORMAT_RGB888:
+            return "DRM_FORMAT_RGB888";
+        case DRM_FORMAT_RGBA1010102:
+            return "DRM_FORMAT_RGBA1010102";
+        case DRM_FORMAT_RGBA4444:
+            return "DRM_FORMAT_RGBA4444";
+        case DRM_FORMAT_RGBA5551:
+            return "DRM_FORMAT_RGBA5551";
+        case DRM_FORMAT_RGBA8888:
+            return "DRM_FORMAT_RGBA8888";
+        case DRM_FORMAT_RGBX1010102:
+            return "DRM_FORMAT_RGBX1010102";
+        case DRM_FORMAT_RGBX4444:
+            return "DRM_FORMAT_RGBX4444";
+        case DRM_FORMAT_RGBX5551:
+            return "DRM_FORMAT_RGBX5551";
+        case DRM_FORMAT_RGBX8888:
+            return "DRM_FORMAT_RGBX8888";
+        case DRM_FORMAT_UYVY:
+            return "DRM_FORMAT_UYVY";
+        case DRM_FORMAT_VYUY:
+            return "DRM_FORMAT_VYUY";
+        case DRM_FORMAT_XBGR1555:
+            return "DRM_FORMAT_XBGR1555";
+        case DRM_FORMAT_XBGR2101010:
+            return "DRM_FORMAT_XBGR2101010";
+        case DRM_FORMAT_XBGR4444:
+            return "DRM_FORMAT_XBGR4444";
+        case DRM_FORMAT_XBGR8888:
+            return "DRM_FORMAT_XBGR8888";
+        case DRM_FORMAT_XRGB1555:
+            return "DRM_FORMAT_XRGB1555";
+        case DRM_FORMAT_XRGB2101010:
+            return "DRM_FORMAT_XRGB2101010";
+        case DRM_FORMAT_XRGB4444:
+            return "DRM_FORMAT_XRGB4444";
+        case DRM_FORMAT_XRGB8888:
+            return "DRM_FORMAT_XRGB8888";
+        case DRM_FORMAT_YUYV:
+            return "DRM_FORMAT_YUYV";
+        case DRM_FORMAT_YVU420:
+            return "DRM_FORMAT_YVU420";
+        case DRM_FORMAT_YVYU:
+            return "DRM_FORMAT_YVYU";
+    }
+    return android::base::StringPrintf("Unknown(%d)", drmFormat);
+}
+
+std::string getPixelFormatString(PixelFormat format) {
+    switch (format) {
+        case PixelFormat::BGRA_8888:
+            return "PixelFormat::BGRA_8888";
+        case PixelFormat::BLOB:
+            return "PixelFormat::BLOB";
+        case PixelFormat::DEPTH_16:
+            return "PixelFormat::DEPTH_16";
+        case PixelFormat::DEPTH_24:
+            return "PixelFormat::DEPTH_24";
+        case PixelFormat::DEPTH_24_STENCIL_8:
+            return "PixelFormat::DEPTH_24_STENCIL_8";
+        case PixelFormat::DEPTH_32F:
+            return "PixelFormat::DEPTH_24";
+        case PixelFormat::DEPTH_32F_STENCIL_8:
+            return "PixelFormat::DEPTH_24_STENCIL_8";
+        case PixelFormat::HSV_888:
+            return "PixelFormat::HSV_888";
+        case PixelFormat::IMPLEMENTATION_DEFINED:
+            return "PixelFormat::IMPLEMENTATION_DEFINED";
+        case PixelFormat::RAW10:
+            return "PixelFormat::RAW10";
+        case PixelFormat::RAW12:
+            return "PixelFormat::RAW12";
+        case PixelFormat::RAW16:
+            return "PixelFormat::RAW16";
+        case PixelFormat::RAW_OPAQUE:
+            return "PixelFormat::RAW_OPAQUE";
+        case PixelFormat::RGBA_1010102:
+            return "PixelFormat::RGBA_1010102";
+        case PixelFormat::RGBA_8888:
+            return "PixelFormat::RGBA_8888";
+        case PixelFormat::RGBA_FP16:
+            return "PixelFormat::RGBA_FP16";
+        case PixelFormat::RGBX_8888:
+            return "PixelFormat::RGBX_8888";
+        case PixelFormat::RGB_565:
+            return "PixelFormat::RGB_565";
+        case PixelFormat::RGB_888:
+            return "PixelFormat::RGB_888";
+        case PixelFormat::STENCIL_8:
+            return "PixelFormat::STENCIL_8";
+        case PixelFormat::Y16:
+            return "PixelFormat::Y16";
+        case PixelFormat::Y8:
+            return "PixelFormat::Y8";
+        case PixelFormat::YCBCR_420_888:
+            return "PixelFormat::YCBCR_420_888";
+        case PixelFormat::YCBCR_422_I:
+            return "PixelFormat::YCBCR_422_I";
+        case PixelFormat::YCBCR_422_SP:
+            return "PixelFormat::YCBCR_422_SP";
+        case PixelFormat::YCBCR_P010:
+            return "PixelFormat::YCBCR_P010";
+        case PixelFormat::YCRCB_420_SP:
+            return "PixelFormat::YCRCB_420_SP";
+        case PixelFormat::YV12:
+            return "PixelFormat::YV12";
+    }
+    return android::base::StringPrintf("PixelFormat::Unknown(%d)", static_cast<uint32_t>(format));
+}
+
+std::string getUsageString(hidl_bitfield<BufferUsage> bufferUsage) {
+    using Underlying = typename std::underlying_type<BufferUsage>::type;
+
+    Underlying usage = static_cast<Underlying>(bufferUsage);
+
+    std::vector<std::string> usages;
+    if (usage & BufferUsage::CAMERA_INPUT) {
+        usage &= ~static_cast<Underlying>(BufferUsage::CAMERA_INPUT);
+        usages.push_back("BufferUsage::CAMERA_INPUT");
+    }
+    if (usage & BufferUsage::CAMERA_OUTPUT) {
+        usage &= ~static_cast<Underlying>(BufferUsage::CAMERA_OUTPUT);
+        usages.push_back("BufferUsage::CAMERA_OUTPUT");
+    }
+    if (usage & BufferUsage::COMPOSER_CURSOR) {
+        usage &= ~static_cast<Underlying>(BufferUsage::COMPOSER_CURSOR);
+        usages.push_back("BufferUsage::COMPOSER_CURSOR");
+    }
+    if (usage & BufferUsage::COMPOSER_OVERLAY) {
+        usage &= ~static_cast<Underlying>(BufferUsage::COMPOSER_OVERLAY);
+        usages.push_back("BufferUsage::COMPOSER_OVERLAY");
+    }
+    if (usage & BufferUsage::CPU_READ_OFTEN) {
+        usage &= ~static_cast<Underlying>(BufferUsage::CPU_READ_OFTEN);
+        usages.push_back("BufferUsage::CPU_READ_OFTEN");
+    }
+    if (usage & BufferUsage::CPU_READ_NEVER) {
+        usage &= ~static_cast<Underlying>(BufferUsage::CPU_READ_NEVER);
+        usages.push_back("BufferUsage::CPU_READ_NEVER");
+    }
+    if (usage & BufferUsage::CPU_READ_RARELY) {
+        usage &= ~static_cast<Underlying>(BufferUsage::CPU_READ_RARELY);
+        usages.push_back("BufferUsage::CPU_READ_RARELY");
+    }
+    if (usage & BufferUsage::CPU_WRITE_NEVER) {
+        usage &= ~static_cast<Underlying>(BufferUsage::CPU_WRITE_NEVER);
+        usages.push_back("BufferUsage::CPU_WRITE_NEVER");
+    }
+    if (usage & BufferUsage::CPU_WRITE_OFTEN) {
+        usage &= ~static_cast<Underlying>(BufferUsage::CPU_WRITE_OFTEN);
+        usages.push_back("BufferUsage::CPU_WRITE_OFTEN");
+    }
+    if (usage & BufferUsage::CPU_WRITE_RARELY) {
+        usage &= ~static_cast<Underlying>(BufferUsage::CPU_WRITE_RARELY);
+        usages.push_back("BufferUsage::CPU_WRITE_RARELY");
+    }
+    if (usage & BufferUsage::GPU_RENDER_TARGET) {
+        usage &= ~static_cast<Underlying>(BufferUsage::GPU_RENDER_TARGET);
+        usages.push_back("BufferUsage::GPU_RENDER_TARGET");
+    }
+    if (usage & BufferUsage::GPU_TEXTURE) {
+        usage &= ~static_cast<Underlying>(BufferUsage::GPU_TEXTURE);
+        usages.push_back("BufferUsage::GPU_TEXTURE");
+    }
+    if (usage & BufferUsage::PROTECTED) {
+        usage &= ~static_cast<Underlying>(BufferUsage::PROTECTED);
+        usages.push_back("BufferUsage::PROTECTED");
+    }
+    if (usage & BufferUsage::RENDERSCRIPT) {
+        usage &= ~static_cast<Underlying>(BufferUsage::RENDERSCRIPT);
+        usages.push_back("BufferUsage::RENDERSCRIPT");
+    }
+    if (usage & BufferUsage::VIDEO_DECODER) {
+        usage &= ~static_cast<Underlying>(BufferUsage::VIDEO_DECODER);
+        usages.push_back("BufferUsage::VIDEO_DECODER");
+    }
+    if (usage & BufferUsage::VIDEO_ENCODER) {
+        usage &= ~static_cast<Underlying>(BufferUsage::VIDEO_ENCODER);
+        usages.push_back("BufferUsage::VIDEO_ENCODER");
+    }
+
+    if (usage) {
+        usages.push_back(android::base::StringPrintf("UnknownUsageBits-%" PRIu64, usage));
+    }
+
+    return android::base::Join(usages, '|');
+}
+
+int convertToDrmFormat(PixelFormat format, uint32_t* outDrmFormat) {
+    switch (format) {
+        case PixelFormat::BGRA_8888:
+            *outDrmFormat = DRM_FORMAT_ARGB8888;
+            return 0;
+        /**
+         * Choose DRM_FORMAT_R8 because <system/graphics.h> requires the buffers
+         * with a format HAL_PIXEL_FORMAT_BLOB have a height of 1, and width
+         * equal to their size in bytes.
+         */
+        case PixelFormat::BLOB:
+            *outDrmFormat = DRM_FORMAT_R8;
+            return 0;
+        case PixelFormat::DEPTH_16:
+            return -EINVAL;
+        case PixelFormat::DEPTH_24:
+            return -EINVAL;
+        case PixelFormat::DEPTH_24_STENCIL_8:
+            return -EINVAL;
+        case PixelFormat::DEPTH_32F:
+            return -EINVAL;
+        case PixelFormat::DEPTH_32F_STENCIL_8:
+            return -EINVAL;
+        case PixelFormat::HSV_888:
+            return -EINVAL;
+        case PixelFormat::IMPLEMENTATION_DEFINED:
+            *outDrmFormat = DRM_FORMAT_FLEX_IMPLEMENTATION_DEFINED;
+            return 0;
+        case PixelFormat::RAW10:
+            return -EINVAL;
+        case PixelFormat::RAW12:
+            return -EINVAL;
+        case PixelFormat::RAW16:
+            *outDrmFormat = DRM_FORMAT_R16;
+            return 0;
+        /* TODO use blob */
+        case PixelFormat::RAW_OPAQUE:
+            return -EINVAL;
+        case PixelFormat::RGBA_1010102:
+            *outDrmFormat = DRM_FORMAT_ABGR2101010;
+            return 0;
+        case PixelFormat::RGBA_8888:
+            *outDrmFormat = DRM_FORMAT_ABGR8888;
+            return 0;
+        case PixelFormat::RGBA_FP16:
+            *outDrmFormat = DRM_FORMAT_ABGR16161616F;
+            return 0;
+        case PixelFormat::RGBX_8888:
+            *outDrmFormat = DRM_FORMAT_XBGR8888;
+            return 0;
+        case PixelFormat::RGB_565:
+            *outDrmFormat = DRM_FORMAT_RGB565;
+            return 0;
+        case PixelFormat::RGB_888:
+            *outDrmFormat = DRM_FORMAT_RGB888;
+            return 0;
+        case PixelFormat::STENCIL_8:
+            return -EINVAL;
+        case PixelFormat::Y16:
+            *outDrmFormat = DRM_FORMAT_R16;
+            return 0;
+        case PixelFormat::Y8:
+            *outDrmFormat = DRM_FORMAT_R8;
+            return 0;
+        case PixelFormat::YCBCR_420_888:
+            *outDrmFormat = DRM_FORMAT_FLEX_YCbCr_420_888;
+            return 0;
+        case PixelFormat::YCBCR_422_SP:
+            return -EINVAL;
+        case PixelFormat::YCBCR_422_I:
+            return -EINVAL;
+        case PixelFormat::YCBCR_P010:
+            *outDrmFormat = DRM_FORMAT_P010;
+            return 0;
+        case PixelFormat::YCRCB_420_SP:
+            *outDrmFormat = DRM_FORMAT_NV21;
+            return 0;
+        case PixelFormat::YV12:
+            *outDrmFormat = DRM_FORMAT_YVU420_ANDROID;
+            return 0;
+    };
+    return -EINVAL;
+}
+
+int convertToBufferUsage(uint64_t grallocUsage, uint64_t* outBufferUsage) {
+    uint64_t bufferUsage = BO_USE_NONE;
+
+    if ((grallocUsage & BufferUsage::CPU_READ_MASK) ==
+        static_cast<uint64_t>(BufferUsage::CPU_READ_RARELY)) {
+        bufferUsage |= BO_USE_SW_READ_RARELY;
+    }
+    if ((grallocUsage & BufferUsage::CPU_READ_MASK) ==
+        static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN)) {
+        bufferUsage |= BO_USE_SW_READ_OFTEN;
+    }
+    if ((grallocUsage & BufferUsage::CPU_WRITE_MASK) ==
+        static_cast<uint64_t>(BufferUsage::CPU_WRITE_RARELY)) {
+        bufferUsage |= BO_USE_SW_WRITE_RARELY;
+    }
+    if ((grallocUsage & BufferUsage::CPU_WRITE_MASK) ==
+        static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN)) {
+        bufferUsage |= BO_USE_SW_WRITE_OFTEN;
+    }
+    if (grallocUsage & BufferUsage::GPU_TEXTURE) {
+        bufferUsage |= BO_USE_TEXTURE;
+    }
+    if (grallocUsage & BufferUsage::GPU_RENDER_TARGET) {
+        bufferUsage |= BO_USE_RENDERING;
+    }
+    if (grallocUsage & BufferUsage::COMPOSER_OVERLAY) {
+        /* HWC wants to use display hardware, but can defer to OpenGL. */
+        bufferUsage |= BO_USE_SCANOUT | BO_USE_TEXTURE;
+    }
+    if (grallocUsage & BufferUsage::PROTECTED) {
+        bufferUsage |= BO_USE_PROTECTED;
+    }
+    if (grallocUsage & BufferUsage::COMPOSER_CURSOR) {
+        bufferUsage |= BO_USE_NONE;
+    }
+    if (grallocUsage & BufferUsage::VIDEO_ENCODER) {
+        /*HACK: See b/30054495 */
+        bufferUsage |= BO_USE_SW_READ_OFTEN;
+    }
+    if (grallocUsage & BufferUsage::CAMERA_OUTPUT) {
+        bufferUsage |= BO_USE_CAMERA_WRITE;
+    }
+    if (grallocUsage & BufferUsage::CAMERA_INPUT) {
+        bufferUsage |= BO_USE_CAMERA_READ;
+    }
+    if (grallocUsage & BufferUsage::RENDERSCRIPT) {
+        bufferUsage |= BO_USE_RENDERSCRIPT;
+    }
+    if (grallocUsage & BufferUsage::VIDEO_DECODER) {
+        bufferUsage |= BO_USE_HW_VIDEO_DECODER;
+    }
+
+    *outBufferUsage = bufferUsage;
+    return 0;
+}
+
+int convertToCrosDescriptor(const BufferDescriptorInfo& descriptor,
+                            struct cros_gralloc_buffer_descriptor* outCrosDescriptor) {
+    outCrosDescriptor->name = descriptor.name;
+    outCrosDescriptor->width = descriptor.width;
+    outCrosDescriptor->height = descriptor.height;
+    outCrosDescriptor->droid_format = static_cast<int32_t>(descriptor.format);
+    outCrosDescriptor->droid_usage = descriptor.usage;
+    outCrosDescriptor->reserved_region_size = descriptor.reservedSize;
+
+    if (convertToDrmFormat(descriptor.format, &outCrosDescriptor->drm_format)) {
+        std::string pixelFormatString = getPixelFormatString(descriptor.format);
+        drv_log("Failed to convert descriptor. Unsupported fomat %s\n", pixelFormatString.c_str());
+        return -1;
+    }
+    if (convertToBufferUsage(descriptor.usage, &outCrosDescriptor->use_flags)) {
+        std::string usageString = getUsageString(descriptor.usage);
+        drv_log("Failed to convert descriptor. Unsupported usage flags %s\n", usageString.c_str());
+        return -1;
+    }
+    return 0;
+}
+
+int convertToMapUsage(uint64_t grallocUsage, uint32_t* outMapUsage) {
+    uint32_t mapUsage = BO_MAP_NONE;
+
+    if (grallocUsage & BufferUsage::CPU_READ_MASK) {
+        mapUsage |= BO_MAP_READ;
+    }
+    if (grallocUsage & BufferUsage::CPU_WRITE_MASK) {
+        mapUsage |= BO_MAP_WRITE;
+    }
+
+    *outMapUsage = mapUsage;
+    return 0;
+}
+
+int convertToFenceFd(const hidl_handle& fenceHandle, int* outFenceFd) {
+    if (!outFenceFd) {
+        return -EINVAL;
+    }
+
+    const native_handle_t* nativeHandle = fenceHandle.getNativeHandle();
+    if (nativeHandle && nativeHandle->numFds > 1) {
+        return -EINVAL;
+    }
+
+    *outFenceFd = (nativeHandle && nativeHandle->numFds == 1) ? nativeHandle->data[0] : -1;
+    return 0;
+}
+
+int convertToFenceHandle(int fenceFd, hidl_handle* outFenceHandle) {
+    if (!outFenceHandle) {
+        return -EINVAL;
+    }
+    if (fenceFd < 0) {
+        return 0;
+    }
+
+    NATIVE_HANDLE_DECLARE_STORAGE(handleStorage, 1, 0);
+    auto fenceHandle = native_handle_init(handleStorage, 1, 0);
+    fenceHandle->data[0] = fenceFd;
+
+    *outFenceHandle = fenceHandle;
+    return 0;
+}
+
+const std::unordered_map<uint32_t, std::vector<PlaneLayout>>& GetPlaneLayoutsMap() {
+    static const auto* kPlaneLayoutsMap =
+            new std::unordered_map<uint32_t, std::vector<PlaneLayout>>({
+                    {DRM_FORMAT_ABGR8888,
+                     {{
+                             .components = {{.type = android::gralloc4::PlaneLayoutComponentType_R,
+                                             .offsetInBits = 0,
+                                             .sizeInBits = 8},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_G,
+                                             .offsetInBits = 8,
+                                             .sizeInBits = 8},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_B,
+                                             .offsetInBits = 16,
+                                             .sizeInBits = 8},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_A,
+                                             .offsetInBits = 24,
+                                             .sizeInBits = 8}},
+                             .sampleIncrementInBits = 32,
+                             .horizontalSubsampling = 1,
+                             .verticalSubsampling = 1,
+                     }}},
+
+                    {DRM_FORMAT_ABGR2101010,
+                     {{
+                             .components = {{.type = android::gralloc4::PlaneLayoutComponentType_R,
+                                             .offsetInBits = 0,
+                                             .sizeInBits = 10},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_G,
+                                             .offsetInBits = 10,
+                                             .sizeInBits = 10},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_B,
+                                             .offsetInBits = 20,
+                                             .sizeInBits = 10},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_A,
+                                             .offsetInBits = 30,
+                                             .sizeInBits = 2}},
+                             .sampleIncrementInBits = 32,
+                             .horizontalSubsampling = 1,
+                             .verticalSubsampling = 1,
+                     }}},
+
+                    {DRM_FORMAT_ABGR16161616F,
+                     {{
+                             .components = {{.type = android::gralloc4::PlaneLayoutComponentType_R,
+                                             .offsetInBits = 0,
+                                             .sizeInBits = 16},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_G,
+                                             .offsetInBits = 16,
+                                             .sizeInBits = 16},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_B,
+                                             .offsetInBits = 32,
+                                             .sizeInBits = 16},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_A,
+                                             .offsetInBits = 48,
+                                             .sizeInBits = 16}},
+                             .sampleIncrementInBits = 64,
+                             .horizontalSubsampling = 1,
+                             .verticalSubsampling = 1,
+                     }}},
+
+                    {DRM_FORMAT_ARGB8888,
+                     {{
+                             .components = {{.type = android::gralloc4::PlaneLayoutComponentType_B,
+                                             .offsetInBits = 0,
+                                             .sizeInBits = 8},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_G,
+                                             .offsetInBits = 8,
+                                             .sizeInBits = 8},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_R,
+                                             .offsetInBits = 16,
+                                             .sizeInBits = 8},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_A,
+                                             .offsetInBits = 24,
+                                             .sizeInBits = 8}},
+                             .sampleIncrementInBits = 32,
+                             .horizontalSubsampling = 1,
+                             .verticalSubsampling = 1,
+                     }}},
+
+                    {DRM_FORMAT_NV12,
+                     {{
+                              .components = {{.type = android::gralloc4::PlaneLayoutComponentType_Y,
+                                              .offsetInBits = 0,
+                                              .sizeInBits = 8}},
+                              .sampleIncrementInBits = 8,
+                              .horizontalSubsampling = 1,
+                              .verticalSubsampling = 1,
+                      },
+                      {
+                              .components =
+                                      {{.type = android::gralloc4::PlaneLayoutComponentType_CB,
+                                        .offsetInBits = 0,
+                                        .sizeInBits = 8},
+                                       {.type = android::gralloc4::PlaneLayoutComponentType_CR,
+                                        .offsetInBits = 8,
+                                        .sizeInBits = 8}},
+                              .sampleIncrementInBits = 16,
+                              .horizontalSubsampling = 2,
+                              .verticalSubsampling = 2,
+                      }}},
+
+                    {DRM_FORMAT_NV21,
+                     {{
+                              .components = {{.type = android::gralloc4::PlaneLayoutComponentType_Y,
+                                              .offsetInBits = 0,
+                                              .sizeInBits = 8}},
+                              .sampleIncrementInBits = 8,
+                              .horizontalSubsampling = 1,
+                              .verticalSubsampling = 1,
+                      },
+                      {
+                              .components =
+                                      {{.type = android::gralloc4::PlaneLayoutComponentType_CR,
+                                        .offsetInBits = 0,
+                                        .sizeInBits = 8},
+                                       {.type = android::gralloc4::PlaneLayoutComponentType_CB,
+                                        .offsetInBits = 8,
+                                        .sizeInBits = 8}},
+                              .sampleIncrementInBits = 16,
+                              .horizontalSubsampling = 2,
+                              .verticalSubsampling = 2,
+                      }}},
+
+                    {DRM_FORMAT_P010,
+                     {{
+                              .components = {{.type = android::gralloc4::PlaneLayoutComponentType_Y,
+                                              .offsetInBits = 6,
+                                              .sizeInBits = 10}},
+                              .sampleIncrementInBits = 16,
+                              .horizontalSubsampling = 1,
+                              .verticalSubsampling = 1,
+                      },
+                      {
+                              .components =
+                                      {{.type = android::gralloc4::PlaneLayoutComponentType_CB,
+                                        .offsetInBits = 6,
+                                        .sizeInBits = 10},
+                                       {.type = android::gralloc4::PlaneLayoutComponentType_CR,
+                                        .offsetInBits = 22,
+                                        .sizeInBits = 10}},
+                              .sampleIncrementInBits = 32,
+                              .horizontalSubsampling = 2,
+                              .verticalSubsampling = 2,
+                      }}},
+
+                    {DRM_FORMAT_R8,
+                     {{
+                             .components = {{.type = android::gralloc4::PlaneLayoutComponentType_R,
+                                             .offsetInBits = 0,
+                                             .sizeInBits = 8}},
+                             .sampleIncrementInBits = 8,
+                             .horizontalSubsampling = 1,
+                             .verticalSubsampling = 1,
+                     }}},
+
+                    {DRM_FORMAT_R16,
+                     {{
+                             .components = {{.type = android::gralloc4::PlaneLayoutComponentType_R,
+                                             .offsetInBits = 0,
+                                             .sizeInBits = 16}},
+                             .sampleIncrementInBits = 16,
+                             .horizontalSubsampling = 1,
+                             .verticalSubsampling = 1,
+                     }}},
+
+                    {DRM_FORMAT_RGB565,
+                     {{
+                             .components = {{.type = android::gralloc4::PlaneLayoutComponentType_R,
+                                             .offsetInBits = 0,
+                                             .sizeInBits = 5},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_G,
+                                             .offsetInBits = 5,
+                                             .sizeInBits = 6},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_B,
+                                             .offsetInBits = 11,
+                                             .sizeInBits = 5}},
+                             .sampleIncrementInBits = 16,
+                             .horizontalSubsampling = 1,
+                             .verticalSubsampling = 1,
+                     }}},
+
+                    {DRM_FORMAT_RGB888,
+                     {{
+                             .components = {{.type = android::gralloc4::PlaneLayoutComponentType_R,
+                                             .offsetInBits = 0,
+                                             .sizeInBits = 8},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_G,
+                                             .offsetInBits = 8,
+                                             .sizeInBits = 8},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_B,
+                                             .offsetInBits = 16,
+                                             .sizeInBits = 8}},
+                             .sampleIncrementInBits = 24,
+                             .horizontalSubsampling = 1,
+                             .verticalSubsampling = 1,
+                     }}},
+
+                    {DRM_FORMAT_XBGR8888,
+                     {{
+                             .components = {{.type = android::gralloc4::PlaneLayoutComponentType_B,
+                                             .offsetInBits = 0,
+                                             .sizeInBits = 8},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_G,
+                                             .offsetInBits = 8,
+                                             .sizeInBits = 8},
+                                            {.type = android::gralloc4::PlaneLayoutComponentType_R,
+                                             .offsetInBits = 16,
+                                             .sizeInBits = 8}},
+                             .sampleIncrementInBits = 32,
+                             .horizontalSubsampling = 1,
+                             .verticalSubsampling = 1,
+                     }}},
+
+                    {DRM_FORMAT_YVU420,
+                     {
+                             {
+                                     .components = {{.type = android::gralloc4::
+                                                             PlaneLayoutComponentType_Y,
+                                                     .offsetInBits = 0,
+                                                     .sizeInBits = 8}},
+                                     .sampleIncrementInBits = 8,
+                                     .horizontalSubsampling = 1,
+                                     .verticalSubsampling = 1,
+                             },
+                             {
+                                     .components = {{.type = android::gralloc4::
+                                                             PlaneLayoutComponentType_CB,
+                                                     .offsetInBits = 0,
+                                                     .sizeInBits = 8}},
+                                     .sampleIncrementInBits = 8,
+                                     .horizontalSubsampling = 2,
+                                     .verticalSubsampling = 2,
+                             },
+                             {
+                                     .components = {{.type = android::gralloc4::
+                                                             PlaneLayoutComponentType_CR,
+                                                     .offsetInBits = 0,
+                                                     .sizeInBits = 8}},
+                                     .sampleIncrementInBits = 8,
+                                     .horizontalSubsampling = 2,
+                                     .verticalSubsampling = 2,
+                             },
+                     }},
+
+                    {DRM_FORMAT_YVU420_ANDROID,
+                     {
+                             {
+                                     .components = {{.type = android::gralloc4::
+                                                             PlaneLayoutComponentType_Y,
+                                                     .offsetInBits = 0,
+                                                     .sizeInBits = 8}},
+                                     .sampleIncrementInBits = 8,
+                                     .horizontalSubsampling = 1,
+                                     .verticalSubsampling = 1,
+                             },
+                             {
+                                     .components = {{.type = android::gralloc4::
+                                                             PlaneLayoutComponentType_CR,
+                                                     .offsetInBits = 0,
+                                                     .sizeInBits = 8}},
+                                     .sampleIncrementInBits = 8,
+                                     .horizontalSubsampling = 2,
+                                     .verticalSubsampling = 2,
+                             },
+                             {
+                                     .components = {{.type = android::gralloc4::
+                                                             PlaneLayoutComponentType_CB,
+                                                     .offsetInBits = 0,
+                                                     .sizeInBits = 8}},
+                                     .sampleIncrementInBits = 8,
+                                     .horizontalSubsampling = 2,
+                                     .verticalSubsampling = 2,
+                             },
+                     }},
+            });
+    return *kPlaneLayoutsMap;
+}
+
+int getPlaneLayouts(uint32_t drmFormat, std::vector<PlaneLayout>* outPlaneLayouts) {
+    const auto& planeLayoutsMap = GetPlaneLayoutsMap();
+    const auto it = planeLayoutsMap.find(drmFormat);
+    if (it == planeLayoutsMap.end()) {
+        drv_log("Unknown plane layout for format %d\n", drmFormat);
+        return -1;
+    }
+
+    *outPlaneLayouts = it->second;
+    return 0;
+}
\ No newline at end of file
diff --git a/cros_gralloc/gralloc4/CrosGralloc4Utils.h b/cros_gralloc/gralloc4/CrosGralloc4Utils.h
new file mode 100644
index 0000000..094ef74
--- /dev/null
+++ b/cros_gralloc/gralloc4/CrosGralloc4Utils.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <string>
+#include <vector>
+
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+
+#include "cros_gralloc/cros_gralloc_types.h"
+
+std::string getDrmFormatString(uint32_t drmFormat);
+
+std::string getPixelFormatString(android::hardware::graphics::common::V1_2::PixelFormat format);
+
+std::string getUsageString(
+        android::hardware::hidl_bitfield<android::hardware::graphics::common::V1_2::BufferUsage>
+                usage);
+
+int convertToDrmFormat(android::hardware::graphics::common::V1_2::PixelFormat format,
+                       uint32_t* outDrmFormat);
+
+int convertToBufferUsage(uint64_t grallocUsage, uint64_t* outBufferUsage);
+
+int convertToCrosDescriptor(
+        const android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo& descriptor,
+        struct cros_gralloc_buffer_descriptor* outCrosDescriptor);
+
+int convertToMapUsage(uint64_t grallocUsage, uint32_t* outMapUsage);
+
+int convertToFenceFd(const android::hardware::hidl_handle& fence_handle, int* out_fence_fd);
+
+int convertToFenceHandle(int fence_fd, android::hardware::hidl_handle* out_fence_handle);
+
+int getPlaneLayouts(
+        uint32_t drm_format,
+        std::vector<aidl::android::hardware::graphics::common::PlaneLayout>* out_layouts);
\ No newline at end of file
diff --git a/cros_gralloc/gralloc4/android.hardware.graphics.allocator@4.0-service.minigbm.rc b/cros_gralloc/gralloc4/android.hardware.graphics.allocator@4.0-service.minigbm.rc
new file mode 100644
index 0000000..a96a6e1
--- /dev/null
+++ b/cros_gralloc/gralloc4/android.hardware.graphics.allocator@4.0-service.minigbm.rc
@@ -0,0 +1,24 @@
+#
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+service vendor.graphics.allocator-4-0 /vendor/bin/hw/android.hardware.graphics.allocator@4.0-service.minigbm
+    interface android.hardware.graphics.allocator@4.0::IAllocator default
+    class hal animation
+    user system
+    group graphics drmrpc
+    capabilities SYS_NICE
+    onrestart restart surfaceflinger
+    writepid /dev/cpuset/system-background/tasks
diff --git a/drv.c b/drv.c
index 920cf4d..636cd07 100644
--- a/drv.c
+++ b/drv.c
@@ -117,7 +117,7 @@
 #ifdef DRV_VC4
 		&backend_vc4,
 #endif
-		&backend_vgem,	   &backend_virtio_gpu,
+		&backend_vgem,	    &backend_virtio_gpu,
 	};
 
 	for (i = 0; i < ARRAY_SIZE(backend_list); i++) {
@@ -558,6 +558,21 @@
 	return ret;
 }
 
+int drv_bo_flush(struct bo *bo, struct mapping *mapping)
+{
+	int ret = 0;
+
+	assert(mapping);
+	assert(mapping->vma);
+	assert(mapping->refcount > 0);
+	assert(mapping->vma->refcount > 0);
+
+	if (bo->drv->backend->bo_flush)
+		ret = bo->drv->backend->bo_flush(bo, mapping);
+
+	return ret;
+}
+
 int drv_bo_flush_or_unmap(struct bo *bo, struct mapping *mapping)
 {
 	int ret = 0;
@@ -648,6 +663,11 @@
 	return bo->meta.format;
 }
 
+size_t drv_bo_get_total_size(struct bo *bo)
+{
+	return bo->meta.total_size;
+}
+
 uint32_t drv_resolve_format(struct driver *drv, uint32_t format, uint64_t use_flags)
 {
 	if (drv->backend->resolve_format)
diff --git a/drv.h b/drv.h
index 2b86aad..f19f9de 100644
--- a/drv.h
+++ b/drv.h
@@ -145,6 +145,8 @@
 
 int drv_bo_invalidate(struct bo *bo, struct mapping *mapping);
 
+int drv_bo_flush(struct bo *bo, struct mapping *mapping);
+
 int drv_bo_flush_or_unmap(struct bo *bo, struct mapping *mapping);
 
 uint32_t drv_bo_get_width(struct bo *bo);
diff --git a/helpers.c b/helpers.c
index fed4af9..17b1765 100644
--- a/helpers.c
+++ b/helpers.c
@@ -10,6 +10,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
 #include <xf86drm.h>
 
 #include "drv_priv.h"
@@ -92,6 +94,9 @@
 	case DRM_FORMAT_RGB332:
 		return &packed_1bpp_layout;
 
+	case DRM_FORMAT_R16:
+		return &packed_2bpp_layout;
+
 	case DRM_FORMAT_YVU420:
 	case DRM_FORMAT_YVU420_ANDROID:
 		return &triplanar_yuv_420_layout;
@@ -312,6 +317,11 @@
 	aligned_width = width;
 	aligned_height = height;
 	switch (format) {
+	case DRM_FORMAT_R16:
+		/* HAL_PIXEL_FORMAT_Y16 requires that the buffer's width be 16 pixel
+		 * aligned. See hardware/interfaces/graphics/common/1.0/types.hal. */
+		aligned_width = ALIGN(width, 16);
+		break;
 	case DRM_FORMAT_YVU420_ANDROID:
 		/* HAL_PIXEL_FORMAT_YV12 requires that the buffer's height not
 		 * be aligned. Update 'height' so that drv_bo_from_format below
@@ -327,6 +337,7 @@
 		break;
 	case DRM_FORMAT_YVU420:
 	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV21:
 		/* Adjust the height to include room for chroma planes */
 		aligned_height = 3 * DIV_ROUND_UP(height, 2);
 		break;
diff --git a/virgl_hw.h b/virgl_hw.h
index 145780b..1c493d1 100644
--- a/virgl_hw.h
+++ b/virgl_hw.h
@@ -69,7 +69,7 @@
 
    VIRGL_FORMAT_R8_UNORM                = 64,
    VIRGL_FORMAT_R8G8_UNORM              = 65,
-
+   VIRGL_FORMAT_R8G8B8_UNORM            = 66,
    VIRGL_FORMAT_R8G8B8A8_UNORM          = 67,
 
    VIRGL_FORMAT_R8_SNORM                = 74,
diff --git a/virtio_gpu.c b/virtio_gpu.c
index 4dbcc4f..83d46d3 100644
--- a/virtio_gpu.c
+++ b/virtio_gpu.c
@@ -4,6 +4,7 @@
  * found in the LICENSE file.
  */
 
+#include <assert.h>
 #include <errno.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -50,21 +51,27 @@
 						  DRM_FORMAT_RGB565, DRM_FORMAT_XBGR8888,
 						  DRM_FORMAT_XRGB8888 };
 
-static const uint32_t dumb_texture_source_formats[] = { DRM_FORMAT_R8, DRM_FORMAT_YVU420,
-							DRM_FORMAT_NV12,
-							DRM_FORMAT_YVU420_ANDROID };
+static const uint32_t dumb_texture_source_formats[] = {
+	DRM_FORMAT_R8,	 DRM_FORMAT_R16,  DRM_FORMAT_YVU420,
+	DRM_FORMAT_NV12, DRM_FORMAT_NV21, DRM_FORMAT_YVU420_ANDROID
+};
 
-static const uint32_t texture_source_formats[] = { DRM_FORMAT_NV12, DRM_FORMAT_R8, DRM_FORMAT_RG88,
-						   DRM_FORMAT_YVU420_ANDROID };
+static const uint32_t texture_source_formats[] = { DRM_FORMAT_NV12, DRM_FORMAT_NV21,
+						   DRM_FORMAT_R8,   DRM_FORMAT_R16,
+						   DRM_FORMAT_RG88, DRM_FORMAT_YVU420_ANDROID };
 
 struct virtio_gpu_priv {
 	int caps_is_v2;
 	union virgl_caps caps;
+	int host_gbm_enabled;
 };
 
 static uint32_t translate_format(uint32_t drm_fourcc)
 {
 	switch (drm_fourcc) {
+	case DRM_FORMAT_BGR888:
+	case DRM_FORMAT_RGB888:
+		return VIRGL_FORMAT_R8G8B8_UNORM;
 	case DRM_FORMAT_XRGB8888:
 		return VIRGL_FORMAT_B8G8R8X8_UNORM;
 	case DRM_FORMAT_ARGB8888:
@@ -73,6 +80,8 @@
 		return VIRGL_FORMAT_R8G8B8X8_UNORM;
 	case DRM_FORMAT_ABGR8888:
 		return VIRGL_FORMAT_R8G8B8A8_UNORM;
+	case DRM_FORMAT_ABGR16161616F:
+		return VIRGL_FORMAT_R16G16B16A16_UNORM;
 	case DRM_FORMAT_RGB565:
 		return VIRGL_FORMAT_B5G6R5_UNORM;
 	case DRM_FORMAT_R8:
@@ -81,6 +90,8 @@
 		return VIRGL_FORMAT_R8G8_UNORM;
 	case DRM_FORMAT_NV12:
 		return VIRGL_FORMAT_NV12;
+	case DRM_FORMAT_NV21:
+		return VIRGL_FORMAT_NV21;
 	case DRM_FORMAT_YVU420:
 	case DRM_FORMAT_YVU420_ANDROID:
 		return VIRGL_FORMAT_YV12;
@@ -89,8 +100,8 @@
 	}
 }
 
-static bool virtio_gpu_supports_format(struct virgl_supported_format_mask *supported,
-				       uint32_t drm_format)
+static bool virtio_gpu_bitmask_supports_format(struct virgl_supported_format_mask *supported,
+					       uint32_t drm_format)
 {
 	uint32_t virgl_format = translate_format(drm_format);
 	if (!virgl_format) {
@@ -102,6 +113,243 @@
 	return supported->bitmask[bitmask_index] & (1 << bit_index);
 }
 
+// The metadata generated here for emulated buffers is slightly different than the metadata
+// generated by drv_bo_from_format. In order to simplify transfers in the flush and invalidate
+// functions below, the emulated buffers are oversized. For example, ignoring stride alignment
+// requirements to demonstrate, a 6x6 YUV420 image buffer might have the following layout from
+// drv_bo_from_format:
+//
+// | Y | Y | Y | Y | Y | Y |
+// | Y | Y | Y | Y | Y | Y |
+// | Y | Y | Y | Y | Y | Y |
+// | Y | Y | Y | Y | Y | Y |
+// | Y | Y | Y | Y | Y | Y |
+// | Y | Y | Y | Y | Y | Y |
+// | U | U | U | U | U | U |
+// | U | U | U | V | V | V |
+// | V | V | V | V | V | V |
+//
+// where each plane immediately follows the previous plane in memory. This layout makes it
+// difficult to compute the transfers needed for example when the middle 2x2 region of the
+// image is locked and needs to be flushed/invalidated.
+//
+// Emulated multi-plane buffers instead have a layout of:
+//
+// | Y | Y | Y | Y | Y | Y |
+// | Y | Y | Y | Y | Y | Y |
+// | Y | Y | Y | Y | Y | Y |
+// | Y | Y | Y | Y | Y | Y |
+// | Y | Y | Y | Y | Y | Y |
+// | Y | Y | Y | Y | Y | Y |
+// | U | U | U |   |   |   |
+// | U | U | U |   |   |   |
+// | U | U | U |   |   |   |
+// | V | V | V |   |   |   |
+// | V | V | V |   |   |   |
+// | V | V | V |   |   |   |
+//
+// where each plane is placed as a sub-image (albeit with a very large stride) in order to
+// simplify transfers into 3 sub-image transfers for the above example.
+//
+// Additional note: the V-plane is not placed to the right of the U-plane due to some
+// observed failures in media framework code which assumes the V-plane is not
+// "row-interlaced" with the U-plane.
+static void virtio_gpu_get_emulated_metadata(const struct bo *bo, struct bo_metadata *metadata)
+{
+	uint32_t y_plane_height;
+	uint32_t c_plane_height;
+	uint32_t original_width = bo->meta.width;
+	uint32_t original_height = bo->meta.height;
+
+	metadata->format = DRM_FORMAT_R8;
+	switch (bo->meta.format) {
+	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV21:
+		// Bi-planar
+		metadata->num_planes = 2;
+
+		y_plane_height = original_height;
+		c_plane_height = DIV_ROUND_UP(original_height, 2);
+
+		metadata->width = original_width;
+		metadata->height = y_plane_height + c_plane_height;
+
+		// Y-plane (full resolution)
+		metadata->strides[0] = metadata->width;
+		metadata->offsets[0] = 0;
+		metadata->sizes[0] = metadata->width * y_plane_height;
+
+		// CbCr-plane  (half resolution, interleaved, placed below Y-plane)
+		metadata->strides[1] = metadata->width;
+		metadata->offsets[1] = metadata->offsets[0] + metadata->sizes[0];
+		metadata->sizes[1] = metadata->width * c_plane_height;
+
+		metadata->total_size = metadata->width * metadata->height;
+		break;
+	case DRM_FORMAT_YVU420:
+	case DRM_FORMAT_YVU420_ANDROID:
+		// Tri-planar
+		metadata->num_planes = 3;
+
+		y_plane_height = original_height;
+		c_plane_height = DIV_ROUND_UP(original_height, 2);
+
+		metadata->width = ALIGN(original_width, 32);
+		metadata->height = y_plane_height + (2 * c_plane_height);
+
+		// Y-plane (full resolution)
+		metadata->strides[0] = metadata->width;
+		metadata->offsets[0] = 0;
+		metadata->sizes[0] = metadata->width * original_height;
+
+		// Cb-plane (half resolution, placed below Y-plane)
+		metadata->strides[1] = metadata->width;
+		metadata->offsets[1] = metadata->offsets[0] + metadata->sizes[0];
+		metadata->sizes[1] = metadata->width * c_plane_height;
+
+		// Cr-plane (half resolution, placed below Cb-plane)
+		metadata->strides[2] = metadata->width;
+		metadata->offsets[2] = metadata->offsets[1] + metadata->sizes[1];
+		metadata->sizes[2] = metadata->width * c_plane_height;
+
+		metadata->total_size = metadata->width * metadata->height;
+		break;
+	default:
+		break;
+	}
+}
+
+struct virtio_transfers_params {
+	size_t xfers_needed;
+	struct rectangle xfer_boxes[DRV_MAX_PLANES];
+};
+
+static void virtio_gpu_get_emulated_transfers_params(const struct bo *bo,
+						     const struct rectangle *transfer_box,
+						     struct virtio_transfers_params *xfer_params)
+{
+	uint32_t y_plane_height;
+	uint32_t c_plane_height;
+	struct bo_metadata emulated_metadata;
+
+	if (transfer_box->x == 0 && transfer_box->y == 0 && transfer_box->width == bo->meta.width &&
+	    transfer_box->height == bo->meta.height) {
+		virtio_gpu_get_emulated_metadata(bo, &emulated_metadata);
+
+		xfer_params->xfers_needed = 1;
+		xfer_params->xfer_boxes[0].x = 0;
+		xfer_params->xfer_boxes[0].y = 0;
+		xfer_params->xfer_boxes[0].width = emulated_metadata.width;
+		xfer_params->xfer_boxes[0].height = emulated_metadata.height;
+
+		return;
+	}
+
+	switch (bo->meta.format) {
+	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV21:
+		// Bi-planar
+		xfer_params->xfers_needed = 2;
+
+		y_plane_height = bo->meta.height;
+		c_plane_height = DIV_ROUND_UP(bo->meta.height, 2);
+
+		// Y-plane (full resolution)
+		xfer_params->xfer_boxes[0].x = transfer_box->x;
+		xfer_params->xfer_boxes[0].y = transfer_box->y;
+		xfer_params->xfer_boxes[0].width = transfer_box->width;
+		xfer_params->xfer_boxes[0].height = transfer_box->height;
+
+		// CbCr-plane (half resolution, interleaved, placed below Y-plane)
+		xfer_params->xfer_boxes[1].x = transfer_box->x;
+		xfer_params->xfer_boxes[1].y = transfer_box->y + y_plane_height;
+		xfer_params->xfer_boxes[1].width = transfer_box->width;
+		xfer_params->xfer_boxes[1].height = DIV_ROUND_UP(transfer_box->height, 2);
+
+		break;
+	case DRM_FORMAT_YVU420:
+	case DRM_FORMAT_YVU420_ANDROID:
+		// Tri-planar
+		xfer_params->xfers_needed = 3;
+
+		y_plane_height = bo->meta.height;
+		c_plane_height = DIV_ROUND_UP(bo->meta.height, 2);
+
+		// Y-plane (full resolution)
+		xfer_params->xfer_boxes[0].x = transfer_box->x;
+		xfer_params->xfer_boxes[0].y = transfer_box->y;
+		xfer_params->xfer_boxes[0].width = transfer_box->width;
+		xfer_params->xfer_boxes[0].height = transfer_box->height;
+
+		// Cb-plane (half resolution, placed below Y-plane)
+		xfer_params->xfer_boxes[1].x = transfer_box->x;
+		xfer_params->xfer_boxes[1].y = transfer_box->y + y_plane_height;
+		xfer_params->xfer_boxes[1].width = DIV_ROUND_UP(transfer_box->width, 2);
+		xfer_params->xfer_boxes[1].height = DIV_ROUND_UP(transfer_box->height, 2);
+
+		// Cr-plane (half resolution, placed below Cb-plane)
+		xfer_params->xfer_boxes[2].x = transfer_box->x;
+		xfer_params->xfer_boxes[2].y = transfer_box->y + y_plane_height + c_plane_height;
+		xfer_params->xfer_boxes[2].width = DIV_ROUND_UP(transfer_box->width, 2);
+		xfer_params->xfer_boxes[2].height = DIV_ROUND_UP(transfer_box->height, 2);
+
+		break;
+	}
+}
+
+static bool virtio_gpu_supports_combination_natively(struct driver *drv, uint32_t drm_format,
+						     uint64_t use_flags)
+{
+	struct virtio_gpu_priv *priv = (struct virtio_gpu_priv *)drv->priv;
+
+	if (priv->caps.max_version == 0) {
+		return true;
+	}
+
+	if ((use_flags & BO_USE_RENDERING) &&
+	    !virtio_gpu_bitmask_supports_format(&priv->caps.v1.render, drm_format)) {
+		return false;
+	}
+
+	if ((use_flags & BO_USE_TEXTURE) &&
+	    !virtio_gpu_bitmask_supports_format(&priv->caps.v1.sampler, drm_format)) {
+		return false;
+	}
+
+	if ((use_flags & BO_USE_SCANOUT) && priv->caps_is_v2 &&
+	    !virtio_gpu_bitmask_supports_format(&priv->caps.v2.scanout, drm_format)) {
+		return false;
+	}
+
+	return true;
+}
+
+// For virtio backends that do not support formats natively (e.g. multi-planar formats are not
+// supported in virglrenderer when gbm is unavailable on the host machine), whether or not the
+// format and usage combination can be handled as a blob (byte buffer).
+static bool virtio_gpu_supports_combination_through_emulation(struct driver *drv,
+							      uint32_t drm_format,
+							      uint64_t use_flags)
+{
+	struct virtio_gpu_priv *priv = (struct virtio_gpu_priv *)drv->priv;
+
+	// Only enable emulation on non-gbm virtio backends.
+	if (priv->host_gbm_enabled) {
+		return false;
+	}
+
+	if (use_flags & (BO_USE_RENDERING | BO_USE_SCANOUT)) {
+		return false;
+	}
+
+	if (!virtio_gpu_supports_combination_natively(drv, DRM_FORMAT_R8, use_flags)) {
+		return false;
+	}
+
+	return drm_format == DRM_FORMAT_NV12 || drm_format == DRM_FORMAT_NV21 ||
+	       drm_format == DRM_FORMAT_YVU420 || drm_format == DRM_FORMAT_YVU420_ANDROID;
+}
+
 // Adds the given buffer combination to the list of supported buffer combinations if the
 // combination is supported by the virtio backend.
 static void virtio_gpu_add_combination(struct driver *drv, uint32_t drm_format,
@@ -110,22 +358,18 @@
 	struct virtio_gpu_priv *priv = (struct virtio_gpu_priv *)drv->priv;
 
 	if (features[feat_3d].enabled && priv->caps.max_version >= 1) {
-		if ((use_flags & BO_USE_RENDERING) &&
-		    !virtio_gpu_supports_format(&priv->caps.v1.render, drm_format)) {
-			drv_log("Skipping unsupported render format: %d\n", drm_format);
-			return;
+		if ((use_flags & BO_USE_SCANOUT) && priv->caps_is_v2 &&
+		    !virtio_gpu_supports_combination_natively(drv, drm_format, use_flags)) {
+			drv_log("Scanout format: %d\n", drm_format);
+			use_flags &= ~BO_USE_SCANOUT;
 		}
 
-		if ((use_flags & BO_USE_TEXTURE) &&
-		    !virtio_gpu_supports_format(&priv->caps.v1.sampler, drm_format)) {
-			drv_log("Skipping unsupported texture format: %d\n", drm_format);
+		if (!virtio_gpu_supports_combination_natively(drv, drm_format, use_flags) &&
+		    !virtio_gpu_supports_combination_through_emulation(drv, drm_format,
+								       use_flags)) {
+			drv_log("Skipping unsupported combination format:%d\n", drm_format);
 			return;
 		}
-		if ((use_flags & BO_USE_SCANOUT) && priv->caps_is_v2 &&
-		    !virtio_gpu_supports_format(&priv->caps.v2.scanout, drm_format)) {
-			drv_log("Unsupported scanout format: %d\n", drm_format);
-			use_flags &= ~BO_USE_SCANOUT;
-		}
 	}
 
 	drv_add_combination(drv, drm_format, metadata, use_flags);
@@ -196,11 +440,30 @@
 				  uint64_t use_flags)
 {
 	int ret;
+	size_t i;
 	uint32_t stride;
 	struct drm_virtgpu_resource_create res_create;
+	struct bo_metadata emulated_metadata;
 
-	stride = drv_stride_from_format(format, width, 0);
-	drv_bo_from_format(bo, stride, height, format);
+	if (virtio_gpu_supports_combination_natively(bo->drv, format, use_flags)) {
+		stride = drv_stride_from_format(format, width, 0);
+		drv_bo_from_format(bo, stride, height, format);
+	} else {
+		assert(
+		    virtio_gpu_supports_combination_through_emulation(bo->drv, format, use_flags));
+
+		virtio_gpu_get_emulated_metadata(bo, &emulated_metadata);
+
+		format = emulated_metadata.format;
+		width = emulated_metadata.width;
+		height = emulated_metadata.height;
+		for (i = 0; i < emulated_metadata.num_planes; i++) {
+			bo->meta.strides[i] = emulated_metadata.strides[i];
+			bo->meta.offsets[i] = emulated_metadata.offsets[i];
+			bo->meta.sizes[i] = emulated_metadata.sizes[i];
+		}
+		bo->meta.total_size = emulated_metadata.total_size;
+	}
 
 	/*
 	 * Setting the target is intended to ensure this resource gets bound as a 2D
@@ -290,26 +553,39 @@
 	return ret;
 }
 
-static int virtio_gpu_init(struct driver *drv)
+static void virtio_gpu_init_features_and_caps(struct driver *drv)
 {
-	int ret;
-	struct virtio_gpu_priv *priv;
+	struct virtio_gpu_priv *priv = (struct virtio_gpu_priv *)drv->priv;
 
-	priv = calloc(1, sizeof(*priv));
-	drv->priv = priv;
 	for (uint32_t i = 0; i < ARRAY_SIZE(features); i++) {
 		struct drm_virtgpu_getparam params = { 0 };
 
 		params.param = features[i].feature;
 		params.value = (uint64_t)(uintptr_t)&features[i].enabled;
-		ret = drmIoctl(drv->fd, DRM_IOCTL_VIRTGPU_GETPARAM, &params);
+		int ret = drmIoctl(drv->fd, DRM_IOCTL_VIRTGPU_GETPARAM, &params);
 		if (ret)
 			drv_log("DRM_IOCTL_VIRTGPU_GET_PARAM failed with %s\n", strerror(errno));
 	}
 
 	if (features[feat_3d].enabled) {
 		virtio_gpu_get_caps(drv, &priv->caps, &priv->caps_is_v2);
+	}
 
+	// Multi-planar formats are currently only supported in virglrenderer through gbm.
+	priv->host_gbm_enabled =
+	    virtio_gpu_supports_combination_natively(drv, DRM_FORMAT_NV12, BO_USE_TEXTURE);
+}
+
+static int virtio_gpu_init(struct driver *drv)
+{
+	struct virtio_gpu_priv *priv;
+
+	priv = calloc(1, sizeof(*priv));
+	drv->priv = priv;
+
+	virtio_gpu_init_features_and_caps(drv);
+
+	if (features[feat_3d].enabled) {
 		/* This doesn't mean host can scanout everything, it just means host
 		 * hypervisor can show it. */
 		virtio_gpu_add_combinations(drv, render_target_formats,
@@ -336,16 +612,29 @@
 					    &LINEAR_METADATA, BO_USE_TEXTURE_MASK);
 		virtio_gpu_add_combination(drv, DRM_FORMAT_NV12, &LINEAR_METADATA,
 					   BO_USE_SW_MASK | BO_USE_LINEAR);
+		virtio_gpu_add_combination(drv, DRM_FORMAT_NV21, &LINEAR_METADATA,
+					   BO_USE_SW_MASK | BO_USE_LINEAR);
 	}
 
 	/* Android CTS tests require this. */
+	virtio_gpu_add_combination(drv, DRM_FORMAT_RGB888, &LINEAR_METADATA, BO_USE_SW_MASK);
 	virtio_gpu_add_combination(drv, DRM_FORMAT_BGR888, &LINEAR_METADATA, BO_USE_SW_MASK);
+	virtio_gpu_add_combination(drv, DRM_FORMAT_ABGR16161616F, &LINEAR_METADATA,
+				   BO_USE_SW_MASK | BO_USE_TEXTURE_MASK);
 
 	drv_modify_combination(drv, DRM_FORMAT_NV12, &LINEAR_METADATA,
 			       BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE | BO_USE_HW_VIDEO_DECODER |
 				   BO_USE_HW_VIDEO_ENCODER);
+	drv_modify_combination(drv, DRM_FORMAT_NV21, &LINEAR_METADATA,
+			       BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE | BO_USE_HW_VIDEO_DECODER |
+				   BO_USE_HW_VIDEO_ENCODER);
+	drv_modify_combination(drv, DRM_FORMAT_YVU420, &LINEAR_METADATA,
+			       BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE | BO_USE_HW_VIDEO_DECODER |
+				   BO_USE_HW_VIDEO_ENCODER | BO_USE_RENDERSCRIPT);
 	drv_modify_combination(drv, DRM_FORMAT_R8, &LINEAR_METADATA,
 			       BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE | BO_USE_HW_VIDEO_DECODER);
+	drv_modify_combination(drv, DRM_FORMAT_R16, &LINEAR_METADATA,
+			       BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE | BO_USE_HW_VIDEO_DECODER);
 
 	return drv_modify_linear_combinations(drv);
 }
@@ -384,8 +673,11 @@
 static int virtio_gpu_bo_invalidate(struct bo *bo, struct mapping *mapping)
 {
 	int ret;
+	size_t i;
 	struct drm_virtgpu_3d_transfer_from_host xfer;
 	struct drm_virtgpu_3d_wait waitcmd;
+	struct virtio_transfers_params xfer_params;
+	struct virtio_gpu_priv *priv = (struct virtio_gpu_priv *)bo->drv->priv;
 
 	if (!features[feat_3d].enabled)
 		return 0;
@@ -397,27 +689,44 @@
 
 	memset(&xfer, 0, sizeof(xfer));
 	xfer.bo_handle = mapping->vma->handle;
-	xfer.box.x = mapping->rect.x;
-	xfer.box.y = mapping->rect.y;
-	xfer.box.w = mapping->rect.width;
-	xfer.box.h = mapping->rect.height;
-	xfer.box.d = 1;
 
 	if ((bo->meta.use_flags & BO_USE_RENDERING) == 0) {
-		// Unfortunately, the kernel doesn't actually pass the guest layer_stride and
-		// guest stride to the host (compare virtio_gpu.h and virtgpu_drm.h). For gbm
-		// based resources, we can work around this by using the level field to pass
-		// the stride to virglrenderer's gbm transfer code. However, we need to avoid
-		// doing this for resources which don't rely on that transfer code, which is
-		// resources with the BO_USE_RENDERING flag set.
+		// Unfortunately, the kernel doesn't actually pass the guest layer_stride
+		// and guest stride to the host (compare virtio_gpu.h and virtgpu_drm.h).
+		// For gbm based resources, we can work around this by using the level field
+		// to pass the stride to virglrenderer's gbm transfer code. However, we need
+		// to avoid doing this for resources which don't rely on that transfer code,
+		// which is resources with the BO_USE_RENDERING flag set.
 		// TODO(b/145993887): Send also stride when the patches are landed
-		xfer.level = bo->meta.strides[0];
+		if (priv->host_gbm_enabled) {
+			xfer.level = bo->meta.strides[0];
+		}
 	}
 
-	ret = drmIoctl(bo->drv->fd, DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST, &xfer);
-	if (ret) {
-		drv_log("DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST failed with %s\n", strerror(errno));
-		return -errno;
+	if (virtio_gpu_supports_combination_natively(bo->drv, bo->meta.format,
+						     bo->meta.use_flags)) {
+		xfer_params.xfers_needed = 1;
+		xfer_params.xfer_boxes[0] = mapping->rect;
+	} else {
+		assert(virtio_gpu_supports_combination_through_emulation(bo->drv, bo->meta.format,
+									 bo->meta.use_flags));
+
+		virtio_gpu_get_emulated_transfers_params(bo, &mapping->rect, &xfer_params);
+	}
+
+	for (i = 0; i < xfer_params.xfers_needed; i++) {
+		xfer.box.x = xfer_params.xfer_boxes[i].x;
+		xfer.box.y = xfer_params.xfer_boxes[i].y;
+		xfer.box.w = xfer_params.xfer_boxes[i].width;
+		xfer.box.h = xfer_params.xfer_boxes[i].height;
+		xfer.box.d = 1;
+
+		ret = drmIoctl(bo->drv->fd, DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST, &xfer);
+		if (ret) {
+			drv_log("DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST failed with %s\n",
+				strerror(errno));
+			return -errno;
+		}
 	}
 
 	// The transfer needs to complete before invalidate returns so that any host changes
@@ -437,8 +746,11 @@
 static int virtio_gpu_bo_flush(struct bo *bo, struct mapping *mapping)
 {
 	int ret;
+	size_t i;
 	struct drm_virtgpu_3d_transfer_to_host xfer;
 	struct drm_virtgpu_3d_wait waitcmd;
+	struct virtio_transfers_params xfer_params;
+	struct virtio_gpu_priv *priv = (struct virtio_gpu_priv *)bo->drv->priv;
 
 	if (!features[feat_3d].enabled)
 		return 0;
@@ -448,21 +760,38 @@
 
 	memset(&xfer, 0, sizeof(xfer));
 	xfer.bo_handle = mapping->vma->handle;
-	xfer.box.x = mapping->rect.x;
-	xfer.box.y = mapping->rect.y;
-	xfer.box.w = mapping->rect.width;
-	xfer.box.h = mapping->rect.height;
-	xfer.box.d = 1;
 
 	// Unfortunately, the kernel doesn't actually pass the guest layer_stride and
 	// guest stride to the host (compare virtio_gpu.h and virtgpu_drm.h). We can use
 	// the level to work around this.
-	xfer.level = bo->meta.strides[0];
+	if (priv->host_gbm_enabled) {
+		xfer.level = bo->meta.strides[0];
+	}
 
-	ret = drmIoctl(bo->drv->fd, DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST, &xfer);
-	if (ret) {
-		drv_log("DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST failed with %s\n", strerror(errno));
-		return -errno;
+	if (virtio_gpu_supports_combination_natively(bo->drv, bo->meta.format,
+						     bo->meta.use_flags)) {
+		xfer_params.xfers_needed = 1;
+		xfer_params.xfer_boxes[0] = mapping->rect;
+	} else {
+		assert(virtio_gpu_supports_combination_through_emulation(bo->drv, bo->meta.format,
+									 bo->meta.use_flags));
+
+		virtio_gpu_get_emulated_transfers_params(bo, &mapping->rect, &xfer_params);
+	}
+
+	for (i = 0; i < xfer_params.xfers_needed; i++) {
+		xfer.box.x = xfer_params.xfer_boxes[i].x;
+		xfer.box.y = xfer_params.xfer_boxes[i].y;
+		xfer.box.w = xfer_params.xfer_boxes[i].width;
+		xfer.box.h = xfer_params.xfer_boxes[i].height;
+		xfer.box.d = 1;
+
+		ret = drmIoctl(bo->drv->fd, DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST, &xfer);
+		if (ret) {
+			drv_log("DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST failed with %s\n",
+				strerror(errno));
+			return -errno;
+		}
 	}
 
 	// If the buffer is only accessed by the host GPU, then the flush is ordered
@@ -485,7 +814,6 @@
 
 static uint32_t virtio_gpu_resolve_format(struct driver *drv, uint32_t format, uint64_t use_flags)
 {
-
 	switch (format) {
 	case DRM_FORMAT_FLEX_IMPLEMENTATION_DEFINED:
 		/* Camera subsystem requires NV12. */