SF: Parse, hash, and cache block 0 serial number

More EDID fields are required as a part of migrating to EDID-based
display IDs. This CL parses the device's serial number from bytes 12-15
of block 0 in the EDID blob, hashes it using a stable hash, and serves
it as a part of the Edid struct.

Later, amongst others, this value will be used to fabricate a unique
display ID that is based on the display's EDID.

See:
1. EDID spec: https://glenwing.github.io/docs/VESA-EEDID-A2.pdf
2. https://en.wikipedia.org/wiki/Extended_Display_Identification_Data#Structure,_version_1.4

Flag: com.android.graphics.surfaceflinger.flags.stable_edid_ids
Bug: 378923334
Test: DisplayIdentification_test
Change-Id: I8e5c79f2f51c2fd2085dfaba7f5c45fbc698cbcb
diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp
index 503f92f..5cdaa71 100644
--- a/libs/ui/DisplayIdentification.cpp
+++ b/libs/ui/DisplayIdentification.cpp
@@ -19,9 +19,12 @@
 
 #include <algorithm>
 #include <cctype>
+#include <cstdint>
 #include <numeric>
 #include <optional>
 #include <span>
+#include <string>
+#include <string_view>
 
 #include <ftl/hash.h>
 #include <log/log.h>
@@ -194,6 +197,21 @@
     const uint16_t productId =
             static_cast<uint16_t>(edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8));
 
+    //   Bytes 12-15: display serial number, in little-endian (LSB). This field is
+    //   optional and its absence is marked by having all bytes set to 0x00.
+    //   Values do not represent ASCII characters.
+    constexpr size_t kSerialNumberOffset = 12;
+    if (edid.size() < kSerialNumberOffset + sizeof(uint32_t)) {
+        ALOGE("Invalid EDID: block zero S/N is truncated.");
+        return {};
+    }
+    const uint32_t blockZeroSerialNumber = edid[kSerialNumberOffset] +
+            (edid[kSerialNumberOffset + 1] << 8) + (edid[kSerialNumberOffset + 2] << 16) +
+            (edid[kSerialNumberOffset + 3] << 24);
+    const auto hashedBlockZeroSNOpt = blockZeroSerialNumber == 0
+            ? std::nullopt
+            : ftl::stable_hash(std::string_view(std::to_string(blockZeroSerialNumber)));
+
     constexpr size_t kManufactureWeekOffset = 16;
     if (edid.size() < kManufactureWeekOffset + sizeof(uint8_t)) {
         ALOGE("Invalid EDID: manufacture week is truncated.");
@@ -350,6 +368,7 @@
     return Edid{
             .manufacturerId = manufacturerId,
             .productId = productId,
+            .hashedBlockZeroSerialNumberOpt = hashedBlockZeroSNOpt,
             .pnpId = *pnpId,
             .modelHash = modelHash,
             .displayName = displayName,