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
This commit is contained in:
Marin Shalamanov 2019-10-08 10:57:25 +02:00
parent 13b6539091
commit f5de90d082
11 changed files with 399 additions and 21 deletions

View file

@ -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 @@ std::string decodeStandard(android_dataspace dataspace) {
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 @@ std::string decodeTransfer(android_dataspace dataspace) {
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 @@ std::string decodeRange(android_dataspace dataspace) {
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 @@ std::string decodeColorMode(ColorMode colorMode) {
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 @@ std::string decodeColorTransform(android_color_transform colorTransform) {
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 @@ std::string decodePixelFormat(android::PixelFormat format) {
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 decodeRenderIntent(RenderIntent renderIntent) {
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());
}

View file

@ -23,6 +23,7 @@
namespace android {
class Rect;
struct DeviceProductInfo;
}
std::string decodeStandard(android_dataspace dataspace);
@ -34,3 +35,4 @@ std::string decodeColorTransform(android_color_transform colorTransform);
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&);

View file

@ -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

View file

@ -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 @@ struct DisplayInfo {
DisplayConnectionType connectionType = DisplayConnectionType::Internal;
float density = 0.f;
bool secure = false;
std::optional<DeviceProductInfo> deviceProductInfo;
};
static_assert(std::is_trivially_copyable_v<DisplayInfo>);

View file

@ -0,0 +1 @@
../../include/ui/DeviceProductInfo.h

View file

@ -68,6 +68,36 @@ char getPnpLetter(uint16_t id) {
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 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) {
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 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) {
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 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) {
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 @@ std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
// 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) {

View file

@ -23,6 +23,7 @@
#include <string_view>
#include <vector>
#include <ui/DeviceProductInfo.h>
#include <ui/PhysicalDisplayId.h>
namespace android {
@ -53,15 +54,16 @@ using DisplayIdentificationData = std::vector<uint8_t>;
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&);

View file

@ -208,7 +208,9 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hwc2_display_t hw
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 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect(hwc2_displ
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() {

View file

@ -798,6 +798,7 @@ status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& displayToken, Display
}
info->secure = display->isSecure();
info->deviceProductInfo = getDeviceProductInfoLocked(*display);
return NO_ERROR;
}
@ -1272,6 +1273,30 @@ status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& displayToken,
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,

View file

@ -758,6 +758,8 @@ private:
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);

View file

@ -61,6 +61,64 @@ const unsigned char kExternalEedid[] =
"\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 @@ const DisplayIdentificationData& getExternalEedid() {
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 @@ TEST(DisplayIdentificationTest, parseEdid) {
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 @@ TEST(DisplayIdentificationTest, parseDisplayIdentificationData) {
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)));