SF: Add EDID-ID Fabrication Logic

Adds logic to fabricate a display ID that is based solely on a given,
pre-parsed EDID data.

It does so by concatenating the EDID's generic fields, such as
manufacturer ID, product ID, etc. as a string, and hash-combine them
with the hashed values of the display serial numbers.

All hash functions are stable in order to consistently reproduce display
IDs when the same information is fed via the EDID blob.

Flag: com.android.graphics.surfaceflinger.flags.stable_edid_ids
Bug: 366042891
Test: N/A
Change-Id: I7693d4b0ca9fee5ed190c3530a69300a8a530cd2
diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp
index ee38f50..b7ef9b3 100644
--- a/libs/ui/DisplayIdentification.cpp
+++ b/libs/ui/DisplayIdentification.cpp
@@ -26,6 +26,7 @@
 #include <string>
 #include <string_view>
 
+#include <ftl/concat.h>
 #include <ftl/hash.h>
 #include <log/log.h>
 #include <ui/DisplayIdentification.h>
@@ -423,4 +424,27 @@
     return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
 }
 
+PhysicalDisplayId generateEdidDisplayId(const Edid& edid) {
+    const ftl::Concat displayDetailsString{edid.manufacturerId,
+                                           edid.productId,
+                                           ftl::truncated<13>(edid.displayName),
+                                           edid.manufactureWeek,
+                                           edid.manufactureOrModelYear,
+                                           edid.physicalSizeInCm.getWidth(),
+                                           edid.physicalSizeInCm.getHeight()};
+
+    // String has to be cropped to 64 characters (at most) for ftl::stable_hash.
+    // This is fine as the accuracy or completeness of the above fields is not
+    // critical for a ID fabrication.
+    const std::optional<uint64_t> hashedDisplayDetailsOpt =
+            ftl::stable_hash(std::string_view(displayDetailsString.c_str(), 64));
+
+    // Combine the hashes via bit-shifted XORs.
+    const uint64_t id = (hashedDisplayDetailsOpt.value_or(0) << 17) ^
+            (edid.hashedBlockZeroSerialNumberOpt.value_or(0) >> 11) ^
+            (edid.hashedDescriptorBlockSerialNumberOpt.value_or(0) << 23);
+
+    return PhysicalDisplayId::fromEdidHash(id);
+}
+
 } // namespace android
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index 9ea6cec..13ed754 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -81,12 +81,17 @@
         return PhysicalDisplayId(id);
     }
 
-    // Returns a stable ID based on EDID information.
+    // Returns a stable ID based on EDID and port information.
     static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId,
                                                 uint32_t modelHash) {
         return PhysicalDisplayId(FLAG_STABLE, port, manufacturerId, modelHash);
     }
 
+    // Returns a stable and consistent ID based exclusively on EDID information.
+    static constexpr PhysicalDisplayId fromEdidHash(uint64_t hashedEdid) {
+        return PhysicalDisplayId(hashedEdid);
+    }
+
     // Returns an unstable ID. If EDID is available using "fromEdid" is preferred.
     static constexpr PhysicalDisplayId fromPort(uint8_t port) {
         constexpr uint16_t kManufacturerId = 0;
@@ -103,6 +108,8 @@
     // Flag indicating that the ID is stable across reboots.
     static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
 
+    using DisplayId::DisplayId;
+
     constexpr PhysicalDisplayId(uint64_t flags, uint8_t port, uint16_t manufacturerId,
                                 uint32_t modelHash)
           : DisplayId(flags | (static_cast<uint64_t>(manufacturerId) << 40) |
diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h
index 5883dba..1e3449c 100644
--- a/libs/ui/include/ui/DisplayIdentification.h
+++ b/libs/ui/include/ui/DisplayIdentification.h
@@ -74,6 +74,7 @@
     std::optional<uint64_t> hashedDescriptorBlockSerialNumberOpt;
     PnpId pnpId;
     uint32_t modelHash;
+    // Up to 13 characters of ASCII text terminated by LF and padded with SP.
     std::string_view displayName;
     uint8_t manufactureOrModelYear;
     uint8_t manufactureWeek;
@@ -91,4 +92,8 @@
 
 PhysicalDisplayId getVirtualDisplayId(uint32_t id);
 
+// Generates a consistent, stable, and hashed display ID that is based on the
+// display's parsed EDID fields.
+PhysicalDisplayId generateEdidDisplayId(const Edid& edid);
+
 } // namespace android
diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp
index fdcf112..75c71a5 100644
--- a/libs/ui/tests/DisplayIdentification_test.cpp
+++ b/libs/ui/tests/DisplayIdentification_test.cpp
@@ -376,6 +376,22 @@
     EXPECT_EQ(4633127902230889474, tertiaryInfo->id.value);
 }
 
+TEST(DisplayIdentificationTest, generateEdidDisplayId) {
+    const auto firstExternalDisplayEdidOpt = parseEdid(getExternalEdid());
+    ASSERT_TRUE(firstExternalDisplayEdidOpt);
+    const PhysicalDisplayId firstExternalDisplayId =
+            generateEdidDisplayId(firstExternalDisplayEdidOpt.value());
+
+    const auto secondExternalDisplayEdidOpt = parseEdid(getExternalEedid());
+    ASSERT_TRUE(secondExternalDisplayEdidOpt);
+    const PhysicalDisplayId secondExternalDisplayId =
+            generateEdidDisplayId(secondExternalDisplayEdidOpt.value());
+
+    // Display IDs should be unique.
+    EXPECT_EQ(4067182673952280501u, firstExternalDisplayId.value);
+    EXPECT_EQ(14712168404707886855u, secondExternalDisplayId.value);
+}
+
 TEST(DisplayIdentificationTest, deviceProductInfo) {
     using ManufactureYear = DeviceProductInfo::ManufactureYear;
     using ManufactureWeekAndYear = DeviceProductInfo::ManufactureWeekAndYear;