Expose EDID fields in DeviceProductInfo
Expose product-specific information (display name, manufacture date,
manufacturer Pnp ID) about the display or the directly connected device
on the display chain. For example, if the display is transitively
connected, these fields may contain product information about the
intermediate device.
Additionally this information can be used to prime a TV with entries
from an infrared database for controlling connected audio and TV devices.
Bug: 145299597
Fixes: 140223709
Test: atest DisplayIdentificationTest
Change-Id: Idec0d053d945e3c171e2cdd80773c9d869934020
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index 277081f..9aaef65 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -68,6 +68,36 @@
return letter < 'A' || letter > 'Z' ? '\0' : letter;
}
+DeviceProductInfo buildDeviceProductInfo(const Edid& edid) {
+ DeviceProductInfo info;
+ std::copy(edid.displayName.begin(), edid.displayName.end(), info.name.begin());
+ info.name[edid.displayName.size()] = '\0';
+
+ const auto productId = std::to_string(edid.productId);
+ std::copy(productId.begin(), productId.end(), info.productId.begin());
+ info.productId[productId.size()] = '\0';
+ info.manufacturerPnpId = edid.pnpId;
+
+ constexpr uint8_t kModelYearFlag = 0xff;
+ constexpr uint32_t kYearOffset = 1990;
+
+ const auto year = edid.manufactureOrModelYear + kYearOffset;
+ if (edid.manufactureWeek == kModelYearFlag) {
+ info.manufactureOrModelDate = DeviceProductInfo::ModelYear{.year = year};
+ } else if (edid.manufactureWeek == 0) {
+ DeviceProductInfo::ManufactureYear date;
+ date.year = year;
+ info.manufactureOrModelDate = date;
+ } else {
+ DeviceProductInfo::ManufactureWeekAndYear date;
+ date.year = year;
+ date.week = edid.manufactureWeek;
+ info.manufactureOrModelDate = date;
+ }
+
+ return info;
+}
+
} // namespace
uint16_t DisplayId::manufacturerId() const {
@@ -112,6 +142,31 @@
return {};
}
+ constexpr size_t kProductIdOffset = 10;
+ if (edid.size() < kProductIdOffset + sizeof(uint16_t)) {
+ ALOGE("Invalid EDID: product ID is truncated.");
+ return {};
+ }
+ const uint16_t productId = edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8);
+
+ constexpr size_t kManufactureWeekOffset = 16;
+ if (edid.size() < kManufactureWeekOffset + sizeof(uint8_t)) {
+ ALOGE("Invalid EDID: manufacture week is truncated.");
+ return {};
+ }
+ const uint8_t manufactureWeek = edid[kManufactureWeekOffset];
+ ALOGW_IF(0x37 <= manufactureWeek && manufactureWeek <= 0xfe,
+ "Invalid EDID: week of manufacture cannot be in the range [0x37, 0xfe].");
+
+ constexpr size_t kManufactureYearOffset = 17;
+ if (edid.size() < kManufactureYearOffset + sizeof(uint8_t)) {
+ ALOGE("Invalid EDID: manufacture year is truncated.");
+ return {};
+ }
+ const uint8_t manufactureOrModelYear = edid[kManufactureYearOffset];
+ ALOGW_IF(manufactureOrModelYear <= 0xf,
+ "Invalid EDID: model year or manufacture year cannot be in the range [0x0, 0xf].");
+
constexpr size_t kDescriptorOffset = 54;
if (edid.size() < kDescriptorOffset) {
ALOGE("Invalid EDID: descriptors are missing.");
@@ -127,6 +182,7 @@
constexpr size_t kDescriptorCount = 4;
constexpr size_t kDescriptorLength = 18;
+ static_assert(kDescriptorLength - kEdidHeaderLength < DeviceProductInfo::TEXT_BUFFER_SIZE);
for (size_t i = 0; i < kDescriptorCount; i++) {
if (view.size() < kDescriptorLength) {
@@ -166,7 +222,12 @@
return {};
}
- return Edid{manufacturerId, *pnpId, displayName};
+ return Edid{.manufacturerId = manufacturerId,
+ .pnpId = *pnpId,
+ .displayName = displayName,
+ .productId = productId,
+ .manufactureWeek = manufactureWeek,
+ .manufactureOrModelYear = manufactureOrModelYear};
}
std::optional<PnpId> getPnpId(uint16_t manufacturerId) {
@@ -195,8 +256,9 @@
// Hash display name instead of using product code or serial number, since the latter have been
// observed to change on some displays with multiple inputs.
const auto hash = static_cast<uint32_t>(std::hash<std::string_view>()(edid->displayName));
- return DisplayIdentificationInfo{DisplayId::fromEdid(port, edid->manufacturerId, hash),
- std::string(edid->displayName)};
+ return DisplayIdentificationInfo{.id = DisplayId::fromEdid(port, edid->manufacturerId, hash),
+ .name = std::string(edid->displayName),
+ .deviceProductInfo = buildDeviceProductInfo(*edid)};
}
DisplayId getFallbackDisplayId(uint8_t port) {
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
index 22b268a..0a18ba1 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -23,6 +23,7 @@
#include <string_view>
#include <vector>
+#include <ui/DeviceProductInfo.h>
#include <ui/PhysicalDisplayId.h>
namespace android {
@@ -53,15 +54,16 @@
struct DisplayIdentificationInfo {
DisplayId id;
std::string name;
+ std::optional<DeviceProductInfo> deviceProductInfo;
};
-// NUL-terminated plug and play ID.
-using PnpId = std::array<char, 4>;
-
struct Edid {
uint16_t manufacturerId;
+ uint16_t productId;
PnpId pnpId;
std::string_view displayName;
+ uint8_t manufactureOrModelYear;
+ uint8_t manufactureWeek;
};
bool isEdid(const DisplayIdentificationData&);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 784fa74..1c1e113 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -208,7 +208,9 @@
std::optional<DisplayIdentificationInfo> info;
if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) {
- info = DisplayIdentificationInfo{*displayId, std::string()};
+ info = DisplayIdentificationInfo{.id = *displayId,
+ .name = std::string(),
+ .deviceProductInfo = std::nullopt};
} else {
if (connection == HWC2::Connection::Disconnected) {
ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
@@ -951,9 +953,11 @@
if (info) return info;
- return DisplayIdentificationInfo{getFallbackDisplayId(port),
- hwcDisplayId == mInternalHwcDisplayId ? "Internal display"
- : "External display"};
+ return DisplayIdentificationInfo{.id = getFallbackDisplayId(port),
+ .name = hwcDisplayId == mInternalHwcDisplayId
+ ? "Internal display"
+ : "External display",
+ .deviceProductInfo = std::nullopt};
}
void HWComposer::loadCapabilities() {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a98ff4f..dcbd934 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -798,6 +798,7 @@
}
info->secure = display->isSecure();
+ info->deviceProductInfo = getDeviceProductInfoLocked(*display);
return NO_ERROR;
}
@@ -1272,6 +1273,30 @@
return NO_ERROR;
}
+std::optional<DeviceProductInfo> SurfaceFlinger::getDeviceProductInfoLocked(
+ const DisplayDevice& display) const {
+ // TODO(b/149075047): Populate DeviceProductInfo on hotplug and store it in DisplayDevice to
+ // avoid repetitive HAL IPC and EDID parsing.
+ const auto displayId = display.getId();
+ LOG_FATAL_IF(!displayId);
+
+ const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
+ LOG_FATAL_IF(!hwcDisplayId);
+
+ uint8_t port;
+ DisplayIdentificationData data;
+ if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) {
+ ALOGV("%s: No identification data.", __FUNCTION__);
+ return {};
+ }
+
+ const auto info = parseDisplayIdentificationData(port, data);
+ if (!info) {
+ return {};
+ }
+ return info->deviceProductInfo;
+}
+
status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0144b4e..6770c1c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -758,6 +758,8 @@
return nullptr;
}
+ std::optional<DeviceProductInfo> getDeviceProductInfoLocked(const DisplayDevice&) const;
+
// mark a region of a layer stack dirty. this updates the dirty
// region of all screens presenting this layer stack.
void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index 55995d0..a023367 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -61,6 +61,64 @@
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6";
+const unsigned char kPanasonicTvEdid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x34\xa9\x96\xa2\x01\x01\x01"
+ "\x01\x00\x1d\x01\x03\x80\x80\x48\x78\x0a\xda\xff\xa3\x58\x4a"
+ "\xa2\x29\x17\x49\x4b\x20\x08\x00\x31\x40\x61\x40\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x08\xe8\x00\x30\xf2\x70"
+ "\x5a\x80\xb0\x58\x8a\x00\xba\x88\x21\x00\x00\x1e\x02\x3a\x80"
+ "\x18\x71\x38\x2d\x40\x58\x2c\x45\x00\xba\x88\x21\x00\x00\x1e"
+ "\x00\x00\x00\xfc\x00\x50\x61\x6e\x61\x73\x6f\x6e\x69\x63\x2d"
+ "\x54\x56\x0a\x00\x00\x00\xfd\x00\x17\x3d\x0f\x88\x3c\x00\x0a"
+ "\x20\x20\x20\x20\x20\x20\x01\x1d\x02\x03\x6b\xf0\x57\x61\x60"
+ "\x10\x1f\x66\x65\x05\x14\x20\x21\x22\x04\x13\x03\x12\x07\x16"
+ "\x5d\x5e\x5f\x62\x63\x64\x2c\x0d\x07\x01\x15\x07\x50\x57\x07"
+ "\x01\x67\x04\x03\x83\x0f\x00\x00\x6e\x03\x0c\x00\x20\x00\x38"
+ "\x3c\x2f\x08\x80\x01\x02\x03\x04\x67\xd8\x5d\xc4\x01\x78\x80"
+ "\x03\xe2\x00\x4b\xe3\x05\xff\x01\xe2\x0f\x33\xe3\x06\x0f\x01"
+ "\xe5\x01\x8b\x84\x90\x01\xeb\x01\x46\xd0\x00\x44\x03\x70\x80"
+ "\x5e\x75\x94\xe6\x11\x46\xd0\x00\x70\x00\x66\x21\x56\xaa\x51"
+ "\x00\x1e\x30\x46\x8f\x33\x00\xba\x88\x21\x00\x00\x1e\x00\x00"
+ "\xc8";
+
+const unsigned char kHisenseTvEdid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x20\xa3\x00\x00\x00\x00\x00"
+ "\x00\x12\x1d\x01\x03\x80\x00\x00\x78\x0a\xd7\xa5\xa2\x59\x4a"
+ "\x96\x24\x14\x50\x54\xa3\x08\x00\xd1\xc0\xb3\x00\x81\x00\x81"
+ "\x80\x81\x40\x81\xc0\x01\x01\x01\x01\x02\x3a\x80\x18\x71\x38"
+ "\x2d\x40\x58\x2c\x45\x00\x3f\x43\x21\x00\x00\x1a\x02\x3a\x80"
+ "\x18\x71\x38\x2d\x40\x58\x2c\x45\x00\x3f\x43\x21\x00\x00\x1a"
+ "\x00\x00\x00\xfd\x00\x1e\x4c\x1e\x5a\x1e\x00\x0a\x20\x20\x20"
+ "\x20\x20\x20\x00\x00\x00\xfc\x00\x48\x69\x73\x65\x6e\x73\x65"
+ "\x0a\x20\x20\x20\x20\x20\x01\x47\x02\x03\x2d\x71\x50\x90\x05"
+ "\x04\x03\x07\x02\x06\x01\x1f\x14\x13\x12\x16\x11\x15\x20\x2c"
+ "\x09\x07\x03\x15\x07\x50\x57\x07\x00\x39\x07\xbb\x66\x03\x0c"
+ "\x00\x20\x00\x00\x83\x01\x00\x00\x01\x1d\x00\x72\x51\xd0\x1e"
+ "\x20\x6e\x28\x55\x00\xc4\x8e\x21\x00\x00\x1e\x01\x1d\x80\x18"
+ "\x71\x1c\x16\x20\x58\x2c\x25\x00\xc4\x8e\x21\x00\x00\x9e\x8c"
+ "\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\x13\x8e\x21\x00"
+ "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x07";
+
+const unsigned char kCtlDisplayEdid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x0e\x8c\x9d\x24\x00\x00\x00\x00"
+ "\xff\x17\x01\x04\xa5\x34\x1d\x78\x3a\xa7\x25\xa4\x57\x51\xa0\x26"
+ "\x10\x50\x54\xbf\xef\x80\xb3\x00\xa9\x40\x95\x00\x81\x40\x81\x80"
+ "\x95\x0f\x71\x4f\x90\x40\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c"
+ "\x45\x00\x09\x25\x21\x00\x00\x1e\x66\x21\x50\xb0\x51\x00\x1b\x30"
+ "\x40\x70\x36\x00\x09\x25\x21\x00\x00\x1e\x00\x00\x00\xfd\x00\x31"
+ "\x4c\x1e\x52\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfc"
+ "\x00\x4c\x50\x32\x33\x36\x31\x0a\x20\x20\x20\x20\x20\x20\x01\x3e"
+ "\x02\x03\x22\xf2\x4f\x90\x9f\x05\x14\x04\x13\x03\x02\x12\x11\x07"
+ "\x06\x16\x15\x01\x23\x09\x07\x07\x83\x01\x00\x00\x65\xb9\x14\x00"
+ "\x04\x00\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c\x45\x00\x09\x25"
+ "\x21\x00\x00\x1e\x02\x3a\x80\xd0\x72\x38\x2d\x40\x10\x2c\x45\x80"
+ "\x09\x25\x21\x00\x00\x1e\x01\x1d\x00\x72\x51\xd0\x1e\x20\x6e\x28"
+ "\x55\x00\x09\x25\x21\x00\x00\x1e\x8c\x0a\xd0\x8a\x20\xe0\x2d\x10"
+ "\x10\x3e\x96\x00\x09\x25\x21\x00\x00\x18\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4";
+
template <size_t N>
DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&bytes)[N]) {
return DisplayIdentificationData(bytes, bytes + N - 1);
@@ -83,12 +141,30 @@
return data;
}
+const DisplayIdentificationData& getPanasonicTvEdid() {
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kPanasonicTvEdid);
+ return data;
+}
+
+const DisplayIdentificationData& getHisenseTvEdid() {
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kHisenseTvEdid);
+ return data;
+}
+
+const DisplayIdentificationData& getCtlDisplayEdid() {
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kCtlDisplayEdid);
+ return data;
+}
+
TEST(DisplayIdentificationTest, isEdid) {
EXPECT_FALSE(isEdid({}));
EXPECT_TRUE(isEdid(getInternalEdid()));
EXPECT_TRUE(isEdid(getExternalEdid()));
EXPECT_TRUE(isEdid(getExternalEedid()));
+ EXPECT_TRUE(isEdid(getPanasonicTvEdid()));
+ EXPECT_TRUE(isEdid(getHisenseTvEdid()));
+ EXPECT_TRUE(isEdid(getCtlDisplayEdid()));
}
TEST(DisplayIdentificationTest, parseEdid) {
@@ -98,18 +174,54 @@
EXPECT_STREQ("SEC", edid->pnpId.data());
// ASCII text should be used as fallback if display name and serial number are missing.
EXPECT_EQ("121AT11-801", edid->displayName);
+ EXPECT_EQ(12610, edid->productId);
+ EXPECT_EQ(21, edid->manufactureOrModelYear);
+ EXPECT_EQ(0, edid->manufactureWeek);
edid = parseEdid(getExternalEdid());
ASSERT_TRUE(edid);
EXPECT_EQ(0x22f0u, edid->manufacturerId);
EXPECT_STREQ("HWP", edid->pnpId.data());
EXPECT_EQ("HP ZR30w", edid->displayName);
+ EXPECT_EQ(10348, edid->productId);
+ EXPECT_EQ(22, edid->manufactureOrModelYear);
+ EXPECT_EQ(2, edid->manufactureWeek);
edid = parseEdid(getExternalEedid());
ASSERT_TRUE(edid);
EXPECT_EQ(0x4c2du, edid->manufacturerId);
EXPECT_STREQ("SAM", edid->pnpId.data());
EXPECT_EQ("SAMSUNG", edid->displayName);
+ EXPECT_EQ(2302, edid->productId);
+ EXPECT_EQ(21, edid->manufactureOrModelYear);
+ EXPECT_EQ(41, edid->manufactureWeek);
+
+ edid = parseEdid(getPanasonicTvEdid());
+ ASSERT_TRUE(edid);
+ EXPECT_EQ(13481, edid->manufacturerId);
+ EXPECT_STREQ("MEI", edid->pnpId.data());
+ EXPECT_EQ("Panasonic-TV", edid->displayName);
+ EXPECT_EQ(41622, edid->productId);
+ EXPECT_EQ(29, edid->manufactureOrModelYear);
+ EXPECT_EQ(0, edid->manufactureWeek);
+
+ edid = parseEdid(getHisenseTvEdid());
+ ASSERT_TRUE(edid);
+ EXPECT_EQ(8355, edid->manufacturerId);
+ EXPECT_STREQ("HEC", edid->pnpId.data());
+ EXPECT_EQ("Hisense", edid->displayName);
+ EXPECT_EQ(0, edid->productId);
+ EXPECT_EQ(29, edid->manufactureOrModelYear);
+ EXPECT_EQ(18, edid->manufactureWeek);
+
+ edid = parseEdid(getCtlDisplayEdid());
+ ASSERT_TRUE(edid);
+ EXPECT_EQ(3724, edid->manufacturerId);
+ EXPECT_STREQ("CTL", edid->pnpId.data());
+ EXPECT_EQ("LP2361", edid->displayName);
+ EXPECT_EQ(9373, edid->productId);
+ EXPECT_EQ(23, edid->manufactureOrModelYear);
+ EXPECT_EQ(0xff, edid->manufactureWeek);
}
TEST(DisplayIdentificationTest, parseInvalidEdid) {
@@ -156,6 +268,86 @@
EXPECT_NE(secondaryInfo->id, tertiaryInfo->id);
}
+TEST(DisplayIdentificationTest, deviceProductInfo) {
+ using ManufactureYear = DeviceProductInfo::ManufactureYear;
+ using ManufactureWeekAndYear = DeviceProductInfo::ManufactureWeekAndYear;
+ using ModelYear = DeviceProductInfo::ModelYear;
+
+ {
+ const auto displayIdInfo = parseDisplayIdentificationData(0, getInternalEdid());
+ ASSERT_TRUE(displayIdInfo);
+ ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+ const auto& info = *displayIdInfo->deviceProductInfo;
+ EXPECT_STREQ("121AT11-801", info.name.data());
+ EXPECT_STREQ("SEC", info.manufacturerPnpId.data());
+ EXPECT_STREQ("12610", info.productId.data());
+ ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
+ EXPECT_EQ(2011, std::get<ManufactureYear>(info.manufactureOrModelDate).year);
+ }
+ {
+ const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEdid());
+ ASSERT_TRUE(displayIdInfo);
+ ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+ const auto& info = *displayIdInfo->deviceProductInfo;
+ EXPECT_STREQ("HP ZR30w", info.name.data());
+ EXPECT_STREQ("HWP", info.manufacturerPnpId.data());
+ EXPECT_STREQ("10348", info.productId.data());
+ ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
+ const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
+ EXPECT_EQ(2012, date.year);
+ EXPECT_EQ(2, date.week);
+ }
+ {
+ const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEedid());
+ ASSERT_TRUE(displayIdInfo);
+ ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+ const auto& info = *displayIdInfo->deviceProductInfo;
+ EXPECT_STREQ("SAMSUNG", info.name.data());
+ EXPECT_STREQ("SAM", info.manufacturerPnpId.data());
+ EXPECT_STREQ("2302", info.productId.data());
+ ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
+ const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
+ EXPECT_EQ(2011, date.year);
+ EXPECT_EQ(41, date.week);
+ }
+ {
+ const auto displayIdInfo = parseDisplayIdentificationData(0, getPanasonicTvEdid());
+ ASSERT_TRUE(displayIdInfo);
+ ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+ const auto& info = *displayIdInfo->deviceProductInfo;
+ EXPECT_STREQ("Panasonic-TV", info.name.data());
+ EXPECT_STREQ("MEI", info.manufacturerPnpId.data());
+ EXPECT_STREQ("41622", info.productId.data());
+ ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
+ const auto& date = std::get<ManufactureYear>(info.manufactureOrModelDate);
+ EXPECT_EQ(2019, date.year);
+ }
+ {
+ const auto displayIdInfo = parseDisplayIdentificationData(0, getHisenseTvEdid());
+ ASSERT_TRUE(displayIdInfo);
+ ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+ const auto& info = *displayIdInfo->deviceProductInfo;
+ EXPECT_STREQ("Hisense", info.name.data());
+ EXPECT_STREQ("HEC", info.manufacturerPnpId.data());
+ EXPECT_STREQ("0", info.productId.data());
+ ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
+ const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
+ EXPECT_EQ(2019, date.year);
+ EXPECT_EQ(18, date.week);
+ }
+ {
+ const auto displayIdInfo = parseDisplayIdentificationData(0, getCtlDisplayEdid());
+ ASSERT_TRUE(displayIdInfo);
+ ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+ const auto& info = *displayIdInfo->deviceProductInfo;
+ EXPECT_STREQ("LP2361", info.name.data());
+ EXPECT_STREQ("CTL", info.manufacturerPnpId.data());
+ EXPECT_STREQ("9373", info.productId.data());
+ ASSERT_TRUE(std::holds_alternative<ModelYear>(info.manufactureOrModelDate));
+ EXPECT_EQ(2013, std::get<ModelYear>(info.manufactureOrModelDate).year);
+ }
+}
+
TEST(DisplayIdentificationTest, getFallbackDisplayId) {
// Manufacturer ID should be invalid.
ASSERT_FALSE(getPnpId(getFallbackDisplayId(0)));