Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 667b7072 authored by Dominik Laskowski's avatar Dominik Laskowski Committed by Android (Google) Code Review
Browse files

Merge "SF: Generate stable display IDs using EDID"

parents 51262580 e9ef7c43
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ sharedLibraries = [
    "android.hardware.graphics.allocator@2.0",
    "android.hardware.graphics.composer@2.1",
    "android.hardware.graphics.composer@2.2",
    "android.hardware.graphics.composer@2.3",
    "libbinder",
    "libbase",
    "libbufferhubqueue",
+3 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ cc_defaults {
        "android.hardware.graphics.allocator@2.0",
        "android.hardware.graphics.composer@2.1",
        "android.hardware.graphics.composer@2.2",
        "android.hardware.graphics.composer@2.3",
        "android.hardware.power@1.0",
        "libbase",
        "libbinder",
@@ -67,6 +68,7 @@ cc_defaults {
        "android.hardware.graphics.allocator@2.0",
        "android.hardware.graphics.composer@2.1",
        "android.hardware.graphics.composer@2.2",
        "android.hardware.graphics.composer@2.3",
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
@@ -90,6 +92,7 @@ filegroup {
        "ContainerLayer.cpp",
        "DisplayDevice.cpp",
        "DisplayHardware/ComposerHal.cpp",
        "DisplayHardware/DisplayIdentification.cpp",
        "DisplayHardware/FramebufferSurface.cpp",
        "DisplayHardware/HWC2.cpp",
        "DisplayHardware/HWComposer.cpp",
+47 −15
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@

#include "ComposerHal.h"

#include <android/hardware/graphics/composer/2.2/IComposer.h>
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <gui/BufferQueue.h>
#include <hidl/HidlTransportUtils.h>
@@ -172,22 +171,31 @@ Composer::Composer(const std::string& serviceName)
        LOG_ALWAYS_FATAL("failed to get hwcomposer service");
    }

    mComposer->createClient(
            [&](const auto& tmpError, const auto& tmpClient)
            {
    if (sp<IComposer> composer_2_3 = IComposer::castFrom(mComposer)) {
        composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
            if (tmpError == Error::NONE) {
                mClient = tmpClient;
                mClient_2_2 = tmpClient;
                mClient_2_3 = tmpClient;
            }
        });
    } else {
        mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
            if (tmpError != Error::NONE) {
                return;
            }

            mClient = tmpClient;
            if (sp<V2_2::IComposer> composer_2_2 = V2_2::IComposer::castFrom(mComposer)) {
                mClient_2_2 = V2_2::IComposerClient::castFrom(mClient);
                LOG_ALWAYS_FATAL_IF(mClient_2_2 == nullptr,
                                    "IComposer 2.2 did not return IComposerClient 2.2");
            }
        });
    if (mClient == nullptr) {
        LOG_ALWAYS_FATAL("failed to create composer client");
    }

    // 2.2 support is optional
    sp<IComposer> composer_2_2 = IComposer::castFrom(mComposer);
    if (composer_2_2 != nullptr) {
        mClient_2_2 = IComposerClient::castFrom(mClient);
        LOG_ALWAYS_FATAL_IF(mClient_2_2 == nullptr, "IComposer 2.2 did not return IComposerClient 2.2");
    if (mClient == nullptr) {
        LOG_ALWAYS_FATAL("failed to create composer client");
    }

    if (mIsUsingVrComposer) {
@@ -957,6 +965,30 @@ Error Composer::getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatri
    return error;
}

// Composer HAL 2.3

Error Composer::getDisplayIdentificationData(Display display, uint8_t* outPort,
                                             std::vector<uint8_t>* outData) {
    if (!mClient_2_3) {
        return Error::UNSUPPORTED;
    }

    Error error = kDefaultError;
    mClient_2_3->getDisplayIdentificationData(display,
                                              [&](const auto& tmpError, const auto& tmpPort,
                                                  const auto& tmpData) {
                                                  error = tmpError;
                                                  if (error != Error::NONE) {
                                                      return;
                                                  }

                                                  *outPort = tmpPort;
                                                  *outData = tmpData;
                                              });

    return error;
}

CommandReader::~CommandReader()
{
    resetData();
+16 −5
Original line number Diff line number Diff line
@@ -25,8 +25,8 @@

#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/composer/2.2/IComposer.h>
#include <android/hardware/graphics/composer/2.2/IComposerClient.h>
#include <android/hardware/graphics/composer/2.3/IComposer.h>
#include <android/hardware/graphics/composer/2.3/IComposerClient.h>
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
@@ -43,6 +43,7 @@ namespace types = hardware::graphics::common;

namespace V2_1 = hardware::graphics::composer::V2_1;
namespace V2_2 = hardware::graphics::composer::V2_2;
namespace V2_3 = hardware::graphics::composer::V2_3;

using types::V1_0::ColorTransform;
using types::V1_0::Hdr;
@@ -61,8 +62,9 @@ using V2_1::Layer;

using V2_2::CommandReaderBase;
using V2_2::CommandWriterBase;
using V2_2::IComposer;
using V2_2::IComposerClient;

using V2_3::IComposer;
using V2_3::IComposerClient;

using PerFrameMetadata = IComposerClient::PerFrameMetadata;
using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
@@ -185,6 +187,10 @@ public:
    virtual Error getRenderIntents(Display display, ColorMode colorMode,
            std::vector<RenderIntent>* outRenderIntents) = 0;
    virtual Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) = 0;

    // Composer HAL 2.3
    virtual Error getDisplayIdentificationData(Display display, uint8_t* outPort,
                                               std::vector<uint8_t>* outData) = 0;
};

namespace impl {
@@ -380,6 +386,10 @@ public:
            std::vector<RenderIntent>* outRenderIntents) override;
    Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) override;

    // Composer HAL 2.3
    Error getDisplayIdentificationData(Display display, uint8_t* outPort,
                                       std::vector<uint8_t>* outData) override;

private:
    class CommandWriter : public CommandWriterBase {
    public:
@@ -405,7 +415,8 @@ private:
    sp<V2_1::IComposer> mComposer;

    sp<V2_1::IComposerClient> mClient;
    sp<IComposerClient> mClient_2_2;
    sp<V2_2::IComposerClient> mClient_2_2;
    sp<IComposerClient> mClient_2_3;

    // 64KiB minus a small space for metadata such as read/write pointers
    static constexpr size_t kWriterInitialSize =
+185 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#undef LOG_TAG
#define LOG_TAG "DisplayIdentification"

#include <algorithm>
#include <cctype>
#include <numeric>
#include <optional>

#include <log/log.h>

#include "DisplayIdentification.h"

namespace android {
namespace {

using byte_view = std::basic_string_view<uint8_t>;

constexpr size_t kEdidHeaderLength = 5;

std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) {
    if (view.size() < kEdidHeaderLength || view[0] || view[1] || view[2] || view[4]) {
        return {};
    }

    return view[3];
}

std::string_view parseEdidText(const byte_view& view) {
    std::string_view text(reinterpret_cast<const char*>(view.data()), view.size());
    text = text.substr(0, text.find('\n'));

    if (!std::all_of(text.begin(), text.end(), ::isprint)) {
        ALOGW("Invalid EDID: ASCII text is not printable.");
        return {};
    }

    return text;
}

// Big-endian 16-bit value encodes three 5-bit letters where A is 0b00001.
template <size_t I>
char getPnpLetter(uint16_t id) {
    static_assert(I < 3);
    const char letter = 'A' + (static_cast<uint8_t>(id >> ((2 - I) * 5)) & 0b00011111) - 1;
    return letter < 'A' || letter > 'Z' ? '\0' : letter;
}

DisplayId getEdidDisplayId(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash) {
    return (static_cast<DisplayId>(manufacturerId) << 40) |
            (static_cast<DisplayId>(displayNameHash) << 8) | port;
}

} // namespace

bool isEdid(const DisplayIdentificationData& data) {
    const uint8_t kMagic[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0};
    return data.size() >= sizeof(kMagic) &&
            std::equal(std::begin(kMagic), std::end(kMagic), data.begin());
}

std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) {
    constexpr size_t kMinLength = 128;
    if (edid.size() < kMinLength) {
        ALOGW("Invalid EDID: structure is truncated.");
        // Attempt parsing even if EDID is malformed.
    } else {
        ALOGW_IF(edid[126] != 0, "EDID extensions are currently unsupported.");
        ALOGW_IF(std::accumulate(edid.begin(), edid.begin() + kMinLength, static_cast<uint8_t>(0)),
                 "Invalid EDID: structure does not checksum.");
    }

    constexpr size_t kManufacturerOffset = 8;
    if (edid.size() < kManufacturerOffset + sizeof(uint16_t)) {
        ALOGE("Invalid EDID: manufacturer ID is truncated.");
        return {};
    }

    // Plug and play ID encoded as big-endian 16-bit value.
    const uint16_t manufacturerId =
            (edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1];

    const auto pnpId = getPnpId(manufacturerId);
    if (!pnpId) {
        ALOGE("Invalid EDID: manufacturer ID is not a valid PnP ID.");
        return {};
    }

    constexpr size_t kDescriptorOffset = 54;
    if (edid.size() < kDescriptorOffset) {
        ALOGE("Invalid EDID: descriptors are missing.");
        return {};
    }

    byte_view view(edid.data(), edid.size());
    view.remove_prefix(kDescriptorOffset);

    std::string_view displayName;
    std::string_view serialNumber;
    std::string_view asciiText;

    constexpr size_t kDescriptorCount = 4;
    constexpr size_t kDescriptorLength = 18;

    for (size_t i = 0; i < kDescriptorCount; i++) {
        if (view.size() < kDescriptorLength) {
            break;
        }

        if (const auto type = getEdidDescriptorType(view)) {
            byte_view descriptor(view.data(), kDescriptorLength);
            descriptor.remove_prefix(kEdidHeaderLength);

            switch (*type) {
                case 0xfc:
                    displayName = parseEdidText(descriptor);
                    break;
                case 0xfe:
                    asciiText = parseEdidText(descriptor);
                    break;
                case 0xff:
                    serialNumber = parseEdidText(descriptor);
                    break;
            }
        }

        view.remove_prefix(kDescriptorLength);
    }

    if (displayName.empty()) {
        ALOGW("Invalid EDID: falling back to serial number due to missing display name.");
        displayName = serialNumber;
    }
    if (displayName.empty()) {
        ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number.");
        displayName = asciiText;
    }
    if (displayName.empty()) {
        ALOGE("Invalid EDID: display name and fallback descriptors are missing.");
        return {};
    }

    return Edid{manufacturerId, *pnpId, displayName};
}

std::optional<PnpId> getPnpId(uint16_t manufacturerId) {
    const char a = getPnpLetter<0>(manufacturerId);
    const char b = getPnpLetter<1>(manufacturerId);
    const char c = getPnpLetter<2>(manufacturerId);
    return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt;
}

std::optional<DisplayId> generateDisplayId(uint8_t port, const DisplayIdentificationData& data) {
    if (!isEdid(data)) {
        ALOGE("Display identification data has unknown format.");
        return {};
    }

    const auto edid = parseEdid(data);
    if (!edid) {
        return {};
    }

    // Hash display name instead of using product code or serial number, since the latter have been
    // observed to change on some displays with multiple inputs.
    const auto hash = static_cast<uint32_t>(std::hash<std::string_view>()(edid->displayName));
    return getEdidDisplayId(port, edid->manufacturerId, hash);
}

} // namespace android
Loading