1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker * Copyright (C) 2018 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker *
4*38e8c45fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker *
8*38e8c45fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker *
10*38e8c45fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker */
16*38e8c45fSAndroid Build Coastguard Worker
17*38e8c45fSAndroid Build Coastguard Worker #undef LOG_TAG
18*38e8c45fSAndroid Build Coastguard Worker #define LOG_TAG "DisplayIdentification"
19*38e8c45fSAndroid Build Coastguard Worker
20*38e8c45fSAndroid Build Coastguard Worker #include <algorithm>
21*38e8c45fSAndroid Build Coastguard Worker #include <cctype>
22*38e8c45fSAndroid Build Coastguard Worker #include <cstdint>
23*38e8c45fSAndroid Build Coastguard Worker #include <numeric>
24*38e8c45fSAndroid Build Coastguard Worker #include <optional>
25*38e8c45fSAndroid Build Coastguard Worker #include <span>
26*38e8c45fSAndroid Build Coastguard Worker #include <string>
27*38e8c45fSAndroid Build Coastguard Worker #include <string_view>
28*38e8c45fSAndroid Build Coastguard Worker
29*38e8c45fSAndroid Build Coastguard Worker #include <ftl/hash.h>
30*38e8c45fSAndroid Build Coastguard Worker #include <log/log.h>
31*38e8c45fSAndroid Build Coastguard Worker #include <ui/DisplayIdentification.h>
32*38e8c45fSAndroid Build Coastguard Worker #include <ui/Size.h>
33*38e8c45fSAndroid Build Coastguard Worker
34*38e8c45fSAndroid Build Coastguard Worker namespace android {
35*38e8c45fSAndroid Build Coastguard Worker namespace {
36*38e8c45fSAndroid Build Coastguard Worker
37*38e8c45fSAndroid Build Coastguard Worker using byte_view = std::span<const uint8_t>;
38*38e8c45fSAndroid Build Coastguard Worker
39*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kEdidBlockSize = 128;
40*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kEdidHeaderLength = 5;
41*38e8c45fSAndroid Build Coastguard Worker
42*38e8c45fSAndroid Build Coastguard Worker constexpr uint16_t kVirtualEdidManufacturerId = 0xffffu;
43*38e8c45fSAndroid Build Coastguard Worker
getEdidDescriptorType(const byte_view & view)44*38e8c45fSAndroid Build Coastguard Worker std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) {
45*38e8c45fSAndroid Build Coastguard Worker if (static_cast<size_t>(view.size()) < kEdidHeaderLength || view[0] || view[1] || view[2] ||
46*38e8c45fSAndroid Build Coastguard Worker view[4]) {
47*38e8c45fSAndroid Build Coastguard Worker return {};
48*38e8c45fSAndroid Build Coastguard Worker }
49*38e8c45fSAndroid Build Coastguard Worker
50*38e8c45fSAndroid Build Coastguard Worker return view[3];
51*38e8c45fSAndroid Build Coastguard Worker }
52*38e8c45fSAndroid Build Coastguard Worker
isDetailedTimingDescriptor(const byte_view & view)53*38e8c45fSAndroid Build Coastguard Worker bool isDetailedTimingDescriptor(const byte_view& view) {
54*38e8c45fSAndroid Build Coastguard Worker return view[0] != 0 && view[1] != 0;
55*38e8c45fSAndroid Build Coastguard Worker }
56*38e8c45fSAndroid Build Coastguard Worker
parseEdidText(const byte_view & view)57*38e8c45fSAndroid Build Coastguard Worker std::string_view parseEdidText(const byte_view& view) {
58*38e8c45fSAndroid Build Coastguard Worker std::string_view text(reinterpret_cast<const char*>(view.data()), view.size());
59*38e8c45fSAndroid Build Coastguard Worker text = text.substr(0, text.find('\n'));
60*38e8c45fSAndroid Build Coastguard Worker
61*38e8c45fSAndroid Build Coastguard Worker if (!std::all_of(text.begin(), text.end(), ::isprint)) {
62*38e8c45fSAndroid Build Coastguard Worker ALOGW("Invalid EDID: ASCII text is not printable.");
63*38e8c45fSAndroid Build Coastguard Worker return {};
64*38e8c45fSAndroid Build Coastguard Worker }
65*38e8c45fSAndroid Build Coastguard Worker
66*38e8c45fSAndroid Build Coastguard Worker return text;
67*38e8c45fSAndroid Build Coastguard Worker }
68*38e8c45fSAndroid Build Coastguard Worker
69*38e8c45fSAndroid Build Coastguard Worker // Big-endian 16-bit value encodes three 5-bit letters where A is 0b00001.
70*38e8c45fSAndroid Build Coastguard Worker template <size_t I>
getPnpLetter(uint16_t id)71*38e8c45fSAndroid Build Coastguard Worker char getPnpLetter(uint16_t id) {
72*38e8c45fSAndroid Build Coastguard Worker static_assert(I < 3);
73*38e8c45fSAndroid Build Coastguard Worker const char letter = 'A' + (static_cast<uint8_t>(id >> ((2 - I) * 5)) & 0b00011111) - 1;
74*38e8c45fSAndroid Build Coastguard Worker return letter < 'A' || letter > 'Z' ? '\0' : letter;
75*38e8c45fSAndroid Build Coastguard Worker }
76*38e8c45fSAndroid Build Coastguard Worker
buildDeviceProductInfo(const Edid & edid)77*38e8c45fSAndroid Build Coastguard Worker DeviceProductInfo buildDeviceProductInfo(const Edid& edid) {
78*38e8c45fSAndroid Build Coastguard Worker DeviceProductInfo info;
79*38e8c45fSAndroid Build Coastguard Worker info.name.assign(edid.displayName);
80*38e8c45fSAndroid Build Coastguard Worker info.productId = std::to_string(edid.productId);
81*38e8c45fSAndroid Build Coastguard Worker info.manufacturerPnpId = edid.pnpId;
82*38e8c45fSAndroid Build Coastguard Worker
83*38e8c45fSAndroid Build Coastguard Worker constexpr uint8_t kModelYearFlag = 0xff;
84*38e8c45fSAndroid Build Coastguard Worker constexpr uint32_t kYearOffset = 1990;
85*38e8c45fSAndroid Build Coastguard Worker
86*38e8c45fSAndroid Build Coastguard Worker const auto year = edid.manufactureOrModelYear + kYearOffset;
87*38e8c45fSAndroid Build Coastguard Worker if (edid.manufactureWeek == kModelYearFlag) {
88*38e8c45fSAndroid Build Coastguard Worker info.manufactureOrModelDate = DeviceProductInfo::ModelYear{.year = year};
89*38e8c45fSAndroid Build Coastguard Worker } else if (edid.manufactureWeek == 0) {
90*38e8c45fSAndroid Build Coastguard Worker DeviceProductInfo::ManufactureYear date;
91*38e8c45fSAndroid Build Coastguard Worker date.year = year;
92*38e8c45fSAndroid Build Coastguard Worker info.manufactureOrModelDate = date;
93*38e8c45fSAndroid Build Coastguard Worker } else {
94*38e8c45fSAndroid Build Coastguard Worker DeviceProductInfo::ManufactureWeekAndYear date;
95*38e8c45fSAndroid Build Coastguard Worker date.year = year;
96*38e8c45fSAndroid Build Coastguard Worker date.week = edid.manufactureWeek;
97*38e8c45fSAndroid Build Coastguard Worker info.manufactureOrModelDate = date;
98*38e8c45fSAndroid Build Coastguard Worker }
99*38e8c45fSAndroid Build Coastguard Worker
100*38e8c45fSAndroid Build Coastguard Worker if (edid.cea861Block && edid.cea861Block->hdmiVendorDataBlock) {
101*38e8c45fSAndroid Build Coastguard Worker const auto& address = edid.cea861Block->hdmiVendorDataBlock->physicalAddress;
102*38e8c45fSAndroid Build Coastguard Worker info.relativeAddress = {address.a, address.b, address.c, address.d};
103*38e8c45fSAndroid Build Coastguard Worker }
104*38e8c45fSAndroid Build Coastguard Worker return info;
105*38e8c45fSAndroid Build Coastguard Worker }
106*38e8c45fSAndroid Build Coastguard Worker
parseCea861Block(const byte_view & block)107*38e8c45fSAndroid Build Coastguard Worker Cea861ExtensionBlock parseCea861Block(const byte_view& block) {
108*38e8c45fSAndroid Build Coastguard Worker Cea861ExtensionBlock cea861Block;
109*38e8c45fSAndroid Build Coastguard Worker
110*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kRevisionNumberOffset = 1;
111*38e8c45fSAndroid Build Coastguard Worker cea861Block.revisionNumber = block[kRevisionNumberOffset];
112*38e8c45fSAndroid Build Coastguard Worker
113*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kDetailedTimingDescriptorsOffset = 2;
114*38e8c45fSAndroid Build Coastguard Worker const size_t dtdStart =
115*38e8c45fSAndroid Build Coastguard Worker std::min(kEdidBlockSize, static_cast<size_t>(block[kDetailedTimingDescriptorsOffset]));
116*38e8c45fSAndroid Build Coastguard Worker
117*38e8c45fSAndroid Build Coastguard Worker // Parse data blocks.
118*38e8c45fSAndroid Build Coastguard Worker for (size_t dataBlockOffset = 4; dataBlockOffset < dtdStart;) {
119*38e8c45fSAndroid Build Coastguard Worker const uint8_t header = block[dataBlockOffset];
120*38e8c45fSAndroid Build Coastguard Worker const uint8_t tag = header >> 5;
121*38e8c45fSAndroid Build Coastguard Worker const size_t bodyLength = header & 0b11111;
122*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kDataBlockHeaderSize = 1;
123*38e8c45fSAndroid Build Coastguard Worker const size_t dataBlockSize = bodyLength + kDataBlockHeaderSize;
124*38e8c45fSAndroid Build Coastguard Worker
125*38e8c45fSAndroid Build Coastguard Worker if (static_cast<size_t>(block.size()) < dataBlockOffset + dataBlockSize) {
126*38e8c45fSAndroid Build Coastguard Worker ALOGW("Invalid EDID: CEA 861 data block is truncated.");
127*38e8c45fSAndroid Build Coastguard Worker break;
128*38e8c45fSAndroid Build Coastguard Worker }
129*38e8c45fSAndroid Build Coastguard Worker
130*38e8c45fSAndroid Build Coastguard Worker const byte_view dataBlock(block.data() + dataBlockOffset, dataBlockSize);
131*38e8c45fSAndroid Build Coastguard Worker constexpr uint8_t kVendorSpecificDataBlockTag = 0x3;
132*38e8c45fSAndroid Build Coastguard Worker
133*38e8c45fSAndroid Build Coastguard Worker if (tag == kVendorSpecificDataBlockTag) {
134*38e8c45fSAndroid Build Coastguard Worker const uint32_t ieeeRegistrationId = static_cast<uint32_t>(
135*38e8c45fSAndroid Build Coastguard Worker dataBlock[1] | (dataBlock[2] << 8) | (dataBlock[3] << 16));
136*38e8c45fSAndroid Build Coastguard Worker constexpr uint32_t kHdmiIeeeRegistrationId = 0xc03;
137*38e8c45fSAndroid Build Coastguard Worker
138*38e8c45fSAndroid Build Coastguard Worker if (ieeeRegistrationId == kHdmiIeeeRegistrationId) {
139*38e8c45fSAndroid Build Coastguard Worker const uint8_t a = dataBlock[4] >> 4;
140*38e8c45fSAndroid Build Coastguard Worker const uint8_t b = dataBlock[4] & 0b1111;
141*38e8c45fSAndroid Build Coastguard Worker const uint8_t c = dataBlock[5] >> 4;
142*38e8c45fSAndroid Build Coastguard Worker const uint8_t d = dataBlock[5] & 0b1111;
143*38e8c45fSAndroid Build Coastguard Worker cea861Block.hdmiVendorDataBlock =
144*38e8c45fSAndroid Build Coastguard Worker HdmiVendorDataBlock{.physicalAddress = HdmiPhysicalAddress{a, b, c, d}};
145*38e8c45fSAndroid Build Coastguard Worker } else {
146*38e8c45fSAndroid Build Coastguard Worker ALOGV("Ignoring vendor specific data block for vendor with IEEE OUI %x",
147*38e8c45fSAndroid Build Coastguard Worker ieeeRegistrationId);
148*38e8c45fSAndroid Build Coastguard Worker }
149*38e8c45fSAndroid Build Coastguard Worker } else {
150*38e8c45fSAndroid Build Coastguard Worker ALOGV("Ignoring CEA-861 data block with tag %x", tag);
151*38e8c45fSAndroid Build Coastguard Worker }
152*38e8c45fSAndroid Build Coastguard Worker dataBlockOffset += bodyLength + kDataBlockHeaderSize;
153*38e8c45fSAndroid Build Coastguard Worker }
154*38e8c45fSAndroid Build Coastguard Worker
155*38e8c45fSAndroid Build Coastguard Worker return cea861Block;
156*38e8c45fSAndroid Build Coastguard Worker }
157*38e8c45fSAndroid Build Coastguard Worker
158*38e8c45fSAndroid Build Coastguard Worker } // namespace
159*38e8c45fSAndroid Build Coastguard Worker
isEdid(const DisplayIdentificationData & data)160*38e8c45fSAndroid Build Coastguard Worker bool isEdid(const DisplayIdentificationData& data) {
161*38e8c45fSAndroid Build Coastguard Worker const uint8_t kMagic[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0};
162*38e8c45fSAndroid Build Coastguard Worker return data.size() >= sizeof(kMagic) &&
163*38e8c45fSAndroid Build Coastguard Worker std::equal(std::begin(kMagic), std::end(kMagic), data.begin());
164*38e8c45fSAndroid Build Coastguard Worker }
165*38e8c45fSAndroid Build Coastguard Worker
parseEdid(const DisplayIdentificationData & edid)166*38e8c45fSAndroid Build Coastguard Worker std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) {
167*38e8c45fSAndroid Build Coastguard Worker if (edid.size() < kEdidBlockSize) {
168*38e8c45fSAndroid Build Coastguard Worker ALOGW("Invalid EDID: structure is truncated.");
169*38e8c45fSAndroid Build Coastguard Worker // Attempt parsing even if EDID is malformed.
170*38e8c45fSAndroid Build Coastguard Worker } else {
171*38e8c45fSAndroid Build Coastguard Worker ALOGW_IF(std::accumulate(edid.begin(), edid.begin() + kEdidBlockSize,
172*38e8c45fSAndroid Build Coastguard Worker static_cast<uint8_t>(0)),
173*38e8c45fSAndroid Build Coastguard Worker "Invalid EDID: structure does not checksum.");
174*38e8c45fSAndroid Build Coastguard Worker }
175*38e8c45fSAndroid Build Coastguard Worker
176*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kManufacturerOffset = 8;
177*38e8c45fSAndroid Build Coastguard Worker if (edid.size() < kManufacturerOffset + sizeof(uint16_t)) {
178*38e8c45fSAndroid Build Coastguard Worker ALOGE("Invalid EDID: manufacturer ID is truncated.");
179*38e8c45fSAndroid Build Coastguard Worker return {};
180*38e8c45fSAndroid Build Coastguard Worker }
181*38e8c45fSAndroid Build Coastguard Worker
182*38e8c45fSAndroid Build Coastguard Worker // Plug and play ID encoded as big-endian 16-bit value.
183*38e8c45fSAndroid Build Coastguard Worker const uint16_t manufacturerId =
184*38e8c45fSAndroid Build Coastguard Worker static_cast<uint16_t>((edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1]);
185*38e8c45fSAndroid Build Coastguard Worker
186*38e8c45fSAndroid Build Coastguard Worker const auto pnpId = getPnpId(manufacturerId);
187*38e8c45fSAndroid Build Coastguard Worker if (!pnpId) {
188*38e8c45fSAndroid Build Coastguard Worker ALOGE("Invalid EDID: manufacturer ID is not a valid PnP ID.");
189*38e8c45fSAndroid Build Coastguard Worker return {};
190*38e8c45fSAndroid Build Coastguard Worker }
191*38e8c45fSAndroid Build Coastguard Worker
192*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kProductIdOffset = 10;
193*38e8c45fSAndroid Build Coastguard Worker if (edid.size() < kProductIdOffset + sizeof(uint16_t)) {
194*38e8c45fSAndroid Build Coastguard Worker ALOGE("Invalid EDID: product ID is truncated.");
195*38e8c45fSAndroid Build Coastguard Worker return {};
196*38e8c45fSAndroid Build Coastguard Worker }
197*38e8c45fSAndroid Build Coastguard Worker const uint16_t productId =
198*38e8c45fSAndroid Build Coastguard Worker static_cast<uint16_t>(edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8));
199*38e8c45fSAndroid Build Coastguard Worker
200*38e8c45fSAndroid Build Coastguard Worker // Bytes 12-15: display serial number, in little-endian (LSB). This field is
201*38e8c45fSAndroid Build Coastguard Worker // optional and its absence is marked by having all bytes set to 0x00.
202*38e8c45fSAndroid Build Coastguard Worker // Values do not represent ASCII characters.
203*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kSerialNumberOffset = 12;
204*38e8c45fSAndroid Build Coastguard Worker if (edid.size() < kSerialNumberOffset + sizeof(uint32_t)) {
205*38e8c45fSAndroid Build Coastguard Worker ALOGE("Invalid EDID: block zero S/N is truncated.");
206*38e8c45fSAndroid Build Coastguard Worker return {};
207*38e8c45fSAndroid Build Coastguard Worker }
208*38e8c45fSAndroid Build Coastguard Worker const uint32_t blockZeroSerialNumber = edid[kSerialNumberOffset] +
209*38e8c45fSAndroid Build Coastguard Worker (edid[kSerialNumberOffset + 1] << 8) + (edid[kSerialNumberOffset + 2] << 16) +
210*38e8c45fSAndroid Build Coastguard Worker (edid[kSerialNumberOffset + 3] << 24);
211*38e8c45fSAndroid Build Coastguard Worker const auto hashedBlockZeroSNOpt = blockZeroSerialNumber == 0
212*38e8c45fSAndroid Build Coastguard Worker ? std::nullopt
213*38e8c45fSAndroid Build Coastguard Worker : ftl::stable_hash(std::string_view(std::to_string(blockZeroSerialNumber)));
214*38e8c45fSAndroid Build Coastguard Worker
215*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kManufactureWeekOffset = 16;
216*38e8c45fSAndroid Build Coastguard Worker if (edid.size() < kManufactureWeekOffset + sizeof(uint8_t)) {
217*38e8c45fSAndroid Build Coastguard Worker ALOGE("Invalid EDID: manufacture week is truncated.");
218*38e8c45fSAndroid Build Coastguard Worker return {};
219*38e8c45fSAndroid Build Coastguard Worker }
220*38e8c45fSAndroid Build Coastguard Worker const uint8_t manufactureWeek = edid[kManufactureWeekOffset];
221*38e8c45fSAndroid Build Coastguard Worker ALOGW_IF(0x37 <= manufactureWeek && manufactureWeek <= 0xfe,
222*38e8c45fSAndroid Build Coastguard Worker "Invalid EDID: week of manufacture cannot be in the range [0x37, 0xfe].");
223*38e8c45fSAndroid Build Coastguard Worker
224*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kManufactureYearOffset = 17;
225*38e8c45fSAndroid Build Coastguard Worker if (edid.size() < kManufactureYearOffset + sizeof(uint8_t)) {
226*38e8c45fSAndroid Build Coastguard Worker ALOGE("Invalid EDID: manufacture year is truncated.");
227*38e8c45fSAndroid Build Coastguard Worker return {};
228*38e8c45fSAndroid Build Coastguard Worker }
229*38e8c45fSAndroid Build Coastguard Worker const uint8_t manufactureOrModelYear = edid[kManufactureYearOffset];
230*38e8c45fSAndroid Build Coastguard Worker ALOGW_IF(manufactureOrModelYear <= 0xf,
231*38e8c45fSAndroid Build Coastguard Worker "Invalid EDID: model year or manufacture year cannot be in the range [0x0, 0xf].");
232*38e8c45fSAndroid Build Coastguard Worker
233*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kMaxHorizontalPhysicalSizeOffset = 21;
234*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kMaxVerticalPhysicalSizeOffset = 22;
235*38e8c45fSAndroid Build Coastguard Worker if (edid.size() < kMaxVerticalPhysicalSizeOffset + sizeof(uint8_t)) {
236*38e8c45fSAndroid Build Coastguard Worker ALOGE("Invalid EDID: display's physical size is truncated.");
237*38e8c45fSAndroid Build Coastguard Worker return {};
238*38e8c45fSAndroid Build Coastguard Worker }
239*38e8c45fSAndroid Build Coastguard Worker ui::Size maxPhysicalSizeInCm(edid[kMaxHorizontalPhysicalSizeOffset],
240*38e8c45fSAndroid Build Coastguard Worker edid[kMaxVerticalPhysicalSizeOffset]);
241*38e8c45fSAndroid Build Coastguard Worker
242*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kDescriptorOffset = 54;
243*38e8c45fSAndroid Build Coastguard Worker if (edid.size() < kDescriptorOffset) {
244*38e8c45fSAndroid Build Coastguard Worker ALOGE("Invalid EDID: descriptors are missing.");
245*38e8c45fSAndroid Build Coastguard Worker return {};
246*38e8c45fSAndroid Build Coastguard Worker }
247*38e8c45fSAndroid Build Coastguard Worker
248*38e8c45fSAndroid Build Coastguard Worker byte_view view(edid.data(), edid.size());
249*38e8c45fSAndroid Build Coastguard Worker view = view.subspan(kDescriptorOffset);
250*38e8c45fSAndroid Build Coastguard Worker
251*38e8c45fSAndroid Build Coastguard Worker std::string_view displayName;
252*38e8c45fSAndroid Build Coastguard Worker std::string_view descriptorBlockSerialNumber;
253*38e8c45fSAndroid Build Coastguard Worker std::optional<uint64_t> hashedDescriptorBlockSNOpt = std::nullopt;
254*38e8c45fSAndroid Build Coastguard Worker std::string_view asciiText;
255*38e8c45fSAndroid Build Coastguard Worker ui::Size preferredDTDPixelSize;
256*38e8c45fSAndroid Build Coastguard Worker ui::Size preferredDTDPhysicalSize;
257*38e8c45fSAndroid Build Coastguard Worker
258*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kDescriptorCount = 4;
259*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kDescriptorLength = 18;
260*38e8c45fSAndroid Build Coastguard Worker
261*38e8c45fSAndroid Build Coastguard Worker for (size_t i = 0; i < kDescriptorCount; i++) {
262*38e8c45fSAndroid Build Coastguard Worker if (static_cast<size_t>(view.size()) < kDescriptorLength) {
263*38e8c45fSAndroid Build Coastguard Worker break;
264*38e8c45fSAndroid Build Coastguard Worker }
265*38e8c45fSAndroid Build Coastguard Worker
266*38e8c45fSAndroid Build Coastguard Worker if (const auto type = getEdidDescriptorType(view)) {
267*38e8c45fSAndroid Build Coastguard Worker byte_view descriptor(view.data(), kDescriptorLength);
268*38e8c45fSAndroid Build Coastguard Worker descriptor = descriptor.subspan(kEdidHeaderLength);
269*38e8c45fSAndroid Build Coastguard Worker
270*38e8c45fSAndroid Build Coastguard Worker switch (*type) {
271*38e8c45fSAndroid Build Coastguard Worker case 0xfc:
272*38e8c45fSAndroid Build Coastguard Worker displayName = parseEdidText(descriptor);
273*38e8c45fSAndroid Build Coastguard Worker break;
274*38e8c45fSAndroid Build Coastguard Worker case 0xfe:
275*38e8c45fSAndroid Build Coastguard Worker asciiText = parseEdidText(descriptor);
276*38e8c45fSAndroid Build Coastguard Worker break;
277*38e8c45fSAndroid Build Coastguard Worker case 0xff:
278*38e8c45fSAndroid Build Coastguard Worker descriptorBlockSerialNumber = parseEdidText(descriptor);
279*38e8c45fSAndroid Build Coastguard Worker hashedDescriptorBlockSNOpt = descriptorBlockSerialNumber.empty()
280*38e8c45fSAndroid Build Coastguard Worker ? std::nullopt
281*38e8c45fSAndroid Build Coastguard Worker : ftl::stable_hash(descriptorBlockSerialNumber);
282*38e8c45fSAndroid Build Coastguard Worker break;
283*38e8c45fSAndroid Build Coastguard Worker }
284*38e8c45fSAndroid Build Coastguard Worker } else if (isDetailedTimingDescriptor(view)) {
285*38e8c45fSAndroid Build Coastguard Worker static constexpr size_t kHorizontalPhysicalLsbOffset = 12;
286*38e8c45fSAndroid Build Coastguard Worker static constexpr size_t kHorizontalPhysicalMsbOffset = 14;
287*38e8c45fSAndroid Build Coastguard Worker static constexpr size_t kVerticalPhysicalLsbOffset = 13;
288*38e8c45fSAndroid Build Coastguard Worker static constexpr size_t kVerticalPhysicalMsbOffset = 14;
289*38e8c45fSAndroid Build Coastguard Worker const uint32_t hSize =
290*38e8c45fSAndroid Build Coastguard Worker static_cast<uint32_t>(view[kHorizontalPhysicalLsbOffset] |
291*38e8c45fSAndroid Build Coastguard Worker ((view[kHorizontalPhysicalMsbOffset] >> 4) << 8));
292*38e8c45fSAndroid Build Coastguard Worker const uint32_t vSize =
293*38e8c45fSAndroid Build Coastguard Worker static_cast<uint32_t>(view[kVerticalPhysicalLsbOffset] |
294*38e8c45fSAndroid Build Coastguard Worker ((view[kVerticalPhysicalMsbOffset] & 0b1111) << 8));
295*38e8c45fSAndroid Build Coastguard Worker
296*38e8c45fSAndroid Build Coastguard Worker static constexpr size_t kHorizontalPixelLsbOffset = 2;
297*38e8c45fSAndroid Build Coastguard Worker static constexpr size_t kHorizontalPixelMsbOffset = 4;
298*38e8c45fSAndroid Build Coastguard Worker static constexpr size_t kVerticalPixelLsbOffset = 5;
299*38e8c45fSAndroid Build Coastguard Worker static constexpr size_t kVerticalPixelMsbOffset = 7;
300*38e8c45fSAndroid Build Coastguard Worker
301*38e8c45fSAndroid Build Coastguard Worker const uint8_t hLsb = view[kHorizontalPixelLsbOffset];
302*38e8c45fSAndroid Build Coastguard Worker const uint8_t hMsb = view[kHorizontalPixelMsbOffset];
303*38e8c45fSAndroid Build Coastguard Worker const int32_t hPixel = hLsb + ((hMsb & 0xF0) << 4);
304*38e8c45fSAndroid Build Coastguard Worker
305*38e8c45fSAndroid Build Coastguard Worker const uint8_t vLsb = view[kVerticalPixelLsbOffset];
306*38e8c45fSAndroid Build Coastguard Worker const uint8_t vMsb = view[kVerticalPixelMsbOffset];
307*38e8c45fSAndroid Build Coastguard Worker const int32_t vPixel = vLsb + ((vMsb & 0xF0) << 4);
308*38e8c45fSAndroid Build Coastguard Worker
309*38e8c45fSAndroid Build Coastguard Worker preferredDTDPixelSize.setWidth(hPixel);
310*38e8c45fSAndroid Build Coastguard Worker preferredDTDPixelSize.setHeight(vPixel);
311*38e8c45fSAndroid Build Coastguard Worker preferredDTDPhysicalSize.setWidth(hSize);
312*38e8c45fSAndroid Build Coastguard Worker preferredDTDPhysicalSize.setHeight(vSize);
313*38e8c45fSAndroid Build Coastguard Worker }
314*38e8c45fSAndroid Build Coastguard Worker
315*38e8c45fSAndroid Build Coastguard Worker view = view.subspan(kDescriptorLength);
316*38e8c45fSAndroid Build Coastguard Worker }
317*38e8c45fSAndroid Build Coastguard Worker
318*38e8c45fSAndroid Build Coastguard Worker std::string_view modelString = displayName;
319*38e8c45fSAndroid Build Coastguard Worker
320*38e8c45fSAndroid Build Coastguard Worker if (modelString.empty()) {
321*38e8c45fSAndroid Build Coastguard Worker ALOGW("Invalid EDID: falling back to serial number due to missing display name.");
322*38e8c45fSAndroid Build Coastguard Worker modelString = descriptorBlockSerialNumber;
323*38e8c45fSAndroid Build Coastguard Worker }
324*38e8c45fSAndroid Build Coastguard Worker if (modelString.empty()) {
325*38e8c45fSAndroid Build Coastguard Worker ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number.");
326*38e8c45fSAndroid Build Coastguard Worker modelString = asciiText;
327*38e8c45fSAndroid Build Coastguard Worker }
328*38e8c45fSAndroid Build Coastguard Worker if (modelString.empty()) {
329*38e8c45fSAndroid Build Coastguard Worker ALOGE("Invalid EDID: display name and fallback descriptors are missing.");
330*38e8c45fSAndroid Build Coastguard Worker return {};
331*38e8c45fSAndroid Build Coastguard Worker }
332*38e8c45fSAndroid Build Coastguard Worker
333*38e8c45fSAndroid Build Coastguard Worker // Hash model string instead of using product code or (integer) serial number, since the latter
334*38e8c45fSAndroid Build Coastguard Worker // have been observed to change on some displays with multiple inputs. Use a stable hash instead
335*38e8c45fSAndroid Build Coastguard Worker // of std::hash which is only required to be same within a single execution of a program.
336*38e8c45fSAndroid Build Coastguard Worker const uint32_t modelHash = static_cast<uint32_t>(*ftl::stable_hash(modelString));
337*38e8c45fSAndroid Build Coastguard Worker
338*38e8c45fSAndroid Build Coastguard Worker // Parse extension blocks.
339*38e8c45fSAndroid Build Coastguard Worker std::optional<Cea861ExtensionBlock> cea861Block;
340*38e8c45fSAndroid Build Coastguard Worker if (edid.size() < kEdidBlockSize) {
341*38e8c45fSAndroid Build Coastguard Worker ALOGW("Invalid EDID: block 0 is truncated.");
342*38e8c45fSAndroid Build Coastguard Worker } else {
343*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kNumExtensionsOffset = 126;
344*38e8c45fSAndroid Build Coastguard Worker const size_t numExtensions = edid[kNumExtensionsOffset];
345*38e8c45fSAndroid Build Coastguard Worker view = byte_view(edid.data(), edid.size());
346*38e8c45fSAndroid Build Coastguard Worker for (size_t blockNumber = 1; blockNumber <= numExtensions; blockNumber++) {
347*38e8c45fSAndroid Build Coastguard Worker view = view.subspan(kEdidBlockSize);
348*38e8c45fSAndroid Build Coastguard Worker if (static_cast<size_t>(view.size()) < kEdidBlockSize) {
349*38e8c45fSAndroid Build Coastguard Worker ALOGW("Invalid EDID: block %zu is truncated.", blockNumber);
350*38e8c45fSAndroid Build Coastguard Worker break;
351*38e8c45fSAndroid Build Coastguard Worker }
352*38e8c45fSAndroid Build Coastguard Worker
353*38e8c45fSAndroid Build Coastguard Worker const byte_view block(view.data(), kEdidBlockSize);
354*38e8c45fSAndroid Build Coastguard Worker ALOGW_IF(std::accumulate(block.begin(), block.end(), static_cast<uint8_t>(0)),
355*38e8c45fSAndroid Build Coastguard Worker "Invalid EDID: block %zu does not checksum.", blockNumber);
356*38e8c45fSAndroid Build Coastguard Worker const uint8_t tag = block[0];
357*38e8c45fSAndroid Build Coastguard Worker
358*38e8c45fSAndroid Build Coastguard Worker constexpr uint8_t kCea861BlockTag = 0x2;
359*38e8c45fSAndroid Build Coastguard Worker if (tag == kCea861BlockTag) {
360*38e8c45fSAndroid Build Coastguard Worker cea861Block = parseCea861Block(block);
361*38e8c45fSAndroid Build Coastguard Worker } else {
362*38e8c45fSAndroid Build Coastguard Worker ALOGV("Ignoring block number %zu with tag %x.", blockNumber, tag);
363*38e8c45fSAndroid Build Coastguard Worker }
364*38e8c45fSAndroid Build Coastguard Worker }
365*38e8c45fSAndroid Build Coastguard Worker }
366*38e8c45fSAndroid Build Coastguard Worker
367*38e8c45fSAndroid Build Coastguard Worker DetailedTimingDescriptor preferredDetailedTimingDescriptor{
368*38e8c45fSAndroid Build Coastguard Worker .pixelSizeCount = preferredDTDPixelSize,
369*38e8c45fSAndroid Build Coastguard Worker .physicalSizeInMm = preferredDTDPhysicalSize,
370*38e8c45fSAndroid Build Coastguard Worker };
371*38e8c45fSAndroid Build Coastguard Worker
372*38e8c45fSAndroid Build Coastguard Worker return Edid{
373*38e8c45fSAndroid Build Coastguard Worker .manufacturerId = manufacturerId,
374*38e8c45fSAndroid Build Coastguard Worker .productId = productId,
375*38e8c45fSAndroid Build Coastguard Worker .hashedBlockZeroSerialNumberOpt = hashedBlockZeroSNOpt,
376*38e8c45fSAndroid Build Coastguard Worker .hashedDescriptorBlockSerialNumberOpt = hashedDescriptorBlockSNOpt,
377*38e8c45fSAndroid Build Coastguard Worker .pnpId = *pnpId,
378*38e8c45fSAndroid Build Coastguard Worker .modelHash = modelHash,
379*38e8c45fSAndroid Build Coastguard Worker .displayName = displayName,
380*38e8c45fSAndroid Build Coastguard Worker .manufactureOrModelYear = manufactureOrModelYear,
381*38e8c45fSAndroid Build Coastguard Worker .manufactureWeek = manufactureWeek,
382*38e8c45fSAndroid Build Coastguard Worker .physicalSizeInCm = maxPhysicalSizeInCm,
383*38e8c45fSAndroid Build Coastguard Worker .cea861Block = cea861Block,
384*38e8c45fSAndroid Build Coastguard Worker .preferredDetailedTimingDescriptor = preferredDetailedTimingDescriptor,
385*38e8c45fSAndroid Build Coastguard Worker };
386*38e8c45fSAndroid Build Coastguard Worker }
387*38e8c45fSAndroid Build Coastguard Worker
getPnpId(uint16_t manufacturerId)388*38e8c45fSAndroid Build Coastguard Worker std::optional<PnpId> getPnpId(uint16_t manufacturerId) {
389*38e8c45fSAndroid Build Coastguard Worker const char a = getPnpLetter<0>(manufacturerId);
390*38e8c45fSAndroid Build Coastguard Worker const char b = getPnpLetter<1>(manufacturerId);
391*38e8c45fSAndroid Build Coastguard Worker const char c = getPnpLetter<2>(manufacturerId);
392*38e8c45fSAndroid Build Coastguard Worker return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt;
393*38e8c45fSAndroid Build Coastguard Worker }
394*38e8c45fSAndroid Build Coastguard Worker
getPnpId(PhysicalDisplayId displayId)395*38e8c45fSAndroid Build Coastguard Worker std::optional<PnpId> getPnpId(PhysicalDisplayId displayId) {
396*38e8c45fSAndroid Build Coastguard Worker return getPnpId(displayId.getManufacturerId());
397*38e8c45fSAndroid Build Coastguard Worker }
398*38e8c45fSAndroid Build Coastguard Worker
parseDisplayIdentificationData(uint8_t port,const DisplayIdentificationData & data)399*38e8c45fSAndroid Build Coastguard Worker std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
400*38e8c45fSAndroid Build Coastguard Worker uint8_t port, const DisplayIdentificationData& data) {
401*38e8c45fSAndroid Build Coastguard Worker if (data.empty()) {
402*38e8c45fSAndroid Build Coastguard Worker ALOGI("Display identification data is empty.");
403*38e8c45fSAndroid Build Coastguard Worker return {};
404*38e8c45fSAndroid Build Coastguard Worker }
405*38e8c45fSAndroid Build Coastguard Worker
406*38e8c45fSAndroid Build Coastguard Worker if (!isEdid(data)) {
407*38e8c45fSAndroid Build Coastguard Worker ALOGE("Display identification data has unknown format.");
408*38e8c45fSAndroid Build Coastguard Worker return {};
409*38e8c45fSAndroid Build Coastguard Worker }
410*38e8c45fSAndroid Build Coastguard Worker
411*38e8c45fSAndroid Build Coastguard Worker const auto edid = parseEdid(data);
412*38e8c45fSAndroid Build Coastguard Worker if (!edid) {
413*38e8c45fSAndroid Build Coastguard Worker return {};
414*38e8c45fSAndroid Build Coastguard Worker }
415*38e8c45fSAndroid Build Coastguard Worker
416*38e8c45fSAndroid Build Coastguard Worker const auto displayId = PhysicalDisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
417*38e8c45fSAndroid Build Coastguard Worker return DisplayIdentificationInfo{
418*38e8c45fSAndroid Build Coastguard Worker .id = displayId,
419*38e8c45fSAndroid Build Coastguard Worker .name = std::string(edid->displayName),
420*38e8c45fSAndroid Build Coastguard Worker .deviceProductInfo = buildDeviceProductInfo(*edid),
421*38e8c45fSAndroid Build Coastguard Worker .preferredDetailedTimingDescriptor = edid->preferredDetailedTimingDescriptor,
422*38e8c45fSAndroid Build Coastguard Worker };
423*38e8c45fSAndroid Build Coastguard Worker }
424*38e8c45fSAndroid Build Coastguard Worker
getVirtualDisplayId(uint32_t id)425*38e8c45fSAndroid Build Coastguard Worker PhysicalDisplayId getVirtualDisplayId(uint32_t id) {
426*38e8c45fSAndroid Build Coastguard Worker return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
427*38e8c45fSAndroid Build Coastguard Worker }
428*38e8c45fSAndroid Build Coastguard Worker
429*38e8c45fSAndroid Build Coastguard Worker } // namespace android
430