Merge "libgui: export EGL headers." into oc-dr1-dev
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index d61cbc9..93ae9da 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -769,7 +769,8 @@
     ok &= setCategoriesEnableFromFile(g_categoriesFile);
     ok &= setTraceOverwriteEnable(g_traceOverwrite);
     ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
-    ok &= setCmdlineSize();
+    // TODO: Re-enable after stabilization
+    //ok &= setCmdlineSize();
     ok &= setClock();
     ok &= setPrintTgidEnableIfPresent(true);
     ok &= setKernelTraceFuncs(g_kernelTraceFuncs);
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 089e66c..2f8840b 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -126,7 +126,8 @@
 static const std::string kDumpstateBoardPath = "/bugreports/";
 static const std::string kDumpstateBoardFiles[] = {
     "dumpstate_board.txt",
-    "dumpstate_board.bin"
+    // TODO: rename to dumpstate_board.bin once vendors can handle it
+    "modem_log_all.tar"
 };
 static const int NUM_OF_DUMPS = arraysize(kDumpstateBoardFiles);
 
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 497bd19..4558fe8 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -63,7 +63,7 @@
         brillo: {
             cflags: ["-DHAVE_NO_SURFACE_FLINGER"],
         },
-        debuggable: {
+        eng: {
             cppflags: [
                 "-UDEBUG_ONLY_CODE",
                 "-DDEBUG_ONLY_CODE=1",
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index e149803..87dbaf4 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -16,7 +16,6 @@
 
 #define LOG_TAG "Gralloc2"
 
-#include <hidl/ServiceManagement.h>
 #include <hwbinder/IPCThreadState.h>
 #include <ui/Gralloc2.h>
 
@@ -251,17 +250,4 @@
 
 } // namespace Gralloc2
 
-namespace {
-// Load the IMapper implementation library when this shared library is loaded, rather than when
-// we (lazily) create the Gralloc2::Mapper instance. Since these libraries will all be needed by
-// nearly all apps, this allows us to load them in Zygote rather than on each app launch.
-class PreloadMapperImpl {
-public:
-    PreloadMapperImpl() {
-        android::hardware::preloadPassthroughService<hardware::graphics::mapper::V2_0::IMapper>();
-    }
-};
-static PreloadMapperImpl preloadMapperImpl;
-} // namespace
-
 } // namespace android
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 2a222ac..c5feb89 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -457,8 +457,8 @@
 // EGL_GL_COLORSPACE_SRGB_KHR, or turn sRGB formats into corresponding linear
 // formats when colorspace is EGL_GL_COLORSPACE_LINEAR_KHR. In any cases where
 // the modification isn't possible, the original dataSpace is returned.
-static android_dataspace modifyBufferDataspace( android_dataspace dataSpace,
-                                                EGLint colorspace) {
+static android_dataspace modifyBufferDataspace(android_dataspace dataSpace,
+                                               EGLint colorspace) {
     if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) {
         return HAL_DATASPACE_SRGB_LINEAR;
     } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
@@ -473,6 +473,59 @@
     return dataSpace;
 }
 
+// Return true if we stripped any EGL_GL_COLORSPACE_KHR attributes.
+static EGLBoolean stripColorSpaceAttribute(egl_display_ptr dp, const EGLint* attrib_list,
+                                           EGLint format,
+                                           std::vector<EGLint>& stripped_attrib_list) {
+    std::vector<EGLint> allowedColorSpaces;
+    switch (format) {
+        case HAL_PIXEL_FORMAT_RGBA_8888:
+        case HAL_PIXEL_FORMAT_RGB_565:
+            // driver okay with linear & sRGB for 8888, but can't handle
+            // Display-P3 or other spaces.
+            allowedColorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR);
+            allowedColorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR);
+            break;
+
+        case HAL_PIXEL_FORMAT_RGBA_FP16:
+        case HAL_PIXEL_FORMAT_RGBA_1010102:
+        default:
+            // driver does not want to see colorspace attributes for 1010102 or fp16.
+            // Future: if driver supports XXXX extension, we can pass down that colorspace
+            break;
+    }
+
+    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;
+                    }
+                }
+                if (!found) {
+                    stripped = true;
+                } else {
+                    stripped_attrib_list.push_back(attr[0]);
+                    stripped_attrib_list.push_back(attr[1]);
+                }
+            } else {
+                stripped_attrib_list.push_back(attr[0]);
+                stripped_attrib_list.push_back(attr[1]);
+            }
+        }
+    }
+    if (stripped) {
+        stripped_attrib_list.push_back(EGL_NONE);
+        stripped_attrib_list.push_back(EGL_NONE);
+    }
+    return stripped;
+}
+
 static EGLBoolean getColorSpaceAttribute(egl_display_ptr dp, const EGLint* attrib_list,
                                          EGLint& colorSpace, android_dataspace& dataSpace) {
     colorSpace = EGL_GL_COLORSPACE_LINEAR_KHR;
@@ -514,6 +567,65 @@
     return true;
 }
 
+void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config, EGLint& format) {
+    // Set the native window's buffers format to match what this config requests.
+    // Whether to use sRGB gamma is not part of the EGLconfig, but is part
+    // of our native format. So if sRGB gamma is requested, we have to
+    // modify the EGLconfig's format before setting the native window's
+    // format.
+
+    EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType);
+
+    EGLint a = 0;
+    EGLint r, g, b;
+    r = g = b = 0;
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r);
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g);
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b);
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a);
+    EGLint colorDepth = r + g + b;
+
+    // Today, the driver only understands sRGB and linear on 888X
+    // formats. Strip other colorspaces from the attribute list and
+    // only use them to set the dataspace via
+    // native_window_set_buffers_dataspace
+    // if pixel format is RGBX 8888
+    //    TBD: Can test for future extensions that indicate that driver
+    //    handles requested color space and we can let it through.
+    //    allow SRGB and LINEAR. All others need to be stripped.
+    // else if 565, 4444
+    //    TBD: Can we assume these are supported if 8888 is?
+    // else if FP16 or 1010102
+    //    strip colorspace from attribs.
+    // endif
+    if (a == 0) {
+        if (colorDepth <= 16) {
+            format = HAL_PIXEL_FORMAT_RGB_565;
+        } else {
+            if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+                if (colorDepth > 24) {
+                    format = HAL_PIXEL_FORMAT_RGBA_1010102;
+                } else {
+                    format = HAL_PIXEL_FORMAT_RGBX_8888;
+                }
+            } else {
+                format = HAL_PIXEL_FORMAT_RGBA_FP16;
+            }
+        }
+    } else {
+        if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+            if (colorDepth > 24) {
+                format = HAL_PIXEL_FORMAT_RGBA_1010102;
+            } else {
+                format = HAL_PIXEL_FORMAT_RGBA_8888;
+            }
+        } else {
+            format = HAL_PIXEL_FORMAT_RGBA_FP16;
+        }
+    }
+}
+
 EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
                                     NativeWindowType window,
                                     const EGLint *attrib_list)
@@ -543,60 +655,24 @@
             return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
         }
 
-        // Set the native window's buffers format to match what this config requests.
-        // Whether to use sRGB gamma is not part of the EGLconfig, but is part
-        // of our native format. So if sRGB gamma is requested, we have to
-        // modify the EGLconfig's format before setting the native window's
-        // format.
-
-        EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
-        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_COLOR_COMPONENT_TYPE_EXT,
-                                    &componentType);
-
         EGLint format;
+        getNativePixelFormat(iDpy, cnx, config, format);
+
+        // now select correct colorspace and dataspace based on user's attribute list
         EGLint colorSpace;
         android_dataspace dataSpace;
-        EGLint a = 0;
-        EGLint r, g, b;
-        r = g = b = 0;
-        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_RED_SIZE,   &r);
-        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_GREEN_SIZE, &g);
-        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_BLUE_SIZE,  &b);
-        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_ALPHA_SIZE, &a);
-        EGLint colorDepth = r + g + b;
-
-        if (a == 0) {
-            if (colorDepth <= 16) {
-                format = HAL_PIXEL_FORMAT_RGB_565;
-            } else {
-                if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
-                    if (colorDepth > 24) {
-                        format = HAL_PIXEL_FORMAT_RGBA_1010102;
-                    } else {
-                        format = HAL_PIXEL_FORMAT_RGBX_8888;
-                    }
-                } else {
-                    format = HAL_PIXEL_FORMAT_RGBA_FP16;
-                }
-            }
-        } else {
-            if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
-                if (colorDepth > 24) {
-                    format = HAL_PIXEL_FORMAT_RGBA_1010102;
-                } else {
-                    format = HAL_PIXEL_FORMAT_RGBA_8888;
-                }
-            } else {
-                format = HAL_PIXEL_FORMAT_RGBA_FP16;
-            }
-        }
-
-        // now select a corresponding sRGB format if needed
         if (!getColorSpaceAttribute(dp, attrib_list, colorSpace, dataSpace)) {
             ALOGE("error invalid colorspace: %d", colorSpace);
             return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
         }
 
+        std::vector<EGLint> strippedAttribList;
+        if (stripColorSpaceAttribute(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();
+        }
+
         if (format != 0) {
             int err = native_window_set_buffers_format(window, format);
             if (err != 0) {
@@ -671,15 +747,29 @@
 
     egl_connection_t* cnx = NULL;
     egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    EGLint colorSpace;
-    android_dataspace dataSpace;
     if (dp) {
-        // now select a corresponding sRGB format if needed
+        EGLDisplay iDpy = dp->disp.dpy;
+        EGLint format;
+        getNativePixelFormat(iDpy, cnx, config, format);
+
+        // now select correct colorspace and dataspace based on user's attribute list
+        EGLint colorSpace;
+        android_dataspace dataSpace;
         if (!getColorSpaceAttribute(dp, attrib_list, colorSpace, dataSpace)) {
             ALOGE("error invalid colorspace: %d", colorSpace);
             return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
         }
 
+        // Pbuffers are not displayed so we don't need to store the
+        // 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)) {
+            // Had to modify the attribute list due to use of color space.
+            // Use modified list from here on.
+            attrib_list = strippedAttribList.data();
+        }
+
         EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
                 dp->disp.dpy, config, attrib_list);
         if (surface != EGL_NO_SURFACE) {
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index ae628e1..e34fa16 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -553,6 +553,29 @@
     return Error::NONE;
 }
 
+Error Composer::presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
+                               uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) {
+   mWriter.selectDisplay(display);
+   mWriter.presentOrvalidateDisplay();
+
+   Error error = execute();
+   if (error != Error::NONE) {
+       return error;
+   }
+
+   mReader.takePresentOrValidateStage(display, state);
+
+   if (*state == 1) { // Present succeeded
+       mReader.takePresentFence(display, outPresentFence);
+   }
+
+   if (*state == 0) { // Validate succeeded.
+       mReader.hasChanges(display, outNumTypes, outNumRequests);
+   }
+
+   return Error::NONE;
+}
+
 Error Composer::setCursorPosition(Display display, Layer layer,
         int32_t x, int32_t y)
 {
@@ -770,7 +793,8 @@
             auto command = mWriter.getCommand(cmdErr.location);
 
             if (command == IComposerClient::Command::VALIDATE_DISPLAY ||
-                command == IComposerClient::Command::PRESENT_DISPLAY) {
+                command == IComposerClient::Command::PRESENT_DISPLAY ||
+                command == IComposerClient::Command::PRESENT_OR_VALIDATE_DISPLAY) {
                 error = cmdErr.error;
             } else {
                 ALOGW("command 0x%x generated error %d",
@@ -821,6 +845,9 @@
         case IComposerClient::Command::SET_RELEASE_FENCES:
             parsed = parseSetReleaseFences(length);
             break;
+        case IComposerClient::Command ::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT:
+            parsed = parseSetPresentOrValidateDisplayResult(length);
+            break;
         default:
             parsed = false;
             break;
@@ -949,6 +976,15 @@
     return true;
 }
 
+bool CommandReader::parseSetPresentOrValidateDisplayResult(uint16_t length)
+{
+    if (length != CommandWriterBase::kPresentOrValidateDisplayResultLength || !mCurrentReturnData) {
+        return false;
+    }
+    mCurrentReturnData->presentOrValidateState = read();
+    return true;
+}
+
 void CommandReader::resetData()
 {
     mErrors.clear();
@@ -1058,6 +1094,16 @@
     data.presentFence = -1;
 }
 
+void CommandReader::takePresentOrValidateStage(Display display, uint32_t* state) {
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        *state= -1;
+        return;
+    }
+    ReturnData& data = found->second;
+    *state = data.presentOrValidateState;
+}
+
 } // namespace Hwc2
 
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 68d6e6f..96dd833 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -93,6 +93,9 @@
     // Get and clear saved present fence.
     void takePresentFence(Display display, int* outPresentFence);
 
+    // Get what stage succeeded during PresentOrValidate: Present or Validate
+    void takePresentOrValidateStage(Display display, uint32_t * state);
+
 private:
     void resetData();
 
@@ -102,6 +105,7 @@
     bool parseSetDisplayRequests(uint16_t length);
     bool parseSetPresentFence(uint16_t length);
     bool parseSetReleaseFences(uint16_t length);
+    bool parseSetPresentOrValidateDisplayResult(uint16_t length);
 
     struct ReturnData {
         uint32_t displayRequests = 0;
@@ -116,6 +120,8 @@
 
         std::vector<Layer> releasedLayers;
         std::vector<int> releaseFences;
+
+        uint32_t presentOrValidateState;
     };
 
     std::vector<CommandError> mErrors;
@@ -202,6 +208,11 @@
     Error validateDisplay(Display display, uint32_t* outNumTypes,
             uint32_t* outNumRequests);
 
+    Error presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
+                                   uint32_t* outNumRequests,
+                                   int* outPresentFence,
+                                   uint32_t* state);
+
     Error setCursorPosition(Display display, Layer layer,
             int32_t x, int32_t y);
     /* see setClientTarget for the purpose of slot */
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 263ff00..270a732 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -695,6 +695,34 @@
     return error;
 }
 
+Error Display::presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
+                                 sp<android::Fence>* outPresentFence, uint32_t* state) {
+
+    uint32_t numTypes = 0;
+    uint32_t numRequests = 0;
+    int32_t presentFenceFd = -1;
+    auto intError = mDevice.mComposer->presentOrValidateDisplay(mId, &numTypes, &numRequests, &presentFenceFd, state);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None && error != Error::HasChanges) {
+        return error;
+    }
+
+    if (*state == 1) {
+        *outPresentFence = new Fence(presentFenceFd);
+    }
+
+    if (*state == 0) {
+        *outNumTypes = numTypes;
+        *outNumRequests = numRequests;
+    }
+    return error;
+}
+
+void Display::discardCommands()
+{
+    mDevice.mComposer->resetCommands();
+}
+
 // For use by Device
 
 int32_t Display::getAttribute(hwc2_config_t configId, Attribute attribute)
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index b7376d0..404bb28 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -255,6 +255,15 @@
     [[clang::warn_unused_result]] Error setVsyncEnabled(Vsync enabled);
     [[clang::warn_unused_result]] Error validate(uint32_t* outNumTypes,
             uint32_t* outNumRequests);
+    [[clang::warn_unused_result]] Error presentOrValidate(uint32_t* outNumTypes,
+                                                 uint32_t* outNumRequests,
+                                                          android::sp<android::Fence>* outPresentFence, uint32_t* state);
+
+    // Most methods in this class write a command to a command buffer.  The
+    // command buffer is implicitly submitted in validate, present, and
+    // presentOrValidate.  This method provides a way to discard the commands,
+    // which can be used to discard stale commands.
+    void discardCommands();
 
     // Other Display methods
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 42be935..ac2dde2 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -455,7 +455,34 @@
 
     uint32_t numTypes = 0;
     uint32_t numRequests = 0;
-    auto error = hwcDisplay->validate(&numTypes, &numRequests);
+
+    HWC2::Error error = HWC2::Error::None;
+
+    // First try to skip validate altogether if the HWC supports it.
+    displayData.validateWasSkipped = false;
+    if (hasCapability(HWC2::Capability::SkipValidate) &&
+            !displayData.hasClientComposition) {
+        sp<android::Fence> outPresentFence;
+        uint32_t state = UINT32_MAX;
+        error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
+        if (error != HWC2::Error::None && error != HWC2::Error::HasChanges) {
+            ALOGV("skipValidate: Failed to Present or Validate");
+            return UNKNOWN_ERROR;
+        }
+        if (state == 1) { //Present Succeeded.
+            std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> releaseFences;
+            error = hwcDisplay->getReleaseFences(&releaseFences);
+            displayData.releaseFences = std::move(releaseFences);
+            displayData.lastPresentFence = outPresentFence;
+            displayData.validateWasSkipped = true;
+            displayData.presentError = error;
+            return NO_ERROR;
+        }
+        // Present failed but Validate ran.
+    } else {
+        error = hwcDisplay->validate(&numTypes, &numRequests);
+    }
+    ALOGV("SkipValidate failed, Falling back to SLOW validate/present");
     if (error != HWC2::Error::None && error != HWC2::Error::HasChanges) {
         ALOGE("prepare: validate failed for display %d: %s (%d)", displayId,
                 to_string(error).c_str(), static_cast<int32_t>(error));
@@ -592,6 +619,18 @@
 
     auto& displayData = mDisplayData[displayId];
     auto& hwcDisplay = displayData.hwcDisplay;
+
+    if (displayData.validateWasSkipped) {
+        hwcDisplay->discardCommands();
+        auto error = displayData.presentError;
+        if (error != HWC2::Error::None) {
+            ALOGE("skipValidate: failed for display %d: %s (%d)",
+                  displayId, to_string(error).c_str(), static_cast<int32_t>(error));
+            return UNKNOWN_ERROR;
+        }
+        return NO_ERROR;
+    }
+
     auto error = hwcDisplay->present(&displayData.lastPresentFence);
     if (error != HWC2::Error::None) {
         ALOGE("presentAndGetReleaseFences: failed for display %d: %s (%d)",
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 3eb968d..7463362 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -202,6 +202,9 @@
 
         // protected by mVsyncLock
         HWC2::Vsync vsyncEnabled;
+
+        bool validateWasSkipped;
+        HWC2::Error presentError;
     };
 
     std::unique_ptr<HWC2::Device>   mHwcDevice;
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index 9bbb7f3..861114d 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -15,6 +15,7 @@
  */
 #include "impl/vr_hwc.h"
 
+#include <cutils/properties.h>
 #include <private/dvr/display_client.h>
 #include <ui/Fence.h>
 
@@ -352,7 +353,14 @@
       break;
     case IComposerClient::Attribute::DPI_X:
     case IComposerClient::Attribute::DPI_Y:
-      *outValue = 300 * 1000;  // 300dpi
+      {
+        constexpr int32_t kDefaultDPI = 300;
+        int32_t dpi = property_get_int32("ro.vr.hwc.dpi", kDefaultDPI);
+        if (dpi <= 0) {
+          dpi = kDefaultDPI;
+        }
+        *outValue = 1000 * dpi;
+      }
       break;
     default:
       return Error::BAD_PARAMETER;