Loading services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +10 −0 Original line number Diff line number Diff line Loading @@ -109,6 +109,11 @@ public class DisplayManagerFlags { Flags.FLAG_FAST_HDR_TRANSITIONS, Flags::fastHdrTransitions); private final FlagState mRefreshRateVotingTelemetry = new FlagState( Flags.FLAG_REFRESH_RATE_VOTING_TELEMETRY, Flags::refreshRateVotingTelemetry ); /** Returns whether connected display management is enabled or not. */ public boolean isConnectedDisplayManagementEnabled() { return mConnectedDisplayManagementFlagState.isEnabled(); Loading Loading @@ -220,6 +225,10 @@ public class DisplayManagerFlags { return mFastHdrTransitions.isEnabled(); } public boolean isRefreshRateVotingTelemetryEnabled() { return mRefreshRateVotingTelemetry.isEnabled(); } /** * dumps all flagstates * @param pw printWriter Loading @@ -242,6 +251,7 @@ public class DisplayManagerFlags { pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState); pw.println(" " + mAutoBrightnessModesFlagState); pw.println(" " + mFastHdrTransitions); pw.println(" " + mRefreshRateVotingTelemetry); } private static class FlagState { Loading services/core/java/com/android/server/display/feature/display_flags.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -161,3 +161,10 @@ flag { is_fixed_read_only: true } flag { name: "refresh_rate_voting_telemetry" namespace: "display_manager" description: "Feature flag for enabling telemetry for refresh rate voting in DisplayManager" bug: "310029108" is_fixed_read_only: true } services/core/java/com/android/server/display/mode/DisplayModeDirector.java +22 −1 Original line number Diff line number Diff line Loading @@ -154,6 +154,9 @@ public class DisplayModeDirector { private final VotesStorage mVotesStorage; @Nullable private final VotesStatsReporter mVotesStatsReporter; /** * The allowed refresh rate switching type. This is used by SurfaceFlinger. */ Loading Loading @@ -204,6 +207,8 @@ public class DisplayModeDirector { mContext = context; mHandler = new DisplayModeDirectorHandler(handler.getLooper()); mInjector = injector; mVotesStatsReporter = injector.getVotesStatsReporter( displayManagerFlags.isRefreshRateVotingTelemetryEnabled()); mSupportedModesByDisplay = new SparseArray<>(); mDefaultModeByDisplay = new SparseArray<>(); mAppRequestObserver = new AppRequestObserver(); Loading @@ -214,7 +219,8 @@ public class DisplayModeDirector { mBrightnessObserver = new BrightnessObserver(context, handler, injector); mDefaultDisplayDeviceConfig = null; mUdfpsObserver = new UdfpsObserver(); mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked); mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked, mVotesStatsReporter); mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage); mSensorObserver = new SensorObserver(context, mVotesStorage, injector); mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, mVotesStorage); Loading Loading @@ -341,6 +347,11 @@ public class DisplayModeDirector { appRequestSummary.limitRefreshRanges(primarySummary); Display.Mode baseMode = primarySummary.selectBaseMode(availableModes, defaultMode); if (mVotesStatsReporter != null) { mVotesStatsReporter.reportVotesActivated(displayId, lowestConsideredPriority, baseMode, votes); } if (baseMode == null) { Slog.w(TAG, "Can't find a set of allowed modes which satisfies the votes. Falling" + " back to the default mode. Display = " + displayId + ", votes = " + votes Loading Loading @@ -2821,6 +2832,9 @@ public class DisplayModeDirector { StatusBarManagerInternal getStatusBarManagerInternal(); SensorManagerInternal getSensorManagerInternal(); @Nullable VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled); } @VisibleForTesting Loading Loading @@ -2953,6 +2967,13 @@ public class DisplayModeDirector { return LocalServices.getService(SensorManagerInternal.class); } @Override public VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled) { // if frame rate override supported, renderRates will be ignored in mode selection return new VotesStatsReporter(supportsFrameRateOverride(), refreshRateVotingTelemetryEnabled); } private DisplayManager getDisplayManager() { if (mDisplayManager == null) { mDisplayManager = mContext.getSystemService(DisplayManager.class); Loading services/core/java/com/android/server/display/mode/VotesStatsReporter.java 0 → 100644 +93 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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. */ package com.android.server.display.mode; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Trace; import android.util.SparseArray; import android.view.Display; /** * The VotesStatsReporter is responsible for collecting and sending Vote related statistics */ class VotesStatsReporter { private static final String TAG = "VotesStatsReporter"; private static final int REFRESH_RATE_NOT_LIMITED = 1000; private final boolean mIgnoredRenderRate; private final boolean mFrameworkStatsLogReportingEnabled; public VotesStatsReporter(boolean ignoreRenderRate, boolean refreshRateVotingTelemetryEnabled) { mIgnoredRenderRate = ignoreRenderRate; mFrameworkStatsLogReportingEnabled = refreshRateVotingTelemetryEnabled; } void reportVoteAdded(int displayId, int priority, @NonNull Vote vote) { int maxRefreshRate = getMaxRefreshRate(vote, mIgnoredRenderRate); Trace.traceCounter(Trace.TRACE_TAG_POWER, TAG + "." + displayId + ":" + Vote.priorityToString(priority), maxRefreshRate); // if ( mFrameworkStatsLogReportingEnabled) { // FrameworkStatsLog.write(VOTE_CHANGED, displayID, priority, ADDED, maxRefreshRate, -1); // } } void reportVoteRemoved(int displayId, int priority) { Trace.traceCounter(Trace.TRACE_TAG_POWER, TAG + "." + displayId + ":" + Vote.priorityToString(priority), -1); // if ( mFrameworkStatsLogReportingEnabled) { // FrameworkStatsLog.write(VOTE_CHANGED, displayID, priority, REMOVED, -1, -1); // } } void reportVotesActivated(int displayId, int minPriority, @Nullable Display.Mode baseMode, SparseArray<Vote> votes) { // if (!mFrameworkStatsLogReportingEnabled) { // return; // } // int selectedRefreshRate = baseMode != null ? (int) baseMode.getRefreshRate() : -1; // for (int priority = minPriority; priority <= Vote.MAX_PRIORITY; priority ++) { // Vote vote = votes.get(priority); // if (vote != null) { // int maxRefreshRate = getMaxRefreshRate(vote, mIgnoredRenderRate); // FrameworkStatsLog.write(VOTE_CHANGED, displayId, priority, // ACTIVE, maxRefreshRate, selectedRefreshRate); // } // } } private static int getMaxRefreshRate(@NonNull Vote vote, boolean ignoreRenderRate) { int maxRefreshRate = REFRESH_RATE_NOT_LIMITED; if (vote instanceof RefreshRateVote.PhysicalVote physicalVote) { maxRefreshRate = (int) physicalVote.mMaxRefreshRate; } else if (!ignoreRenderRate && (vote instanceof RefreshRateVote.RenderVote renderVote)) { maxRefreshRate = (int) renderVote.mMaxRefreshRate; } else if (vote instanceof SupportedModesVote supportedModesVote) { // SupportedModesVote limits mode by specific refreshRates, so highest rr is allowed maxRefreshRate = 0; for (SupportedModesVote.SupportedMode mode : supportedModesVote.mSupportedModes) { maxRefreshRate = Math.max(maxRefreshRate, (int) mode.mPeakRefreshRate); } } else if (vote instanceof CombinedVote combinedVote) { for (Vote subVote: combinedVote.mVotes) { // CombinedVote should not have CombinedVote in mVotes, so recursion depth will be 1 maxRefreshRate = Math.min(maxRefreshRate, getMaxRefreshRate(subVote, ignoreRenderRate)); } } return maxRefreshRate; } } services/core/java/com/android/server/display/mode/VotesStorage.java +17 −20 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.server.display.mode; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Trace; import android.util.Slog; import android.util.SparseArray; Loading @@ -38,6 +37,9 @@ class VotesStorage { private final Listener mListener; @Nullable private final VotesStatsReporter mVotesStatsReporter; private final Object mStorageLock = new Object(); // A map from the display ID to the collection of votes and their priority. The latter takes // the form of another map from the priority to the vote itself so that each priority is Loading @@ -45,8 +47,9 @@ class VotesStorage { @GuardedBy("mStorageLock") private final SparseArray<SparseArray<Vote>> mVotesByDisplay = new SparseArray<>(); VotesStorage(@NonNull Listener listener) { VotesStorage(@NonNull Listener listener, @Nullable VotesStatsReporter votesStatsReporter) { mListener = listener; mVotesStatsReporter = votesStatsReporter; } /** sets logging enabled/disabled for this class */ void setLoggingEnabled(boolean loggingEnabled) { Loading Loading @@ -110,17 +113,26 @@ class VotesStorage { changed = true; } } Trace.traceCounter(Trace.TRACE_TAG_POWER, TAG + "." + displayId + ":" + Vote.priorityToString(priority), getMaxPhysicalRefreshRate(vote)); if (mLoggingEnabled) { Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes); } if (changed) { reportVoteStats(displayId, priority, vote); mListener.onChanged(); } } private void reportVoteStats(int displayId, int priority, @Nullable Vote vote) { if (mVotesStatsReporter == null) { return; } if (vote == null) { mVotesStatsReporter.reportVoteRemoved(displayId, priority); } else { mVotesStatsReporter.reportVoteAdded(displayId, priority, vote); } } /** dump class values, for debugging */ void dump(@NonNull PrintWriter pw) { SparseArray<SparseArray<Vote>> votesByDisplayLocal = new SparseArray<>(); Loading Loading @@ -157,21 +169,6 @@ class VotesStorage { } } private static int getMaxPhysicalRefreshRate(@Nullable Vote vote) { if (vote == null) { return -1; } else if (vote instanceof RefreshRateVote.PhysicalVote physicalVote) { return (int) physicalVote.mMaxRefreshRate; } else if (vote instanceof CombinedVote combinedVote) { return combinedVote.mVotes.stream() .filter(v -> v instanceof RefreshRateVote.PhysicalVote) .map(pv -> (int) (((RefreshRateVote.PhysicalVote) pv).mMaxRefreshRate)) .min(Integer::compare) .orElse(1000); // for visualisation } return 1000; // for visualisation, otherwise e.g. -1 -> 60 will be unnoticeable } interface Listener { void onChanged(); } Loading Loading
services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +10 −0 Original line number Diff line number Diff line Loading @@ -109,6 +109,11 @@ public class DisplayManagerFlags { Flags.FLAG_FAST_HDR_TRANSITIONS, Flags::fastHdrTransitions); private final FlagState mRefreshRateVotingTelemetry = new FlagState( Flags.FLAG_REFRESH_RATE_VOTING_TELEMETRY, Flags::refreshRateVotingTelemetry ); /** Returns whether connected display management is enabled or not. */ public boolean isConnectedDisplayManagementEnabled() { return mConnectedDisplayManagementFlagState.isEnabled(); Loading Loading @@ -220,6 +225,10 @@ public class DisplayManagerFlags { return mFastHdrTransitions.isEnabled(); } public boolean isRefreshRateVotingTelemetryEnabled() { return mRefreshRateVotingTelemetry.isEnabled(); } /** * dumps all flagstates * @param pw printWriter Loading @@ -242,6 +251,7 @@ public class DisplayManagerFlags { pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState); pw.println(" " + mAutoBrightnessModesFlagState); pw.println(" " + mFastHdrTransitions); pw.println(" " + mRefreshRateVotingTelemetry); } private static class FlagState { Loading
services/core/java/com/android/server/display/feature/display_flags.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -161,3 +161,10 @@ flag { is_fixed_read_only: true } flag { name: "refresh_rate_voting_telemetry" namespace: "display_manager" description: "Feature flag for enabling telemetry for refresh rate voting in DisplayManager" bug: "310029108" is_fixed_read_only: true }
services/core/java/com/android/server/display/mode/DisplayModeDirector.java +22 −1 Original line number Diff line number Diff line Loading @@ -154,6 +154,9 @@ public class DisplayModeDirector { private final VotesStorage mVotesStorage; @Nullable private final VotesStatsReporter mVotesStatsReporter; /** * The allowed refresh rate switching type. This is used by SurfaceFlinger. */ Loading Loading @@ -204,6 +207,8 @@ public class DisplayModeDirector { mContext = context; mHandler = new DisplayModeDirectorHandler(handler.getLooper()); mInjector = injector; mVotesStatsReporter = injector.getVotesStatsReporter( displayManagerFlags.isRefreshRateVotingTelemetryEnabled()); mSupportedModesByDisplay = new SparseArray<>(); mDefaultModeByDisplay = new SparseArray<>(); mAppRequestObserver = new AppRequestObserver(); Loading @@ -214,7 +219,8 @@ public class DisplayModeDirector { mBrightnessObserver = new BrightnessObserver(context, handler, injector); mDefaultDisplayDeviceConfig = null; mUdfpsObserver = new UdfpsObserver(); mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked); mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked, mVotesStatsReporter); mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage); mSensorObserver = new SensorObserver(context, mVotesStorage, injector); mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, mVotesStorage); Loading Loading @@ -341,6 +347,11 @@ public class DisplayModeDirector { appRequestSummary.limitRefreshRanges(primarySummary); Display.Mode baseMode = primarySummary.selectBaseMode(availableModes, defaultMode); if (mVotesStatsReporter != null) { mVotesStatsReporter.reportVotesActivated(displayId, lowestConsideredPriority, baseMode, votes); } if (baseMode == null) { Slog.w(TAG, "Can't find a set of allowed modes which satisfies the votes. Falling" + " back to the default mode. Display = " + displayId + ", votes = " + votes Loading Loading @@ -2821,6 +2832,9 @@ public class DisplayModeDirector { StatusBarManagerInternal getStatusBarManagerInternal(); SensorManagerInternal getSensorManagerInternal(); @Nullable VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled); } @VisibleForTesting Loading Loading @@ -2953,6 +2967,13 @@ public class DisplayModeDirector { return LocalServices.getService(SensorManagerInternal.class); } @Override public VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled) { // if frame rate override supported, renderRates will be ignored in mode selection return new VotesStatsReporter(supportsFrameRateOverride(), refreshRateVotingTelemetryEnabled); } private DisplayManager getDisplayManager() { if (mDisplayManager == null) { mDisplayManager = mContext.getSystemService(DisplayManager.class); Loading
services/core/java/com/android/server/display/mode/VotesStatsReporter.java 0 → 100644 +93 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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. */ package com.android.server.display.mode; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Trace; import android.util.SparseArray; import android.view.Display; /** * The VotesStatsReporter is responsible for collecting and sending Vote related statistics */ class VotesStatsReporter { private static final String TAG = "VotesStatsReporter"; private static final int REFRESH_RATE_NOT_LIMITED = 1000; private final boolean mIgnoredRenderRate; private final boolean mFrameworkStatsLogReportingEnabled; public VotesStatsReporter(boolean ignoreRenderRate, boolean refreshRateVotingTelemetryEnabled) { mIgnoredRenderRate = ignoreRenderRate; mFrameworkStatsLogReportingEnabled = refreshRateVotingTelemetryEnabled; } void reportVoteAdded(int displayId, int priority, @NonNull Vote vote) { int maxRefreshRate = getMaxRefreshRate(vote, mIgnoredRenderRate); Trace.traceCounter(Trace.TRACE_TAG_POWER, TAG + "." + displayId + ":" + Vote.priorityToString(priority), maxRefreshRate); // if ( mFrameworkStatsLogReportingEnabled) { // FrameworkStatsLog.write(VOTE_CHANGED, displayID, priority, ADDED, maxRefreshRate, -1); // } } void reportVoteRemoved(int displayId, int priority) { Trace.traceCounter(Trace.TRACE_TAG_POWER, TAG + "." + displayId + ":" + Vote.priorityToString(priority), -1); // if ( mFrameworkStatsLogReportingEnabled) { // FrameworkStatsLog.write(VOTE_CHANGED, displayID, priority, REMOVED, -1, -1); // } } void reportVotesActivated(int displayId, int minPriority, @Nullable Display.Mode baseMode, SparseArray<Vote> votes) { // if (!mFrameworkStatsLogReportingEnabled) { // return; // } // int selectedRefreshRate = baseMode != null ? (int) baseMode.getRefreshRate() : -1; // for (int priority = minPriority; priority <= Vote.MAX_PRIORITY; priority ++) { // Vote vote = votes.get(priority); // if (vote != null) { // int maxRefreshRate = getMaxRefreshRate(vote, mIgnoredRenderRate); // FrameworkStatsLog.write(VOTE_CHANGED, displayId, priority, // ACTIVE, maxRefreshRate, selectedRefreshRate); // } // } } private static int getMaxRefreshRate(@NonNull Vote vote, boolean ignoreRenderRate) { int maxRefreshRate = REFRESH_RATE_NOT_LIMITED; if (vote instanceof RefreshRateVote.PhysicalVote physicalVote) { maxRefreshRate = (int) physicalVote.mMaxRefreshRate; } else if (!ignoreRenderRate && (vote instanceof RefreshRateVote.RenderVote renderVote)) { maxRefreshRate = (int) renderVote.mMaxRefreshRate; } else if (vote instanceof SupportedModesVote supportedModesVote) { // SupportedModesVote limits mode by specific refreshRates, so highest rr is allowed maxRefreshRate = 0; for (SupportedModesVote.SupportedMode mode : supportedModesVote.mSupportedModes) { maxRefreshRate = Math.max(maxRefreshRate, (int) mode.mPeakRefreshRate); } } else if (vote instanceof CombinedVote combinedVote) { for (Vote subVote: combinedVote.mVotes) { // CombinedVote should not have CombinedVote in mVotes, so recursion depth will be 1 maxRefreshRate = Math.min(maxRefreshRate, getMaxRefreshRate(subVote, ignoreRenderRate)); } } return maxRefreshRate; } }
services/core/java/com/android/server/display/mode/VotesStorage.java +17 −20 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.server.display.mode; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Trace; import android.util.Slog; import android.util.SparseArray; Loading @@ -38,6 +37,9 @@ class VotesStorage { private final Listener mListener; @Nullable private final VotesStatsReporter mVotesStatsReporter; private final Object mStorageLock = new Object(); // A map from the display ID to the collection of votes and their priority. The latter takes // the form of another map from the priority to the vote itself so that each priority is Loading @@ -45,8 +47,9 @@ class VotesStorage { @GuardedBy("mStorageLock") private final SparseArray<SparseArray<Vote>> mVotesByDisplay = new SparseArray<>(); VotesStorage(@NonNull Listener listener) { VotesStorage(@NonNull Listener listener, @Nullable VotesStatsReporter votesStatsReporter) { mListener = listener; mVotesStatsReporter = votesStatsReporter; } /** sets logging enabled/disabled for this class */ void setLoggingEnabled(boolean loggingEnabled) { Loading Loading @@ -110,17 +113,26 @@ class VotesStorage { changed = true; } } Trace.traceCounter(Trace.TRACE_TAG_POWER, TAG + "." + displayId + ":" + Vote.priorityToString(priority), getMaxPhysicalRefreshRate(vote)); if (mLoggingEnabled) { Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes); } if (changed) { reportVoteStats(displayId, priority, vote); mListener.onChanged(); } } private void reportVoteStats(int displayId, int priority, @Nullable Vote vote) { if (mVotesStatsReporter == null) { return; } if (vote == null) { mVotesStatsReporter.reportVoteRemoved(displayId, priority); } else { mVotesStatsReporter.reportVoteAdded(displayId, priority, vote); } } /** dump class values, for debugging */ void dump(@NonNull PrintWriter pw) { SparseArray<SparseArray<Vote>> votesByDisplayLocal = new SparseArray<>(); Loading Loading @@ -157,21 +169,6 @@ class VotesStorage { } } private static int getMaxPhysicalRefreshRate(@Nullable Vote vote) { if (vote == null) { return -1; } else if (vote instanceof RefreshRateVote.PhysicalVote physicalVote) { return (int) physicalVote.mMaxRefreshRate; } else if (vote instanceof CombinedVote combinedVote) { return combinedVote.mVotes.stream() .filter(v -> v instanceof RefreshRateVote.PhysicalVote) .map(pv -> (int) (((RefreshRateVote.PhysicalVote) pv).mMaxRefreshRate)) .min(Integer::compare) .orElse(1000); // for visualisation } return 1000; // for visualisation, otherwise e.g. -1 -> 60 will be unnoticeable } interface Listener { void onChanged(); } Loading