Merge "Expose EDID fields in DeviceProductInfo"
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index ee06d93..f394635 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -15,12 +15,14 @@
*/
#include <ui/DebugUtils.h>
+#include <ui/DeviceProductInfo.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <android-base/stringprintf.h>
#include <string>
+using android::base::StringAppendF;
using android::base::StringPrintf;
using android::ui::ColorMode;
using android::ui::RenderIntent;
@@ -85,12 +87,11 @@
case HAL_DATASPACE_UNKNOWN:
// Fallthrough
default:
- return android::base::StringPrintf("Unknown deprecated dataspace code %d",
- dataspace);
+ return StringPrintf("Unknown deprecated dataspace code %d", dataspace);
}
}
- return android::base::StringPrintf("Unknown dataspace code %d", dataspaceSelect);
+ return StringPrintf("Unknown dataspace code %d", dataspaceSelect);
}
std::string decodeTransfer(android_dataspace dataspace) {
@@ -147,7 +148,7 @@
return std::string("STD-B67");
}
- return android::base::StringPrintf("Unknown dataspace transfer %d", dataspaceTransfer);
+ return StringPrintf("Unknown dataspace transfer %d", dataspaceTransfer);
}
std::string decodeRange(android_dataspace dataspace) {
@@ -187,16 +188,15 @@
return std::string("Extended range");
}
- return android::base::StringPrintf("Unknown dataspace range %d", dataspaceRange);
+ return StringPrintf("Unknown dataspace range %d", dataspaceRange);
}
std::string dataspaceDetails(android_dataspace dataspace) {
if (dataspace == 0) {
return "Default";
}
- return android::base::StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(),
- decodeTransfer(dataspace).c_str(),
- decodeRange(dataspace).c_str());
+ return StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(),
+ decodeTransfer(dataspace).c_str(), decodeRange(dataspace).c_str());
}
std::string decodeColorMode(ColorMode colorMode) {
@@ -244,7 +244,7 @@
return std::string("ColorMode::BT2100_HLG");
}
- return android::base::StringPrintf("Unknown color mode %d", colorMode);
+ return StringPrintf("Unknown color mode %d", colorMode);
}
std::string decodeColorTransform(android_color_transform colorTransform) {
@@ -271,7 +271,7 @@
return std::string("Correct tritanopia");
}
- return android::base::StringPrintf("Unknown color transform %d", colorTransform);
+ return StringPrintf("Unknown color transform %d", colorTransform);
}
// Converts a PixelFormat to a human-readable string. Max 11 chars.
@@ -303,7 +303,7 @@
case android::PIXEL_FORMAT_BGRA_8888:
return std::string("BGRA_8888");
default:
- return android::base::StringPrintf("Unknown %#08x", format);
+ return StringPrintf("Unknown %#08x", format);
}
}
@@ -324,3 +324,28 @@
std::string to_string(const android::Rect& rect) {
return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom);
}
+
+std::string toString(const android::DeviceProductInfo::ManufactureOrModelDate& date) {
+ using ModelYear = android::DeviceProductInfo::ModelYear;
+ using ManufactureYear = android::DeviceProductInfo::ManufactureYear;
+ using ManufactureWeekAndYear = android::DeviceProductInfo::ManufactureWeekAndYear;
+
+ if (const auto* model = std::get_if<ModelYear>(&date)) {
+ return StringPrintf("ModelYear{%d}", model->year);
+ } else if (const auto* manufacture = std::get_if<ManufactureYear>(&date)) {
+ return StringPrintf("ManufactureDate{year=%d}", manufacture->year);
+ } else if (const auto* manufacture = std::get_if<ManufactureWeekAndYear>(&date)) {
+ return StringPrintf("ManufactureDate{week=%d, year=%d}", manufacture->week,
+ manufacture->year);
+ } else {
+ LOG_FATAL("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
+ return {};
+ }
+}
+
+std::string toString(const android::DeviceProductInfo& info) {
+ return StringPrintf("DeviceProductInfo{name=%s, productId=%s, manufacturerPnpId=%s, "
+ "manufactureOrModelDate=%s}",
+ info.name.data(), info.productId.data(), info.manufacturerPnpId.data(),
+ toString(info.manufactureOrModelDate).c_str());
+}
diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h
index 92b2bfb..4685575 100644
--- a/libs/ui/include/ui/DebugUtils.h
+++ b/libs/ui/include/ui/DebugUtils.h
@@ -23,6 +23,7 @@
namespace android {
class Rect;
+struct DeviceProductInfo;
}
std::string decodeStandard(android_dataspace dataspace);
@@ -34,3 +35,4 @@
std::string decodePixelFormat(android::PixelFormat format);
std::string decodeRenderIntent(android::ui::RenderIntent renderIntent);
std::string to_string(const android::Rect& rect);
+std::string toString(const android::DeviceProductInfo&);
diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h
new file mode 100644
index 0000000..c396e73
--- /dev/null
+++ b/libs/ui/include/ui/DeviceProductInfo.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <optional>
+#include <variant>
+
+namespace android {
+
+// NUL-terminated plug and play ID.
+using PnpId = std::array<char, 4>;
+
+// Product-specific information about the display or the directly connected device on the
+// display chain. For example, if the display is transitively connected, this field may contain
+// product information about the intermediate device.
+struct DeviceProductInfo {
+ static constexpr size_t TEXT_BUFFER_SIZE = 20;
+
+ struct ModelYear {
+ uint32_t year;
+ };
+
+ struct ManufactureYear : ModelYear {};
+
+ struct ManufactureWeekAndYear : ManufactureYear {
+ // 1-base week number. Week numbering may not be consistent between manufacturers.
+ uint8_t week;
+ };
+
+ // Display name.
+ std::array<char, TEXT_BUFFER_SIZE> name;
+
+ // Manufacturer Plug and Play ID.
+ PnpId manufacturerPnpId;
+
+ // Manufacturer product ID.
+ std::array<char, TEXT_BUFFER_SIZE> productId;
+
+ using ManufactureOrModelDate = std::variant<ModelYear, ManufactureYear, ManufactureWeekAndYear>;
+ ManufactureOrModelDate manufactureOrModelDate;
+};
+
+} // namespace android
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h
index 69f86d3..897060c 100644
--- a/libs/ui/include/ui/DisplayInfo.h
+++ b/libs/ui/include/ui/DisplayInfo.h
@@ -16,8 +16,11 @@
#pragma once
+#include <optional>
#include <type_traits>
+#include <ui/DeviceProductInfo.h>
+
namespace android {
enum class DisplayConnectionType { Internal, External };
@@ -27,6 +30,7 @@
DisplayConnectionType connectionType = DisplayConnectionType::Internal;
float density = 0.f;
bool secure = false;
+ std::optional<DeviceProductInfo> deviceProductInfo;
};
static_assert(std::is_trivially_copyable_v<DisplayInfo>);
diff --git a/libs/ui/include_vndk/ui/DeviceProductInfo.h b/libs/ui/include_vndk/ui/DeviceProductInfo.h
new file mode 120000
index 0000000..c8f1d43
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DeviceProductInfo.h
@@ -0,0 +1 @@
+../../include/ui/DeviceProductInfo.h
\ No newline at end of file
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)));