Merge "hdr: rename OMX format for 10-bit YUV to Y410"
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index ef5064c..cce0579 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1463,10 +1463,8 @@
     printf("========================================================\n");
 }
 
-// This method collects dumpsys for telephony debugging only
-static void DumpstateTelephonyOnly() {
-    DurationReporter duration_reporter("DUMPSTATE");
-
+// This method collects common dumpsys for telephony and wifi
+static void DumpstateRadioCommon() {
     DumpIpTablesAsRoot();
 
     if (!DropRootUser()) {
@@ -1482,6 +1480,13 @@
 
     RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
                CommandOptions::WithTimeout(10).Build());
+}
+
+// This method collects dumpsys for telephony debugging only
+static void DumpstateTelephonyOnly() {
+    DurationReporter duration_reporter("DUMPSTATE");
+
+    DumpstateRadioCommon();
 
     RunCommand("SYSTEM PROPERTIES", {"getprop"});
 
@@ -1505,6 +1510,26 @@
     printf("========================================================\n");
 }
 
+// This method collects dumpsys for wifi debugging only
+static void DumpstateWifiOnly() {
+    DurationReporter duration_reporter("DUMPSTATE");
+
+    DumpstateRadioCommon();
+
+    printf("========================================================\n");
+    printf("== Android Framework Services\n");
+    printf("========================================================\n");
+
+    RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(),
+               SEC_TO_MSEC(10));
+    RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(),
+               SEC_TO_MSEC(10));
+
+    printf("========================================================\n");
+    printf("== dumpstate: done (id %d)\n", ds.id_);
+    printf("========================================================\n");
+}
+
 void Dumpstate::DumpstateBoard() {
     DurationReporter duration_reporter("dumpstate_board()");
     printf("========================================================\n");
@@ -1751,6 +1776,7 @@
     bool show_header_only = false;
     bool do_start_service = false;
     bool telephony_only = false;
+    bool wifi_only = false;
     int dup_stdout_fd;
     int dup_stderr_fd;
 
@@ -1823,6 +1849,9 @@
             do_zip_file = 1;
         } else if (ds.extra_options_ == "bugreporttelephony") {
             telephony_only = true;
+        } else if (ds.extra_options_ == "bugreportwifi") {
+            wifi_only = true;
+            do_zip_file = 1;
         } else {
             MYLOGE("Unknown extra option: %s\n", ds.extra_options_.c_str());
         }
@@ -1943,6 +1972,8 @@
 
         if (telephony_only) {
             ds.base_name_ += "-telephony";
+        } else if (wifi_only) {
+            ds.base_name_ += "-wifi";
         }
 
         if (do_fb) {
@@ -2052,6 +2083,8 @@
     if (telephony_only) {
         DumpstateTelephonyOnly();
         ds.DumpstateBoard();
+    } else if (wifi_only) {
+        DumpstateWifiOnly();
     } else {
         // Dumps systrace right away, otherwise it will be filled with unnecessary events.
         // First try to dump anrd trace if the daemon is running. Otherwise, dump
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 0653af6..950c9a0 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -2396,7 +2396,7 @@
 
     // TODO(71871109): Validate filePath.
     // 1. Seek to the next page boundary beyond the end of the file.
-    ::android::base::unique_fd wfd(open(filePath.c_str(), O_WRONLY | O_APPEND));
+    ::android::base::unique_fd wfd(open(filePath.c_str(), O_WRONLY));
     if (wfd.get() < 0) {
         return error("Failed to open " + filePath + ": " + strerror(errno));
     }
@@ -2405,7 +2405,11 @@
         return error("Failed to stat " + filePath + ": " + strerror(errno));
     }
     // fsverity starts from the block boundary.
-    if (lseek(wfd.get(), (st.st_size + kVerityPageSize - 1) / kVerityPageSize, SEEK_SET) < 0) {
+    off_t padding = kVerityPageSize - st.st_size % kVerityPageSize;
+    if (padding == kVerityPageSize) {
+        padding = 0;
+    }
+    if (lseek(wfd.get(), st.st_size + padding, SEEK_SET) < 0) {
         return error("Failed to lseek " + filePath + ": " + strerror(errno));
     }
 
@@ -2414,18 +2418,20 @@
     if (size < 0) {
         return error("Failed to get ashmem size: " + std::to_string(size));
     }
-    void* data = mmap(NULL, size, PROT_READ, MAP_SHARED, wfd.get(), 0);
+    void* data = mmap(NULL, size, PROT_READ, MAP_SHARED, verityInputAshmem.get(), 0);
     if (data == MAP_FAILED) {
         return error("Failed to mmap the ashmem: " + std::string(strerror(errno)));
     }
+    char* cursor = reinterpret_cast<char*>(data);
     int remaining = size;
     while (remaining > 0) {
-        int ret = TEMP_FAILURE_RETRY(write(wfd.get(), data, remaining));
+        int ret = TEMP_FAILURE_RETRY(write(wfd.get(), cursor, remaining));
         if (ret < 0) {
             munmap(data, size);
             return error("Failed to write to " + filePath + " (" + std::to_string(remaining) +
                          + "/" + std::to_string(size) + "): " + strerror(errno));
         }
+        cursor += ret;
         remaining -= ret;
     }
     munmap(data, size);
diff --git a/data/etc/android.hardware.camera.ar.xml b/data/etc/android.hardware.camera.ar.xml
new file mode 100644
index 0000000..2fc3280
--- /dev/null
+++ b/data/etc/android.hardware.camera.ar.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<!-- This is the set of features required for a camera2 device that supports the motion tracking
+     feature -->
+<permissions>
+    <feature name="android.hardware.camera.any" />
+    <feature name="android.hardware.camera.ar" />
+</permissions>
diff --git a/headers/media_plugin/media/openmax/OMX_Video.h b/headers/media_plugin/media/openmax/OMX_Video.h
index 76efac9..dc5cdab 100644
--- a/headers/media_plugin/media/openmax/OMX_Video.h
+++ b/headers/media_plugin/media/openmax/OMX_Video.h
@@ -240,6 +240,7 @@
     OMX_Video_ControlRateConstant,
     OMX_Video_ControlRateVariableSkipFrames,
     OMX_Video_ControlRateConstantSkipFrames,
+    OMX_Video_ControlRateConstantQuality,
     OMX_Video_ControlRateKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
     OMX_Video_ControlRateVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
     OMX_Video_ControlRateMax = 0x7FFFFFFF
@@ -254,14 +255,20 @@
  *  nVersion       : OMX spec version info
  *  nPortIndex     : Port that this struct applies to
  *  eControlRate   : Control rate type enum
- *  nTargetBitrate : Target bitrate to encode with
+ *  nTargetBitrate : Target bitrate to encode with (used when eControlRate is
+ *                   not OMX_Video_ControlRateConstantQuality)
+ *  nQualityFactor : Quality to encode with (used when eControlRate is
+ *                   OMX_Video_ControlRateConstantQuality only)
  */
 typedef struct OMX_VIDEO_PARAM_BITRATETYPE {
     OMX_U32 nSize;
     OMX_VERSIONTYPE nVersion;
     OMX_U32 nPortIndex;
     OMX_VIDEO_CONTROLRATETYPE eControlRate;
-    OMX_U32 nTargetBitrate;
+    union {
+        OMX_U32 nTargetBitrate;
+        OMX_U32 nQualityFactor;
+    };
 } OMX_VIDEO_PARAM_BITRATETYPE;
 
 
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 60ec38c..153d35a 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -57,8 +57,7 @@
         for (const auto bit : hardware::hidl_enum_iterator<BufferUsage>()) {
             bits = bits | bit;
         }
-        // Return only the overlapping bits.
-        return bits & ~getValid10UsageBits();
+        return bits;
     }();
     return valid11UsageBits;
 }
@@ -71,7 +70,7 @@
 
 Mapper::Mapper()
 {
-    mMapper = IMapper::getService();
+    mMapper = hardware::graphics::mapper::V2_0::IMapper::getService();
     if (mMapper == nullptr) {
         LOG_ALWAYS_FATAL("gralloc-mapper is missing");
     }
@@ -80,7 +79,7 @@
     }
 
     // IMapper 2.1 is optional
-    mMapperV2_1 = hardware::graphics::mapper::V2_1::IMapper::castFrom(mMapper);
+    mMapperV2_1 = IMapper::castFrom(mMapper);
 }
 
 Gralloc2::Error Mapper::validateBufferDescriptorInfo(
@@ -102,30 +101,34 @@
         const IMapper::BufferDescriptorInfo& descriptorInfo,
         BufferDescriptor* outDescriptor) const
 {
-    Error error;
-
-    if (descriptorInfo.usage & getValid11UsageBits()) {
-        // TODO(b/66900669): Use mMapperV2_1->createDescriptorV2_1().
-        ALOGW("full support for new usage bits is unimplemented 0x%" PRIx64,
-              descriptorInfo.usage & getValid11UsageBits());
-        return Error::BAD_VALUE;
-    }
-
-    error = validateBufferDescriptorInfo(descriptorInfo);
+    Error error = validateBufferDescriptorInfo(descriptorInfo);
     if (error != Error::NONE) {
         return error;
     }
 
-    auto ret = mMapper->createDescriptor(descriptorInfo,
-            [&](const auto& tmpError, const auto& tmpDescriptor)
-            {
-                error = tmpError;
-                if (error != Error::NONE) {
-                    return;
-                }
+    auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor)
+                   {
+                       error = tmpError;
+                       if (error != Error::NONE) {
+                           return;
+                       }
 
-                *outDescriptor = tmpDescriptor;
-            });
+                       *outDescriptor = tmpDescriptor;
+                   };
+
+    hardware::Return<void> ret;
+    if (mMapperV2_1 != nullptr) {
+        ret = mMapperV2_1->createDescriptor_2_1(descriptorInfo, hidl_cb);
+    } else {
+        const hardware::graphics::mapper::V2_0::IMapper::BufferDescriptorInfo info = {
+            descriptorInfo.width,
+            descriptorInfo.height,
+            descriptorInfo.layerCount,
+            static_cast<hardware::graphics::common::V1_0::PixelFormat>(descriptorInfo.format),
+            descriptorInfo.usage,
+        };
+        ret = mMapper->createDescriptor(info, hidl_cb);
+    }
 
     return (ret.isOk()) ? error : kTransactionError;
 }
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index db3f10a..5a8dbda 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -30,11 +30,11 @@
 namespace Gralloc2 {
 
 using hardware::graphics::allocator::V2_0::IAllocator;
-using hardware::graphics::common::V1_0::PixelFormat;
 using hardware::graphics::common::V1_1::BufferUsage;
+using hardware::graphics::common::V1_1::PixelFormat;
+using hardware::graphics::mapper::V2_1::IMapper;
 using hardware::graphics::mapper::V2_0::BufferDescriptor;
 using hardware::graphics::mapper::V2_0::Error;
-using hardware::graphics::mapper::V2_0::IMapper;
 using hardware::graphics::mapper::V2_0::YCbCrLayout;
 
 // A wrapper to IMapper
@@ -85,8 +85,8 @@
     Error validateBufferDescriptorInfo(
             const IMapper::BufferDescriptorInfo& descriptorInfo) const;
 
-    sp<IMapper> mMapper;
-    sp<hardware::graphics::mapper::V2_1::IMapper> mMapperV2_1;
+    sp<hardware::graphics::mapper::V2_0::IMapper> mMapper;
+    sp<IMapper> mMapperV2_1;
 };
 
 // A wrapper to IAllocator
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 94dfe6a..484e0ba 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -468,12 +468,17 @@
         return HAL_DATASPACE_V0_SCRGB;
     } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) {
         return HAL_DATASPACE_V0_SCRGB_LINEAR;
+    } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
+        return HAL_DATASPACE_BT2020_LINEAR;
+    } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
+        return HAL_DATASPACE_BT2020_PQ;
     }
     return dataSpace;
 }
 
-// Return true if we stripped any EGL_GL_COLORSPACE_KHR attributes.
-static EGLBoolean stripColorSpaceAttribute(egl_display_ptr dp, const EGLint* attrib_list,
+// Return true if we stripped any EGL_GL_COLORSPACE_KHR or HDR metadata attributes.
+// Protect devices from attributes they don't recognize that are  managed by Android
+static EGLBoolean stripAttributes(egl_display_ptr dp, const EGLint* attrib_list,
                                            EGLint format,
                                            std::vector<EGLint>& stripped_attrib_list) {
     std::vector<EGLint> allowedColorSpaces;
@@ -494,33 +499,64 @@
             break;
     }
 
+    if (!attrib_list) return false;
+
     bool stripped = false;
-    if (attrib_list && dp->haveExtension("EGL_KHR_gl_colorspace")) {
-        for (const EGLint* attr = attrib_list; attr[0] != EGL_NONE; attr += 2) {
-            if (attr[0] == EGL_GL_COLORSPACE_KHR) {
-                EGLint colorSpace = attr[1];
-                bool found = false;
-                // Verify that color space is allowed
-                for (auto it : allowedColorSpaces) {
-                    if (colorSpace == it) {
-                        found = true;
+    for (const EGLint* attr = attrib_list; attr[0] != EGL_NONE; attr += 2) {
+        switch (attr[0]) {
+            case EGL_GL_COLORSPACE_KHR: {
+                    EGLint colorSpace = attr[1];
+                    bool found = false;
+                    // Verify that color space is allowed
+                    for (auto it : allowedColorSpaces) {
+                        if (colorSpace == it) {
+                            found = true;
+                        }
+                    }
+                    if (found && dp->haveExtension("EGL_KHR_gl_colorspace")) {
+                        stripped = true;
+                    } else {
+                        stripped_attrib_list.push_back(attr[0]);
+                        stripped_attrib_list.push_back(attr[1]);
                     }
                 }
-                if (!found) {
+                break;
+            case EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT:
+            case EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT:
+            case EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT:
+            case EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT:
+            case EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT:
+            case EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT:
+            case EGL_SMPTE2086_WHITE_POINT_X_EXT:
+            case EGL_SMPTE2086_WHITE_POINT_Y_EXT:
+            case EGL_SMPTE2086_MAX_LUMINANCE_EXT:
+            case EGL_SMPTE2086_MIN_LUMINANCE_EXT:
+                if (dp->haveExtension("EGL_EXT_surface_SMPTE2086_metadata")) {
                     stripped = true;
                 } else {
                     stripped_attrib_list.push_back(attr[0]);
                     stripped_attrib_list.push_back(attr[1]);
                 }
-            } else {
+                break;
+            case EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT:
+            case EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT:
+                if (dp->haveExtension("EGL_EXT_surface_CTA861_3_metadata")) {
+                    stripped = true;
+                } else {
+                    stripped_attrib_list.push_back(attr[0]);
+                    stripped_attrib_list.push_back(attr[1]);
+                }
+                break;
+            default:
                 stripped_attrib_list.push_back(attr[0]);
                 stripped_attrib_list.push_back(attr[1]);
-            }
+                break;
         }
     }
+
+    // Make sure there is at least one attribute
     if (stripped) {
         stripped_attrib_list.push_back(EGL_NONE);
-        stripped_attrib_list.push_back(EGL_NONE);
     }
     return stripped;
 }
@@ -544,10 +580,10 @@
                     // is available, so no need to verify.
                     found = true;
                     verify = false;
-                } else if (colorSpace == EGL_EXT_gl_colorspace_bt2020_linear &&
+                } else if (colorSpace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT &&
                            dp->haveExtension("EGL_EXT_gl_colorspace_bt2020_linear")) {
                     found = true;
-                } else if (colorSpace == EGL_EXT_gl_colorspace_bt2020_pq &&
+                } else if (colorSpace == EGL_GL_COLORSPACE_BT2020_PQ_EXT &&
                            dp->haveExtension("EGL_EXT_gl_colorspace_bt2020_pq")) {
                     found = true;
                 } else if (colorSpace == EGL_GL_COLORSPACE_SCRGB_EXT &&
@@ -664,10 +700,43 @@
     }
 }
 
+EGLBoolean setSurfaceMetadata(egl_surface_t* s, NativeWindowType window,
+                              const EGLint *attrib_list) {
+    // set any HDR metadata
+    bool smpte2086 = false;
+    bool cta8613 = false;
+    if (attrib_list == nullptr) return EGL_TRUE;
+
+    for (const EGLint* attr = attrib_list; attr[0] != EGL_NONE; attr += 2) {
+        smpte2086 |= s->setSmpte2086Attribute(attr[0], attr[1]);
+        cta8613 |= s->setCta8613Attribute(attr[0], attr[1]);
+    }
+    if (smpte2086) {
+        int err = native_window_set_buffers_smpte2086_metadata(window, s->getSmpte2086Metadata());
+        if (err != 0) {
+            ALOGE("error setting native window smpte2086 metadata: %s (%d)",
+                  strerror(-err), err);
+            native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+            return EGL_FALSE;
+        }
+    }
+    if (cta8613) {
+        int err = native_window_set_buffers_cta861_3_metadata(window, s->getCta8613Metadata());
+        if (err != 0) {
+            ALOGE("error setting native window CTS 861.3 metadata: %s (%d)",
+                  strerror(-err), err);
+            native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+            return EGL_FALSE;
+        }
+    }
+    return EGL_TRUE;
+}
+
 EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
                                     NativeWindowType window,
                                     const EGLint *attrib_list)
 {
+    const EGLint *origAttribList = attrib_list;
     clearError();
 
     egl_connection_t* cnx = NULL;
@@ -705,7 +774,7 @@
         }
 
         std::vector<EGLint> strippedAttribList;
-        if (stripColorSpaceAttribute(dp, attrib_list, format, strippedAttribList)) {
+        if (stripAttributes(dp, attrib_list, format, strippedAttribList)) {
             // Had to modify the attribute list due to use of color space.
             // Use modified list from here on.
             attrib_list = strippedAttribList.data();
@@ -741,7 +810,11 @@
         if (surface != EGL_NO_SURFACE) {
             egl_surface_t* s =
                     new egl_surface_t(dp.get(), config, window, surface, colorSpace, cnx);
-            return s;
+
+            if (setSurfaceMetadata(s, window, origAttribList)) {
+                return s;
+            }
+            eglDestroySurface(dpy, s);
         }
 
         // EGLSurface creation failed
@@ -802,7 +875,7 @@
         // colorspace. We do need to filter out color spaces the
         // driver doesn't know how to process.
         std::vector<EGLint> strippedAttribList;
-        if (stripColorSpaceAttribute(dp, attrib_list, format, strippedAttribList)) {
+        if (stripAttributes(dp, attrib_list, format, strippedAttribList)) {
             // Had to modify the attribute list due to use of color space.
             // Use modified list from here on.
             attrib_list = strippedAttribList.data();
@@ -850,12 +923,14 @@
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
-    if (attribute == EGL_GL_COLORSPACE_KHR) {
-        *value = s->getColorSpace();
+    if (s->getColorSpaceAttribute(attribute, value)) {
+        return EGL_TRUE;
+    } else if (s->getSmpte2086Attribute(attribute, value)) {
+        return EGL_TRUE;
+    } else if (s->getCta8613Attribute(attribute, value)) {
         return EGL_TRUE;
     }
-    return s->cnx->egl.eglQuerySurface(
-            dp->disp.dpy, s->surface, attribute, value);
+    return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value);
 }
 
 void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 3c1edd1..0f36614 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -214,6 +214,23 @@
                     "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 ");
         }
 
+        bool hasHdrBoardConfig =
+                getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(false);
+
+        if (hasHdrBoardConfig) {
+            // hasHDRBoardConfig indicates the system is capable of supporting HDR content.
+            // Typically that means there is an HDR capable display attached, but could be
+            // support for attaching an HDR display. In either case, advertise support for
+            // HDR color spaces.
+            mExtensionString.append(
+                    "EGL_EXT_gl_colorspace_bt2020_linear EGL_EXT_gl_colorspace_bt2020_pq ");
+        }
+
+        // Always advertise HDR metadata extensions since it's okay for an application
+        // to specify such information even though it may not be used by the system.
+        mExtensionString.append(
+                "EGL_EXT_surface_SMPTE2086_metadata EGL_EXT_surface_CTA861_3_metadata ");
+
         char const* start = gExtensionString;
         do {
             // length of the extension name
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 72b4823..13b94b6 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -63,7 +63,9 @@
         win(win),
         cnx(cnx),
         connected(true),
-        colorSpace(colorSpace) {
+        colorSpace(colorSpace),
+        smpte2086_metadata({}),
+        cta861_3_metadata({}) {
     if (win) {
         win->incStrong(this);
     }
@@ -86,6 +88,123 @@
     }
 }
 
+EGLBoolean egl_surface_t::setSmpte2086Attribute(EGLint attribute, EGLint value) {
+    switch (attribute) {
+            break;
+        case EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT:
+            smpte2086_metadata.displayPrimaryRed.x = value;
+            return EGL_TRUE;
+        case EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT:
+            smpte2086_metadata.displayPrimaryRed.y = value;
+            return EGL_TRUE;
+        case EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT:
+            smpte2086_metadata.displayPrimaryGreen.x = value;
+            return EGL_TRUE;
+        case EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT:
+            smpte2086_metadata.displayPrimaryGreen.y = value;
+            return EGL_TRUE;
+        case EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT:
+            smpte2086_metadata.displayPrimaryBlue.x = value;
+            return EGL_TRUE;
+        case EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT:
+            smpte2086_metadata.displayPrimaryBlue.y = value;
+            return EGL_TRUE;
+        case EGL_SMPTE2086_WHITE_POINT_X_EXT:
+            smpte2086_metadata.whitePoint.x = value;
+            return EGL_TRUE;
+        case EGL_SMPTE2086_WHITE_POINT_Y_EXT:
+            smpte2086_metadata.whitePoint.y = value;
+            return EGL_TRUE;
+        case EGL_SMPTE2086_MAX_LUMINANCE_EXT:
+            smpte2086_metadata.maxLuminance = value;
+            return EGL_TRUE;
+        case EGL_SMPTE2086_MIN_LUMINANCE_EXT:
+            smpte2086_metadata.minLuminance = value;
+            return EGL_TRUE;
+    }
+    return EGL_FALSE;
+}
+
+EGLBoolean egl_surface_t::setCta8613Attribute(EGLint attribute, EGLint value) {
+    switch (attribute) {
+        case EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT:
+            cta861_3_metadata.maxContentLightLevel = value;
+            return EGL_TRUE;
+        case EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT:
+            cta861_3_metadata.maxFrameAverageLightLevel = value;
+            return EGL_TRUE;
+    }
+    return EGL_FALSE;
+}
+
+EGLBoolean egl_surface_t::getColorSpaceAttribute(EGLint attribute, EGLint* value) const {
+    if (attribute == EGL_GL_COLORSPACE_KHR) {
+        *value = colorSpace;
+        return EGL_TRUE;
+    }
+    return EGL_FALSE;
+}
+
+EGLBoolean egl_surface_t::getSmpte2086Attribute(EGLint attribute, EGLint *value) const {
+    switch (attribute) {
+        case EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT:
+            *value = *reinterpret_cast<const int*>(&smpte2086_metadata.displayPrimaryRed.x);
+            return EGL_TRUE;
+            break;
+        case EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT:
+            *value = *reinterpret_cast<const int*>(&smpte2086_metadata.displayPrimaryRed.y);
+            return EGL_TRUE;
+            break;
+        case EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT:
+            *value = *reinterpret_cast<const int*>(&smpte2086_metadata.displayPrimaryGreen.x);
+            return EGL_TRUE;
+            break;
+        case EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT:
+            *value = *reinterpret_cast<const int*>(&smpte2086_metadata.displayPrimaryGreen.y);
+            return EGL_TRUE;
+            break;
+        case EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT:
+            *value = *reinterpret_cast<const int*>(&smpte2086_metadata.displayPrimaryBlue.x);
+            return EGL_TRUE;
+            break;
+        case EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT:
+            *value = *reinterpret_cast<const int*>(&smpte2086_metadata.displayPrimaryBlue.y);
+            return EGL_TRUE;
+            break;
+        case EGL_SMPTE2086_WHITE_POINT_X_EXT:
+            *value = *reinterpret_cast<const int*>(&smpte2086_metadata.whitePoint.x);
+            return EGL_TRUE;
+            break;
+        case EGL_SMPTE2086_WHITE_POINT_Y_EXT:
+            *value = *reinterpret_cast<const int*>(&smpte2086_metadata.whitePoint.y);
+            return EGL_TRUE;
+            break;
+        case EGL_SMPTE2086_MAX_LUMINANCE_EXT:
+            *value = *reinterpret_cast<const int*>(&smpte2086_metadata.maxLuminance);
+            return EGL_TRUE;
+            break;
+        case EGL_SMPTE2086_MIN_LUMINANCE_EXT:
+            *value = *reinterpret_cast<const int*>(&smpte2086_metadata.minLuminance);
+            return EGL_TRUE;
+            break;
+    }
+    return EGL_FALSE;
+}
+
+EGLBoolean egl_surface_t::getCta8613Attribute(EGLint attribute, EGLint *value) const {
+    switch (attribute) {
+        case EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT:
+            *value = *reinterpret_cast<const int*>(&cta861_3_metadata.maxContentLightLevel);
+            return EGL_TRUE;
+            break;
+        case EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT:
+            *value = *reinterpret_cast<const int*>(&cta861_3_metadata.maxFrameAverageLightLevel);
+            return EGL_TRUE;
+            break;
+    }
+    return EGL_FALSE;
+}
+
 void egl_surface_t::terminate() {
     disconnect();
     egl_object_t::terminate();
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 7c3075c..a9020a1 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -137,6 +137,13 @@
     ANativeWindow* getNativeWindow() { return win; }
     ANativeWindow* getNativeWindow() const { return win; }
     EGLint getColorSpace() const { return colorSpace; }
+    EGLBoolean setSmpte2086Attribute(EGLint attribute, EGLint value);
+    EGLBoolean setCta8613Attribute(EGLint attribute, EGLint value);
+    EGLBoolean getColorSpaceAttribute(EGLint attribute, EGLint* value) const;
+    EGLBoolean getSmpte2086Attribute(EGLint attribute, EGLint* value) const;
+    EGLBoolean getCta8613Attribute(EGLint attribute, EGLint* value) const;
+    const android_smpte2086_metadata* getSmpte2086Metadata() const { return &smpte2086_metadata; }
+    const android_cta861_3_metadata* getCta8613Metadata() const { return &cta861_3_metadata; }
 
     // Try to keep the order of these fields and size unchanged. It's not public API, but
     // it's not hard to imagine native games accessing them.
@@ -150,6 +157,8 @@
     bool connected;
     void disconnect();
     EGLint colorSpace;
+    android_smpte2086_metadata smpte2086_metadata;
+    android_cta861_3_metadata cta861_3_metadata;
 };
 
 class egl_context_t: public egl_object_t {
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index b67a053..8bb74a2 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -53,7 +53,20 @@
 static bool hasWideColorDisplay =
         getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
 
+static bool hasHdrDisplay =
+        getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(false);
+
+union FlexAttribute {
+    EGLint uint_value;
+    float float_value;
+};
+
 class EGLTest : public ::testing::Test {
+public:
+    void get8BitConfig(EGLConfig& config);
+    void addOptionalWindowMetadata(std::vector<EGLint>& attrs);
+    void checkOptionalWindowMetadata(EGLSurface eglSurface);
+
 protected:
     EGLDisplay mEglDisplay;
 
@@ -365,6 +378,261 @@
     EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
 }
 
+void EGLTest::get8BitConfig(EGLConfig& config) {
+    EGLint numConfigs;
+    EGLBoolean success;
+
+    // Use 8-bit to keep focus on colorspace aspect
+    const EGLint attrs[] = {
+            // clang-format off
+            EGL_SURFACE_TYPE,             EGL_WINDOW_BIT,
+            EGL_RENDERABLE_TYPE,          EGL_OPENGL_ES2_BIT,
+            EGL_SURFACE_TYPE,             EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+            EGL_RED_SIZE,                 8,
+            EGL_GREEN_SIZE,               8,
+            EGL_BLUE_SIZE,                8,
+            EGL_ALPHA_SIZE,               8,
+            EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FIXED_EXT,
+            EGL_NONE,
+            // clang-format on
+    };
+    success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(1, numConfigs);
+
+    EGLint components[4];
+    EGLint value;
+    eglGetConfigAttrib(mEglDisplay, config, EGL_CONFIG_ID, &value);
+
+    success = eglGetConfigAttrib(mEglDisplay, config, EGL_RED_SIZE, &components[0]);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    success = eglGetConfigAttrib(mEglDisplay, config, EGL_GREEN_SIZE, &components[1]);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    success = eglGetConfigAttrib(mEglDisplay, config, EGL_BLUE_SIZE, &components[2]);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    success = eglGetConfigAttrib(mEglDisplay, config, EGL_ALPHA_SIZE, &components[3]);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    // Verify component sizes on config match what was asked for.
+    EXPECT_EQ(components[0], 8);
+    EXPECT_EQ(components[1], 8);
+    EXPECT_EQ(components[2], 8);
+    EXPECT_EQ(components[3], 8);
+}
+
+void EGLTest::addOptionalWindowMetadata(std::vector<EGLint>& attrs) {
+    FlexAttribute data;
+    if (hasEglExtension(mEglDisplay, "EGL_EXT_surface_SMPTE2086_metadata")) {
+        attrs.push_back(EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT);
+        data.float_value = 0.640;
+        attrs.push_back(data.uint_value);
+        attrs.push_back(EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT);
+        data.float_value = 0.330;
+        attrs.push_back(data.uint_value);
+        attrs.push_back(EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT);
+        data.float_value = 0.290;
+        attrs.push_back(data.uint_value);
+        attrs.push_back(EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT);
+        data.float_value = 0.600;
+        attrs.push_back(data.uint_value);
+        attrs.push_back(EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT);
+        data.float_value = 0.150;
+        attrs.push_back(data.uint_value);
+        attrs.push_back(EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT);
+        data.float_value = 0.060;
+        attrs.push_back(data.uint_value);
+        attrs.push_back(EGL_SMPTE2086_WHITE_POINT_X_EXT);
+        data.float_value = 0.3127;
+        attrs.push_back(data.uint_value);
+        attrs.push_back(EGL_SMPTE2086_WHITE_POINT_Y_EXT);
+        data.float_value = 0.3290;
+        attrs.push_back(data.uint_value);
+        attrs.push_back(EGL_SMPTE2086_MAX_LUMINANCE_EXT);
+        data.float_value = 300.0;
+        attrs.push_back(data.uint_value);
+        attrs.push_back(EGL_SMPTE2086_MIN_LUMINANCE_EXT);
+        data.float_value = 0.7;
+        attrs.push_back(data.uint_value);
+    }
+
+    if (hasEglExtension(mEglDisplay, "EGL_EXT_surface_CTA861_3_metadata")) {
+        attrs.push_back(EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT);
+        data.float_value = 300.0;
+        attrs.push_back(data.uint_value);
+        attrs.push_back(EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT);
+        data.float_value = 75.0;
+        attrs.push_back(data.uint_value);
+    }
+}
+
+void EGLTest::checkOptionalWindowMetadata(EGLSurface eglSurface) {
+    EGLBoolean success;
+    EGLint value;
+    FlexAttribute expected;
+
+    if (hasEglExtension(mEglDisplay, "EGL_EXT_surface_SMPTE2086_metadata")) {
+        success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT, &value);
+        ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+        expected.float_value = 0.640;
+        ASSERT_EQ(expected.uint_value, value);
+        success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT, &value);
+        ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+        expected.float_value = 0.330;
+        ASSERT_EQ(expected.uint_value, value);
+        ASSERT_EQ(0, value);
+        success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT, &value);
+        ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+        expected.float_value = 0.290;
+        ASSERT_EQ(expected.uint_value, value);
+        success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT, &value);
+        ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+        expected.float_value = 0.600;
+        ASSERT_EQ(expected.uint_value, value);
+        success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT, &value);
+        ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+        expected.float_value = 0.150;
+        ASSERT_EQ(expected.uint_value, value);
+        success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT, &value);
+        ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+        expected.float_value = 0.060;
+        ASSERT_EQ(expected.uint_value, value);
+        success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_WHITE_POINT_X_EXT, &value);
+        ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+        expected.float_value = 0.3127;
+        ASSERT_EQ(expected.uint_value, value);
+        success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_WHITE_POINT_Y_EXT, &value);
+        ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+        expected.float_value = 0.3290;
+        ASSERT_EQ(expected.uint_value, value);
+        success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_MAX_LUMINANCE_EXT, &value);
+        ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+        expected.float_value = 300.0;
+        ASSERT_EQ(expected.uint_value, value);
+        success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_MIN_LUMINANCE_EXT, &value);
+        ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+        expected.float_value = 0.7;
+        ASSERT_EQ(expected.uint_value, value);
+    }
+
+    if (hasEglExtension(mEglDisplay, "EGL_EXT_surface_CTA861_3_metadata")) {
+        success = eglQuerySurface(mEglDisplay, eglSurface, EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT, &value);
+        ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+        expected.float_value = 300.0;
+        ASSERT_EQ(expected.uint_value, value);
+        success = eglQuerySurface(mEglDisplay, eglSurface, EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT, &value);
+        ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+        expected.float_value = 75.0;
+        ASSERT_EQ(expected.uint_value, value);
+    }
+}
+
+TEST_F(EGLTest, EGLBT2020Linear) {
+    EGLConfig config;
+    EGLBoolean success;
+
+    if (!hasHdrDisplay) {
+        // skip this test if device does not have HDR display
+        RecordProperty("hasHdrDisplay", false);
+        return;
+    }
+
+    // Test that bt2020 linear extension exists
+    ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_bt2020_linear"))
+            << "EGL_EXT_gl_colorspace_bt2020_linear extension not available";
+
+    ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
+
+    struct DummyConsumer : public BnConsumerListener {
+        void onFrameAvailable(const BufferItem& /* item */) override {}
+        void onBuffersReleased() override {}
+        void onSidebandStreamChanged() override {}
+    };
+
+    // Create a EGLSurface
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+    consumer->consumerConnect(new DummyConsumer, false);
+    sp<Surface> mSTC = new Surface(producer);
+    sp<ANativeWindow> mANW = mSTC;
+
+    std::vector<EGLint> winAttrs;
+    winAttrs.push_back(EGL_GL_COLORSPACE_KHR);
+    winAttrs.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
+
+    ASSERT_NO_FATAL_FAILURE(addOptionalWindowMetadata(winAttrs));
+
+    winAttrs.push_back(EGL_NONE);
+
+    EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, config, mANW.get(), winAttrs.data());
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_SURFACE, eglSurface);
+
+    EGLint value;
+    success = eglQuerySurface(mEglDisplay, eglSurface, EGL_GL_COLORSPACE_KHR, &value);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(EGL_GL_COLORSPACE_BT2020_PQ_EXT, value);
+
+    ASSERT_NO_FATAL_FAILURE(checkOptionalWindowMetadata(eglSurface));
+
+    EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
+}
+
+TEST_F(EGLTest, EGLBT2020PQ) {
+    EGLConfig config;
+    EGLBoolean success;
+
+    if (!hasHdrDisplay) {
+        // skip this test if device does not have HDR display
+        RecordProperty("hasHdrDisplay", false);
+        return;
+    }
+
+    // Test that bt2020-pq extension exists
+    ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_bt2020_pq"))
+            << "EGL_EXT_gl_colorspace_bt2020_pq extension not available";
+
+    ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
+
+    struct DummyConsumer : public BnConsumerListener {
+        void onFrameAvailable(const BufferItem& /* item */) override {}
+        void onBuffersReleased() override {}
+        void onSidebandStreamChanged() override {}
+    };
+
+    // Create a EGLSurface
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+    consumer->consumerConnect(new DummyConsumer, false);
+    sp<Surface> mSTC = new Surface(producer);
+    sp<ANativeWindow> mANW = mSTC;
+    std::vector<EGLint> winAttrs;
+    winAttrs.push_back(EGL_GL_COLORSPACE_KHR);
+    winAttrs.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
+
+    ASSERT_NO_FATAL_FAILURE(addOptionalWindowMetadata(winAttrs));
+
+    winAttrs.push_back(EGL_NONE);
+
+    EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, config, mANW.get(), winAttrs.data());
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_SURFACE, eglSurface);
+
+    EGLint value;
+    success = eglQuerySurface(mEglDisplay, eglSurface, EGL_GL_COLORSPACE_KHR, &value);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(EGL_GL_COLORSPACE_BT2020_PQ_EXT, value);
+
+    ASSERT_NO_FATAL_FAILURE(checkOptionalWindowMetadata(eglSurface));
+
+    EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
+}
+
 TEST_F(EGLTest, EGLConfigFP16) {
     EGLint numConfigs;
     EGLConfig config;
@@ -372,13 +640,13 @@
 
     if (!hasWideColorDisplay) {
         // skip this test if device does not have wide-color display
-        std::cerr << "[          ] Device does not support wide-color, test skipped" << std::endl;
+        RecordProperty("hasWideColorDisplay", false);
         return;
     }
 
     ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_pixel_format_float"));
 
-    EGLint attrs[] = {
+    const EGLint attrs[] = {
             // clang-format off
             EGL_SURFACE_TYPE,             EGL_WINDOW_BIT,
             EGL_RENDERABLE_TYPE,          EGL_OPENGL_ES2_BIT,
@@ -387,7 +655,7 @@
             EGL_BLUE_SIZE,                16,
             EGL_ALPHA_SIZE,               16,
             EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT,
-            EGL_NONE,                     EGL_NONE
+            EGL_NONE,
             // clang-format on
     };
     success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs);
@@ -437,7 +705,7 @@
 TEST_F(EGLTest, EGLNoConfigContext) {
     if (!hasWideColorDisplay) {
         // skip this test if device does not have wide-color display
-        std::cerr << "[          ] Device does not support wide-color, test skipped" << std::endl;
+        RecordProperty("hasWideColorDisplay", false);
         return;
     }
 
@@ -475,11 +743,11 @@
 
     if (!hasWideColorDisplay) {
         // skip this test if device does not have wide-color display
-        std::cerr << "[          ] Device does not support wide-color, test skipped" << std::endl;
+        RecordProperty("hasWideColorDisplay", false);
         return;
     }
 
-    EGLint attrs[] = {
+    const EGLint attrs[] = {
             // clang-format off
             EGL_SURFACE_TYPE,             EGL_WINDOW_BIT,
             EGL_RENDERABLE_TYPE,          EGL_OPENGL_ES2_BIT,
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index ba9eaf6..4d4c9fc 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -822,7 +822,7 @@
                               getColor());
     engine.setSourceDataSpace(mCurrentState.dataSpace);
 
-    if (mCurrentState.dataSpace == HAL_DATASPACE_BT2020_PQ &&
+    if (mCurrentState.dataSpace == HAL_DATASPACE_BT2020_ITU_PQ &&
         mConsumer->getCurrentApi() == NATIVE_WINDOW_API_MEDIA &&
         getBE().compositionInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102) {
         engine.setSourceY410BT2020(true);
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index 323cec7..5133bac 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -326,11 +326,12 @@
 
     if (usesWideColor()) {
         Description wideColorState = mState;
-        switch (mDataSpace) {
+        switch (int(mDataSpace)) {
             case HAL_DATASPACE_DISPLAY_P3:
                 // input matches output
                 break;
             case HAL_DATASPACE_BT2020_PQ:
+            case HAL_DATASPACE_BT2020_ITU_PQ:
                 wideColorState.setColorMatrix(mState.getColorMatrix() * mBt2020ToDisplayP3);
                 wideColorState.setInputTransferFunction(Description::TransferFunction::ST2084);
                 wideColorState.setOutputTransferFunction(Description::TransferFunction::SRGB);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 974a261..d8740d9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1816,8 +1816,13 @@
     if (a == HAL_DATASPACE_V0_SCRGB || b == HAL_DATASPACE_V0_SCRGB) {
         return HAL_DATASPACE_DISPLAY_P3;
     }
-    if (!hasHdr && (a == HAL_DATASPACE_BT2020_PQ || b == HAL_DATASPACE_BT2020_PQ)) {
-        return HAL_DATASPACE_DISPLAY_P3;
+    if (!hasHdr) {
+        if (a == HAL_DATASPACE_BT2020_PQ || b == HAL_DATASPACE_BT2020_PQ) {
+            return HAL_DATASPACE_DISPLAY_P3;
+        }
+        if (a == HAL_DATASPACE_BT2020_ITU_PQ || b == HAL_DATASPACE_BT2020_ITU_PQ) {
+            return HAL_DATASPACE_DISPLAY_P3;
+        }
     }
 
     return HAL_DATASPACE_V0_SRGB;
@@ -1900,7 +1905,8 @@
                     "display %zd: %d", displayId, result);
         }
         for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-            if (layer->getDataSpace() == HAL_DATASPACE_BT2020_PQ &&
+            if ((layer->getDataSpace() == HAL_DATASPACE_BT2020_PQ ||
+                 layer->getDataSpace() == HAL_DATASPACE_BT2020_ITU_PQ) &&
                     !displayDevice->getHdrSupport()) {
                 layer->forceClientComposition(hwcId);
             }
diff --git a/services/vr/hardware_composer/vr_hwc.rc b/services/vr/hardware_composer/vr_hwc.rc
index 52d4da8..6f930a8 100644
--- a/services/vr/hardware_composer/vr_hwc.rc
+++ b/services/vr/hardware_composer/vr_hwc.rc
@@ -1,5 +1,5 @@
 service vr_hwc /system/bin/vr_hwc
-  class hal
+  class hal animation
   user system
   group system graphics
   onrestart restart surfaceflinger
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index c9faf28..7f4f2c4 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -68,10 +68,14 @@
         "vulkan_headers",
     ],
     shared_libs: [
+        "android.hardware.configstore@1.0",
+        "android.hardware.configstore-utils",
         "libziparchive",
         "libhardware",
         "libsync",
         "libbase",
+        "libhidlbase",
+        "libhidltransport",
         "liblog",
         "libui",
         "libgraphicsenv",
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index b27d1b7..c8e7bd9 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -27,6 +27,8 @@
 #include <log/log.h>
 
 #include <android/dlext.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
 #include <cutils/properties.h>
 #include <graphicsenv/GraphicsEnv.h>
 #include <utils/Vector.h>
@@ -36,6 +38,9 @@
 #include "driver.h"
 #include "stubhal.h"
 
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
 // TODO(b/37049319) Get this from a header once one exists
 extern "C" {
 android_namespace_t* android_get_exported_namespace(const char*);
@@ -826,6 +831,14 @@
         VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME,
         VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION});
 
+    bool hdrBoardConfig =
+        getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(
+            false);
+    if (hdrBoardConfig) {
+        loader_extensions.push_back({VK_EXT_HDR_METADATA_EXTENSION_NAME,
+                                     VK_EXT_HDR_METADATA_SPEC_VERSION});
+    }
+
     VkPhysicalDevicePresentationPropertiesANDROID presentation_properties;
     if (QueryPresentationProperties(physicalDevice, &presentation_properties) &&
         presentation_properties.sharedImage) {
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 03e6ee0..9fbde8c 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1650,15 +1650,39 @@
 }
 
 VKAPI_ATTR void SetHdrMetadataEXT(
-    VkDevice device,
+    VkDevice,
     uint32_t swapchainCount,
     const VkSwapchainKHR* pSwapchains,
     const VkHdrMetadataEXT* pHdrMetadataEXTs) {
-    // TODO: courtneygo: implement actual function
-    (void)device;
-    (void)swapchainCount;
-    (void)pSwapchains;
-    (void)pHdrMetadataEXTs;
+
+    for (uint32_t idx = 0; idx < swapchainCount; idx++) {
+        Swapchain* swapchain = SwapchainFromHandle(pSwapchains[idx]);
+        if (!swapchain)
+            continue;
+
+        if (swapchain->surface.swapchain_handle != pSwapchains[idx]) continue;
+
+        ANativeWindow* window = swapchain->surface.window.get();
+
+        VkHdrMetadataEXT vulkanMetadata = pHdrMetadataEXTs[idx];
+        const android_smpte2086_metadata smpteMetdata = {
+            {vulkanMetadata.displayPrimaryRed.x,
+             vulkanMetadata.displayPrimaryRed.y},
+            {vulkanMetadata.displayPrimaryGreen.x,
+             vulkanMetadata.displayPrimaryGreen.y},
+            {vulkanMetadata.displayPrimaryBlue.x,
+             vulkanMetadata.displayPrimaryBlue.y},
+            {vulkanMetadata.whitePoint.x, vulkanMetadata.whitePoint.y},
+            vulkanMetadata.maxLuminance,
+            vulkanMetadata.minLuminance};
+        native_window_set_buffers_smpte2086_metadata(window, &smpteMetdata);
+
+        const android_cta861_3_metadata cta8613Metadata = {
+            vulkanMetadata.maxContentLightLevel,
+            vulkanMetadata.maxFrameAverageLightLevel};
+        native_window_set_buffers_cta861_3_metadata(window, &cta8613Metadata);
+    }
+
     return;
 }