1 /* 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> 22 #include <numeric> 23 #include <optional> 24 25 #include <log/log.h> 26 27 #include "DisplayIdentification.h" 28 29 namespace android { 30 namespace { 31 32 using byte_view = std::basic_string_view<uint8_t>; 33 34 constexpr size_t kEdidHeaderLength = 5; 35 36 constexpr uint16_t kFallbackEdidManufacturerId = 0; 37 constexpr uint16_t kVirtualEdidManufacturerId = 0xffffu; 38 39 std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) { 40 if (view.size() < kEdidHeaderLength || view[0] || view[1] || view[2] || view[4]) { 41 return {}; 42 } 43 44 return view[3]; 45 } 46 47 std::string_view parseEdidText(const byte_view& view) { 48 std::string_view text(reinterpret_cast<const char*>(view.data()), view.size()); 49 text = text.substr(0, text.find('\n')); 50 51 if (!std::all_of(text.begin(), text.end(), ::isprint)) { 52 ALOGW("Invalid EDID: ASCII text is not printable."); 53 return {}; 54 } 55 56 return text; 57 } 58 59 // Big-endian 16-bit value encodes three 5-bit letters where A is 0b00001. 60 template <size_t I> 61 char getPnpLetter(uint16_t id) { 62 static_assert(I < 3); 63 const char letter = 'A' + (static_cast<uint8_t>(id >> ((2 - I) * 5)) & 0b00011111) - 1; 64 return letter < 'A' || letter > 'Z' ? '\0' : letter; 65 } 66 67 } // namespace 68 69 uint16_t DisplayId::manufacturerId() const { 70 return static_cast<uint16_t>(value >> 40); 71 } 72 73 DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash) { 74 return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(displayNameHash) << 8) | 75 port}; 76 } 77 78 bool isEdid(const DisplayIdentificationData& data) { 79 const uint8_t kMagic[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}; 80 return data.size() >= sizeof(kMagic) && 81 std::equal(std::begin(kMagic), std::end(kMagic), data.begin()); 82 } 83 84 std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { 85 constexpr size_t kMinLength = 128; 86 if (edid.size() < kMinLength) { 87 ALOGW("Invalid EDID: structure is truncated."); 88 // Attempt parsing even if EDID is malformed. 89 } else { 90 ALOGW_IF(edid[126] != 0, "EDID extensions are currently unsupported."); 91 ALOGW_IF(std::accumulate(edid.begin(), edid.begin() + kMinLength, static_cast<uint8_t>(0)), 92 "Invalid EDID: structure does not checksum."); 93 } 94 95 constexpr size_t kManufacturerOffset = 8; 96 if (edid.size() < kManufacturerOffset + sizeof(uint16_t)) { 97 ALOGE("Invalid EDID: manufacturer ID is truncated."); 98 return {}; 99 } 100 101 // Plug and play ID encoded as big-endian 16-bit value. 102 const uint16_t manufacturerId = 103 (edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1]; 104 105 const auto pnpId = getPnpId(manufacturerId); 106 if (!pnpId) { 107 ALOGE("Invalid EDID: manufacturer ID is not a valid PnP ID."); 108 return {}; 109 } 110 111 constexpr size_t kDescriptorOffset = 54; 112 if (edid.size() < kDescriptorOffset) { 113 ALOGE("Invalid EDID: descriptors are missing."); 114 return {}; 115 } 116 117 byte_view view(edid.data(), edid.size()); 118 view.remove_prefix(kDescriptorOffset); 119 120 std::string_view displayName; 121 std::string_view serialNumber; 122 std::string_view asciiText; 123 124 constexpr size_t kDescriptorCount = 4; 125 constexpr size_t kDescriptorLength = 18; 126 127 for (size_t i = 0; i < kDescriptorCount; i++) { 128 if (view.size() < kDescriptorLength) { 129 break; 130 } 131 132 if (const auto type = getEdidDescriptorType(view)) { 133 byte_view descriptor(view.data(), kDescriptorLength); 134 descriptor.remove_prefix(kEdidHeaderLength); 135 136 switch (*type) { 137 case 0xfc: 138 displayName = parseEdidText(descriptor); 139 break; 140 case 0xfe: 141 asciiText = parseEdidText(descriptor); 142 break; 143 case 0xff: 144 serialNumber = parseEdidText(descriptor); 145 break; 146 } 147 } 148 149 view.remove_prefix(kDescriptorLength); 150 } 151 152 if (displayName.empty()) { 153 ALOGW("Invalid EDID: falling back to serial number due to missing display name."); 154 displayName = serialNumber; 155 } 156 if (displayName.empty()) { 157 ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number."); 158 displayName = asciiText; 159 } 160 if (displayName.empty()) { 161 ALOGE("Invalid EDID: display name and fallback descriptors are missing."); 162 return {}; 163 } 164 165 return Edid{manufacturerId, *pnpId, displayName}; 166 } 167 168 std::optional<PnpId> getPnpId(uint16_t manufacturerId) { 169 const char a = getPnpLetter<0>(manufacturerId); 170 const char b = getPnpLetter<1>(manufacturerId); 171 const char c = getPnpLetter<2>(manufacturerId); 172 return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt; 173 } 174 175 std::optional<PnpId> getPnpId(DisplayId displayId) { 176 return getPnpId(displayId.manufacturerId()); 177 } 178 179 std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData( 180 uint8_t port, const DisplayIdentificationData& data) { 181 if (!isEdid(data)) { 182 ALOGE("Display identification data has unknown format."); 183 return {}; 184 } 185 186 const auto edid = parseEdid(data); 187 if (!edid) { 188 return {}; 189 } 190 191 // Hash display name instead of using product code or serial number, since the latter have been 192 // observed to change on some displays with multiple inputs. 193 const auto hash = static_cast<uint32_t>(std::hash<std::string_view>()(edid->displayName)); 194 return DisplayIdentificationInfo{DisplayId::fromEdid(port, edid->manufacturerId, hash), 195 std::string(edid->displayName)}; 196 } 197 198 DisplayId getFallbackDisplayId(uint8_t port) { 199 return DisplayId::fromEdid(port, kFallbackEdidManufacturerId, 0); 200 } 201 202 DisplayId getVirtualDisplayId(uint32_t id) { 203 return DisplayId::fromEdid(0, kVirtualEdidManufacturerId, id); 204 } 205 206 } // namespace android 207