/*
 * Copyright (C) 2020 Arm Limited.
 *
 * Copyright 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "MapperMetadata.h"
#include "SharedMetadata.h"
#include "core/format_info.h"
#include "core/mali_gralloc_bufferallocation.h"
#include "mali_gralloc_buffer.h"
#include "mali_gralloc_log.h"
#include "drmutils.h"
#include "gralloctypes/Gralloc4.h"

#include "exynos_format.h"
#include "mali_gralloc_formats.h"

#include <pixel-gralloc/metadata.h>
#include <pixel-gralloc/utils.h>

#include <vector>

namespace arm
{
namespace mapper
{
namespace common
{

using aidl::android::hardware::graphics::common::PlaneLayout;
using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
using aidl::android::hardware::graphics::common::Rect;
using aidl::android::hardware::graphics::common::Dataspace;
using aidl::android::hardware::graphics::common::BlendMode;
using aidl::android::hardware::graphics::common::StandardMetadataType;
using aidl::android::hardware::graphics::common::XyColor;
using aidl::android::hardware::graphics::common::Smpte2086;
using aidl::android::hardware::graphics::common::Cta861_3;
#if 0
using aidl::arm::graphics::ArmMetadataType;
#endif

bool isStandardMetadataType(const MetadataType &metadataType) {
	return !std::strncmp(metadataType.name.c_str(),
			     GRALLOC4_STANDARD_METADATA_TYPE,
			     metadataType.name.size());
}

int get_num_planes(const private_handle_t *hnd)
{
	if (is_exynos_format(hnd->get_alloc_format()))
	{
		switch (hnd->get_alloc_format())
		{
			case HAL_PIXEL_FORMAT_YCrCb_420_SP:
			case HAL_PIXEL_FORMAT_EXYNOS_YV12_M:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_P:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_P_M:
				return 3;

			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_TILED:
			case HAL_PIXEL_FORMAT_EXYNOS_YCrCb_420_SP_M:
			case HAL_PIXEL_FORMAT_EXYNOS_YCrCb_420_SP_M_FULL:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_S10B:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_S10B:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_P010_M:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_SBWC:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_10B_SBWC:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_SBWC:
			case HAL_PIXEL_FORMAT_EXYNOS_YCrCb_420_SP_M_SBWC:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_10B_SBWC:
			case HAL_PIXEL_FORMAT_EXYNOS_YCrCb_420_SP_M_10B_SBWC:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_SBWC_L50:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_SBWC_L75:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_SBWC_L50:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_SBWC_L75:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_10B_SBWC_L40:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_10B_SBWC_L60:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_10B_SBWC_L80:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_10B_SBWC_L40:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_10B_SBWC_L60:
			case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_10B_SBWC_L80:
				return 2;
		}
	}

	return hnd->is_multi_plane() ? (hnd->plane_info[2].offset == 0 ? 2 : 3) : 1;
}

static std::vector<std::vector<PlaneLayoutComponent>> plane_layout_components_from_handle(const private_handle_t *hnd)
{
	/* Re-define the component constants to make the table easier to read. */
	const ExtendableType R = android::gralloc4::PlaneLayoutComponentType_R;
	const ExtendableType G = android::gralloc4::PlaneLayoutComponentType_G;
	const ExtendableType B = android::gralloc4::PlaneLayoutComponentType_B;
	const ExtendableType A = android::gralloc4::PlaneLayoutComponentType_A;
	const ExtendableType CB = android::gralloc4::PlaneLayoutComponentType_CB;
	const ExtendableType CR = android::gralloc4::PlaneLayoutComponentType_CR;
	const ExtendableType Y = android::gralloc4::PlaneLayoutComponentType_Y;
	const ExtendableType RAW = android::gralloc4::PlaneLayoutComponentType_RAW;

	struct table_entry
	{
		uint32_t drm_fourcc;
		std::vector<std::vector<PlaneLayoutComponent>> components;
	};

	/* clang-format off */
	static table_entry table[] = {
		/* 16 bit RGB(A) */
		{
			.drm_fourcc = DRM_FORMAT_RGB565,
			.components = { { { B, 0, 5 }, { G, 5, 6 }, { R, 11, 5 } } }
		},
		{
			.drm_fourcc = DRM_FORMAT_BGR565,
			.components = { { { R, 0, 5 }, { G, 5, 6 }, { B, 11, 5 } } }
		},
		/* 24 bit RGB(A) */
		{
			.drm_fourcc = DRM_FORMAT_BGR888,
			.components = { { { R, 0, 8 }, { G, 8, 8 }, { B, 16, 8 } } }
		},
		/* 32 bit RGB(A) */
		{
			.drm_fourcc = DRM_FORMAT_ARGB8888,
			.components = { { { B, 0, 8 }, { G, 8, 8 }, { R, 16, 8 }, { A, 24, 8 } } }
		},
		{
			.drm_fourcc = DRM_FORMAT_ABGR8888,
			.components = { { { R, 0, 8 }, { G, 8, 8 }, { B, 16, 8 }, { A, 24, 8 } } }
		},
		{
			.drm_fourcc = DRM_FORMAT_XBGR8888,
			.components = { { { R, 0, 8 }, { G, 8, 8 }, { B, 16, 8 } } }
		},
		{
			.drm_fourcc = DRM_FORMAT_ABGR2101010,
			.components = { { { R, 0, 10 }, { G, 10, 10 }, { B, 20, 10 }, { A, 30, 2 } } }
		},
		/* 64 bit RGB(A) */
		{
			.drm_fourcc = DRM_FORMAT_ABGR16161616F,
			.components = { { { R, 0, 16 }, { G, 16, 16 }, { B, 32, 16 }, { A, 48, 16 } } }
		},
		/* Single plane 8 bit YUV 4:2:2 */
		{
			.drm_fourcc = DRM_FORMAT_YUYV,
			.components = { { { Y, 0, 8 }, { CB, 8, 8 }, { Y, 16, 8 }, { CR, 24, 8 } } }
		},
		/* Single plane 10 bit YUV 4:4:4 */
		{
			.drm_fourcc = DRM_FORMAT_Y410,
			.components = { { { CB, 0, 10 }, { Y, 10, 10 }, { CR, 20, 10 }, { A, 30, 2 } } }
		},
		/* Single plane 10 bit YUV 4:2:2 */
		{
			.drm_fourcc = DRM_FORMAT_Y210,
			.components = { { { Y, 6, 10 }, { CB, 22, 10 }, { Y, 38, 10 }, { CR, 54, 10 } } }
		},
		/* Single plane 10 bit YUV 4:2:0 */
		{
			.drm_fourcc = DRM_FORMAT_Y0L2,
			.components = { {
				{ Y, 0, 10 }, { CB, 10, 10 }, { Y, 20, 10 }, { A, 30, 1 }, { A, 31, 1 },
				{ Y, 32, 10 }, { CR, 42, 10 }, { Y, 52, 10 }, { A, 62, 1 }, { A, 63, 1 }
			} }
		},
		/* Semi-planar 8 bit YUV 4:2:2 */
		{
			.drm_fourcc = DRM_FORMAT_NV16,
			.components = {
				{ { Y, 0, 8 } },
				{ { CB, 0, 8 }, { CR, 8, 8 } }
			}
		},
		/* Semi-planar 8 bit YUV 4:2:0 */
		{
			.drm_fourcc = DRM_FORMAT_NV12,
			.components = {
				{ { Y, 0, 8 } },
				{ { CB, 0, 8 }, { CR, 8, 8 } }
			}
		},
		{
			.drm_fourcc = DRM_FORMAT_NV21,
			.components = {
				{ { Y, 0, 8 } },
				{ { CR, 0, 8 }, { CB, 8, 8 } }
			}
		},
		/* Semi-planar 10 bit YUV 4:2:2 */
		{
			.drm_fourcc = DRM_FORMAT_P210,
			.components = {
				{ { Y, 6, 10 } },
				{ { CB, 6, 10 }, { CB, 22, 10 } }
			}
		},
		/* Semi-planar 10 bit YUV 4:2:0 */
		{
			.drm_fourcc = DRM_FORMAT_P010,
			.components = {
				{ { Y, 6, 10 } },
				{ { CB, 6, 10 }, { CR, 22, 10 } }
			}
		},
		/* Planar 8 bit YUV 4:2:0 */
		{
			.drm_fourcc = DRM_FORMAT_YVU420,
			.components = {
				{ { Y, 0, 8 } },
				{ { CR, 0, 8 } },
				{ { CB, 0, 8 } }
			}
		},
		/* Planar 8 bit YUV 4:4:4 */
		{
			.drm_fourcc = DRM_FORMAT_YUV444,
			.components = {
				{ { Y, 0, 8 } },
				{ { CB, 0, 8 } },
				{ { CR, 0, 8 } }
			}
		},

		/* AFBC Only FourCC */
		{.drm_fourcc = DRM_FORMAT_YUV420_8BIT, .components = { {} } },
		{.drm_fourcc = DRM_FORMAT_YUV420_10BIT, .components = { {} } },

		/* Google specific formats */
		{
			.drm_fourcc = DRM_FORMAT_R8,
			.components = {
				{ { R, 0, 8 } }
			}
		},
		{
			.drm_fourcc = DRM_FORMAT_RG88,
			.components = {
				{ { R, 0, 8 }, { G, 8, 8 } }
			}
		},
	};
	/* clang-format on */

	const uint32_t drm_fourcc = drm_fourcc_from_handle(hnd);
	if (drm_fourcc != DRM_FORMAT_INVALID)
	{
		for (const auto& entry : table)
		{
			if (entry.drm_fourcc == drm_fourcc)
			{
				return entry.components;
			}
		}
	}

	switch (hnd->get_alloc_format())
	{
		case HAL_PIXEL_FORMAT_RAW10:
			return {{{RAW, 0, -1}}};
		case HAL_PIXEL_FORMAT_RAW12:
			return {{{RAW, 0, -1}}};
		case HAL_PIXEL_FORMAT_BLOB:
			break;
		default:
			MALI_GRALLOC_LOGW("Could not find component description for Format(%#x) FourCC value(%#x)",
				hnd->get_alloc_format(), drm_fourcc);
	}

	return std::vector<std::vector<PlaneLayoutComponent>>(0);
}

android::status_t get_plane_layouts(const private_handle_t *handle, std::vector<PlaneLayout> *layouts)
{
	const int num_planes = get_num_planes(handle);
	uint32_t base_format = handle->alloc_format & MALI_GRALLOC_INTFMT_FMT_MASK;
	int32_t format_index = get_format_index(base_format);
	if (format_index < 0)
	{
		MALI_GRALLOC_LOGE("Negative format index in get_plane_layouts");
		return android::BAD_VALUE;
	}
	const format_info_t format_info = formats[format_index];
	layouts->reserve(num_planes);

	if (is_exynos_format(handle->get_alloc_format()))
	{
		std::vector<std::vector<PlaneLayoutComponent>> components = plane_layout_components_from_handle(handle);
		for (size_t plane_index = 0; plane_index < num_planes; ++plane_index)
		{
			int64_t plane_size = handle->plane_info[plane_index].size;
			int64_t sample_increment_in_bits = format_info.bpp[plane_index];
			int64_t offset = handle->plane_info[plane_index].offset;

			static bool warn_multifd = true;
			if (warn_multifd) {
				uint8_t fd_count = get_exynos_fd_count(base_format);
				if (fd_count != 1) {
					warn_multifd = false;
					MALI_GRALLOC_LOGW("Offsets in plane layouts of multi-fd format (%s %" PRIu64
							") are not reliable. This can lead to image corruption.",
							format_name(base_format), handle->alloc_format);
				}
			}

			PlaneLayout layout = {.offsetInBytes = offset,
				                  .sampleIncrementInBits = sample_increment_in_bits,
				                  .strideInBytes = static_cast<int64_t>(handle->plane_info[plane_index].byte_stride),
				                  .widthInSamples = static_cast<int64_t>(handle->plane_info[plane_index].alloc_width),
				                  .heightInSamples = static_cast<int64_t>(handle->plane_info[plane_index].alloc_height),
				                  .totalSizeInBytes = plane_size,
				                  .horizontalSubsampling = (plane_index == 0 ? 1 : format_info.hsub),
				                  .verticalSubsampling = (plane_index == 0 ? 1 : format_info.vsub),
				                  .components = components.size() > plane_index ? components[plane_index] :
				                                                                  std::vector<PlaneLayoutComponent>(0) };
			layouts->push_back(layout);
		}
	}
	else
	{
		std::vector<std::vector<PlaneLayoutComponent>> components = plane_layout_components_from_handle(handle);
		for (size_t plane_index = 0; plane_index < num_planes; ++plane_index)
		{
			int64_t plane_size;
			if (plane_index < num_planes - 1)
			{
				plane_size = handle->plane_info[plane_index + 1].offset;
			}
			else
			{
				int64_t layer_size = handle->alloc_sizes[0] / handle->layer_count;
				plane_size = layer_size - handle->plane_info[plane_index].offset;
			}

			if (handle->fd_count > 1 && handle->plane_info[plane_index].fd_idx == plane_index)
			{
				plane_size = handle->plane_info[plane_index].size;
			}

			int64_t sample_increment_in_bits = 0;
			if (handle->alloc_format & MALI_GRALLOC_INTFMT_AFBC_BASIC)
			{
				sample_increment_in_bits = format_info.bpp_afbc[plane_index];
			}
			else
			{
				sample_increment_in_bits = format_info.bpp[plane_index];
			}

			switch (handle->get_alloc_format())
			{
				case HAL_PIXEL_FORMAT_RAW10:
				case HAL_PIXEL_FORMAT_RAW12:
					sample_increment_in_bits = 0;
					break;
				default:
					break;
			}

			PlaneLayout layout = {.offsetInBytes = handle->plane_info[plane_index].offset,
				                  .sampleIncrementInBits = sample_increment_in_bits,
				                  .strideInBytes = static_cast<int64_t>(handle->plane_info[plane_index].byte_stride),
				                  .widthInSamples = static_cast<int64_t>(handle->plane_info[plane_index].alloc_width),
				                  .heightInSamples = static_cast<int64_t>(handle->plane_info[plane_index].alloc_height),
				                  .totalSizeInBytes = plane_size,
				                  .horizontalSubsampling = (plane_index == 0 ? 1 : format_info.hsub),
				                  .verticalSubsampling = (plane_index == 0 ? 1 : format_info.vsub),
				                  .components = components.size() > plane_index ? components[plane_index] :
				                                                                  std::vector<PlaneLayoutComponent>(0) };
			layouts->push_back(layout);
		}
	}

	return android::OK;
}

static frameworks_vec<uint8_t> encodePointer(void* ptr) {
	constexpr uint8_t kPtrSize = sizeof(void*);

	frameworks_vec<uint8_t> output(kPtrSize);
	std::memcpy(output.data(), &ptr, kPtrSize);

	return output;
}

Error get_metadata(const private_handle_t *handle, const MetadataType &metadataType, std::vector<uint8_t> &outVec)
{
	android::status_t err = android::OK;
	frameworks_vec<uint8_t> vec;

	if (isStandardMetadataType(metadataType))
	{
		switch (static_cast<StandardMetadataType>(metadataType.value))
		{
		case StandardMetadataType::BUFFER_ID:
			err = android::gralloc4::encodeBufferId(handle->backing_store_id, &vec);
			break;
		case StandardMetadataType::NAME:
		{
			std::string name;
			get_name(handle, &name);
			err = android::gralloc4::encodeName(name, &vec);
			break;
		}
		case StandardMetadataType::WIDTH:
			err = android::gralloc4::encodeWidth(handle->width, &vec);
			break;
		case StandardMetadataType::HEIGHT:
			err = android::gralloc4::encodeHeight(handle->height, &vec);
			break;
		case StandardMetadataType::LAYER_COUNT:
			err = android::gralloc4::encodeLayerCount(handle->layer_count, &vec);
			break;
		case StandardMetadataType::PIXEL_FORMAT_REQUESTED:
			err = android::gralloc4::encodePixelFormatRequested(static_cast<hidl::PixelFormat>(handle->req_format), &vec);
			break;
		case StandardMetadataType::PIXEL_FORMAT_FOURCC:
			err = android::gralloc4::encodePixelFormatFourCC(drm_fourcc_from_handle(handle), &vec);
			break;
		case StandardMetadataType::PIXEL_FORMAT_MODIFIER:
			err = android::gralloc4::encodePixelFormatModifier(drm_modifier_from_handle(handle), &vec);
			break;
		case StandardMetadataType::USAGE:
			err = android::gralloc4::encodeUsage(handle->consumer_usage | handle->producer_usage, &vec);
			break;
		case StandardMetadataType::ALLOCATION_SIZE:
		{
			uint64_t total_size = 0;
			for (int fidx = 0; fidx < handle->fd_count; fidx++)
			{
				total_size += handle->alloc_sizes[fidx];
			}
			err = android::gralloc4::encodeAllocationSize(total_size, &vec);
			break;
		}
		case StandardMetadataType::PROTECTED_CONTENT:
		{
			/* This is set to 1 if the buffer has protected content. */
			const int is_protected =
			    (((handle->consumer_usage | handle->producer_usage) & static_cast<uint64_t>(BufferUsage::PROTECTED)) == 0) ? 0 : 1;
			err = android::gralloc4::encodeProtectedContent(is_protected, &vec);
			break;
		}
		case StandardMetadataType::COMPRESSION:
		{
			ExtendableType compression;
			if (handle->alloc_format & MALI_GRALLOC_INTFMT_AFBC_BASIC)
			{
				compression = Compression_AFBC;
			}
			else
			{
				compression = android::gralloc4::Compression_None;
			}
			err = android::gralloc4::encodeCompression(compression, &vec);
			break;
		}
		case StandardMetadataType::INTERLACED:
			err = android::gralloc4::encodeInterlaced(android::gralloc4::Interlaced_None, &vec);
			break;
		case StandardMetadataType::CHROMA_SITING:
		{
			int format_index = get_format_index(handle->alloc_format & MALI_GRALLOC_INTFMT_FMT_MASK);
			if (format_index < 0)
			{
				err = android::BAD_VALUE;
				break;
			}
			ExtendableType siting = android::gralloc4::ChromaSiting_None;
			if (formats[format_index].is_yuv)
			{
				siting = android::gralloc4::ChromaSiting_Unknown;
			}
			err = android::gralloc4::encodeChromaSiting(siting, &vec);
			break;
		}
		case StandardMetadataType::PLANE_LAYOUTS:
		{
			std::vector<PlaneLayout> layouts;
			err = get_plane_layouts(handle, &layouts);
			if (!err)
			{
				err = android::gralloc4::encodePlaneLayouts(layouts, &vec);
			}
			break;
		}
		case StandardMetadataType::DATASPACE:
		{
			std::optional<Dataspace> dataspace;
			get_dataspace(handle, &dataspace);
			err = android::gralloc4::encodeDataspace(dataspace.value_or(Dataspace::UNKNOWN), &vec);
			break;
		}
		case StandardMetadataType::BLEND_MODE:
		{
			std::optional<BlendMode> blend_mode;
			get_blend_mode(handle, &blend_mode);
			err = android::gralloc4::encodeBlendMode(blend_mode.value_or(BlendMode::INVALID), &vec);
			break;
		}
		case StandardMetadataType::CROP:
		{
			const int num_planes = get_num_planes(handle);
			std::vector<Rect> crops(num_planes);
			for (size_t plane_index = 0; plane_index < num_planes; ++plane_index)
			{
				/* Set the default crop rectangle. Android mandates that it must fit [0, 0, widthInSamples, heightInSamples]
				 * We always require using the requested width and height for the crop rectangle size.
				 * For planes > 0 the size might need to be scaled, but since we only use plane[0] for crop set it to the
				 * Android default of [0, 0, widthInSamples, heightInSamples] for other planes.
				 */
				Rect rect = {.top = 0,
				             .left = 0,
				             .right = static_cast<int32_t>(handle->plane_info[plane_index].alloc_width),
				             .bottom = static_cast<int32_t>(handle->plane_info[plane_index].alloc_height) };
				if (plane_index == 0)
				{
					std::optional<Rect> crop_rect;
					get_crop_rect(handle, &crop_rect);
					if (crop_rect.has_value())
					{
						rect = crop_rect.value();
					}
					else
					{
						rect = {.top = 0, .left = 0, .right = handle->width, .bottom = handle->height };
					}
				}
				crops[plane_index] = rect;
			}
			err = android::gralloc4::encodeCrop(crops, &vec);
			break;
		}
		case StandardMetadataType::SMPTE2086:
		{
			std::optional<Smpte2086> smpte2086;
			get_smpte2086(handle, &smpte2086);
			err = android::gralloc4::encodeSmpte2086(smpte2086, &vec);
			break;
		}
		case StandardMetadataType::CTA861_3:
		{
			std::optional<Cta861_3> cta861_3;
			get_cta861_3(handle, &cta861_3);
			err = android::gralloc4::encodeCta861_3(cta861_3, &vec);
			break;
		}
		case StandardMetadataType::SMPTE2094_40:
		{
			std::optional<std::vector<uint8_t>> smpte2094_40;
			get_smpte2094_40(handle, &smpte2094_40);
			err = android::gralloc4::encodeSmpte2094_40(smpte2094_40, &vec);
			break;
		}
		case StandardMetadataType::INVALID:
		default:
			err = android::BAD_VALUE;
		}
	}
	else if (metadataType.name == ::pixel::graphics::kPixelMetadataTypeName) {
		switch (static_cast<::pixel::graphics::MetadataType>(metadataType.value)) {
			case ::pixel::graphics::MetadataType::VIDEO_HDR:
				vec = ::pixel::graphics::utils::encode(get_video_hdr(handle));
				break;
			case ::pixel::graphics::MetadataType::VIDEO_ROI:
			{
				auto roi = get_video_roiinfo(handle);
				if (roi == nullptr) {
					err = android::BAD_VALUE;
				} else {
					vec = ::pixel::graphics::utils::encode(roi);
				}
				break;
			}
			case ::pixel::graphics::MetadataType::PLANE_DMA_BUFS:
			{
				std::vector<int> plane_fds(MAX_BUFFER_FDS, -1);
				for (int i = 0; i < get_num_planes(handle); i++) {
					plane_fds[i] = handle->fds[handle->plane_info[i].fd_idx];
				}
				vec = ::pixel::graphics::utils::encode(plane_fds);
				break;
			}
			case ::pixel::graphics::MetadataType::VIDEO_GMV:
			{
				auto gmv = get_video_gmv(handle);
				vec = ::pixel::graphics::utils::encode(gmv);
				break;
			}
			default:
				err = android::BAD_VALUE;
		}
	}
	else
	{
		err = android::BAD_VALUE;
	}

	outVec = std::vector<uint8_t>(vec);
	return ((err) ? Error::UNSUPPORTED : Error::NONE);
}

Error set_metadata(const private_handle_t *handle, const MetadataType &metadataType, const frameworks_vec<uint8_t> &metadata)
{
	android::status_t err = android::OK;
	if (isStandardMetadataType(metadataType))
	{
		switch (static_cast<StandardMetadataType>(metadataType.value))
		{
		case StandardMetadataType::DATASPACE:
		{
			Dataspace dataspace;
			err = android::gralloc4::decodeDataspace(metadata, &dataspace);
			if (!err)
			{
				set_dataspace(handle, dataspace);
			}
			break;
		}
		case StandardMetadataType::BLEND_MODE:
		{
			BlendMode blend_mode;
			err = android::gralloc4::decodeBlendMode(metadata, &blend_mode);
			if (!err)
			{
				set_blend_mode(handle, blend_mode);
			}
			break;
		}
		case StandardMetadataType::SMPTE2086:
		{
			std::optional<Smpte2086> smpte2086;
			err = android::gralloc4::decodeSmpte2086(metadata, &smpte2086);
			if (!err)
			{
				err = set_smpte2086(handle, smpte2086);
			}
			break;
		}
		case StandardMetadataType::CTA861_3:
		{
			std::optional<Cta861_3> cta861_3;
			err = android::gralloc4::decodeCta861_3(metadata, &cta861_3);
			if (!err)
			{
				err = set_cta861_3(handle, cta861_3);
			}
			break;
		}
		case StandardMetadataType::SMPTE2094_40:
		{
			std::optional<std::vector<uint8_t>> smpte2094_40;
			err = android::gralloc4::decodeSmpte2094_40(metadata, &smpte2094_40);
			if (!err)
			{
				err = set_smpte2094_40(handle, smpte2094_40);
			}
			break;
		}
		case StandardMetadataType::CROP:
		{
			std::vector<Rect> crops;
			err = android::gralloc4::decodeCrop(metadata, &crops);
			if (!err)
			{
				err = set_crop_rect(handle, crops[0]);
			}
			break;
		}
		/* The following meta data types cannot be changed after allocation. */
		case StandardMetadataType::BUFFER_ID:
		case StandardMetadataType::NAME:
		case StandardMetadataType::WIDTH:
		case StandardMetadataType::HEIGHT:
		case StandardMetadataType::LAYER_COUNT:
		case StandardMetadataType::PIXEL_FORMAT_REQUESTED:
		case StandardMetadataType::USAGE:
			return Error::BAD_VALUE;
		/* Changing other metadata types is unsupported. */
		case StandardMetadataType::PLANE_LAYOUTS:
		case StandardMetadataType::PIXEL_FORMAT_FOURCC:
		case StandardMetadataType::PIXEL_FORMAT_MODIFIER:
		case StandardMetadataType::ALLOCATION_SIZE:
		case StandardMetadataType::PROTECTED_CONTENT:
		case StandardMetadataType::COMPRESSION:
		case StandardMetadataType::INTERLACED:
		case StandardMetadataType::CHROMA_SITING:
		case StandardMetadataType::INVALID:
		default:
			return Error::UNSUPPORTED;
		}
	}
	else if (metadataType.name == ::pixel::graphics::kPixelMetadataTypeName)
	{
		switch (static_cast<::pixel::graphics::MetadataType>(metadataType.value))
		{
		case ::pixel::graphics::MetadataType::VIDEO_GMV:
		{
			auto gmv = ::pixel::graphics::utils::decode<VideoGMV>(metadata);
			if (!gmv.has_value())
			{
				MALI_GRALLOC_LOGE("Failed to decode VideoGMV");
				return Error::BAD_VALUE;
			}
			err = set_video_gmv(handle, gmv.value());
			break;
		}
		default:
			return Error::UNSUPPORTED;
		}
	}
	else
	{
		err = android::BAD_VALUE;
	}
	return (err ? Error::UNSUPPORTED : Error::NONE);
}

#ifdef GRALLOC_MAPPER_4
Error getFromBufferDescriptorInfo(IMapper::BufferDescriptorInfo const &description,
                                 MetadataType const &metadataType, std::vector<uint8_t> &outVec)
{
	/* This will hold the metadata that is returned. */
	frameworks_vec<uint8_t> vec;

	buffer_descriptor_t descriptor;
	descriptor.width = description.width;
	descriptor.height = description.height;
	descriptor.layer_count = description.layerCount;
	descriptor.hal_format = static_cast<uint64_t>(description.format);
	descriptor.producer_usage = static_cast<uint64_t>(description.usage);
	descriptor.consumer_usage = descriptor.producer_usage;
	descriptor.format_type = MALI_GRALLOC_FORMAT_TYPE_USAGE;

	/* Check if it is possible to allocate a buffer for the given description */
	const int alloc_result = mali_gralloc_derive_format_and_size(&descriptor);
	if (alloc_result != 0)
	{
		MALI_GRALLOC_LOGV("Allocation for the given description will not succeed. error: %d", alloc_result);
		outVec = vec;
		return Error::BAD_VALUE;
	}
	/* Create buffer handle from the initialized descriptor without a backing store or shared metadata region.
	 * Used to share functionality with the normal metadata get function that can only use the allocated buffer handle
	 * and does not have the buffer descriptor available. */
	private_handle_t partial_handle(0, descriptor.alloc_sizes, descriptor.consumer_usage, descriptor.producer_usage,
			                nullptr, descriptor.fd_count,
	                                descriptor.hal_format, descriptor.alloc_format,
	                                descriptor.width, descriptor.height, descriptor.pixel_stride,
	                                descriptor.layer_count, descriptor.plane_info);
	if (isStandardMetadataType(metadataType))
	{
		android::status_t err = android::OK;

		switch (static_cast<StandardMetadataType>(metadataType.value))
		{
		case StandardMetadataType::NAME:
			err = android::gralloc4::encodeName(description.name, &vec);
			break;
		case StandardMetadataType::WIDTH:
			err = android::gralloc4::encodeWidth(description.width, &vec);
			break;
		case StandardMetadataType::HEIGHT:
			err = android::gralloc4::encodeHeight(description.height, &vec);
			break;
		case StandardMetadataType::LAYER_COUNT:
			err = android::gralloc4::encodeLayerCount(description.layerCount, &vec);
			break;
		case StandardMetadataType::PIXEL_FORMAT_REQUESTED:
			err = android::gralloc4::encodePixelFormatRequested(static_cast<hidl::PixelFormat>(description.format), &vec);
			break;
		case StandardMetadataType::USAGE:
			err = android::gralloc4::encodeUsage(description.usage, &vec);
			break;
		case StandardMetadataType::PIXEL_FORMAT_FOURCC:
			err = android::gralloc4::encodePixelFormatFourCC(drm_fourcc_from_handle(&partial_handle), &vec);
			break;
		case StandardMetadataType::PIXEL_FORMAT_MODIFIER:
			err = android::gralloc4::encodePixelFormatModifier(drm_modifier_from_handle(&partial_handle), &vec);
			break;
		case StandardMetadataType::ALLOCATION_SIZE:
		{
			/* TODO: Returns expetected size if the buffer was actually allocated.
			 * Is this the right behavior?
			 */
			uint64_t total_size = 0;
			for (int fidx = 0; fidx < descriptor.fd_count; fidx++)
			{
				total_size += descriptor.alloc_sizes[fidx];
			}
			err = android::gralloc4::encodeAllocationSize(total_size, &vec);
			break;
		}
		case StandardMetadataType::PROTECTED_CONTENT:
		{
			/* This is set to 1 if the buffer has protected content. */
			const int is_protected =
			    (((partial_handle.consumer_usage | partial_handle.producer_usage) & static_cast<uint64_t>(BufferUsage::PROTECTED))) ? 1 : 0;
			err = android::gralloc4::encodeProtectedContent(is_protected, &vec);
			break;
		}
		case StandardMetadataType::COMPRESSION:
		{
			ExtendableType compression;
			if (partial_handle.alloc_format & MALI_GRALLOC_INTFMT_AFBC_BASIC)
			{
				compression = Compression_AFBC;
			}
			else
			{
				compression = android::gralloc4::Compression_None;
			}
			err = android::gralloc4::encodeCompression(compression, &vec);
			break;
		}
		case StandardMetadataType::INTERLACED:
			err = android::gralloc4::encodeInterlaced(android::gralloc4::Interlaced_None, &vec);
			break;
		case StandardMetadataType::CHROMA_SITING:
		{
			int format_index = get_format_index(partial_handle.alloc_format & MALI_GRALLOC_INTFMT_FMT_MASK);
			if (format_index < 0)
			{
				err = android::BAD_VALUE;
				break;
			}
			ExtendableType siting = android::gralloc4::ChromaSiting_None;
			if (formats[format_index].is_yuv)
			{
				siting = android::gralloc4::ChromaSiting_Unknown;
			}
			err = android::gralloc4::encodeChromaSiting(siting, &vec);
			break;
		}
		case StandardMetadataType::PLANE_LAYOUTS:
		{
			std::vector<PlaneLayout> layouts;
			err = get_plane_layouts(&partial_handle, &layouts);
			if (!err)
			{
				err = android::gralloc4::encodePlaneLayouts(layouts, &vec);
			}
			break;
		}
		case StandardMetadataType::DATASPACE:
		{
			android_dataspace_t dataspace;
			get_format_dataspace(partial_handle.alloc_format & MALI_GRALLOC_INTFMT_FMT_MASK,
			                     partial_handle.consumer_usage | partial_handle.producer_usage, partial_handle.width,
			                     partial_handle.height, &dataspace);
			err = android::gralloc4::encodeDataspace(static_cast<Dataspace>(dataspace), &vec);
			break;
		}
		case StandardMetadataType::BLEND_MODE:
			err = android::gralloc4::encodeBlendMode(BlendMode::INVALID, &vec);
			break;
		case StandardMetadataType::CROP:
		{
			const int num_planes = get_num_planes(&partial_handle);
			std::vector<Rect> crops(num_planes);
			for (size_t plane_index = 0; plane_index < num_planes; ++plane_index)
			{
				Rect rect = {.top = 0,
					         .left = 0,
					         .right = static_cast<int32_t>(partial_handle.plane_info[plane_index].alloc_width),
					         .bottom = static_cast<int32_t>(partial_handle.plane_info[plane_index].alloc_height) };
				if (plane_index == 0)
				{
					rect = {.top = 0, .left = 0, .right = partial_handle.width, .bottom = partial_handle.height };
				}
				crops[plane_index] = rect;
			}
			err = android::gralloc4::encodeCrop(crops, &vec);
			break;
		}
		case StandardMetadataType::SMPTE2086:
		{
			std::optional<Smpte2086> smpte2086{};
			err = android::gralloc4::encodeSmpte2086(smpte2086, &vec);
			break;
		}
		case StandardMetadataType::CTA861_3:
		{
			std::optional<Cta861_3> cta861_3{};
			err = android::gralloc4::encodeCta861_3(cta861_3, &vec);
			break;
		}
		case StandardMetadataType::SMPTE2094_40:
		{
			std::optional<std::vector<uint8_t>> smpte2094_40{};
			err = android::gralloc4::encodeSmpte2094_40(smpte2094_40, &vec);
			break;
		}

		case StandardMetadataType::BUFFER_ID:
		case StandardMetadataType::INVALID:
		default:
			err = android::BAD_VALUE;
		}
		outVec = vec;
		return ((err) ? Error::UNSUPPORTED : Error::NONE);
	}
	else
	{
		outVec = vec;
		return Error::UNSUPPORTED;
	}
}

#endif // GRALLOC_MAPPER_4
} // namespace common
} // namespace mapper
} // namespace arm
