SF: protect mDisplayCapabilities with a lock

There is a rare race condition where mDisplayCapabilities can be
read while it is getting updated, which causes a crash.

Bug: 196059993
Test: SF unit tests
Change-Id: Iaca9f1b61cdc1a59639a93f62c1716acddf8186b
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 27146ab..e21b0da 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -283,8 +283,21 @@
     return Error::NONE;
 }
 
+bool Display::hasCapability(hal::DisplayCapability capability) const {
+    std::scoped_lock lock(mDisplayCapabilitiesMutex);
+    if (mDisplayCapabilities) {
+        return mDisplayCapabilities->count(capability) > 0;
+    }
+
+    ALOGW("Can't query capability %d."
+          " Display Capabilities were not queried from HWC yet",
+          static_cast<int>(capability));
+
+    return false;
+}
+
 Error Display::supportsDoze(bool* outSupport) const {
-    *outSupport = mDisplayCapabilities.count(DisplayCapability::DOZE) > 0;
+    *outSupport = hasCapability(DisplayCapability::DOZE);
     return Error::NONE;
 }
 
@@ -447,17 +460,21 @@
             auto error =
                     static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities));
             if (error == Error::NONE) {
+                std::scoped_lock lock(mDisplayCapabilitiesMutex);
+                mDisplayCapabilities.emplace();
                 for (auto capability : tmpCapabilities) {
-                    mDisplayCapabilities.emplace(static_cast<DisplayCapability>(capability));
+                    mDisplayCapabilities->emplace(static_cast<DisplayCapability>(capability));
                 }
             } else if (error == Error::UNSUPPORTED) {
+                std::scoped_lock lock(mDisplayCapabilitiesMutex);
+                mDisplayCapabilities.emplace();
                 if (mCapabilities.count(Capability::SKIP_CLIENT_COLOR_TRANSFORM)) {
-                    mDisplayCapabilities.emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
+                    mDisplayCapabilities->emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
                 }
                 bool dozeSupport = false;
                 error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport));
                 if (error == Error::NONE && dozeSupport) {
-                    mDisplayCapabilities.emplace(DisplayCapability::DOZE);
+                    mDisplayCapabilities->emplace(DisplayCapability::DOZE);
                 }
             }
         });
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 871465d..a65efb2 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android-base/expected.h>
+#include <android-base/thread_annotations.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
 #include <ui/HdrCapabilities.h>
@@ -79,7 +80,7 @@
     virtual hal::HWDisplayId getId() const = 0;
     virtual bool isConnected() const = 0;
     virtual void setConnected(bool connected) = 0; // For use by Device only
-    virtual const std::unordered_set<hal::DisplayCapability>& getCapabilities() const = 0;
+    virtual bool hasCapability(hal::DisplayCapability) const = 0;
     virtual bool isVsyncPeriodSwitchSupported() const = 0;
     virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0;
 
@@ -175,7 +176,7 @@
             hal::DisplayRequest* outDisplayRequests,
             std::unordered_map<HWC2::Layer*, hal::LayerRequest>* outLayerRequests) override;
     hal::Error getConnectionType(ui::DisplayConnectionType*) const override;
-    hal::Error supportsDoze(bool* outSupport) const override;
+    hal::Error supportsDoze(bool* outSupport) const override EXCLUDES(mDisplayCapabilitiesMutex);
     hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
     hal::Error getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
                                                      hal::Dataspace* outDataspace,
@@ -214,9 +215,7 @@
     hal::HWDisplayId getId() const override { return mId; }
     bool isConnected() const override { return mIsConnected; }
     void setConnected(bool connected) override; // For use by Device only
-    const std::unordered_set<hal::DisplayCapability>& getCapabilities() const override {
-        return mDisplayCapabilities;
-    };
+    bool hasCapability(hal::DisplayCapability) const override EXCLUDES(mDisplayCapabilitiesMutex);
     bool isVsyncPeriodSwitchSupported() const override;
     void onLayerDestroyed(hal::HWLayerId layerId) override;
 
@@ -243,8 +242,10 @@
     using Layers = std::unordered_map<hal::HWLayerId, std::weak_ptr<HWC2::impl::Layer>>;
     Layers mLayers;
 
+    mutable std::mutex mDisplayCapabilitiesMutex;
     std::once_flag mDisplayCapabilityQueryFlag;
-    std::unordered_set<hal::DisplayCapability> mDisplayCapabilities;
+    std::optional<std::unordered_set<hal::DisplayCapability>> mDisplayCapabilities
+            GUARDED_BY(mDisplayCapabilitiesMutex);
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 256bca9..c63ba0e 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -183,7 +183,7 @@
 bool HWComposer::hasDisplayCapability(HalDisplayId displayId,
                                       hal::DisplayCapability capability) const {
     RETURN_IF_INVALID_DISPLAY(displayId, false);
-    return mDisplayData.at(displayId).hwcDisplay->getCapabilities().count(capability) > 0;
+    return mDisplayData.at(displayId).hwcDisplay->hasCapability(capability);
 }
 
 std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hal::HWDisplayId hwcDisplayId,
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index c3919d9..fe1544e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -30,8 +30,7 @@
     MOCK_METHOD(hal::HWDisplayId, getId, (), (const, override));
     MOCK_METHOD(bool, isConnected, (), (const, override));
     MOCK_METHOD(void, setConnected, (bool), (override));
-    MOCK_METHOD(const std::unordered_set<hal::DisplayCapability> &, getCapabilities, (),
-                (const, override));
+    MOCK_METHOD(bool, hasCapability, (hal::DisplayCapability), (const, override));
     MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (), (const, override));
     MOCK_METHOD(void, onLayerDestroyed, (hal::HWLayerId), (override));