SF: Requery display modes if the active mode is not supported

Some TV devices send two hotplug events immediately one after another
during startup. Because we query the active display mode
and the supported modes in two separete calls to HWC it's possible
to get an active mode, which is not in the list of supported modes.

If this happens we requery the display modes up to 3 times. If the
problem still persists we throw a fatal error.

Bug: 175678215
Bug: 159590486
Test: check that device boots
Test: plug HDMI out and it
Change-Id: I94dbadac4eb75ed659ede6299df0c3459ed6c74e
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index cf6bc68..f532e50 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -91,6 +91,12 @@
         int32_t dpiX = -1;
         int32_t dpiY = -1;
         int32_t configGroup = -1;
+
+        friend std::ostream& operator<<(std::ostream& os, const HWCDisplayMode& mode) {
+            return os << "id=" << mode.hwcId << " res=" << mode.width << "x" << mode.height
+                      << " vsyncPeriod=" << mode.vsyncPeriod << " dpi=" << mode.dpiX << "x"
+                      << mode.dpiY << " group=" << mode.configGroup;
+        }
     };
 
     virtual ~HWComposer();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 056dd9f..b39231e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -104,6 +104,7 @@
 #include "DisplayHardware/DisplayIdentification.h"
 #include "DisplayHardware/FramebufferSurface.h"
 #include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/Hal.h"
 #include "DisplayHardware/VirtualDisplaySurface.h"
 #include "DisplayRenderArea.h"
 #include "EffectLayer.h"
@@ -131,6 +132,7 @@
 #include "TimeStats/TimeStats.h"
 #include "android-base/parseint.h"
 #include "android-base/stringprintf.h"
+#include "android-base/strings.h"
 
 #define MAIN_THREAD ACQUIRE(mStateLock) RELEASE(mStateLock)
 
@@ -2306,8 +2308,28 @@
     // here the transaction has been committed
 }
 
-DisplayModes SurfaceFlinger::loadSupportedDisplayModes(PhysicalDisplayId displayId) const {
-    const auto hwcModes = getHwComposer().getModes(displayId);
+void SurfaceFlinger::loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& outModes,
+                                      DisplayModePtr& outActiveMode) const {
+    std::vector<HWComposer::HWCDisplayMode> hwcModes;
+    std::optional<hal::HWDisplayId> activeModeHwcId;
+    bool activeModeIsSupported;
+    int attempt = 0;
+    constexpr int kMaxAttempts = 3;
+    do {
+        hwcModes = getHwComposer().getModes(displayId);
+        activeModeHwcId = getHwComposer().getActiveMode(displayId);
+        LOG_ALWAYS_FATAL_IF(!activeModeHwcId, "HWC returned no active mode");
+
+        activeModeIsSupported =
+                std::any_of(hwcModes.begin(), hwcModes.end(),
+                            [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) {
+                                return mode.hwcId == *activeModeHwcId;
+                            });
+    } while (!activeModeIsSupported && ++attempt < kMaxAttempts);
+    LOG_ALWAYS_FATAL_IF(!activeModeIsSupported,
+                        "After %d attempts HWC still returns an active mode which is not"
+                        " supported. Active mode ID = %" PRIu64 " . Supported modes = %s",
+                        kMaxAttempts, *activeModeHwcId, base::Join(hwcModes, ", ").c_str());
 
     DisplayModes oldModes;
 
@@ -2345,10 +2367,15 @@
 
     if (modesAreSame) {
         // The supported modes have not changed, keep the old IDs.
-        return oldModes;
+        outModes = oldModes;
+    } else {
+        outModes = newModes;
     }
 
-    return newModes;
+    outActiveMode = *std::find_if(outModes.begin(), outModes.end(),
+                                  [activeModeHwcId](const DisplayModePtr& mode) {
+                                      return mode->getHwcId() == *activeModeHwcId;
+                                  });
 }
 
 void SurfaceFlinger::processDisplayHotplugEventsLocked() {
@@ -2364,15 +2391,9 @@
         const auto it = mPhysicalDisplayTokens.find(displayId);
 
         if (event.connection == hal::Connection::CONNECTED) {
-            auto supportedModes = loadSupportedDisplayModes(displayId);
-            const auto activeModeHwcId = getHwComposer().getActiveMode(displayId);
-            LOG_ALWAYS_FATAL_IF(!activeModeHwcId, "HWC returned no active mode");
-
-            const auto activeMode = *std::find_if(supportedModes.begin(), supportedModes.end(),
-                                                  [activeModeHwcId](const DisplayModePtr& mode) {
-                                                      return mode->getHwcId() == *activeModeHwcId;
-                                                  });
-            // TODO(b/175678215) Handle the case when activeMode is not in supportedModes
+            DisplayModes supportedModes;
+            DisplayModePtr activeMode;
+            loadDisplayModes(displayId, supportedModes, activeMode);
 
             if (it == mPhysicalDisplayTokens.end()) {
                 ALOGV("Creating display %s", to_string(displayId).c_str());
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d1eae1c..23f0842 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -906,7 +906,8 @@
     /*
      * Display management
      */
-    DisplayModes loadSupportedDisplayModes(PhysicalDisplayId) const REQUIRES(mStateLock);
+    void loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& outModes,
+                          DisplayModePtr& outActiveMode) const REQUIRES(mStateLock);
     sp<DisplayDevice> setupNewDisplayDeviceInternal(
             const wp<IBinder>& displayToken,
             std::shared_ptr<compositionengine::Display> compositionDisplay,