From 406a14f111f9964b25db7b6d62ed2024e955c450 Mon Sep 17 00:00:00 2001 From: Su Hong Koo Date: Fri, 7 Nov 2025 12:37:15 -0500 Subject: [PATCH] Add atom reporting for modes on hotplugged displays Add and use DisplayHotplugConnectModeDetectedAtomReporter, which logs all valid modes of a hotplugged display. Change-Id: Ie93487ad8e4936195e3d949ce46034c5ec4fda83 --- .ci/Makefile | 1 + Android.bp | 2 + hwc/HwcDisplay.cpp | 63 ++++++++++++ hwc/HwcDisplay.h | 6 ++ meson.build | 1 + ...HotplugConnectModeDetectedAtomReporter.cpp | 31 ++++++ ...ayHotplugConnectModeDetectedAtomReporter.h | 58 +++++++++++ ...ConnectModeDetectedAtomReporterDesktop.cpp | 98 +++++++++++++++++++ 8 files changed, 260 insertions(+) create mode 100644 stats/DisplayHotplugConnectModeDetectedAtomReporter.cpp create mode 100644 stats/DisplayHotplugConnectModeDetectedAtomReporter.h create mode 100644 stats/DisplayHotplugConnectModeDetectedAtomReporterDesktop.cpp diff --git a/.ci/Makefile b/.ci/Makefile index 4bb2857..da5fe99 100644 --- a/.ci/Makefile +++ b/.ci/Makefile @@ -103,6 +103,7 @@ SKIP_FILES := \ drm/DrmDisplayPipelineTest.cpp \ stats/CompositionStatsTest.cpp \ stats/CompositionStatsAtomReporterDesktop.cpp \ + stats/DisplayHotplugConnectModeDetectedAtomReporterDesktop.cpp \ stats/DisplayRefreshRatesChangedAtomReporterDesktop.cpp \ BUILD_FILES_AUTO := $(shell find -L $(SRC_DIR) -not -path '*/\.*' -not -path '*/tests/test_include/*' -path '*.cpp') diff --git a/Android.bp b/Android.bp index 6e88481..7ff4a57 100644 --- a/Android.bp +++ b/Android.bp @@ -345,6 +345,7 @@ drm_hwcomposer_atom_reporter_library { srcs: [ "compositor/FlatteningEventAtomReporterDesktop.cpp", "stats/CompositionStatsAtomReporterDesktop.cpp", + "stats/DisplayHotplugConnectModeDetectedAtomReporterDesktop.cpp", "stats/DisplayRefreshRatesChangedAtomReporterDesktop.cpp", ], shared_libs: [ @@ -357,6 +358,7 @@ drm_hwcomposer_atom_reporter_library { srcs: [ "compositor/FlatteningEventAtomReporter.cpp", "stats/CompositionStatsAtomReporter.cpp", + "stats/DisplayHotplugConnectModeDetectedAtomReporter.cpp", "stats/DisplayRefreshRatesChangedAtomReporter.cpp", ], }, diff --git a/hwc/HwcDisplay.cpp b/hwc/HwcDisplay.cpp index 8b60a6e..518a20e 100644 --- a/hwc/HwcDisplay.cpp +++ b/hwc/HwcDisplay.cpp @@ -40,6 +40,7 @@ #include "drm/VSyncWorker.h" #include "hwc/HwcLayer.h" #include "stats/CompositionStats.h" +#include "stats/DisplayHotplugConnectModeDetectedAtomReporter.h" #include "utils/EdidWrapper.h" #include "utils/log.h" #include "utils/properties.h" @@ -144,6 +145,9 @@ HwcDisplay::HwcDisplay(DisplayHandle handle, bool is_virtual, DrmHwc *hwc) writeback_layer_ = std::make_unique(this); identity_color_matrix_ = ToColorTransform(kIdentityMatrix); + + display_mode_reporter_ = DisplayHotplugConnectModeDetectedAtomReporter:: + Create(); } void HwcDisplay::SetColorTransformMatrix( @@ -631,6 +635,9 @@ void HwcDisplay::SetPipeline(std::shared_ptr pipeline) { bool success = Init(); ALOGE_IF(!success, "Failed to init HwcDisplay after setting pipeline."); hwc_->ScheduleHotplugEvent(handle_, DrmHwc::kConnected); + if (pipeline_) { + LogModesOnHotplug(); + } } else { hwc_->ScheduleHotplugEvent(handle_, DrmHwc::kDisconnected); } @@ -1381,4 +1388,60 @@ std::pair HwcDisplay::GetSize() const { config->mode.GetRawMode().vdisplay); } +void HwcDisplay::LogModesOnHotplug() { + if (!display_mode_reporter_) { + return; + } + + const HwcDisplay::DisplayType display_type = GetDisplayType(); + if (display_type != HwcDisplay::DisplayType::kInternal && + display_type != HwcDisplay::DisplayType::kExternal) { + return; + } + + using ModeAtom = DisplayHotplugConnectModeDetectedAtomReporter::Atom; + std::vector submitted_atoms; + for (const auto &[id, hwc_mode] : configs_.hwc_configs) { + const DrmMode &mode = hwc_mode.mode; + const drmModeModeInfo &raw_mode = mode.GetRawMode(); + const bool is_preferred = (raw_mode.type & DRM_MODE_TYPE_PREFERRED) != 0; + + constexpr float kMmPerInch = 25.4; + const auto [width_mm, height_mm] = GetDisplayBoundsMm(); + int32_t dpi_x = -1; + if (width_mm > 0) { + dpi_x = static_cast( + lround((static_cast(raw_mode.hdisplay) * kMmPerInch) / + static_cast(width_mm))); + } + int32_t dpi_y = dpi_x; + if (height_mm > 0) { + dpi_y = static_cast( + lround((static_cast(raw_mode.vdisplay) * kMmPerInch) / + static_cast(height_mm))); + } + + using AtomDisplayType = DisplayHotplugConnectModeDetectedAtomReporter:: + DisplayType; + const ModeAtom atom = + {.display_handle = handle_, + .resolution_x = raw_mode.hdisplay, + .resolution_y = raw_mode.vdisplay, + .refresh_rate = static_cast(lround(mode.GetVRefresh())), + .dpi_x = dpi_x, + .dpi_y = dpi_y, + .display_type = display_type == HwcDisplay::DisplayType::kInternal + ? AtomDisplayType::kInternal + : AtomDisplayType::kExternal, + .is_preferred = is_preferred}; + + if (std::find(submitted_atoms.begin(), submitted_atoms.end(), atom) != + submitted_atoms.end()) { + continue; + } + + display_mode_reporter_->PushAtom(atom); + submitted_atoms.push_back(atom); + } +} } // namespace android::drm_hwcomposer diff --git a/hwc/HwcDisplay.h b/hwc/HwcDisplay.h index 4fbcbe2..023d37c 100644 --- a/hwc/HwcDisplay.h +++ b/hwc/HwcDisplay.h @@ -35,6 +35,7 @@ using aidl::android::hardware::graphics::common::Hdr; namespace android::drm_hwcomposer { class ChangedLayer; +class DisplayHotplugConnectModeDetectedAtomReporter; class DrmHwc; class EdidWrapper; class FlatteningController; @@ -299,6 +300,8 @@ class HwcDisplay { return edid_wrapper_; } + void LogModesOnHotplug(); + HwcDisplayConfigs configs_; DrmHwc *const hwc_; @@ -344,6 +347,9 @@ class HwcDisplay { HwcDisplay::HdcpState hdcp_state_ = HdcpState::kUndesired; std::shared_ptr frontend_private_data_; + + std::unique_ptr + display_mode_reporter_; }; } // namespace android::drm_hwcomposer diff --git a/meson.build b/meson.build index a9257e6..e545c8a 100644 --- a/meson.build +++ b/meson.build @@ -24,6 +24,7 @@ src_common = files( 'stats/CompositionStats.cpp', 'stats/CompositionStatsAtomReporter.cpp', 'stats/CompositionStatsPoller.cpp', + 'stats/DisplayHotplugConnectModeDetectedAtomReporter.cpp', 'stats/DisplayRefreshRatesChangedAtomReporter.cpp', 'utils/fd.cpp', 'utils/LibdisplayEdidWrapper.cpp', diff --git a/stats/DisplayHotplugConnectModeDetectedAtomReporter.cpp b/stats/DisplayHotplugConnectModeDetectedAtomReporter.cpp new file mode 100644 index 0000000..53014f9 --- /dev/null +++ b/stats/DisplayHotplugConnectModeDetectedAtomReporter.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2025 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. + */ + +#define LOG_TAG "drmhwc" + +#include "DisplayHotplugConnectModeDetectedAtomReporter.h" + +#include "utils/log.h" + +namespace android::drm_hwcomposer { + +std::unique_ptr +DisplayHotplugConnectModeDetectedAtomReporter::Create() { + ALOGI("Atom reporting is not enabled."); + return {}; +} + +} // namespace android::drm_hwcomposer diff --git a/stats/DisplayHotplugConnectModeDetectedAtomReporter.h b/stats/DisplayHotplugConnectModeDetectedAtomReporter.h new file mode 100644 index 0000000..2560925 --- /dev/null +++ b/stats/DisplayHotplugConnectModeDetectedAtomReporter.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2025 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. + */ + +#pragma once + +#include + +namespace android::drm_hwcomposer { +class DisplayHotplugConnectModeDetectedAtomReporter { + public: + static std::unique_ptr + Create(); + + enum class DisplayType { + kUnspecified = 0, + kInternal, + kExternal, + }; + + struct Atom { + int64_t display_handle = 0; + int32_t resolution_x = 0; + int32_t resolution_y = 0; + int32_t refresh_rate = 0; + int32_t dpi_x = 0; + int32_t dpi_y = 0; + DisplayType display_type = DisplayType::kUnspecified; + bool is_preferred = false; + + bool operator==(const Atom& other) const { + return display_handle == other.display_handle && + resolution_x == other.resolution_x && + resolution_y == other.resolution_y && + refresh_rate == other.refresh_rate && dpi_x == other.dpi_x && + dpi_y == other.dpi_y && display_type == other.display_type && + is_preferred == other.is_preferred; + }; + }; + + // Pushes a Vendor Atom to IStats::reportVendorAtom. + virtual void PushAtom(Atom atom) = 0; + virtual ~DisplayHotplugConnectModeDetectedAtomReporter() = default; +}; + +} // namespace android::drm_hwcomposer diff --git a/stats/DisplayHotplugConnectModeDetectedAtomReporterDesktop.cpp b/stats/DisplayHotplugConnectModeDetectedAtomReporterDesktop.cpp new file mode 100644 index 0000000..b218b37 --- /dev/null +++ b/stats/DisplayHotplugConnectModeDetectedAtomReporterDesktop.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2025 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. + */ + +#define LOG_TAG "drmhwc" +// #define NLOG_DEBUG 0 + +#include "DisplayHotplugConnectModeDetectedAtomReporter.h" + +#include +#include + +#include +#include + +#include "desktopatoms.h" +#include "utils/log.h" + +using aidl::android::frameworks::stats::IStats; +using aidl::android::frameworks::stats::VendorAtom; +namespace DesktopAtoms = android::vendor::google::desktop::stats::DesktopAtoms; + +namespace android::drm_hwcomposer { +namespace { + +DesktopAtoms::DisplayHotplugConnectModeDetected::DisplayType ToProtoEnum( + DisplayHotplugConnectModeDetectedAtomReporter::DisplayType type) { + switch (type) { + case DisplayHotplugConnectModeDetectedAtomReporter::DisplayType:: + kUnspecified: + return DesktopAtoms::DisplayHotplugConnectModeDetected:: + DISPLAY_TYPE_UNSPECIFIED; + case DisplayHotplugConnectModeDetectedAtomReporter::DisplayType::kInternal: + return DesktopAtoms::DisplayHotplugConnectModeDetected:: + DISPLAY_TYPE_INTERNAL; + case DisplayHotplugConnectModeDetectedAtomReporter::DisplayType::kExternal: + return DesktopAtoms::DisplayHotplugConnectModeDetected:: + DISPLAY_TYPE_EXTERNAL; + } +} + +const std::string kStatsServiceName = std::string(IStats::descriptor) + .append("/default"); + +// Use a private implementation of DisplayHotplugConnectModeDetectedAtomReporter +// to avoid leaking the IStats interface through the public api. +class DisplayHotplugConnectModeDetectedAtomReporterDesktop + : public DisplayHotplugConnectModeDetectedAtomReporter { + public: + void PushAtom(Atom atom) override { + ALOGE("zzzz DisplayHotplugConnectModeDetected"); + + // The order of the arguments to createVendorAtom is determined by the + // proto definition in libdesktopatoms. + const char* deprecated_reverse_domain_name = ""; + const VendorAtom vendor_atom = DesktopAtoms:: + createVendorAtom(DesktopAtoms::DISPLAY_HOTPLUG_CONNECT_MODE_DETECTED, + deprecated_reverse_domain_name, atom.display_handle, + atom.resolution_x, atom.resolution_y, + atom.refresh_rate, atom.dpi_x, atom.dpi_y, + ToProtoEnum(atom.display_type), atom.is_preferred); + + auto stats_service = IStats::fromBinder(ndk::SpAIBinder( + AServiceManager_checkService(kStatsServiceName.c_str()))); + ALOGE_IF(stats_service == nullptr, "Failed to get IStats service"); + if (stats_service) { + const ndk::ScopedAStatus ret = stats_service->reportVendorAtom( + vendor_atom); + ALOGE_IF(!ret.isOk(), "Failed to report stats: %s", + ret.getDescription().c_str()); + } + } +}; +} // namespace + +std::unique_ptr +DisplayHotplugConnectModeDetectedAtomReporter::Create() { + if (!AServiceManager_isDeclared(kStatsServiceName.c_str())) { + ALOGW("Stats service is not declared."); + return nullptr; + } + return std::make_unique< + DisplayHotplugConnectModeDetectedAtomReporterDesktop>(); +} + +} // namespace android::drm_hwcomposer