Add boot time display mode to native framework

Bug: 203520442
Test: m
Test: atest libsurfaceflinger_unittest
Test: atest libgui_test

Change-Id: I61ad0c75576a4e1ee54d657e6906bc5d6c10afaf
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index a931709..0227043 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -428,6 +428,94 @@
         return static_cast<status_t>(reply.readInt32());
     }
 
+    // TODO(b/213909104) : Add unit tests to verify surface flinger boot time APIs
+    status_t getBootDisplayModeSupport(bool* outSupport) const override {
+        Parcel data, reply;
+        status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (error != NO_ERROR) {
+            ALOGE("getBootDisplayModeSupport: failed to write interface token: %d", error);
+            return error;
+        }
+        error = remote()->transact(BnSurfaceComposer::GET_BOOT_DISPLAY_MODE_SUPPORT, data, &reply);
+        if (error != NO_ERROR) {
+            ALOGE("getBootDisplayModeSupport: failed to transact: %d", error);
+            return error;
+        }
+        bool support;
+        error = reply.readBool(&support);
+        if (error != NO_ERROR) {
+            ALOGE("getBootDisplayModeSupport: failed to read support: %d", error);
+            return error;
+        }
+        *outSupport = support;
+        return NO_ERROR;
+    }
+
+    status_t setBootDisplayMode(const sp<IBinder>& display,
+                                ui::DisplayModeId displayModeId) override {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("setBootDisplayMode failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("setBootDisplayMode failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = data.writeInt32(displayModeId);
+        if (result != NO_ERROR) {
+            ALOGE("setBootDisplayMode failed to writeIint32: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::SET_BOOT_DISPLAY_MODE, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("setBootDisplayMode failed to transact: %d", result);
+        }
+        return result;
+    }
+
+    status_t clearBootDisplayMode(const sp<IBinder>& display) override {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("clearBootDisplayMode failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("clearBootDisplayMode failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::CLEAR_BOOT_DISPLAY_MODE, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("clearBootDisplayMode failed to transact: %d", result);
+        }
+        return result;
+    }
+
+    status_t getPreferredBootDisplayMode(const sp<IBinder>& display,
+                                         ui::DisplayModeId* displayModeId) override {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("getPreferredBootDisplayMode failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("getPreferredBootDisplayMode failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::GET_PREFERRED_BOOT_DISPLAY_MODE, data,
+                                    &reply);
+        if (result == NO_ERROR) {
+            reply.writeInt32(*displayModeId);
+        }
+        return result;
+    }
+
     void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1521,6 +1609,56 @@
             result = reply->writeInt32(result);
             return result;
         }
+        case GET_BOOT_DISPLAY_MODE_SUPPORT: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            bool support = false;
+            status_t result = getBootDisplayModeSupport(&support);
+            if (result == NO_ERROR) {
+                reply->writeBool(support);
+            }
+            return result;
+        }
+        case SET_BOOT_DISPLAY_MODE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("setBootDisplayMode failed to readStrongBinder: %d", result);
+                return result;
+            }
+            ui::DisplayModeId displayModeId;
+            result = data.readInt32(&displayModeId);
+            if (result != NO_ERROR) {
+                ALOGE("setBootDisplayMode failed to readInt32: %d", result);
+                return result;
+            }
+            return setBootDisplayMode(display, displayModeId);
+        }
+        case CLEAR_BOOT_DISPLAY_MODE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("clearBootDisplayMode failed to readStrongBinder: %d", result);
+                return result;
+            }
+            return clearBootDisplayMode(display);
+        }
+        case GET_PREFERRED_BOOT_DISPLAY_MODE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("getPreferredBootDisplayMode failed to readStrongBinder: %d", result);
+                return result;
+            }
+            ui::DisplayModeId displayModeId;
+            result = getPreferredBootDisplayMode(display, &displayModeId);
+            if (result == NO_ERROR) {
+                reply->writeInt32(displayModeId);
+            }
+            return result;
+        }
         case SET_AUTO_LOW_LATENCY_MODE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> display = nullptr;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index b10c384..6dbdcd0 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2075,6 +2075,25 @@
     return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
 }
 
+status_t SurfaceComposerClient::getBootDisplayModeSupport(bool* support) {
+    return ComposerService::getComposerService()->getBootDisplayModeSupport(support);
+}
+
+status_t SurfaceComposerClient::setBootDisplayMode(const sp<IBinder>& display,
+                                                   ui::DisplayModeId displayModeId) {
+    return ComposerService::getComposerService()->setBootDisplayMode(display, displayModeId);
+}
+
+status_t SurfaceComposerClient::clearBootDisplayMode(const sp<IBinder>& display) {
+    return ComposerService::getComposerService()->clearBootDisplayMode(display);
+}
+
+status_t SurfaceComposerClient::getPreferredBootDisplayMode(const sp<IBinder>& display,
+                                                            ui::DisplayModeId* displayModeId) {
+    return ComposerService::getComposerService()->getPreferredBootDisplayMode(display,
+                                                                              displayModeId);
+}
+
 void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
     ComposerService::getComposerService()->setAutoLowLatencyMode(display, on);
 }
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 69dce9d..f37580c 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -224,6 +224,35 @@
             ui::ColorMode colorMode) = 0;
 
     /**
+     * Sets the user-preferred display mode that a device should boot in.
+     */
+    virtual status_t setBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId) = 0;
+
+    /**
+     * Clears the user-preferred display mode. The device should now boot in system preferred
+     * display mode.
+     */
+    virtual status_t clearBootDisplayMode(const sp<IBinder>& display) = 0;
+
+    /**
+     * Gets the display mode in which the device boots if there is no user-preferred display mode.
+     */
+    virtual status_t getPreferredBootDisplayMode(const sp<IBinder>& display,
+                                                 ui::DisplayModeId*) = 0;
+
+    /**
+     * Gets whether boot time display mode operations are supported on the device.
+     *
+     * outSupport
+     *      An output parameter for whether boot time display mode operations are supported.
+     *
+     * Returns NO_ERROR upon success. Otherwise,
+     *      NAME_NOT_FOUND if the display is invalid, or
+     *      BAD_VALUE      if the output parameter is invalid.
+     */
+    virtual status_t getBootDisplayModeSupport(bool* outSupport) const = 0;
+
+    /**
      * Switches Auto Low Latency Mode on/off on the connected display, if it is
      * available. This should only be called if the display supports Auto Low
      * Latency Mode as reported in #getDynamicDisplayInfo.
@@ -645,6 +674,10 @@
         REMOVE_WINDOW_INFOS_LISTENER,
         GET_PRIMARY_PHYSICAL_DISPLAY_ID,
         GET_DISPLAY_DECORATION_SUPPORT,
+        GET_BOOT_DISPLAY_MODE_SUPPORT,
+        SET_BOOT_DISPLAY_MODE,
+        CLEAR_BOOT_DISPLAY_MODE,
+        GET_PREFERRED_BOOT_DISPLAY_MODE,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index b739b3c..4eef111 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -167,6 +167,15 @@
     static status_t setActiveColorMode(const sp<IBinder>& display,
             ui::ColorMode colorMode);
 
+    // Gets if boot display mode operations are supported on a device
+    static status_t getBootDisplayModeSupport(bool* support);
+    // Sets the user-preferred display mode that a device should boot in
+    static status_t setBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId);
+    // Clears the user-preferred display mode
+    static status_t clearBootDisplayMode(const sp<IBinder>& display);
+    // Gets the display mode in which the device boots if there is no user-preferred display mode
+    static status_t getPreferredBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId*);
+
     // Switches on/off Auto Low Latency Mode on the connected display. This should only be
     // called if the connected display supports Auto Low Latency Mode as reported by
     // #getAutoLowLatencyModeSupport
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index d5e089a..999874b 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -755,6 +755,15 @@
     }
     status_t setActiveColorMode(const sp<IBinder>& /*display*/,
         ColorMode /*colorMode*/) override { return NO_ERROR; }
+    status_t getBootDisplayModeSupport(bool* /*outSupport*/) const override { return NO_ERROR; }
+    status_t setBootDisplayMode(const sp<IBinder>& /*display*/, ui::DisplayModeId /*id*/) override {
+        return NO_ERROR;
+    }
+    status_t clearBootDisplayMode(const sp<IBinder>& /*display*/) override { return NO_ERROR; }
+    status_t getPreferredBootDisplayMode(const sp<IBinder>& /*display*/,
+                                         ui::DisplayModeId* /*id*/) override {
+        return NO_ERROR;
+    }
     void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
     void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index dc5c5c8..bd3022b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -106,6 +106,9 @@
                  status_t(PhysicalDisplayId, hal::HWConfigId,
                           const hal::VsyncPeriodChangeConstraints&,
                           hal::VsyncPeriodChangeTimeline*));
+    MOCK_METHOD2(setBootDisplayMode, status_t(PhysicalDisplayId, hal::HWConfigId));
+    MOCK_METHOD1(clearBootDisplayMode, status_t(PhysicalDisplayId));
+    MOCK_METHOD1(getPreferredBootDisplayMode, hal::HWConfigId(PhysicalDisplayId));
     MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
     MOCK_METHOD2(getSupportedContentTypes,
                  status_t(PhysicalDisplayId, std::vector<hal::ContentType>*));
@@ -124,6 +127,7 @@
                 (const, override));
     MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId),
                 (const, override));
+    MOCK_METHOD(bool, getBootDisplayModeSupport, (), (override));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 3c578bc..1448e56 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -237,6 +237,7 @@
         case OptionalFeature::RefreshRateSwitching:
         case OptionalFeature::ExpectedPresentTime:
         case OptionalFeature::DisplayBrightnessCommand:
+        case OptionalFeature::BootDisplayConfig:
             return true;
     }
 }
@@ -1009,6 +1010,38 @@
     return V2_4::Error::UNSUPPORTED;
 }
 
+Error AidlComposer::setBootDisplayConfig(Display display, Config config) {
+    const auto status = mAidlComposerClient->setBootDisplayConfig(translate<int64_t>(display),
+                                                                  translate<int32_t>(config));
+    if (!status.isOk()) {
+        ALOGE("setBootDisplayConfig failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::clearBootDisplayConfig(Display display) {
+    const auto status = mAidlComposerClient->clearBootDisplayConfig(translate<int64_t>(display));
+    if (!status.isOk()) {
+        ALOGE("clearBootDisplayConfig failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::getPreferredBootDisplayConfig(Display display, Config* config) {
+    int32_t displayConfig;
+    const auto status =
+            mAidlComposerClient->getPreferredBootDisplayConfig(translate<int64_t>(display),
+                                                               &displayConfig);
+    if (!status.isOk()) {
+        ALOGE("getPreferredBootDisplayConfig failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    *config = translate<uint32_t>(displayConfig);
+    return Error::NONE;
+}
+
 Error AidlComposer::getClientTargetProperty(
         Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
         float* whitePointNits) {
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index cdd16e2..6770017 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -213,6 +213,9 @@
     Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) override;
     Error setLayerBlockingRegion(Display display, Layer layer,
                                  const std::vector<IComposerClient::Rect>& blocking) override;
+    Error setBootDisplayConfig(Display displayId, Config) override;
+    Error clearBootDisplayConfig(Display displayId) override;
+    Error getPreferredBootDisplayConfig(Display displayId, Config*) override;
 
 private:
     // Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index bb4b784..22f424f 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -81,6 +81,7 @@
         ExpectedPresentTime,
         // Whether setDisplayBrightness is able to be applied as part of a display command.
         DisplayBrightnessCommand,
+        BootDisplayConfig,
     };
 
     virtual bool isSupported(OptionalFeature) const = 0;
@@ -248,6 +249,7 @@
                                                 const std::vector<uint8_t>& value) = 0;
     virtual V2_4::Error getLayerGenericMetadataKeys(
             std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0;
+
     virtual Error getClientTargetProperty(
             Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
             float* outWhitePointNits) = 0;
@@ -256,6 +258,9 @@
     virtual Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) = 0;
     virtual Error setLayerBlockingRegion(Display display, Layer layer,
                                          const std::vector<IComposerClient::Rect>& blocking) = 0;
+    virtual Error setBootDisplayConfig(Display displayId, Config) = 0;
+    virtual Error clearBootDisplayConfig(Display displayId) = 0;
+    virtual Error getPreferredBootDisplayConfig(Display displayId, Config*) = 0;
 };
 
 } // namespace android::Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 34f2e76..05e3aef 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -539,6 +539,21 @@
     });
 }
 
+Error Display::setBootDisplayConfig(hal::HWConfigId configId) {
+    auto intError = mComposer.setBootDisplayConfig(mId, configId);
+    return static_cast<Error>(intError);
+}
+
+Error Display::clearBootDisplayConfig() {
+    auto intError = mComposer.clearBootDisplayConfig(mId);
+    return static_cast<Error>(intError);
+}
+
+Error Display::getPreferredBootDisplayConfig(hal::HWConfigId* configId) const {
+    auto intError = mComposer.getPreferredBootDisplayConfig(mId, configId);
+    return static_cast<Error>(intError);
+}
+
 Error Display::setAutoLowLatencyMode(bool on) {
     auto intError = mComposer.setAutoLowLatencyMode(mId, on);
     return static_cast<Error>(intError);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 01a482d..57eb128 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -149,6 +149,11 @@
     [[clang::warn_unused_result]] virtual hal::Error setActiveConfigWithConstraints(
             hal::HWConfigId configId, const hal::VsyncPeriodChangeConstraints& constraints,
             hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setBootDisplayConfig(
+            hal::HWConfigId configId) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error clearBootDisplayConfig() = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getPreferredBootDisplayConfig(
+            hal::HWConfigId* configId) const = 0;
     [[clang::warn_unused_result]] virtual hal::Error setAutoLowLatencyMode(bool on) = 0;
     [[clang::warn_unused_result]] virtual hal::Error getSupportedContentTypes(
             std::vector<hal::ContentType>*) const = 0;
@@ -218,6 +223,9 @@
     hal::Error setActiveConfigWithConstraints(hal::HWConfigId configId,
                                               const hal::VsyncPeriodChangeConstraints& constraints,
                                               hal::VsyncPeriodChangeTimeline* outTimeline) override;
+    hal::Error setBootDisplayConfig(hal::HWConfigId configId) override;
+    hal::Error clearBootDisplayConfig() override;
+    hal::Error getPreferredBootDisplayConfig(hal::HWConfigId* configId) const override;
     hal::Error setAutoLowLatencyMode(bool on) override;
     hal::Error getSupportedContentTypes(
             std::vector<hal::ContentType>* outSupportedContentTypes) const override;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 057db46..44e4597 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -801,6 +801,52 @@
             });
 }
 
+bool HWComposer::getBootDisplayModeSupport() {
+    return mComposer->isSupported(Hwc2::Composer::OptionalFeature::BootDisplayConfig);
+}
+
+status_t HWComposer::setBootDisplayMode(PhysicalDisplayId displayId,
+                                        hal::HWConfigId displayModeId) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error = mDisplayData[displayId].hwcDisplay->setBootDisplayConfig(displayModeId);
+    if (error == hal::Error::UNSUPPORTED) {
+        RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+    }
+    if (error == hal::Error::BAD_PARAMETER) {
+        RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+    }
+    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+    return NO_ERROR;
+}
+
+status_t HWComposer::clearBootDisplayMode(PhysicalDisplayId displayId) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error = mDisplayData[displayId].hwcDisplay->clearBootDisplayConfig();
+    if (error == hal::Error::UNSUPPORTED) {
+        RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+    }
+    if (error == hal::Error::BAD_PARAMETER) {
+        RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+    }
+    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+    return NO_ERROR;
+}
+
+hal::HWConfigId HWComposer::getPreferredBootDisplayMode(PhysicalDisplayId displayId) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    hal::HWConfigId displayModeId = -1;
+    const auto error =
+            mDisplayData[displayId].hwcDisplay->getPreferredBootDisplayConfig(&displayModeId);
+    if (error == hal::Error::UNSUPPORTED) {
+        RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+    }
+    if (error == hal::Error::BAD_PARAMETER) {
+        RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+    }
+    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+    return displayModeId;
+}
+
 status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 4fae06d..9e57602 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -255,6 +255,12 @@
 
     virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0;
     virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0;
+
+    // Composer 3.0
+    virtual bool getBootDisplayModeSupport() = 0;
+    virtual status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) = 0;
+    virtual status_t clearBootDisplayMode(PhysicalDisplayId) = 0;
+    virtual hal::HWConfigId getPreferredBootDisplayMode(PhysicalDisplayId) = 0;
 };
 
 namespace impl {
@@ -381,6 +387,12 @@
 
     const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const override;
 
+    // Composer 3.0
+    bool getBootDisplayModeSupport() override;
+    status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) override;
+    status_t clearBootDisplayMode(PhysicalDisplayId) override;
+    hal::HWConfigId getPreferredBootDisplayMode(PhysicalDisplayId) override;
+
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
 
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 0ab1cfb..d3acecb 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -162,6 +162,7 @@
             return mClient_2_4 != nullptr;
         case OptionalFeature::ExpectedPresentTime:
         case OptionalFeature::DisplayBrightnessCommand:
+        case OptionalFeature::BootDisplayConfig:
             return false;
     }
 }
@@ -1216,6 +1217,18 @@
     return error;
 }
 
+Error HidlComposer::setBootDisplayConfig(Display /*displayId*/, Config) {
+    return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::clearBootDisplayConfig(Display /*displayId*/) {
+    return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::getPreferredBootDisplayConfig(Display /*displayId*/, Config*) {
+    return Error::UNSUPPORTED;
+}
+
 Error HidlComposer::getClientTargetProperty(
         Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
         float* outWhitePointNits) {
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 8282d8a..c8c7800 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -323,6 +323,9 @@
     Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) override;
     Error setLayerBlockingRegion(Display display, Layer layer,
                                  const std::vector<IComposerClient::Rect>& blocking) override;
+    Error setBootDisplayConfig(Display displayId, Config) override;
+    Error clearBootDisplayConfig(Display displayId) override;
+    Error getPreferredBootDisplayConfig(Display displayId, Config*) override;
 
 private:
     class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 63fbe78..e84016f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1426,6 +1426,52 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getBootDisplayModeSupport(bool* outSupport) const {
+    auto future = mScheduler->schedule([=]() MAIN_THREAD mutable -> status_t {
+        *outSupport = getHwComposer().getBootDisplayModeSupport();
+        return NO_ERROR;
+    });
+    return future.get();
+}
+
+status_t SurfaceFlinger::setBootDisplayMode(const sp<IBinder>& displayToken, ui::DisplayModeId id) {
+    auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
+        if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
+            return getHwComposer().setBootDisplayMode(*displayId, id);
+        } else {
+            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+            return BAD_VALUE;
+        }
+    });
+    return future.get();
+}
+
+status_t SurfaceFlinger::clearBootDisplayMode(const sp<IBinder>& displayToken) {
+    auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
+        if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
+            return getHwComposer().clearBootDisplayMode(*displayId);
+        } else {
+            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+            return BAD_VALUE;
+        }
+    });
+    return future.get();
+}
+
+status_t SurfaceFlinger::getPreferredBootDisplayMode(const sp<IBinder>& displayToken,
+                                                     ui::DisplayModeId* id) {
+    auto future = mScheduler->schedule([=]() MAIN_THREAD mutable -> status_t {
+        if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
+            *id = getHwComposer().getPreferredBootDisplayMode(*displayId);
+            return NO_ERROR;
+        } else {
+            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+            return BAD_VALUE;
+        }
+    });
+    return future.get();
+}
+
 void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
     const char* const whence = __func__;
     static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
@@ -5404,6 +5450,10 @@
         case SET_DESIRED_DISPLAY_MODE_SPECS:
         case GET_DESIRED_DISPLAY_MODE_SPECS:
         case SET_ACTIVE_COLOR_MODE:
+        case GET_BOOT_DISPLAY_MODE_SUPPORT:
+        case SET_BOOT_DISPLAY_MODE:
+        case CLEAR_BOOT_DISPLAY_MODE:
+        case GET_PREFERRED_BOOT_DISPLAY_MODE:
         case GET_AUTO_LOW_LATENCY_MODE_SUPPORT:
         case SET_AUTO_LOW_LATENCY_MODE:
         case GET_GAME_CONTENT_TYPE_SUPPORT:
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 61cfb4e..08d3463 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -531,6 +531,11 @@
     status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
                                        ui::DisplayPrimaries&) override;
     status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
+    status_t getBootDisplayModeSupport(bool* outSupport) const override;
+    status_t setBootDisplayMode(const sp<IBinder>& displayToken, ui::DisplayModeId id) override;
+    status_t clearBootDisplayMode(const sp<IBinder>& displayToken) override;
+    status_t getPreferredBootDisplayMode(const sp<IBinder>& displayToken,
+                                         ui::DisplayModeId* id) override;
     void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) override;
     void setGameContentType(const sp<IBinder>& displayToken, bool on) override;
     void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index ecdadf7..4273401 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -135,6 +135,10 @@
                  V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
                              VsyncPeriodChangeTimeline*));
     MOCK_METHOD2(setAutoLowLatencyMode, V2_4::Error(Display, bool));
+    MOCK_METHOD2(getBootDisplayConfigSupport, Error(Display, bool*));
+    MOCK_METHOD2(setBootDisplayConfig, Error(Display, Config));
+    MOCK_METHOD1(clearBootDisplayConfig, Error(Display));
+    MOCK_METHOD2(getPreferredBootDisplayConfig, Error(Display, Config*));
     MOCK_METHOD2(getSupportedContentTypes,
                  V2_4::Error(Display, std::vector<IComposerClient::ContentType>*));
     MOCK_METHOD2(setContentType, V2_4::Error(Display, IComposerClient::ContentType));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 7ac0c78..9015944 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -86,6 +86,9 @@
                 (hal::HWConfigId, const hal::VsyncPeriodChangeConstraints &,
                  hal::VsyncPeriodChangeTimeline *),
                 (override));
+    MOCK_METHOD(hal::Error, setBootDisplayConfig, (hal::HWConfigId), (override));
+    MOCK_METHOD(hal::Error, clearBootDisplayConfig, (), (override));
+    MOCK_METHOD(hal::Error, getPreferredBootDisplayConfig, (hal::HWConfigId *), (const, override));
     MOCK_METHOD(hal::Error, setAutoLowLatencyMode, (bool), (override));
     MOCK_METHOD(hal::Error, getSupportedContentTypes, (std::vector<hal::ContentType> *),
                 (const, override));