blob: b7ef9b37380f2f189e92cc402e850dd9bf27d94c [file] [log] [blame]
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#undef LOG_TAG
18#define LOG_TAG "DisplayIdentification"
19
20#include <algorithm>
21#include <cctype>
Gil Dekelc37c9042024-11-13 16:53:33 -050022#include <cstdint>
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -070023#include <numeric>
24#include <optional>
Ryan Prichard3b17f282024-02-08 02:31:50 -080025#include <span>
Gil Dekelc37c9042024-11-13 16:53:33 -050026#include <string>
27#include <string_view>
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -070028
Gil Dekel0bf26cd2024-12-10 15:11:28 -050029#include <ftl/concat.h>
Alan Dingc3ccff12024-04-29 00:44:31 -070030#include <ftl/hash.h>
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -070031#include <log/log.h>
Alec Mouriff793872022-01-13 17:45:06 -080032#include <ui/DisplayIdentification.h>
Lucas Berthou8d0a0c42024-08-27 14:32:31 +000033#include <ui/Size.h>
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -070034
35namespace android {
36namespace {
37
Ryan Prichard3b17f282024-02-08 02:31:50 -080038using byte_view = std::span<const uint8_t>;
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -070039
Marin Shalamanov7a9ba302020-03-02 17:49:16 +010040constexpr size_t kEdidBlockSize = 128;
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -070041constexpr size_t kEdidHeaderLength = 5;
42
Dominik Laskowski075d3172018-05-24 15:50:06 -070043constexpr uint16_t kVirtualEdidManufacturerId = 0xffffu;
44
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -070045std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) {
Ryan Prichard3b17f282024-02-08 02:31:50 -080046 if (static_cast<size_t>(view.size()) < kEdidHeaderLength || view[0] || view[1] || view[2] ||
47 view[4]) {
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -070048 return {};
49 }
50
51 return view[3];
52}
53
Lucas Berthou8d0a0c42024-08-27 14:32:31 +000054bool isDetailedTimingDescriptor(const byte_view& view) {
55 return view[0] != 0 && view[1] != 0;
56}
57
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -070058std::string_view parseEdidText(const byte_view& view) {
59 std::string_view text(reinterpret_cast<const char*>(view.data()), view.size());
60 text = text.substr(0, text.find('\n'));
61
62 if (!std::all_of(text.begin(), text.end(), ::isprint)) {
63 ALOGW("Invalid EDID: ASCII text is not printable.");
64 return {};
65 }
66
67 return text;
68}
69
70// Big-endian 16-bit value encodes three 5-bit letters where A is 0b00001.
71template <size_t I>
72char getPnpLetter(uint16_t id) {
73 static_assert(I < 3);
74 const char letter = 'A' + (static_cast<uint8_t>(id >> ((2 - I) * 5)) & 0b00011111) - 1;
75 return letter < 'A' || letter > 'Z' ? '\0' : letter;
76}
77
Marin Shalamanovf5de90d2019-10-08 10:57:25 +020078DeviceProductInfo buildDeviceProductInfo(const Edid& edid) {
79 DeviceProductInfo info;
Marin Shalamanov359a7e72020-02-17 17:03:07 +010080 info.name.assign(edid.displayName);
81 info.productId = std::to_string(edid.productId);
Marin Shalamanovf5de90d2019-10-08 10:57:25 +020082 info.manufacturerPnpId = edid.pnpId;
83
84 constexpr uint8_t kModelYearFlag = 0xff;
85 constexpr uint32_t kYearOffset = 1990;
86
87 const auto year = edid.manufactureOrModelYear + kYearOffset;
88 if (edid.manufactureWeek == kModelYearFlag) {
89 info.manufactureOrModelDate = DeviceProductInfo::ModelYear{.year = year};
90 } else if (edid.manufactureWeek == 0) {
91 DeviceProductInfo::ManufactureYear date;
92 date.year = year;
93 info.manufactureOrModelDate = date;
94 } else {
95 DeviceProductInfo::ManufactureWeekAndYear date;
96 date.year = year;
97 date.week = edid.manufactureWeek;
98 info.manufactureOrModelDate = date;
99 }
100
Marin Shalamanov896e6302020-04-06 16:11:25 +0200101 if (edid.cea861Block && edid.cea861Block->hdmiVendorDataBlock) {
102 const auto& address = edid.cea861Block->hdmiVendorDataBlock->physicalAddress;
103 info.relativeAddress = {address.a, address.b, address.c, address.d};
104 }
Marin Shalamanovf5de90d2019-10-08 10:57:25 +0200105 return info;
106}
107
Marin Shalamanov7a9ba302020-03-02 17:49:16 +0100108Cea861ExtensionBlock parseCea861Block(const byte_view& block) {
109 Cea861ExtensionBlock cea861Block;
110
111 constexpr size_t kRevisionNumberOffset = 1;
112 cea861Block.revisionNumber = block[kRevisionNumberOffset];
113
114 constexpr size_t kDetailedTimingDescriptorsOffset = 2;
115 const size_t dtdStart =
116 std::min(kEdidBlockSize, static_cast<size_t>(block[kDetailedTimingDescriptorsOffset]));
117
118 // Parse data blocks.
119 for (size_t dataBlockOffset = 4; dataBlockOffset < dtdStart;) {
120 const uint8_t header = block[dataBlockOffset];
121 const uint8_t tag = header >> 5;
122 const size_t bodyLength = header & 0b11111;
123 constexpr size_t kDataBlockHeaderSize = 1;
124 const size_t dataBlockSize = bodyLength + kDataBlockHeaderSize;
125
Ryan Prichard3b17f282024-02-08 02:31:50 -0800126 if (static_cast<size_t>(block.size()) < dataBlockOffset + dataBlockSize) {
Marin Shalamanov7a9ba302020-03-02 17:49:16 +0100127 ALOGW("Invalid EDID: CEA 861 data block is truncated.");
128 break;
129 }
130
131 const byte_view dataBlock(block.data() + dataBlockOffset, dataBlockSize);
132 constexpr uint8_t kVendorSpecificDataBlockTag = 0x3;
133
134 if (tag == kVendorSpecificDataBlockTag) {
Marin Shalamanova524a092020-07-27 21:39:55 +0200135 const uint32_t ieeeRegistrationId = static_cast<uint32_t>(
136 dataBlock[1] | (dataBlock[2] << 8) | (dataBlock[3] << 16));
Marin Shalamanov7a9ba302020-03-02 17:49:16 +0100137 constexpr uint32_t kHdmiIeeeRegistrationId = 0xc03;
138
139 if (ieeeRegistrationId == kHdmiIeeeRegistrationId) {
140 const uint8_t a = dataBlock[4] >> 4;
141 const uint8_t b = dataBlock[4] & 0b1111;
142 const uint8_t c = dataBlock[5] >> 4;
143 const uint8_t d = dataBlock[5] & 0b1111;
144 cea861Block.hdmiVendorDataBlock =
145 HdmiVendorDataBlock{.physicalAddress = HdmiPhysicalAddress{a, b, c, d}};
146 } else {
147 ALOGV("Ignoring vendor specific data block for vendor with IEEE OUI %x",
148 ieeeRegistrationId);
149 }
150 } else {
151 ALOGV("Ignoring CEA-861 data block with tag %x", tag);
152 }
153 dataBlockOffset += bodyLength + kDataBlockHeaderSize;
154 }
155
156 return cea861Block;
157}
158
Dominik Laskowski34157762018-10-31 13:07:19 -0700159} // namespace
160
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700161bool isEdid(const DisplayIdentificationData& data) {
162 const uint8_t kMagic[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0};
163 return data.size() >= sizeof(kMagic) &&
164 std::equal(std::begin(kMagic), std::end(kMagic), data.begin());
165}
166
167std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) {
Marin Shalamanov7a9ba302020-03-02 17:49:16 +0100168 if (edid.size() < kEdidBlockSize) {
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700169 ALOGW("Invalid EDID: structure is truncated.");
170 // Attempt parsing even if EDID is malformed.
171 } else {
Marin Shalamanov7a9ba302020-03-02 17:49:16 +0100172 ALOGW_IF(std::accumulate(edid.begin(), edid.begin() + kEdidBlockSize,
173 static_cast<uint8_t>(0)),
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700174 "Invalid EDID: structure does not checksum.");
175 }
176
177 constexpr size_t kManufacturerOffset = 8;
178 if (edid.size() < kManufacturerOffset + sizeof(uint16_t)) {
179 ALOGE("Invalid EDID: manufacturer ID is truncated.");
180 return {};
181 }
182
183 // Plug and play ID encoded as big-endian 16-bit value.
184 const uint16_t manufacturerId =
Marin Shalamanova524a092020-07-27 21:39:55 +0200185 static_cast<uint16_t>((edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1]);
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700186
187 const auto pnpId = getPnpId(manufacturerId);
188 if (!pnpId) {
189 ALOGE("Invalid EDID: manufacturer ID is not a valid PnP ID.");
190 return {};
191 }
192
Marin Shalamanovf5de90d2019-10-08 10:57:25 +0200193 constexpr size_t kProductIdOffset = 10;
194 if (edid.size() < kProductIdOffset + sizeof(uint16_t)) {
195 ALOGE("Invalid EDID: product ID is truncated.");
196 return {};
197 }
Marin Shalamanova524a092020-07-27 21:39:55 +0200198 const uint16_t productId =
199 static_cast<uint16_t>(edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8));
Marin Shalamanovf5de90d2019-10-08 10:57:25 +0200200
Gil Dekelc37c9042024-11-13 16:53:33 -0500201 // Bytes 12-15: display serial number, in little-endian (LSB). This field is
202 // optional and its absence is marked by having all bytes set to 0x00.
203 // Values do not represent ASCII characters.
204 constexpr size_t kSerialNumberOffset = 12;
205 if (edid.size() < kSerialNumberOffset + sizeof(uint32_t)) {
206 ALOGE("Invalid EDID: block zero S/N is truncated.");
207 return {};
208 }
209 const uint32_t blockZeroSerialNumber = edid[kSerialNumberOffset] +
210 (edid[kSerialNumberOffset + 1] << 8) + (edid[kSerialNumberOffset + 2] << 16) +
211 (edid[kSerialNumberOffset + 3] << 24);
212 const auto hashedBlockZeroSNOpt = blockZeroSerialNumber == 0
213 ? std::nullopt
214 : ftl::stable_hash(std::string_view(std::to_string(blockZeroSerialNumber)));
215
Marin Shalamanovf5de90d2019-10-08 10:57:25 +0200216 constexpr size_t kManufactureWeekOffset = 16;
217 if (edid.size() < kManufactureWeekOffset + sizeof(uint8_t)) {
218 ALOGE("Invalid EDID: manufacture week is truncated.");
219 return {};
220 }
221 const uint8_t manufactureWeek = edid[kManufactureWeekOffset];
222 ALOGW_IF(0x37 <= manufactureWeek && manufactureWeek <= 0xfe,
223 "Invalid EDID: week of manufacture cannot be in the range [0x37, 0xfe].");
224
225 constexpr size_t kManufactureYearOffset = 17;
226 if (edid.size() < kManufactureYearOffset + sizeof(uint8_t)) {
227 ALOGE("Invalid EDID: manufacture year is truncated.");
228 return {};
229 }
230 const uint8_t manufactureOrModelYear = edid[kManufactureYearOffset];
231 ALOGW_IF(manufactureOrModelYear <= 0xf,
232 "Invalid EDID: model year or manufacture year cannot be in the range [0x0, 0xf].");
233
Gil Dekel3e96f942024-11-13 14:51:24 -0500234 constexpr size_t kMaxHorizontalPhysicalSizeOffset = 21;
235 constexpr size_t kMaxVerticalPhysicalSizeOffset = 22;
236 if (edid.size() < kMaxVerticalPhysicalSizeOffset + sizeof(uint8_t)) {
237 ALOGE("Invalid EDID: display's physical size is truncated.");
238 return {};
239 }
240 ui::Size maxPhysicalSizeInCm(edid[kMaxHorizontalPhysicalSizeOffset],
241 edid[kMaxVerticalPhysicalSizeOffset]);
242
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700243 constexpr size_t kDescriptorOffset = 54;
244 if (edid.size() < kDescriptorOffset) {
245 ALOGE("Invalid EDID: descriptors are missing.");
246 return {};
247 }
248
249 byte_view view(edid.data(), edid.size());
Ryan Prichard3b17f282024-02-08 02:31:50 -0800250 view = view.subspan(kDescriptorOffset);
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700251
252 std::string_view displayName;
Gil Dekelf9854d22024-11-14 14:14:25 -0500253 std::string_view descriptorBlockSerialNumber;
254 std::optional<uint64_t> hashedDescriptorBlockSNOpt = std::nullopt;
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700255 std::string_view asciiText;
Lucas Berthou8d0a0c42024-08-27 14:32:31 +0000256 ui::Size preferredDTDPixelSize;
257 ui::Size preferredDTDPhysicalSize;
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700258
259 constexpr size_t kDescriptorCount = 4;
260 constexpr size_t kDescriptorLength = 18;
261
262 for (size_t i = 0; i < kDescriptorCount; i++) {
Ryan Prichard3b17f282024-02-08 02:31:50 -0800263 if (static_cast<size_t>(view.size()) < kDescriptorLength) {
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700264 break;
265 }
266
267 if (const auto type = getEdidDescriptorType(view)) {
268 byte_view descriptor(view.data(), kDescriptorLength);
Ryan Prichard3b17f282024-02-08 02:31:50 -0800269 descriptor = descriptor.subspan(kEdidHeaderLength);
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700270
271 switch (*type) {
272 case 0xfc:
273 displayName = parseEdidText(descriptor);
274 break;
275 case 0xfe:
276 asciiText = parseEdidText(descriptor);
277 break;
278 case 0xff:
Gil Dekelf9854d22024-11-14 14:14:25 -0500279 descriptorBlockSerialNumber = parseEdidText(descriptor);
280 hashedDescriptorBlockSNOpt = descriptorBlockSerialNumber.empty()
281 ? std::nullopt
282 : ftl::stable_hash(descriptorBlockSerialNumber);
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700283 break;
284 }
Lucas Berthou8d0a0c42024-08-27 14:32:31 +0000285 } else if (isDetailedTimingDescriptor(view)) {
286 static constexpr size_t kHorizontalPhysicalLsbOffset = 12;
287 static constexpr size_t kHorizontalPhysicalMsbOffset = 14;
288 static constexpr size_t kVerticalPhysicalLsbOffset = 13;
289 static constexpr size_t kVerticalPhysicalMsbOffset = 14;
290 const uint32_t hSize =
291 static_cast<uint32_t>(view[kHorizontalPhysicalLsbOffset] |
292 ((view[kHorizontalPhysicalMsbOffset] >> 4) << 8));
293 const uint32_t vSize =
294 static_cast<uint32_t>(view[kVerticalPhysicalLsbOffset] |
295 ((view[kVerticalPhysicalMsbOffset] & 0b1111) << 8));
296
297 static constexpr size_t kHorizontalPixelLsbOffset = 2;
298 static constexpr size_t kHorizontalPixelMsbOffset = 4;
299 static constexpr size_t kVerticalPixelLsbOffset = 5;
300 static constexpr size_t kVerticalPixelMsbOffset = 7;
301
302 const uint8_t hLsb = view[kHorizontalPixelLsbOffset];
303 const uint8_t hMsb = view[kHorizontalPixelMsbOffset];
304 const int32_t hPixel = hLsb + ((hMsb & 0xF0) << 4);
305
306 const uint8_t vLsb = view[kVerticalPixelLsbOffset];
307 const uint8_t vMsb = view[kVerticalPixelMsbOffset];
308 const int32_t vPixel = vLsb + ((vMsb & 0xF0) << 4);
309
310 preferredDTDPixelSize.setWidth(hPixel);
311 preferredDTDPixelSize.setHeight(vPixel);
312 preferredDTDPhysicalSize.setWidth(hSize);
313 preferredDTDPhysicalSize.setHeight(vSize);
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700314 }
315
Ryan Prichard3b17f282024-02-08 02:31:50 -0800316 view = view.subspan(kDescriptorLength);
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700317 }
318
Dominik Laskowski17337962020-03-02 15:51:15 -0800319 std::string_view modelString = displayName;
320
321 if (modelString.empty()) {
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700322 ALOGW("Invalid EDID: falling back to serial number due to missing display name.");
Gil Dekelf9854d22024-11-14 14:14:25 -0500323 modelString = descriptorBlockSerialNumber;
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700324 }
Dominik Laskowski17337962020-03-02 15:51:15 -0800325 if (modelString.empty()) {
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700326 ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number.");
Dominik Laskowski17337962020-03-02 15:51:15 -0800327 modelString = asciiText;
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700328 }
Dominik Laskowski17337962020-03-02 15:51:15 -0800329 if (modelString.empty()) {
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700330 ALOGE("Invalid EDID: display name and fallback descriptors are missing.");
331 return {};
332 }
333
Dominik Laskowski17337962020-03-02 15:51:15 -0800334 // Hash model string instead of using product code or (integer) serial number, since the latter
Jason Macnak4afe8572021-07-16 13:57:41 -0700335 // have been observed to change on some displays with multiple inputs. Use a stable hash instead
336 // of std::hash which is only required to be same within a single execution of a program.
Alan Dingc3ccff12024-04-29 00:44:31 -0700337 const uint32_t modelHash = static_cast<uint32_t>(*ftl::stable_hash(modelString));
Dominik Laskowski17337962020-03-02 15:51:15 -0800338
Marin Shalamanov7a9ba302020-03-02 17:49:16 +0100339 // Parse extension blocks.
340 std::optional<Cea861ExtensionBlock> cea861Block;
341 if (edid.size() < kEdidBlockSize) {
342 ALOGW("Invalid EDID: block 0 is truncated.");
343 } else {
344 constexpr size_t kNumExtensionsOffset = 126;
345 const size_t numExtensions = edid[kNumExtensionsOffset];
346 view = byte_view(edid.data(), edid.size());
347 for (size_t blockNumber = 1; blockNumber <= numExtensions; blockNumber++) {
Ryan Prichard3b17f282024-02-08 02:31:50 -0800348 view = view.subspan(kEdidBlockSize);
349 if (static_cast<size_t>(view.size()) < kEdidBlockSize) {
Marin Shalamanov7a9ba302020-03-02 17:49:16 +0100350 ALOGW("Invalid EDID: block %zu is truncated.", blockNumber);
351 break;
352 }
353
354 const byte_view block(view.data(), kEdidBlockSize);
355 ALOGW_IF(std::accumulate(block.begin(), block.end(), static_cast<uint8_t>(0)),
356 "Invalid EDID: block %zu does not checksum.", blockNumber);
357 const uint8_t tag = block[0];
358
359 constexpr uint8_t kCea861BlockTag = 0x2;
360 if (tag == kCea861BlockTag) {
361 cea861Block = parseCea861Block(block);
362 } else {
363 ALOGV("Ignoring block number %zu with tag %x.", blockNumber, tag);
364 }
365 }
366 }
367
Lucas Berthou8d0a0c42024-08-27 14:32:31 +0000368 DetailedTimingDescriptor preferredDetailedTimingDescriptor{
369 .pixelSizeCount = preferredDTDPixelSize,
370 .physicalSizeInMm = preferredDTDPhysicalSize,
371 };
372
373 return Edid{
374 .manufacturerId = manufacturerId,
375 .productId = productId,
Gil Dekelc37c9042024-11-13 16:53:33 -0500376 .hashedBlockZeroSerialNumberOpt = hashedBlockZeroSNOpt,
Gil Dekelf9854d22024-11-14 14:14:25 -0500377 .hashedDescriptorBlockSerialNumberOpt = hashedDescriptorBlockSNOpt,
Lucas Berthou8d0a0c42024-08-27 14:32:31 +0000378 .pnpId = *pnpId,
379 .modelHash = modelHash,
380 .displayName = displayName,
381 .manufactureOrModelYear = manufactureOrModelYear,
382 .manufactureWeek = manufactureWeek,
Gil Dekel3e96f942024-11-13 14:51:24 -0500383 .physicalSizeInCm = maxPhysicalSizeInCm,
Lucas Berthou8d0a0c42024-08-27 14:32:31 +0000384 .cea861Block = cea861Block,
385 .preferredDetailedTimingDescriptor = preferredDetailedTimingDescriptor,
386 };
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700387}
388
389std::optional<PnpId> getPnpId(uint16_t manufacturerId) {
390 const char a = getPnpLetter<0>(manufacturerId);
391 const char b = getPnpLetter<1>(manufacturerId);
392 const char c = getPnpLetter<2>(manufacturerId);
393 return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt;
394}
395
Dominik Laskowski075d3172018-05-24 15:50:06 -0700396std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
397 uint8_t port, const DisplayIdentificationData& data) {
Brian Lindahl8a96ef92024-05-24 14:46:29 +0000398 if (data.empty()) {
399 ALOGI("Display identification data is empty.");
400 return {};
401 }
402
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700403 if (!isEdid(data)) {
404 ALOGE("Display identification data has unknown format.");
405 return {};
406 }
407
408 const auto edid = parseEdid(data);
409 if (!edid) {
410 return {};
411 }
412
Marin Shalamanova524a092020-07-27 21:39:55 +0200413 const auto displayId = PhysicalDisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
Lucas Berthou8d0a0c42024-08-27 14:32:31 +0000414 return DisplayIdentificationInfo{
415 .id = displayId,
416 .name = std::string(edid->displayName),
Gil Dekelffca0ad2024-11-08 16:31:24 -0500417 .port = port,
Lucas Berthou8d0a0c42024-08-27 14:32:31 +0000418 .deviceProductInfo = buildDeviceProductInfo(*edid),
419 .preferredDetailedTimingDescriptor = edid->preferredDetailedTimingDescriptor,
420 };
Dominik Laskowski075d3172018-05-24 15:50:06 -0700421}
422
Marin Shalamanova524a092020-07-27 21:39:55 +0200423PhysicalDisplayId getVirtualDisplayId(uint32_t id) {
424 return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
Dominik Laskowskie9ef7c42018-03-12 19:34:30 -0700425}
426
Gil Dekel0bf26cd2024-12-10 15:11:28 -0500427PhysicalDisplayId generateEdidDisplayId(const Edid& edid) {
428 const ftl::Concat displayDetailsString{edid.manufacturerId,
429 edid.productId,
430 ftl::truncated<13>(edid.displayName),
431 edid.manufactureWeek,
432 edid.manufactureOrModelYear,
433 edid.physicalSizeInCm.getWidth(),
434 edid.physicalSizeInCm.getHeight()};
435
436 // String has to be cropped to 64 characters (at most) for ftl::stable_hash.
437 // This is fine as the accuracy or completeness of the above fields is not
438 // critical for a ID fabrication.
439 const std::optional<uint64_t> hashedDisplayDetailsOpt =
440 ftl::stable_hash(std::string_view(displayDetailsString.c_str(), 64));
441
442 // Combine the hashes via bit-shifted XORs.
443 const uint64_t id = (hashedDisplayDetailsOpt.value_or(0) << 17) ^
444 (edid.hashedBlockZeroSerialNumberOpt.value_or(0) >> 11) ^
445 (edid.hashedDescriptorBlockSerialNumberOpt.value_or(0) << 23);
446
447 return PhysicalDisplayId::fromEdidHash(id);
448}
449
Yi Kong93926f62024-02-20 00:39:46 +0800450} // namespace android