diff --git a/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl b/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl index 8126143dd0..4bca795602 100644 --- a/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl +++ b/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl @@ -22,9 +22,9 @@ package android.hardware.graphics.common; * This is an enum that defines the common types of gralloc 4 buffer metadata. The comments for * each enum include a description of the metadata that is associated with the type. * - * IMapper@4.x must support getting the following standard buffer metadata types, with the exception - * of SMPTE 2094-10 metadata. IMapper@4.x may support setting these standard buffer metadata types - * as well. + * IMapper@4.x & later must support getting the following standard buffer metadata types, with the + * exception of SMPTE 2094-10 and SMPTE 2094-40 metadata. IMapper@4.x & later may support setting + * these standard buffer metadata types as well. * * When encoding these StandardMetadataTypes into a byte stream, the associated MetadataType is * is first encoded followed by the StandardMetadataType value. The MetadataType is encoded by diff --git a/graphics/mapper/stable-c/Android.bp b/graphics/mapper/stable-c/Android.bp index c03f67ea47..d40e160207 100644 --- a/graphics/mapper/stable-c/Android.bp +++ b/graphics/mapper/stable-c/Android.bp @@ -61,6 +61,10 @@ cc_test { srcs: [ "implutils/impltests.cpp", ], + shared_libs: [ + "libgralloctypes", + "libhidlbase", + ], visibility: [":__subpackages__"], cpp_std: "experimental", } diff --git a/graphics/mapper/stable-c/implutils/impltests.cpp b/graphics/mapper/stable-c/implutils/impltests.cpp index 9c5d70b498..f12b069c3f 100644 --- a/graphics/mapper/stable-c/implutils/impltests.cpp +++ b/graphics/mapper/stable-c/implutils/impltests.cpp @@ -18,123 +18,29 @@ #include #include +#include +#include +#include #include +using namespace ::android; using namespace ::android::hardware::graphics::mapper; using namespace ::aidl::android::hardware::graphics::common; +namespace gralloc4 = ::android::gralloc4; +using ::android::hardware::hidl_vec; // These tests are primarily interested in hitting all the different *types* that can be // serialized/deserialized than in exhaustively testing all the StandardMetadataTypes. // Exhaustive testing of the actual metadata types is relegated for IMapper's VTS suite // where meaning & correctness of values are more narrowly defined (eg, read-only values) -TEST(Metadata, setGetBufferId) { - using BufferId = StandardMetadata::value; +static constexpr auto HeaderSize = 69; - std::vector buffer; - buffer.resize(12, 0); - *reinterpret_cast(buffer.data()) = 42; - - EXPECT_EQ(8, BufferId::encode(18, buffer.data(), 0)); - EXPECT_EQ(42, *reinterpret_cast(buffer.data())); - EXPECT_EQ(8, BufferId::encode(18, buffer.data(), buffer.size())); - EXPECT_EQ(18, *reinterpret_cast(buffer.data())); - EXPECT_FALSE(BufferId::decode(buffer.data(), 0)); - auto read = BufferId::decode(buffer.data(), buffer.size()); - EXPECT_TRUE(read.has_value()); - EXPECT_EQ(18, read.value_or(0)); +static std::span SkipHeader(std::vector& buffer) { + return std::span(buffer).subspan(HeaderSize); } -TEST(Metadata, setGetDataspace) { - using DataspaceValue = StandardMetadata::value; - using intType = std::underlying_type_t; - std::vector buffer; - buffer.resize(12, 0); - - EXPECT_EQ(4, DataspaceValue::encode(Dataspace::BT2020, buffer.data(), 0)); - EXPECT_EQ(0, *reinterpret_cast(buffer.data())); - EXPECT_EQ(4, DataspaceValue::encode(Dataspace::BT2020, buffer.data(), buffer.size())); - EXPECT_EQ(static_cast(Dataspace::BT2020), *reinterpret_cast(buffer.data())); - EXPECT_FALSE(DataspaceValue::decode(buffer.data(), 0)); - auto read = DataspaceValue::decode(buffer.data(), buffer.size()); - ASSERT_TRUE(read.has_value()); - EXPECT_EQ(Dataspace::BT2020, *read); -} - -TEST(Metadata, setGetValidName) { - using NameValue = StandardMetadata::value; - - std::vector buffer; - buffer.resize(100, 'a'); - buffer[buffer.size() - 1] = '\0'; - - // len("Hello") + sizeof(int64) - constexpr int expectedSize = 5 + sizeof(int64_t); - EXPECT_EQ(expectedSize, NameValue::encode("Hello", buffer.data(), buffer.size())); - EXPECT_EQ(5, *reinterpret_cast(buffer.data())); - // Verify didn't write past the end of the desired size - EXPECT_EQ('a', buffer[expectedSize]); - - auto readValue = NameValue::decode(buffer.data(), buffer.size()); - ASSERT_TRUE(readValue.has_value()); - EXPECT_EQ(5, readValue->length()); - EXPECT_EQ("Hello", *readValue); -} - -TEST(Metadata, setGetInvalidName) { - using NameValue = StandardMetadata::value; - - std::vector buffer; - buffer.resize(12, 'a'); - buffer[buffer.size() - 1] = '\0'; - - // len("This is a long string") + sizeof(int64) - constexpr int expectedSize = 21 + sizeof(int64_t); - EXPECT_EQ(expectedSize, - NameValue::encode("This is a long string", buffer.data(), buffer.size())); - EXPECT_EQ(21, *reinterpret_cast(buffer.data())); - // Verify didn't write the too-long string - EXPECT_EQ('a', buffer[9]); - EXPECT_EQ('\0', buffer[buffer.size() - 1]); - - auto readValue = NameValue::decode(buffer.data(), buffer.size()); - EXPECT_FALSE(readValue.has_value()); - readValue = NameValue::decode(buffer.data(), 0); - ASSERT_FALSE(readValue.has_value()); -} - -TEST(Metadata, wouldOverflowName) { - using NameValue = StandardMetadata::value; - std::vector buffer(100, 0); - - // int_max + sizeof(int64) overflows int32 - std::string_view bad_string{"badbeef", std::numeric_limits::max()}; - EXPECT_EQ(-AIMAPPER_ERROR_BAD_VALUE, - NameValue::encode(bad_string, buffer.data(), buffer.size())); - - // check barely overflows - bad_string = std::string_view{"badbeef", std::numeric_limits::max() - 7}; - EXPECT_EQ(-AIMAPPER_ERROR_BAD_VALUE, - NameValue::encode(bad_string, buffer.data(), buffer.size())); -} - -TEST(Metadata, setGetCompression) { - using CompressionValue = StandardMetadata::value; - ExtendableType myCompression{"bestest_compression_ever", 42}; - std::vector buffer(100, '\0'); - const int expectedSize = myCompression.name.length() + sizeof(int64_t) + sizeof(int64_t); - EXPECT_EQ(expectedSize, CompressionValue::encode(myCompression, buffer.data(), 0)); - EXPECT_EQ(0, buffer[0]); - EXPECT_EQ(expectedSize, CompressionValue::encode(myCompression, buffer.data(), buffer.size())); - EXPECT_EQ(myCompression.name.length(), *reinterpret_cast(buffer.data())); - EXPECT_FALSE(CompressionValue::decode(buffer.data(), 0).has_value()); - auto read = CompressionValue::decode(buffer.data(), buffer.size()); - ASSERT_TRUE(read.has_value()); - EXPECT_EQ(myCompression, read.value()); -} - -TEST(Metadata, setGetPlaneLayout) { - using PlaneLayoutValue = StandardMetadata::value; +static std::vector fakePlaneLayouts() { PlaneLayout myPlaneLayout; myPlaneLayout.offsetInBytes = 10; myPlaneLayout.sampleIncrementInBits = 11; @@ -153,23 +59,147 @@ TEST(Metadata, setGetPlaneLayout) { it.sizeInBits = 30 + i; } - std::vector layouts{myPlaneLayout, PlaneLayout{}}; + return std::vector{myPlaneLayout, PlaneLayout{}}; +} - std::vector buffer(5000, '\0'); +TEST(Metadata, setGetBufferId) { + using BufferId = StandardMetadata::value; + + std::vector buffer(10000, 0); + int64_t* payload = reinterpret_cast(SkipHeader(buffer).data()); + *payload = 42; + + EXPECT_EQ(8 + HeaderSize, BufferId::encode(18, buffer.data(), 0)); + EXPECT_EQ(42, *payload); + EXPECT_EQ(8 + HeaderSize, BufferId::encode(18, buffer.data(), buffer.size())); + EXPECT_EQ(18, *payload); + EXPECT_FALSE(BufferId::decode(buffer.data(), 0)); + auto read = BufferId::decode(buffer.data(), buffer.size()); + EXPECT_TRUE(read.has_value()); + EXPECT_EQ(18, read.value_or(0)); +} + +TEST(Metadata, setGetDataspace) { + using DataspaceValue = StandardMetadata::value; + using intType = std::underlying_type_t; + std::vector buffer(10000, 0); + auto data = SkipHeader(buffer); + + EXPECT_EQ(4 + HeaderSize, DataspaceValue::encode(Dataspace::BT2020, buffer.data(), 0)); + EXPECT_EQ(0, *reinterpret_cast(data.data())); + EXPECT_EQ(4 + HeaderSize, + DataspaceValue::encode(Dataspace::BT2020, buffer.data(), buffer.size())); + EXPECT_EQ(static_cast(Dataspace::BT2020), *reinterpret_cast(data.data())); + EXPECT_FALSE(DataspaceValue::decode(buffer.data(), 0)); + auto read = DataspaceValue::decode(buffer.data(), buffer.size()); + ASSERT_TRUE(read.has_value()); + EXPECT_EQ(Dataspace::BT2020, *read); +} + +TEST(Metadata, setGetValidName) { + using NameValue = StandardMetadata::value; + + std::vector buffer(10000, 'a'); + + // len("Hello") + sizeof(int64) + constexpr int expectedSize = 5 + sizeof(int64_t) + HeaderSize; + EXPECT_EQ(expectedSize, NameValue::encode("Hello", buffer.data(), buffer.size())); + EXPECT_EQ(5, *reinterpret_cast(SkipHeader(buffer).data())); + // Verify didn't write past the end of the desired size + EXPECT_EQ('a', buffer[expectedSize]); + + auto readValue = NameValue::decode(buffer.data(), buffer.size()); + ASSERT_TRUE(readValue.has_value()); + EXPECT_EQ(5, readValue->length()); + EXPECT_EQ("Hello", *readValue); +} + +TEST(Metadata, setGetInvalidName) { + using NameValue = StandardMetadata::value; + + std::vector buffer; + buffer.resize(12 + HeaderSize, 'a'); + buffer[buffer.size() - 1] = '\0'; + + // len("This is a long string") + sizeof(int64) + constexpr int expectedSize = 21 + sizeof(int64_t) + HeaderSize; + EXPECT_EQ(expectedSize, + NameValue::encode("This is a long string", buffer.data(), buffer.size())); + EXPECT_EQ(21, *reinterpret_cast(SkipHeader(buffer).data())); + + auto readValue = NameValue::decode(buffer.data(), buffer.size()); + EXPECT_FALSE(readValue.has_value()); + readValue = NameValue::decode(buffer.data(), 0); + ASSERT_FALSE(readValue.has_value()); +} + +TEST(Metadata, wouldOverflowName) { + using NameValue = StandardMetadata::value; + std::vector buffer(10000, 0); + + // int_max + sizeof(int64) overflows int32 + std::string_view bad_string{"badbeef", std::numeric_limits::max()}; + EXPECT_EQ(-AIMAPPER_ERROR_BAD_VALUE, + NameValue::encode(bad_string, buffer.data(), buffer.size())); + + // check barely overflows + bad_string = std::string_view{"badbeef", std::numeric_limits::max() - 7}; + EXPECT_EQ(-AIMAPPER_ERROR_BAD_VALUE, + NameValue::encode(bad_string, buffer.data(), buffer.size())); +} + +TEST(Metadata, setGetMismatchedWidthHight) { + // Validates that the header is properly validated on decode + using WidthValue = StandardMetadata::value; + using HeightValue = StandardMetadata::value; + std::vector buffer(10000, 0); + + EXPECT_EQ(8 + HeaderSize, WidthValue::encode(100, buffer.data(), buffer.size())); + EXPECT_EQ(100, *reinterpret_cast(SkipHeader(buffer).data())); + auto read = WidthValue::decode(buffer.data(), buffer.size()); + ASSERT_TRUE(read.has_value()); + EXPECT_EQ(100, *read); + read = HeightValue::decode(buffer.data(), buffer.size()); + EXPECT_FALSE(read.has_value()); +} + +TEST(Metadata, setGetCompression) { + using CompressionValue = StandardMetadata::value; + ExtendableType myCompression{"bestest_compression_ever", 42}; + std::vector buffer(10000, 0); + const int expectedSize = + myCompression.name.length() + sizeof(int64_t) + sizeof(int64_t) + HeaderSize; + EXPECT_EQ(expectedSize, CompressionValue::encode(myCompression, buffer.data(), 0)); + EXPECT_EQ(0, buffer[0]); + EXPECT_EQ(expectedSize, CompressionValue::encode(myCompression, buffer.data(), buffer.size())); + EXPECT_EQ(myCompression.name.length(), *reinterpret_cast(SkipHeader(buffer).data())); + EXPECT_FALSE(CompressionValue::decode(buffer.data(), 0).has_value()); + auto read = CompressionValue::decode(buffer.data(), buffer.size()); + ASSERT_TRUE(read.has_value()); + EXPECT_EQ(myCompression, read.value()); +} + +TEST(Metadata, setGetPlaneLayout) { + using PlaneLayoutValue = StandardMetadata::value; + + std::vector layouts = fakePlaneLayouts(); + + std::vector buffer(10000, 0); constexpr int componentSize = 8 + (4 * sizeof(int64_t)); constexpr int firstLayoutSize = (8 + 1) * sizeof(int64_t) + (3 * componentSize); constexpr int secondLayoutSize = (8 + 1) * sizeof(int64_t); - constexpr int expectedSize = firstLayoutSize + secondLayoutSize + sizeof(int64_t); + constexpr int expectedSize = firstLayoutSize + secondLayoutSize + sizeof(int64_t) + HeaderSize; EXPECT_EQ(expectedSize, PlaneLayoutValue::encode(layouts, buffer.data(), 0)); EXPECT_EQ(0, buffer[0]); EXPECT_EQ(expectedSize, PlaneLayoutValue::encode(layouts, buffer.data(), buffer.size())); - EXPECT_EQ(3, reinterpret_cast(buffer.data())[1]); - EXPECT_EQ(8, reinterpret_cast(buffer.data())[2]); - EXPECT_EQ(40, reinterpret_cast(buffer.data())[4]); - EXPECT_EQ(31, reinterpret_cast(buffer.data())[11]); - EXPECT_EQ(22, reinterpret_cast(buffer.data())[15]); - EXPECT_EQ(10, reinterpret_cast(buffer.data())[17]); - EXPECT_EQ(11, reinterpret_cast(buffer.data())[18]); + int64_t* payload = reinterpret_cast(SkipHeader(buffer).data()); + EXPECT_EQ(3, payload[1]); + EXPECT_EQ(8, payload[2]); + EXPECT_EQ(40, payload[4]); + EXPECT_EQ(31, payload[11]); + EXPECT_EQ(22, payload[15]); + EXPECT_EQ(10, payload[17]); + EXPECT_EQ(11, payload[18]); EXPECT_FALSE(PlaneLayoutValue::decode(buffer.data(), 0).has_value()); auto read = PlaneLayoutValue::decode(buffer.data(), buffer.size()); ASSERT_TRUE(read.has_value()); @@ -178,15 +208,15 @@ TEST(Metadata, setGetPlaneLayout) { TEST(Metadata, setGetRects) { using RectsValue = StandardMetadata::value; - std::vector buffer(500, 0); + std::vector buffer(10000, 0); std::vector cropRects{2}; cropRects[0] = Rect{10, 11, 12, 13}; cropRects[1] = Rect{20, 21, 22, 23}; - constexpr int expectedSize = sizeof(int64_t) + (8 * sizeof(int32_t)); + constexpr int expectedSize = sizeof(int64_t) + (8 * sizeof(int32_t)) + HeaderSize; EXPECT_EQ(expectedSize, RectsValue::encode(cropRects, buffer.data(), buffer.size())); - EXPECT_EQ(2, reinterpret_cast(buffer.data())[0]); - EXPECT_EQ(10, reinterpret_cast(buffer.data())[2]); + EXPECT_EQ(2, reinterpret_cast(SkipHeader(buffer).data())[0]); + EXPECT_EQ(10, reinterpret_cast(SkipHeader(buffer).data())[2]); auto read = RectsValue::decode(buffer.data(), buffer.size()); ASSERT_TRUE(read.has_value()); EXPECT_EQ(cropRects.size(), read->size()); @@ -203,8 +233,8 @@ TEST(Metadata, setGetSmpte2086) { source.primaryGreen = XyColor{.3f, .4f}; source.primaryBlue = XyColor{.5f, .6f}; - constexpr int expectedSize = 10 * sizeof(float); - std::vector buffer(500, 0); + constexpr int expectedSize = 10 * sizeof(float) + HeaderSize; + std::vector buffer(10000, 0); EXPECT_EQ(expectedSize, Smpte2086Value::encode(source, buffer.data(), buffer.size())); auto read = Smpte2086Value::decode(buffer.data(), buffer.size()); ASSERT_TRUE(read.has_value()); @@ -223,8 +253,8 @@ TEST(Metadata, setGetCta861_3) { source.maxFrameAverageLightLevel = 244.55f; source.maxContentLightLevel = 202.202f; - constexpr int expectedSize = 2 * sizeof(float); - std::vector buffer(500, 0); + constexpr int expectedSize = 2 * sizeof(float) + HeaderSize; + std::vector buffer(10000, 0); EXPECT_EQ(expectedSize, Cta861_3Value::encode(source, buffer.data(), buffer.size())); auto read = Cta861_3Value::decode(buffer.data(), buffer.size()); ASSERT_TRUE(read.has_value()); @@ -240,14 +270,14 @@ TEST(Metadata, setGetCta861_3) { TEST(Metadata, setGetSmpte2094_10) { using SMPTE2094_10Value = StandardMetadata::value; - std::vector buffer(500, 0); + std::vector buffer(10000, 0); EXPECT_EQ(0, SMPTE2094_10Value::encode(std::nullopt, buffer.data(), buffer.size())); auto read = SMPTE2094_10Value::decode(buffer.data(), 0); ASSERT_TRUE(read.has_value()); EXPECT_FALSE(read->has_value()); const std::vector emptyBuffer; - EXPECT_EQ(sizeof(int64_t), + EXPECT_EQ(sizeof(int64_t) + HeaderSize, SMPTE2094_10Value::encode(emptyBuffer, buffer.data(), buffer.size())); read = SMPTE2094_10Value::decode(buffer.data(), buffer.size()); ASSERT_TRUE(read.has_value()); @@ -255,7 +285,7 @@ TEST(Metadata, setGetSmpte2094_10) { EXPECT_EQ(0, read->value().size()); const std::vector simpleBuffer{0, 1, 2, 3, 4, 5}; - EXPECT_EQ(sizeof(int64_t) + 6, + EXPECT_EQ(sizeof(int64_t) + 6 + HeaderSize, SMPTE2094_10Value::encode(simpleBuffer, buffer.data(), buffer.size())); read = SMPTE2094_10Value::decode(buffer.data(), buffer.size()); ASSERT_TRUE(read.has_value()); @@ -266,7 +296,7 @@ TEST(Metadata, setGetSmpte2094_10) { TEST(MetadataProvider, bufferId) { using BufferId = StandardMetadata::value; - std::vector buffer(500, 0); + std::vector buffer(10000, 0); int result = provideStandardMetadata(StandardMetadataType::BUFFER_ID, buffer.data(), buffer.size(), [](auto&& provide) { if constexpr (T == StandardMetadataType::BUFFER_ID) { @@ -275,7 +305,7 @@ TEST(MetadataProvider, bufferId) { return 0; }); - EXPECT_EQ(8, result); + EXPECT_EQ(8 + HeaderSize, result); auto read = BufferId::decode(buffer.data(), buffer.size()); EXPECT_EQ(42, read.value_or(0)); } @@ -312,3 +342,193 @@ TEST(MetadataProvider, outOfBounds) { EXPECT_EQ(-AIMAPPER_ERROR_UNSUPPORTED, result) << "100 (out of range) should have resulted in UNSUPPORTED"; } + +template +std::vector encode(const typename StandardMetadata::value_type& value) { + using Value = typename StandardMetadata::value; + + int desiredSize = Value::encode(value, nullptr, 0); + EXPECT_GE(desiredSize, 0); + std::vector buffer; + buffer.resize(desiredSize); + EXPECT_EQ(desiredSize, Value::encode(value, buffer.data(), buffer.size())); + return buffer; +} + +TEST(MetadataGralloc4Interop, BufferId) { + auto mpbuf = encode(42); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeBufferId(42, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, Name) { + auto mpbuf = encode("Hello, Interop!"); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeName("Hello, Interop!", &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, Width) { + auto mpbuf = encode(128); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeWidth(128, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, Height) { + auto mpbuf = encode(64); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeHeight(64, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, LayerCount) { + auto mpbuf = encode(3); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeLayerCount(3, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, PixelFormatRequested) { + auto mpbuf = encode(PixelFormat::RGBX_8888); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodePixelFormatRequested( + hardware::graphics::common::V1_2::PixelFormat::RGBX_8888, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, PixelFormatFourcc) { + auto mpbuf = encode(DRM_FORMAT_ABGR8888); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodePixelFormatFourCC(DRM_FORMAT_ABGR8888, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, PixelFormatModifier) { + auto mpbuf = encode(123456); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodePixelFormatModifier(123456, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, Usage) { + auto mpbuf = encode(BufferUsage::COMPOSER_OVERLAY); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, + gralloc4::encodeUsage( + static_cast( + hardware::graphics::common::V1_2::BufferUsage::COMPOSER_OVERLAY), + &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, AllocationSize) { + auto mpbuf = encode(10200); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeAllocationSize(10200, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, ProtectedContent) { + auto mpbuf = encode(1); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeProtectedContent(1, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, Compression) { + auto mpbuf = encode( + gralloc4::Compression_DisplayStreamCompression); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, + gralloc4::encodeCompression(gralloc4::Compression_DisplayStreamCompression, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, Interlaced) { + auto mpbuf = encode(gralloc4::Interlaced_TopBottom); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeInterlaced(gralloc4::Interlaced_TopBottom, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, ChromeSitting) { + auto mpbuf = + encode(gralloc4::ChromaSiting_SitedInterstitial); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, + gralloc4::encodeChromaSiting(gralloc4::ChromaSiting_SitedInterstitial, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, PlaneLayouts) { + auto mpbuf = encode(fakePlaneLayouts()); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodePlaneLayouts(fakePlaneLayouts(), &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, Crop) { + std::vector cropRects{Rect{10, 11, 12, 13}, Rect{20, 21, 22, 23}}; + auto mpbuf = encode(cropRects); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeCrop(cropRects, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, Dataspace) { + auto mpbuf = encode(Dataspace::DISPLAY_P3); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeDataspace(Dataspace::DISPLAY_P3, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, BlendMode) { + auto mpbuf = encode(BlendMode::PREMULTIPLIED); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeBlendMode(BlendMode::PREMULTIPLIED, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, Smpte2086) { + Smpte2086 hdrdata{XyColor{.1f, .2f}, XyColor{.3f, .4f}, XyColor{.5f, .6f}, + XyColor{.7f, .8f}, 452.889f, 12.335f}; + + auto mpbuf = encode(hdrdata); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeSmpte2086(hdrdata, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, Cta861_3) { + Cta861_3 hdrdata{302.202f, 244.55f}; + auto mpbuf = encode(hdrdata); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeCta861_3(hdrdata, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, Smpte2094_10) { + auto mpbuf = encode(std::nullopt); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeSmpte2094_10(std::nullopt, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); + + std::vector hdrdata{1, 2, 3, 4, 5, 6}; + mpbuf = encode(hdrdata); + ASSERT_EQ(NO_ERROR, gralloc4::encodeSmpte2094_10(hdrdata, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} + +TEST(MetadataGralloc4Interop, Smpte2094_40) { + auto mpbuf = encode(std::nullopt); + hidl_vec g4buf; + ASSERT_EQ(NO_ERROR, gralloc4::encodeSmpte2094_40(std::nullopt, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); + + std::vector hdrdata{1, 2, 3, 4, 5, 6}; + mpbuf = encode(hdrdata); + ASSERT_EQ(NO_ERROR, gralloc4::encodeSmpte2094_40(hdrdata, &g4buf)); + EXPECT_EQ(mpbuf, g4buf); +} diff --git a/graphics/mapper/stable-c/implutils/include/android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h b/graphics/mapper/stable-c/implutils/include/android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h index 7861af87fc..25af6d1158 100644 --- a/graphics/mapper/stable-c/implutils/include/android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h +++ b/graphics/mapper/stable-c/implutils/include/android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h @@ -82,7 +82,12 @@ class MetadataWriter { explicit MetadataWriter(void* _Nullable destBuffer, size_t destBufferSize) : mDest(reinterpret_cast(destBuffer)), mSizeRemaining(destBufferSize) {} - int32_t desiredSize() const { return mDesiredSize; } + [[nodiscard]] int32_t desiredSize() const { return mDesiredSize; } + + template + MetadataWriter& writeHeader() { + return write(HEADER::name).template write(HEADER::value); + } template >> MetadataWriter& write(T value) { @@ -150,6 +155,18 @@ class MetadataReader { [[nodiscard]] size_t remaining() const { return mSizeRemaining; } [[nodiscard]] bool ok() const { return mOk; } + template + MetadataReader& checkHeader() { + if (HEADER::name != readString()) { + mOk = false; + } + auto value = readInt(); + if (!value || *value != HEADER::value) { + mOk = false; + } + return *this; + } + template >> MetadataReader& read(T& dest) { if (const void* src = advance(sizeof(T))) { @@ -228,27 +245,33 @@ class MetadataReader { } }; -template +template struct MetadataValue {}; -template -struct MetadataValue>> { +template +struct MetadataValue>> { [[nodiscard]] static int32_t encode(T value, void* _Nullable destBuffer, size_t destBufferSize) { - return MetadataWriter{destBuffer, destBufferSize}.write(value).desiredSize(); + return MetadataWriter{destBuffer, destBufferSize} + .template writeHeader
() + .write(value) + .desiredSize(); } [[nodiscard]] static std::optional decode(const void* _Nonnull metadata, size_t metadataSize) { - return MetadataReader{metadata, metadataSize}.readInt(); + return MetadataReader{metadata, metadataSize} + .template checkHeader
() + .template readInt(); } }; -template -struct MetadataValue>> { +template +struct MetadataValue>> { [[nodiscard]] static int32_t encode(T value, void* _Nullable destBuffer, size_t destBufferSize) { return MetadataWriter{destBuffer, destBufferSize} + .template writeHeader
() .write(static_cast>(value)) .desiredSize(); } @@ -256,47 +279,56 @@ struct MetadataValue>> { [[nodiscard]] static std::optional decode(const void* _Nonnull metadata, size_t metadataSize) { std::underlying_type_t temp; - return MetadataReader{metadata, metadataSize}.read(temp).ok() + return MetadataReader{metadata, metadataSize}.template checkHeader
().read(temp).ok() ? std::optional(static_cast(temp)) : std::nullopt; } }; -template <> -struct MetadataValue { +template +struct MetadataValue { [[nodiscard]] static int32_t encode(const std::string_view& value, void* _Nullable destBuffer, size_t destBufferSize) { - return MetadataWriter{destBuffer, destBufferSize}.write(value).desiredSize(); + return MetadataWriter{destBuffer, destBufferSize} + .template writeHeader
() + .write(value) + .desiredSize(); } [[nodiscard]] static std::optional decode(const void* _Nonnull metadata, size_t metadataSize) { - auto reader = MetadataReader{metadata, metadataSize}; + auto reader = MetadataReader{metadata, metadataSize}.template checkHeader
(); auto result = reader.readString(); return reader.ok() ? std::optional{result} : std::nullopt; } }; -template <> -struct MetadataValue { +template +struct MetadataValue { static_assert(sizeof(int64_t) == sizeof(ExtendableType::value)); [[nodiscard]] static int32_t encode(const ExtendableType& value, void* _Nullable destBuffer, size_t destBufferSize) { - return MetadataWriter{destBuffer, destBufferSize}.write(value).desiredSize(); + return MetadataWriter{destBuffer, destBufferSize} + .template writeHeader
() + .write(value) + .desiredSize(); } [[nodiscard]] static std::optional decode(const void* _Nonnull metadata, size_t metadataSize) { - return MetadataReader{metadata, metadataSize}.readExtendable(); + return MetadataReader{metadata, metadataSize} + .template checkHeader
() + .readExtendable(); } }; -template <> -struct MetadataValue> { +template +struct MetadataValue> { [[nodiscard]] static int32_t encode(const std::vector& values, void* _Nullable destBuffer, size_t destBufferSize) { MetadataWriter writer{destBuffer, destBufferSize}; + writer.template writeHeader
(); writer.write(values.size()); for (const auto& value : values) { writer.write(value.components.size()); @@ -321,13 +353,14 @@ struct MetadataValue> { [[nodiscard]] static DecodeResult decode(const void* _Nonnull metadata, size_t metadataSize) { std::vector values; MetadataReader reader{metadata, metadataSize}; + reader.template checkHeader
(); auto numPlanes = reader.readInt().value_or(0); values.reserve(numPlanes); for (int i = 0; i < numPlanes && reader.ok(); i++) { PlaneLayout& value = values.emplace_back(); auto numPlaneComponents = reader.readInt().value_or(0); value.components.reserve(numPlaneComponents); - for (int i = 0; i < numPlaneComponents && reader.ok(); i++) { + for (int j = 0; j < numPlaneComponents && reader.ok(); j++) { PlaneLayoutComponent& component = value.components.emplace_back(); reader.read(component.type) .read(component.offsetInBits) @@ -346,11 +379,12 @@ struct MetadataValue> { } }; -template <> -struct MetadataValue> { +template +struct MetadataValue> { [[nodiscard]] static int32_t encode(const std::vector& value, void* _Nullable destBuffer, size_t destBufferSize) { MetadataWriter writer{destBuffer, destBufferSize}; + writer.template writeHeader
(); writer.write(value.size()); for (auto& rect : value) { writer.write(rect.left) @@ -364,6 +398,7 @@ struct MetadataValue> { using DecodeResult = std::optional>; [[nodiscard]] static DecodeResult decode(const void* _Nonnull metadata, size_t metadataSize) { MetadataReader reader{metadata, metadataSize}; + reader.template checkHeader
(); std::vector value; auto numRects = reader.readInt().value_or(0); value.reserve(numRects); @@ -378,13 +413,14 @@ struct MetadataValue> { } }; -template <> -struct MetadataValue> { +template +struct MetadataValue> { [[nodiscard]] static int32_t encode(const std::optional& optValue, void* _Nullable destBuffer, size_t destBufferSize) { if (optValue.has_value()) { const auto& value = *optValue; return MetadataWriter{destBuffer, destBufferSize} + .template writeHeader
() .write(value.primaryRed) .write(value.primaryGreen) .write(value.primaryBlue) @@ -404,6 +440,7 @@ struct MetadataValue> { if (metadataSize > 0) { Smpte2086 value; MetadataReader reader{metadata, metadataSize}; + reader.template checkHeader
(); reader.read(value.primaryRed) .read(value.primaryGreen) .read(value.primaryBlue) @@ -420,13 +457,14 @@ struct MetadataValue> { } }; -template <> -struct MetadataValue> { +template +struct MetadataValue> { [[nodiscard]] static int32_t encode(const std::optional& optValue, void* _Nullable destBuffer, size_t destBufferSize) { if (optValue.has_value()) { const auto& value = *optValue; return MetadataWriter{destBuffer, destBufferSize} + .template writeHeader
() .write(value.maxContentLightLevel) .write(value.maxFrameAverageLightLevel) .desiredSize(); @@ -441,6 +479,7 @@ struct MetadataValue> { std::optional optValue{std::nullopt}; if (metadataSize > 0) { MetadataReader reader{metadata, metadataSize}; + reader.template checkHeader
(); Cta861_3 value; reader.read(value.maxContentLightLevel).read(value.maxFrameAverageLightLevel); if (reader.ok()) { @@ -453,14 +492,17 @@ struct MetadataValue> { } }; -template <> -struct MetadataValue>> { +template +struct MetadataValue>> { [[nodiscard]] static int32_t encode(const std::optional>& value, void* _Nullable destBuffer, size_t destBufferSize) { if (!value.has_value()) { return 0; } - return MetadataWriter{destBuffer, destBufferSize}.write(*value).desiredSize(); + return MetadataWriter{destBuffer, destBufferSize} + .template writeHeader
() + .write(*value) + .desiredSize(); } using DecodeResult = std::optional>>; @@ -468,6 +510,7 @@ struct MetadataValue>> { std::optional> optValue; if (metadataSize > 0) { MetadataReader reader{metadata, metadataSize}; + reader.template checkHeader
(); auto value = reader.readBuffer(); if (reader.ok()) { optValue = std::move(value); @@ -482,16 +525,20 @@ struct MetadataValue>> { template struct StandardMetadata {}; -#define DEFINE_TYPE(name, typeArg) \ - template <> \ - struct StandardMetadata { \ - using value_type = typeArg; \ - using value = MetadataValue; \ - static_assert( \ - StandardMetadataType::name == \ - ndk::internal::enum_values[static_cast( \ - StandardMetadataType::name)], \ - "StandardMetadataType must have equivalent value to index"); \ +#define DEFINE_TYPE(typeName, typeArg) \ + template <> \ + struct StandardMetadata { \ + using value_type = typeArg; \ + struct Header { \ + static constexpr auto name = "android.hardware.graphics.common.StandardMetadataType"; \ + static constexpr auto value = static_cast(StandardMetadataType::typeName); \ + }; \ + using value = MetadataValue; \ + static_assert( \ + StandardMetadataType::typeName == \ + ndk::internal::enum_values[static_cast( \ + StandardMetadataType::typeName)], \ + "StandardMetadataType must have equivalent value to index"); \ } DEFINE_TYPE(BUFFER_ID, uint64_t); diff --git a/graphics/mapper/stable-c/include/android/hardware/graphics/mapper/IMapper.h b/graphics/mapper/stable-c/include/android/hardware/graphics/mapper/IMapper.h index f27b0f4ce7..0f6d146fb8 100644 --- a/graphics/mapper/stable-c/include/android/hardware/graphics/mapper/IMapper.h +++ b/graphics/mapper/stable-c/include/android/hardware/graphics/mapper/IMapper.h @@ -509,11 +509,12 @@ typedef struct AIMapperV5 { * particular Metadata field. * * The framework will attempt to set the following StandardMetadataType - * values: DATASPACE, SMPTE2086, CTA861_3, SMPTE2094_40 and BLEND_MODE. - * We require everyone to support setting those fields. If a device's Composer - * implementation supports a field, it should be supported here. Over time these - * metadata fields will be moved out of Composer/BufferQueue/etc. and into the - * buffer's Metadata fields. + * values: DATASPACE, SMPTE2086, CTA861_3, and BLEND_MODE. + * We require everyone to support setting those fields. Framework will also attempt to set + * SMPTE2094_40 and SMPTE2094_10 if available, and it is required to support setting those + * if it is possible to get them. If a device's Composer implementation supports a field, + * it should be supported here. Over time these metadata fields will be moved out of + * Composer/BufferQueue/etc. and into the buffer's Metadata fields. * * @param buffer Buffer receiving desired metadata * @param metadataType MetadataType for the metadata value being set @@ -546,11 +547,12 @@ typedef struct AIMapperV5 { * particular Metadata field. * * The framework will attempt to set the following StandardMetadataType - * values: DATASPACE, SMPTE2086, CTA861_3, SMPTE2094_40 and BLEND_MODE. - * We require everyone to support setting those fields. If a device's Composer - * implementation supports a field, it should be supported here. Over time these - * metadata fields will be moved out of Composer/BufferQueue/etc. and into the - * buffer's Metadata fields. + * values: DATASPACE, SMPTE2086, CTA861_3, and BLEND_MODE. + * We require everyone to support setting those fields. Framework will also attempt to set + * SMPTE2094_40 and SMPTE2094_10 if available, and it is required to support setting those + * if it is possible to get them. If a device's Composer implementation supports a field, + * it should be supported here. Over time these metadata fields will be moved out of + * Composer/BufferQueue/etc. and into the buffer's Metadata fields. * * @param buffer Buffer receiving desired metadata * @param standardMetadataType StandardMetadataType for the metadata value being set diff --git a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp index 6ab11a305d..85246eec3a 100644 --- a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp +++ b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,24 @@ struct YCbCr { int64_t verticalSubSampling; }; +constexpr const char* STANDARD_METADATA_NAME = + "android.hardware.graphics.common.StandardMetadataType"; + +static bool isStandardMetadata(AIMapper_MetadataType metadataType) { + return strcmp(STANDARD_METADATA_NAME, metadataType.name) == 0; +} + +static std::string toString(const std::vector types) { + std::stringstream buf; + buf << "["; + for (auto type : types) { + buf << toString(type) << ", "; + } + buf.seekp(-2, buf.cur); + buf << "]"; + return buf.str(); +} + class BufferHandle { AIMapper* mIMapper; buffer_handle_t mHandle = nullptr; @@ -215,7 +234,7 @@ class GraphicsTestsBase { sizeRequired = mapper()->v5.getStandardMetadata(bufferHandle, static_cast(T), buffer.data(), buffer.size()); } - if (sizeRequired < 0 || sizeRequired >= buffer.size()) { + if (sizeRequired < 0 || sizeRequired > buffer.size()) { ADD_FAILURE() << "getStandardMetadata failed, received " << sizeRequired << " with buffer size " << buffer.size(); // Generate a fail type @@ -1533,8 +1552,187 @@ TEST_P(GraphicsMapperStableCTests, GetSmpte2094_40) { auto bufferHandle = buffer->import(); ASSERT_TRUE(bufferHandle); auto value = getStandardMetadata(*bufferHandle); - ASSERT_TRUE(value.has_value()); - EXPECT_FALSE(value->has_value()); + if (value.has_value()) { + EXPECT_FALSE(value->has_value()); + } +} + +TEST_P(GraphicsMapperStableCTests, SupportsRequiredGettersSetters) { + auto buffer = allocateGeneric(); + ASSERT_TRUE(buffer); + auto bufferHandle = buffer->import(); + ASSERT_TRUE(bufferHandle); + const AIMapper_MetadataTypeDescription* descriptions = nullptr; + size_t descriptionCount = 0; + ASSERT_EQ(AIMAPPER_ERROR_NONE, + mapper()->v5.listSupportedMetadataTypes(&descriptions, &descriptionCount)); + std::vector requiredGetters = { + StandardMetadataType::BUFFER_ID, + StandardMetadataType::NAME, + StandardMetadataType::WIDTH, + StandardMetadataType::HEIGHT, + StandardMetadataType::LAYER_COUNT, + StandardMetadataType::PIXEL_FORMAT_REQUESTED, + StandardMetadataType::PIXEL_FORMAT_FOURCC, + StandardMetadataType::PIXEL_FORMAT_MODIFIER, + StandardMetadataType::USAGE, + StandardMetadataType::ALLOCATION_SIZE, + StandardMetadataType::PROTECTED_CONTENT, + StandardMetadataType::COMPRESSION, + StandardMetadataType::INTERLACED, + StandardMetadataType::CHROMA_SITING, + StandardMetadataType::PLANE_LAYOUTS, + StandardMetadataType::CROP, + StandardMetadataType::DATASPACE, + StandardMetadataType::BLEND_MODE, + StandardMetadataType::SMPTE2086, + StandardMetadataType::CTA861_3, + }; + + std::vector requiredSetters = { + StandardMetadataType::DATASPACE, + StandardMetadataType::BLEND_MODE, + StandardMetadataType::SMPTE2086, + StandardMetadataType::CTA861_3, + }; + + for (int i = 0; i < descriptionCount; i++) { + const auto& it = descriptions[i]; + if (isStandardMetadata(it.metadataType)) { + EXPECT_GT(it.metadataType.value, static_cast(StandardMetadataType::INVALID)); + EXPECT_LT(it.metadataType.value, + ndk::internal::enum_values.size()); + + if (it.isGettable) { + std::erase(requiredGetters, + static_cast(it.metadataType.value)); + } + if (it.isSettable) { + std::erase(requiredSetters, + static_cast(it.metadataType.value)); + } + } else { + EXPECT_NE(nullptr, it.description) << "Non-standard metadata must have a description"; + int len = strlen(it.description); + EXPECT_GE(len, 0) << "Non-standard metadata must have a description"; + } + } + + EXPECT_EQ(0, requiredGetters.size()) << "Missing required getters" << toString(requiredGetters); + EXPECT_EQ(0, requiredSetters.size()) << "Missing required setters" << toString(requiredSetters); +} + +/* + * Test that verifies that if the optional StandardMetadataTypes have getters, they have + * the required setters as well + */ +TEST_P(GraphicsMapperStableCTests, CheckRequiredSettersIfHasGetters) { + auto buffer = allocateGeneric(); + ASSERT_TRUE(buffer); + auto bufferHandle = buffer->import(); + ASSERT_TRUE(bufferHandle); + const AIMapper_MetadataTypeDescription* descriptions = nullptr; + size_t descriptionCount = 0; + ASSERT_EQ(AIMAPPER_ERROR_NONE, + mapper()->v5.listSupportedMetadataTypes(&descriptions, &descriptionCount)); + + for (int i = 0; i < descriptionCount; i++) { + const auto& it = descriptions[i]; + if (isStandardMetadata(it.metadataType)) { + const auto type = static_cast(it.metadataType.value); + switch (type) { + case StandardMetadataType::SMPTE2094_10: + case StandardMetadataType::SMPTE2094_40: + if (it.isGettable) { + EXPECT_TRUE(it.isSettable) + << "Type " << toString(type) << " must be settable if gettable"; + } + break; + default: + break; + } + } + } +} + +TEST_P(GraphicsMapperStableCTests, ListSupportedWorks) { + auto buffer = allocateGeneric(); + ASSERT_TRUE(buffer); + auto bufferHandle = buffer->import(); + ASSERT_TRUE(bufferHandle); + const AIMapper_MetadataTypeDescription* descriptions = nullptr; + size_t descriptionCount = 0; + ASSERT_EQ(AIMAPPER_ERROR_NONE, + mapper()->v5.listSupportedMetadataTypes(&descriptions, &descriptionCount)); + + std::vector metadataBuffer; + auto get = [&](AIMapper_MetadataType metadataType) -> int32_t { + int32_t size = mapper()->v5.getMetadata(*bufferHandle, metadataType, nullptr, 0); + if (size >= 0) { + metadataBuffer.resize(size); + size = mapper()->v5.getMetadata(*bufferHandle, metadataType, metadataBuffer.data(), + metadataBuffer.size()); + EXPECT_EQ(size, metadataBuffer.size()); + } + return size; + }; + + for (int i = 0; i < descriptionCount; i++) { + const auto& it = descriptions[i]; + if (!isStandardMetadata(it.metadataType)) { + continue; + } + if (!it.isGettable) { + EXPECT_FALSE(it.isSettable) + << "StandardMetadata that isn't gettable must not be settable"; + continue; + } + EXPECT_GE(get(it.metadataType), 0) + << "Get failed for claimed supported getter of " + << toString(static_cast(it.metadataType.value)); + if (it.isSettable) { + EXPECT_EQ(AIMAPPER_ERROR_NONE, + mapper()->v5.setMetadata(*bufferHandle, it.metadataType, + metadataBuffer.data(), metadataBuffer.size())) + << "Failed to set metadata for " + << toString(static_cast(it.metadataType.value)); + } + } +} + +TEST_P(GraphicsMapperStableCTests, GetMetadataBadValue) { + auto get = [this](StandardMetadataType type) -> AIMapper_Error { + // This is a _Nonnull parameter, but this is enough obfuscation to fool the linter + buffer_handle_t buffer = nullptr; + int32_t ret = + mapper()->v5.getStandardMetadata(buffer, static_cast(type), nullptr, 0); + return (ret < 0) ? (AIMapper_Error)-ret : AIMAPPER_ERROR_NONE; + }; + + for (auto type : ndk::enum_range()) { + if (type == StandardMetadataType::INVALID) { + continue; + } + EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, get(type)) << "Wrong error for " << toString(type); + } +} + +TEST_P(GraphicsMapperStableCTests, GetUnsupportedMetadata) { + auto buffer = allocateGeneric(); + ASSERT_TRUE(buffer); + auto bufferHandle = buffer->import(); + ASSERT_TRUE(bufferHandle); + + int result = mapper()->v5.getMetadata(*bufferHandle, {"Fake", 1}, nullptr, 0); + EXPECT_EQ(AIMAPPER_ERROR_UNSUPPORTED, -result); + + result = mapper()->v5.getStandardMetadata( + *bufferHandle, static_cast(StandardMetadataType::INVALID), nullptr, 0); + EXPECT_EQ(AIMAPPER_ERROR_UNSUPPORTED, -result); + + constexpr int64_t unknownStandardType = ndk::internal::enum_values.size(); + result = mapper()->v5.getStandardMetadata(*bufferHandle, unknownStandardType, nullptr, 0); + EXPECT_EQ(AIMAPPER_ERROR_UNSUPPORTED, -result); } std::vector>> getIAllocatorsAtLeastVersion(