xref: /aosp_15_r20/frameworks/native/libs/ui/DisplayIdentification.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
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