diff options
8 files changed, 156 insertions, 23 deletions
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index aa7f07d24dbf..35991b356bba 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -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(); @@ -220,6 +225,10 @@ public class DisplayManagerFlags { return mFastHdrTransitions.isEnabled(); } + public boolean isRefreshRateVotingTelemetryEnabled() { + return mRefreshRateVotingTelemetry.isEnabled(); + } + /** * dumps all flagstates * @param pw printWriter @@ -242,6 +251,7 @@ public class DisplayManagerFlags { pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState); pw.println(" " + mAutoBrightnessModesFlagState); pw.println(" " + mFastHdrTransitions); + pw.println(" " + mRefreshRateVotingTelemetry); } private static class FlagState { diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index 04ecbb9e08ad..e735282a496e 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -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 +} diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index 6e503cbd68f3..e9fc9d1e8a46 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -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. */ @@ -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(); @@ -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); @@ -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 @@ -2811,6 +2822,9 @@ public class DisplayModeDirector { StatusBarManagerInternal getStatusBarManagerInternal(); SensorManagerInternal getSensorManagerInternal(); + + @Nullable + VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled); } @VisibleForTesting @@ -2943,6 +2957,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); diff --git a/services/core/java/com/android/server/display/mode/VotesStatsReporter.java b/services/core/java/com/android/server/display/mode/VotesStatsReporter.java new file mode 100644 index 000000000000..a30c4d2b5b0b --- /dev/null +++ b/services/core/java/com/android/server/display/mode/VotesStatsReporter.java @@ -0,0 +1,93 @@ +/* + * 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; + } +} diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java index 95fb8fc0947a..7a1f7e9d857c 100644 --- a/services/core/java/com/android/server/display/mode/VotesStorage.java +++ b/services/core/java/com/android/server/display/mode/VotesStorage.java @@ -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; @@ -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 @@ -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) { @@ -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<>(); @@ -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(); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index 60a0c039dd03..a0e5fd8e1b34 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -3434,6 +3434,11 @@ public class DisplayModeDirectorTest { return mSensorManagerInternal; } + @Override + public VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled) { + return null; + } + protected Display createDisplay(int id) { return new Display(DisplayManagerGlobal.getInstance(), id, mDisplayInfo, ApplicationProvider.getApplicationContext().getResources()); diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java index f6774017c523..2815fcb764b6 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java @@ -58,7 +58,7 @@ public class SkinThermalStatusObserverTest { private RegisteringFakesInjector mInjector = new RegisteringFakesInjector(); private final TestHandler mHandler = new TestHandler(null); - private final VotesStorage mStorage = new VotesStorage(() -> {}); + private final VotesStorage mStorage = new VotesStorage(() -> {}, null); @Before public void setUp() { diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java index 50e239218fa0..1f6f1a41bea7 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java @@ -51,7 +51,7 @@ public class VotesStorageTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mVotesStorage = new VotesStorage(mVotesListener); + mVotesStorage = new VotesStorage(mVotesListener, null); } @Test |