• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 package com.android.server.display;
18 
19 import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.res.Resources;
26 import android.database.ContentObserver;
27 import android.hardware.Sensor;
28 import android.hardware.SensorEvent;
29 import android.hardware.SensorEventListener;
30 import android.hardware.SensorManager;
31 import android.hardware.display.BrightnessInfo;
32 import android.hardware.display.DisplayManager;
33 import android.hardware.display.DisplayManagerInternal;
34 import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
35 import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
36 import android.hardware.fingerprint.IUdfpsHbmListener;
37 import android.net.Uri;
38 import android.os.Handler;
39 import android.os.Looper;
40 import android.os.Message;
41 import android.os.SystemClock;
42 import android.os.UserHandle;
43 import android.provider.DeviceConfig;
44 import android.provider.Settings;
45 import android.text.TextUtils;
46 import android.util.IndentingPrintWriter;
47 import android.util.Pair;
48 import android.util.Slog;
49 import android.util.SparseArray;
50 import android.util.SparseBooleanArray;
51 import android.view.Display;
52 import android.view.DisplayInfo;
53 
54 import com.android.internal.R;
55 import com.android.internal.annotations.GuardedBy;
56 import com.android.internal.annotations.VisibleForTesting;
57 import com.android.internal.os.BackgroundThread;
58 import com.android.server.LocalServices;
59 import com.android.server.display.utils.AmbientFilter;
60 import com.android.server.display.utils.AmbientFilterFactory;
61 import com.android.server.sensors.SensorManagerInternal;
62 import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
63 import com.android.server.statusbar.StatusBarManagerInternal;
64 import com.android.server.utils.DeviceConfigInterface;
65 
66 import java.io.PrintWriter;
67 import java.text.SimpleDateFormat;
68 import java.util.ArrayList;
69 import java.util.Arrays;
70 import java.util.Date;
71 import java.util.List;
72 import java.util.Locale;
73 import java.util.Objects;
74 
75 
76 /**
77  * The DisplayModeDirector is responsible for determining what modes are allowed to be automatically
78  * picked by the system based on system-wide and display-specific configuration.
79  */
80 public class DisplayModeDirector {
81     private static final String TAG = "DisplayModeDirector";
82     private boolean mLoggingEnabled;
83 
84     private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1;
85     private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
86     private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
87     private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
88     private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5;
89     private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6;
90 
91     // Special ID used to indicate that given vote is to be applied globally, rather than to a
92     // specific display.
93     private static final int GLOBAL_ID = -1;
94 
95     private static final int INVALID_DISPLAY_MODE_ID = -1;
96 
97     private static final float FLOAT_TOLERANCE = RefreshRateRange.FLOAT_TOLERANCE;
98 
99     private final Object mLock = new Object();
100     private final Context mContext;
101 
102     private final DisplayModeDirectorHandler mHandler;
103     private final Injector mInjector;
104 
105     private final AppRequestObserver mAppRequestObserver;
106     private final SettingsObserver mSettingsObserver;
107     private final DisplayObserver mDisplayObserver;
108     private final UdfpsObserver mUdfpsObserver;
109     private final SensorObserver mSensorObserver;
110     private final HbmObserver mHbmObserver;
111     private final DeviceConfigInterface mDeviceConfig;
112     private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
113 
114     // A map from the display ID to the collection of votes and their priority. The latter takes
115     // the form of another map from the priority to the vote itself so that each priority is
116     // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
117     private SparseArray<SparseArray<Vote>> mVotesByDisplay;
118     // A map from the display ID to the supported modes on that display.
119     private SparseArray<Display.Mode[]> mSupportedModesByDisplay;
120     // A map from the display ID to the default mode of that display.
121     private SparseArray<Display.Mode> mDefaultModeByDisplay;
122 
123     private BrightnessObserver mBrightnessObserver;
124 
125     private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
126 
127     private boolean mAlwaysRespectAppRequest;
128 
129     /**
130      * The allowed refresh rate switching type. This is used by SurfaceFlinger.
131      */
132     @DisplayManager.SwitchingType
133     private int mModeSwitchingType = DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
134 
DisplayModeDirector(@onNull Context context, @NonNull Handler handler)135     public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
136         this(context, handler, new RealInjector(context));
137     }
138 
DisplayModeDirector(@onNull Context context, @NonNull Handler handler, @NonNull Injector injector)139     public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
140             @NonNull Injector injector) {
141         mContext = context;
142         mHandler = new DisplayModeDirectorHandler(handler.getLooper());
143         mInjector = injector;
144         mVotesByDisplay = new SparseArray<>();
145         mSupportedModesByDisplay = new SparseArray<>();
146         mDefaultModeByDisplay = new SparseArray<>();
147         mAppRequestObserver = new AppRequestObserver();
148         mSettingsObserver = new SettingsObserver(context, handler);
149         mDisplayObserver = new DisplayObserver(context, handler);
150         mBrightnessObserver = new BrightnessObserver(context, handler);
151         mUdfpsObserver = new UdfpsObserver();
152         final BallotBox ballotBox = (displayId, priority, vote) -> {
153             synchronized (mLock) {
154                 updateVoteLocked(displayId, priority, vote);
155             }
156         };
157         mSensorObserver = new SensorObserver(context, ballotBox, injector);
158         mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler());
159         mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
160         mDeviceConfig = injector.getDeviceConfig();
161         mAlwaysRespectAppRequest = false;
162     }
163 
164     /**
165      * Tells the DisplayModeDirector to update allowed votes and begin observing relevant system
166      * state.
167      *
168      * This has to be deferred because the object may be constructed before the rest of the system
169      * is ready.
170      */
start(SensorManager sensorManager)171     public void start(SensorManager sensorManager) {
172         mSettingsObserver.observe();
173         mDisplayObserver.observe();
174         mBrightnessObserver.observe(sensorManager);
175         mSensorObserver.observe();
176         mHbmObserver.observe();
177         synchronized (mLock) {
178             // We may have a listener already registered before the call to start, so go ahead and
179             // notify them to pick up our newly initialized state.
180             notifyDesiredDisplayModeSpecsChangedLocked();
181         }
182     }
183 
184     /**
185      * Same as {@link #start(SensorManager)}, but for observers that need to be delayed even more,
186      * for example until SystemUI is ready.
187      */
onBootCompleted()188     public void onBootCompleted() {
189         // UDFPS observer registers a listener with SystemUI which might not be ready until the
190         // system is fully booted.
191         mUdfpsObserver.observe();
192     }
193 
setLoggingEnabled(boolean loggingEnabled)194     public void setLoggingEnabled(boolean loggingEnabled) {
195         if (mLoggingEnabled == loggingEnabled) {
196             return;
197         }
198         mLoggingEnabled = loggingEnabled;
199         mBrightnessObserver.setLoggingEnabled(loggingEnabled);
200     }
201 
202     @NonNull
getVotesLocked(int displayId)203     private SparseArray<Vote> getVotesLocked(int displayId) {
204         SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
205         final SparseArray<Vote> votes;
206         if (displayVotes != null) {
207             votes = displayVotes.clone();
208         } else {
209             votes = new SparseArray<>();
210         }
211 
212         SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
213         if (globalVotes != null) {
214             for (int i = 0; i < globalVotes.size(); i++) {
215                 int priority = globalVotes.keyAt(i);
216                 if (votes.indexOfKey(priority) < 0) {
217                     votes.put(priority, globalVotes.valueAt(i));
218                 }
219             }
220         }
221         return votes;
222     }
223 
224     private static final class VoteSummary {
225         public float minRefreshRate;
226         public float maxRefreshRate;
227         public int width;
228         public int height;
229         public boolean disableRefreshRateSwitching;
230         public float baseModeRefreshRate;
231 
VoteSummary()232         VoteSummary() {
233             reset();
234         }
235 
reset()236         public void reset() {
237             minRefreshRate = 0f;
238             maxRefreshRate = Float.POSITIVE_INFINITY;
239             width = Vote.INVALID_SIZE;
240             height = Vote.INVALID_SIZE;
241             disableRefreshRateSwitching = false;
242             baseModeRefreshRate = 0f;
243         }
244     }
245 
246     // VoteSummary is returned as an output param to cut down a bit on the number of temporary
247     // objects.
summarizeVotes( SparseArray<Vote> votes, int lowestConsideredPriority, int highestConsideredPriority, VoteSummary summary)248     private void summarizeVotes(
249             SparseArray<Vote> votes,
250             int lowestConsideredPriority,
251             int highestConsideredPriority,
252             /*out*/ VoteSummary summary) {
253         summary.reset();
254         for (int priority = highestConsideredPriority;
255                 priority >= lowestConsideredPriority;
256                 priority--) {
257             Vote vote = votes.get(priority);
258             if (vote == null) {
259                 continue;
260             }
261             // For refresh rates, just use the tightest bounds of all the votes
262             summary.minRefreshRate = Math.max(summary.minRefreshRate, vote.refreshRateRange.min);
263             summary.maxRefreshRate = Math.min(summary.maxRefreshRate, vote.refreshRateRange.max);
264             // For display size, disable refresh rate switching and base mode refresh rate use only
265             // the first vote we come across (i.e. the highest priority vote that includes the
266             // attribute).
267             if (summary.height == Vote.INVALID_SIZE && summary.width == Vote.INVALID_SIZE
268                     && vote.height > 0 && vote.width > 0) {
269                 summary.width = vote.width;
270                 summary.height = vote.height;
271             }
272             if (!summary.disableRefreshRateSwitching && vote.disableRefreshRateSwitching) {
273                 summary.disableRefreshRateSwitching = true;
274             }
275             if (summary.baseModeRefreshRate == 0f && vote.baseModeRefreshRate > 0f) {
276                 summary.baseModeRefreshRate = vote.baseModeRefreshRate;
277             }
278         }
279     }
280 
281     /**
282      * Calculates the refresh rate ranges and display modes that the system is allowed to freely
283      * switch between based on global and display-specific constraints.
284      *
285      * @param displayId The display to query for.
286      * @return The ID of the default mode the system should use, and the refresh rate range the
287      * system is allowed to switch between.
288      */
289     @NonNull
getDesiredDisplayModeSpecs(int displayId)290     public DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(int displayId) {
291         synchronized (mLock) {
292             SparseArray<Vote> votes = getVotesLocked(displayId);
293             Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
294             Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
295             if (modes == null || defaultMode == null) {
296                 Slog.e(TAG,
297                         "Asked about unknown display, returning empty display mode specs!"
298                                 + "(id=" + displayId + ")");
299                 return new DesiredDisplayModeSpecs();
300             }
301 
302             ArrayList<Display.Mode> availableModes = new ArrayList<>();
303             availableModes.add(defaultMode);
304             VoteSummary primarySummary = new VoteSummary();
305             int lowestConsideredPriority = Vote.MIN_PRIORITY;
306             int highestConsideredPriority = Vote.MAX_PRIORITY;
307 
308             if (mAlwaysRespectAppRequest) {
309                 lowestConsideredPriority = Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE;
310                 highestConsideredPriority = Vote.PRIORITY_APP_REQUEST_SIZE;
311             }
312 
313             // We try to find a range of priorities which define a non-empty set of allowed display
314             // modes. Each time we fail we increase the lowest priority.
315             while (lowestConsideredPriority <= highestConsideredPriority) {
316                 summarizeVotes(
317                         votes, lowestConsideredPriority, highestConsideredPriority, primarySummary);
318 
319                 // If we don't have anything specifying the width / height of the display, just use
320                 // the default width and height. We don't want these switching out from underneath
321                 // us since it's a pretty disruptive behavior.
322                 if (primarySummary.height == Vote.INVALID_SIZE
323                         || primarySummary.width == Vote.INVALID_SIZE) {
324                     primarySummary.width = defaultMode.getPhysicalWidth();
325                     primarySummary.height = defaultMode.getPhysicalHeight();
326                 }
327 
328                 availableModes = filterModes(modes, primarySummary);
329                 if (!availableModes.isEmpty()) {
330                     if (mLoggingEnabled) {
331                         Slog.w(TAG, "Found available modes=" + availableModes
332                                 + " with lowest priority considered "
333                                 + Vote.priorityToString(lowestConsideredPriority)
334                                 + " and constraints: "
335                                 + "width=" + primarySummary.width
336                                 + ", height=" + primarySummary.height
337                                 + ", minRefreshRate=" + primarySummary.minRefreshRate
338                                 + ", maxRefreshRate=" + primarySummary.maxRefreshRate
339                                 + ", disableRefreshRateSwitching="
340                                 + primarySummary.disableRefreshRateSwitching
341                                 + ", baseModeRefreshRate=" + primarySummary.baseModeRefreshRate);
342                     }
343                     break;
344                 }
345 
346                 if (mLoggingEnabled) {
347                     Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
348                             + Vote.priorityToString(lowestConsideredPriority)
349                             + " and with the following constraints: "
350                             + "width=" + primarySummary.width
351                             + ", height=" + primarySummary.height
352                             + ", minRefreshRate=" + primarySummary.minRefreshRate
353                             + ", maxRefreshRate=" + primarySummary.maxRefreshRate
354                             + ", disableRefreshRateSwitching="
355                             + primarySummary.disableRefreshRateSwitching
356                             + ", baseModeRefreshRate=" + primarySummary.baseModeRefreshRate);
357                 }
358 
359                 // If we haven't found anything with the current set of votes, drop the
360                 // current lowest priority vote.
361                 lowestConsideredPriority++;
362             }
363 
364             VoteSummary appRequestSummary = new VoteSummary();
365             summarizeVotes(
366                     votes,
367                     Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF,
368                     Vote.MAX_PRIORITY,
369                     appRequestSummary);
370             appRequestSummary.minRefreshRate =
371                     Math.min(appRequestSummary.minRefreshRate, primarySummary.minRefreshRate);
372             appRequestSummary.maxRefreshRate =
373                     Math.max(appRequestSummary.maxRefreshRate, primarySummary.maxRefreshRate);
374             if (mLoggingEnabled) {
375                 Slog.i(TAG,
376                         String.format("App request range: [%.0f %.0f]",
377                                 appRequestSummary.minRefreshRate,
378                                 appRequestSummary.maxRefreshRate));
379             }
380 
381             // Select the base mode id based on the base mode refresh rate, if available, since this
382             // will be the mode id the app voted for.
383             Display.Mode baseMode = null;
384             for (Display.Mode availableMode : availableModes) {
385                 if (primarySummary.baseModeRefreshRate
386                         >= availableMode.getRefreshRate() - FLOAT_TOLERANCE
387                         && primarySummary.baseModeRefreshRate
388                         <= availableMode.getRefreshRate() + FLOAT_TOLERANCE) {
389                     baseMode = availableMode;
390                 }
391             }
392 
393             // Select the default mode if available. This is important because SurfaceFlinger
394             // can do only seamless switches by default. Some devices (e.g. TV) don't support
395             // seamless switching so the mode we select here won't be changed.
396             if (baseMode == null) {
397                 for (Display.Mode availableMode : availableModes) {
398                     if (availableMode.getModeId() == defaultMode.getModeId()) {
399                         baseMode = defaultMode;
400                         break;
401                     }
402                 }
403             }
404 
405             // If the application requests a display mode by setting
406             // LayoutParams.preferredDisplayModeId, it will be the only available mode and it'll
407             // be stored as baseModeId.
408             if (baseMode == null && !availableModes.isEmpty()) {
409                 baseMode = availableModes.get(0);
410             }
411 
412             if (baseMode == null) {
413                 Slog.w(TAG, "Can't find a set of allowed modes which satisfies the votes. Falling"
414                         + " back to the default mode. Display = " + displayId + ", votes = " + votes
415                         + ", supported modes = " + Arrays.toString(modes));
416 
417                 float fps = defaultMode.getRefreshRate();
418                 return new DesiredDisplayModeSpecs(defaultMode.getModeId(),
419                         /*allowGroupSwitching */ false,
420                         new RefreshRateRange(fps, fps),
421                         new RefreshRateRange(fps, fps));
422             }
423 
424             if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE
425                     || primarySummary.disableRefreshRateSwitching) {
426                 float fps = baseMode.getRefreshRate();
427                 primarySummary.minRefreshRate = primarySummary.maxRefreshRate = fps;
428                 if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
429                     appRequestSummary.minRefreshRate = appRequestSummary.maxRefreshRate = fps;
430                 }
431             }
432 
433             boolean allowGroupSwitching =
434                     mModeSwitchingType == DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
435 
436             return new DesiredDisplayModeSpecs(baseMode.getModeId(),
437                     allowGroupSwitching,
438                     new RefreshRateRange(
439                             primarySummary.minRefreshRate, primarySummary.maxRefreshRate),
440                     new RefreshRateRange(
441                             appRequestSummary.minRefreshRate, appRequestSummary.maxRefreshRate));
442         }
443     }
444 
filterModes(Display.Mode[] supportedModes, VoteSummary summary)445     private ArrayList<Display.Mode> filterModes(Display.Mode[] supportedModes,
446             VoteSummary summary) {
447         ArrayList<Display.Mode> availableModes = new ArrayList<>();
448         boolean missingBaseModeRefreshRate = summary.baseModeRefreshRate > 0f;
449         for (Display.Mode mode : supportedModes) {
450             if (mode.getPhysicalWidth() != summary.width
451                     || mode.getPhysicalHeight() != summary.height) {
452                 if (mLoggingEnabled) {
453                     Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
454                             + ": desiredWidth=" + summary.width
455                             + ": desiredHeight=" + summary.height
456                             + ": actualWidth=" + mode.getPhysicalWidth()
457                             + ": actualHeight=" + mode.getPhysicalHeight());
458                 }
459                 continue;
460             }
461             final float refreshRate = mode.getRefreshRate();
462             // Some refresh rates are calculated based on frame timings, so they aren't *exactly*
463             // equal to expected refresh rate. Given that, we apply a bit of tolerance to this
464             // comparison.
465             if (refreshRate < (summary.minRefreshRate - FLOAT_TOLERANCE)
466                     || refreshRate > (summary.maxRefreshRate + FLOAT_TOLERANCE)) {
467                 if (mLoggingEnabled) {
468                     Slog.w(TAG, "Discarding mode " + mode.getModeId()
469                             + ", outside refresh rate bounds"
470                             + ": minRefreshRate=" + summary.minRefreshRate
471                             + ", maxRefreshRate=" + summary.maxRefreshRate
472                             + ", modeRefreshRate=" + refreshRate);
473                 }
474                 continue;
475             }
476             availableModes.add(mode);
477             if (mode.getRefreshRate() >= summary.baseModeRefreshRate - FLOAT_TOLERANCE
478                     && mode.getRefreshRate() <= summary.baseModeRefreshRate + FLOAT_TOLERANCE) {
479                 missingBaseModeRefreshRate = false;
480             }
481         }
482         if (missingBaseModeRefreshRate) {
483             return new ArrayList<>();
484         }
485 
486         return availableModes;
487     }
488 
489     /**
490      * Gets the observer responsible for application display mode requests.
491      */
492     @NonNull
getAppRequestObserver()493     public AppRequestObserver getAppRequestObserver() {
494         // We don't need to lock here because mAppRequestObserver is a final field, which is
495         // guaranteed to be visible on all threads after construction.
496         return mAppRequestObserver;
497     }
498 
499     /**
500      * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate ranges.
501      */
setDesiredDisplayModeSpecsListener( @ullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener)502     public void setDesiredDisplayModeSpecsListener(
503             @Nullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener) {
504         synchronized (mLock) {
505             mDesiredDisplayModeSpecsListener = desiredDisplayModeSpecsListener;
506         }
507     }
508 
509     /**
510      * When enabled the app requested display mode is always selected and all
511      * other votes will be ignored. This is used for testing purposes.
512      */
setShouldAlwaysRespectAppRequestedMode(boolean enabled)513     public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
514         synchronized (mLock) {
515             mAlwaysRespectAppRequest = enabled;
516         }
517     }
518 
519     /**
520      * Returns whether we are running in a mode which always selects the app requested display mode
521      * and ignores user settings and policies for low brightness, low battery etc.
522      */
shouldAlwaysRespectAppRequestedMode()523     public boolean shouldAlwaysRespectAppRequestedMode() {
524         synchronized (mLock) {
525             return mAlwaysRespectAppRequest;
526         }
527     }
528 
529     /**
530      * Sets the display mode switching type.
531      * @param newType
532      */
setModeSwitchingType(@isplayManager.SwitchingType int newType)533     public void setModeSwitchingType(@DisplayManager.SwitchingType int newType) {
534         synchronized (mLock) {
535             if (newType != mModeSwitchingType) {
536                 mModeSwitchingType = newType;
537                 notifyDesiredDisplayModeSpecsChangedLocked();
538             }
539         }
540     }
541 
542     /**
543      * Returns the display mode switching type.
544      */
545     @DisplayManager.SwitchingType
getModeSwitchingType()546     public int getModeSwitchingType() {
547         synchronized (mLock) {
548             return mModeSwitchingType;
549         }
550     }
551 
552     /**
553      * Retrieve the Vote for the given display and priority. Intended only for testing purposes.
554      *
555      * @param displayId the display to query for
556      * @param priority the priority of the vote to return
557      * @return the vote corresponding to the given {@code displayId} and {@code priority},
558      *         or {@code null} if there isn't one
559      */
560     @VisibleForTesting
561     @Nullable
getVote(int displayId, int priority)562     Vote getVote(int displayId, int priority) {
563         synchronized (mLock) {
564             SparseArray<Vote> votes = getVotesLocked(displayId);
565             return votes.get(priority);
566         }
567     }
568 
569     /**
570      * Print the object's state and debug information into the given stream.
571      *
572      * @param pw The stream to dump information to.
573      */
dump(PrintWriter pw)574     public void dump(PrintWriter pw) {
575         pw.println("DisplayModeDirector");
576         synchronized (mLock) {
577             pw.println("  mSupportedModesByDisplay:");
578             for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
579                 final int id = mSupportedModesByDisplay.keyAt(i);
580                 final Display.Mode[] modes = mSupportedModesByDisplay.valueAt(i);
581                 pw.println("    " + id + " -> " + Arrays.toString(modes));
582             }
583             pw.println("  mDefaultModeByDisplay:");
584             for (int i = 0; i < mDefaultModeByDisplay.size(); i++) {
585                 final int id = mDefaultModeByDisplay.keyAt(i);
586                 final Display.Mode mode = mDefaultModeByDisplay.valueAt(i);
587                 pw.println("    " + id + " -> " + mode);
588             }
589             pw.println("  mVotesByDisplay:");
590             for (int i = 0; i < mVotesByDisplay.size(); i++) {
591                 pw.println("    " + mVotesByDisplay.keyAt(i) + ":");
592                 SparseArray<Vote> votes = mVotesByDisplay.valueAt(i);
593                 for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
594                     Vote vote = votes.get(p);
595                     if (vote == null) {
596                         continue;
597                     }
598                     pw.println("      " + Vote.priorityToString(p) + " -> " + vote);
599                 }
600             }
601             pw.println("  mModeSwitchingType: " + switchingTypeToString(mModeSwitchingType));
602             pw.println("  mAlwaysRespectAppRequest: " + mAlwaysRespectAppRequest);
603             mSettingsObserver.dumpLocked(pw);
604             mAppRequestObserver.dumpLocked(pw);
605             mBrightnessObserver.dumpLocked(pw);
606             mUdfpsObserver.dumpLocked(pw);
607             mSensorObserver.dumpLocked(pw);
608             mHbmObserver.dumpLocked(pw);
609         }
610     }
611 
updateVoteLocked(int priority, Vote vote)612     private void updateVoteLocked(int priority, Vote vote) {
613         updateVoteLocked(GLOBAL_ID, priority, vote);
614     }
615 
updateVoteLocked(int displayId, int priority, Vote vote)616     private void updateVoteLocked(int displayId, int priority, Vote vote) {
617         if (mLoggingEnabled) {
618             Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
619                     + ", priority=" + Vote.priorityToString(priority)
620                     + ", vote=" + vote + ")");
621         }
622         if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
623             Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
624                     + " priority=" + Vote.priorityToString(priority)
625                     + ", vote=" + vote, new Throwable());
626             return;
627         }
628         final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId);
629 
630         if (vote != null) {
631             votes.put(priority, vote);
632         } else {
633             votes.remove(priority);
634         }
635 
636         if (votes.size() == 0) {
637             if (mLoggingEnabled) {
638                 Slog.i(TAG, "No votes left for display " + displayId + ", removing.");
639             }
640             mVotesByDisplay.remove(displayId);
641         }
642 
643         notifyDesiredDisplayModeSpecsChangedLocked();
644     }
645 
notifyDesiredDisplayModeSpecsChangedLocked()646     private void notifyDesiredDisplayModeSpecsChangedLocked() {
647         if (mDesiredDisplayModeSpecsListener != null
648                 && !mHandler.hasMessages(MSG_REFRESH_RATE_RANGE_CHANGED)) {
649             // We need to post this to a handler to avoid calling out while holding the lock
650             // since we know there are things that both listen for changes as well as provide
651             // information. If we did call out while holding the lock, then there's no
652             // guaranteed lock order and we run the real of risk deadlock.
653             Message msg = mHandler.obtainMessage(
654                     MSG_REFRESH_RATE_RANGE_CHANGED, mDesiredDisplayModeSpecsListener);
655             msg.sendToTarget();
656         }
657     }
658 
getOrCreateVotesByDisplay(int displayId)659     private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) {
660         if (mVotesByDisplay.indexOfKey(displayId) >= 0) {
661             return mVotesByDisplay.get(displayId);
662         } else {
663             SparseArray<Vote> votes = new SparseArray<>();
664             mVotesByDisplay.put(displayId, votes);
665             return votes;
666         }
667     }
668 
switchingTypeToString(@isplayManager.SwitchingType int type)669     private static String switchingTypeToString(@DisplayManager.SwitchingType int type) {
670         switch (type) {
671             case DisplayManager.SWITCHING_TYPE_NONE:
672                 return "SWITCHING_TYPE_NONE";
673             case DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS:
674                 return "SWITCHING_TYPE_WITHIN_GROUPS";
675             case DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS:
676                 return "SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS";
677             default:
678                 return "Unknown SwitchingType " + type;
679         }
680     }
681 
682     @VisibleForTesting
injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay)683     void injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay) {
684         mSupportedModesByDisplay = supportedModesByDisplay;
685     }
686 
687     @VisibleForTesting
injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay)688     void injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay) {
689         mDefaultModeByDisplay = defaultModeByDisplay;
690     }
691 
692     @VisibleForTesting
injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay)693     void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) {
694         mVotesByDisplay = votesByDisplay;
695     }
696 
697     @VisibleForTesting
injectBrightnessObserver(BrightnessObserver brightnessObserver)698     void injectBrightnessObserver(BrightnessObserver brightnessObserver) {
699         mBrightnessObserver = brightnessObserver;
700     }
701 
702     @VisibleForTesting
getBrightnessObserver()703     BrightnessObserver getBrightnessObserver() {
704         return mBrightnessObserver;
705     }
706 
707     @VisibleForTesting
getSettingsObserver()708     SettingsObserver getSettingsObserver() {
709         return mSettingsObserver;
710     }
711 
712     @VisibleForTesting
getUdpfsObserver()713     UdfpsObserver getUdpfsObserver() {
714         return mUdfpsObserver;
715     }
716 
717 
718     @VisibleForTesting
getDesiredDisplayModeSpecsWithInjectedFpsSettings( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate)719     DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
720             float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
721         synchronized (mLock) {
722             mSettingsObserver.updateRefreshRateSettingLocked(
723                     minRefreshRate, peakRefreshRate, defaultRefreshRate);
724             return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY);
725         }
726     }
727 
728     /**
729      * Listens for changes refresh rate coordination.
730      */
731     public interface DesiredDisplayModeSpecsListener {
732         /**
733          * Called when the refresh rate range may have changed.
734          */
onDesiredDisplayModeSpecsChanged()735         void onDesiredDisplayModeSpecsChanged();
736     }
737 
738     private final class DisplayModeDirectorHandler extends Handler {
DisplayModeDirectorHandler(Looper looper)739         DisplayModeDirectorHandler(Looper looper) {
740             super(looper, null, true /*async*/);
741         }
742 
743         @Override
handleMessage(Message msg)744         public void handleMessage(Message msg) {
745             switch (msg.what) {
746                 case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED: {
747                     Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
748                     mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged(
749                             thresholds.first, thresholds.second);
750                     break;
751                 }
752 
753                 case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED: {
754                     int refreshRateInZone = msg.arg1;
755                     mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(
756                             refreshRateInZone);
757                     break;
758                 }
759 
760                 case MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED: {
761                     Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
762 
763                     mBrightnessObserver.onDeviceConfigHighBrightnessThresholdsChanged(
764                             thresholds.first, thresholds.second);
765 
766                     break;
767                 }
768 
769                 case MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED: {
770                     int refreshRateInZone = msg.arg1;
771                     mBrightnessObserver.onDeviceConfigRefreshRateInHighZoneChanged(
772                             refreshRateInZone);
773                     break;
774                 }
775 
776                 case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
777                     Float defaultPeakRefreshRate = (Float) msg.obj;
778                     mSettingsObserver.onDeviceConfigDefaultPeakRefreshRateChanged(
779                             defaultPeakRefreshRate);
780                     break;
781 
782                 case MSG_REFRESH_RATE_RANGE_CHANGED:
783                     DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener =
784                             (DesiredDisplayModeSpecsListener) msg.obj;
785                     desiredDisplayModeSpecsListener.onDesiredDisplayModeSpecsChanged();
786                     break;
787             }
788         }
789     }
790 
791     /**
792      * Information about the desired display mode to be set by the system. Includes the base
793      * mode ID and the primary and app request refresh rate ranges.
794      *
795      * We have this class in addition to SurfaceControl.DesiredDisplayConfigSpecs to make clear the
796      * distinction between the config ID / physical index that
797      * SurfaceControl.DesiredDisplayConfigSpecs uses, and the mode ID used here.
798      */
799     public static final class DesiredDisplayModeSpecs {
800 
801         /**
802          * Base mode ID. This is what system defaults to for all other settings, or
803          * if the refresh rate range is not available.
804          */
805         public int baseModeId;
806 
807         /**
808          * If true this will allow switching between modes in different display configuration
809          * groups. This way the user may see visual interruptions when the display mode changes.
810          */
811         public boolean allowGroupSwitching;
812 
813         /**
814          * The primary refresh rate range.
815          */
816         public final RefreshRateRange primaryRefreshRateRange;
817         /**
818          * The app request refresh rate range. Lower priority considerations won't be included in
819          * this range, allowing SurfaceFlinger to consider additional refresh rates for apps that
820          * call setFrameRate(). This range will be greater than or equal to the primary refresh rate
821          * range, never smaller.
822          */
823         public final RefreshRateRange appRequestRefreshRateRange;
824 
DesiredDisplayModeSpecs()825         public DesiredDisplayModeSpecs() {
826             primaryRefreshRateRange = new RefreshRateRange();
827             appRequestRefreshRateRange = new RefreshRateRange();
828         }
829 
DesiredDisplayModeSpecs(int baseModeId, boolean allowGroupSwitching, @NonNull RefreshRateRange primaryRefreshRateRange, @NonNull RefreshRateRange appRequestRefreshRateRange)830         public DesiredDisplayModeSpecs(int baseModeId,
831                 boolean allowGroupSwitching,
832                 @NonNull RefreshRateRange primaryRefreshRateRange,
833                 @NonNull RefreshRateRange appRequestRefreshRateRange) {
834             this.baseModeId = baseModeId;
835             this.allowGroupSwitching = allowGroupSwitching;
836             this.primaryRefreshRateRange = primaryRefreshRateRange;
837             this.appRequestRefreshRateRange = appRequestRefreshRateRange;
838         }
839 
840         /**
841          * Returns a string representation of the object.
842          */
843         @Override
toString()844         public String toString() {
845             return String.format("baseModeId=%d allowGroupSwitching=%b"
846                             + " primaryRefreshRateRange=[%.0f %.0f]"
847                             + " appRequestRefreshRateRange=[%.0f %.0f]",
848                     baseModeId, allowGroupSwitching, primaryRefreshRateRange.min,
849                     primaryRefreshRateRange.max, appRequestRefreshRateRange.min,
850                     appRequestRefreshRateRange.max);
851         }
852         /**
853          * Checks whether the two objects have the same values.
854          */
855         @Override
equals(Object other)856         public boolean equals(Object other) {
857             if (other == this) {
858                 return true;
859             }
860 
861             if (!(other instanceof DesiredDisplayModeSpecs)) {
862                 return false;
863             }
864 
865             DesiredDisplayModeSpecs desiredDisplayModeSpecs = (DesiredDisplayModeSpecs) other;
866 
867             if (baseModeId != desiredDisplayModeSpecs.baseModeId) {
868                 return false;
869             }
870             if (allowGroupSwitching != desiredDisplayModeSpecs.allowGroupSwitching) {
871                 return false;
872             }
873             if (!primaryRefreshRateRange.equals(desiredDisplayModeSpecs.primaryRefreshRateRange)) {
874                 return false;
875             }
876             if (!appRequestRefreshRateRange.equals(
877                         desiredDisplayModeSpecs.appRequestRefreshRateRange)) {
878                 return false;
879             }
880             return true;
881         }
882 
883         @Override
hashCode()884         public int hashCode() {
885             return Objects.hash(baseModeId, allowGroupSwitching, primaryRefreshRateRange,
886                     appRequestRefreshRateRange);
887         }
888 
889         /**
890          * Copy values from the other object.
891          */
copyFrom(DesiredDisplayModeSpecs other)892         public void copyFrom(DesiredDisplayModeSpecs other) {
893             baseModeId = other.baseModeId;
894             allowGroupSwitching = other.allowGroupSwitching;
895             primaryRefreshRateRange.min = other.primaryRefreshRateRange.min;
896             primaryRefreshRateRange.max = other.primaryRefreshRateRange.max;
897             appRequestRefreshRateRange.min = other.appRequestRefreshRateRange.min;
898             appRequestRefreshRateRange.max = other.appRequestRefreshRateRange.max;
899         }
900     }
901 
902     @VisibleForTesting
903     static final class Vote {
904         // DEFAULT_FRAME_RATE votes for [0, DEFAULT]. As the lowest priority vote, it's overridden
905         // by all other considerations. It acts to set a default frame rate for a device.
906         public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0;
907 
908         // PRIORITY_FLICKER_REFRESH_RATE votes for a single refresh rate like [60,60], [90,90] or
909         // null. It is used to set a preferred refresh rate value in case the higher priority votes
910         // result is a range.
911         public static final int PRIORITY_FLICKER_REFRESH_RATE = 1;
912 
913         // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
914         // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
915         public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2;
916 
917         // APP_REQUEST_REFRESH_RATE_RANGE is used to for internal apps to limit the refresh
918         // rate in certain cases, mostly to preserve power.
919         // @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate
920         // @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate
921         // It votes to [preferredMinRefreshRate, preferredMaxRefreshRate].
922         public static final int PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE = 3;
923 
924         // We split the app request into different priorities in case we can satisfy one desire
925         // without the other.
926 
927         // Application can specify preferred refresh rate with below attrs.
928         // @see android.view.WindowManager.LayoutParams#preferredRefreshRate
929         // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
930         // These translates into votes for the base mode refresh rate and resolution to be
931         // used by SurfaceFlinger as the policy of choosing the display mode. The system also
932         // forces some apps like denylisted app to run at a lower refresh rate.
933         // @see android.R.array#config_highRefreshRateBlacklist
934         // The preferred refresh rate is set on the main surface of the app outside of
935         // DisplayModeDirector.
936         // @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
937         public static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 4;
938         public static final int PRIORITY_APP_REQUEST_SIZE = 5;
939 
940         // SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest
941         // of low priority voters. It votes [0, max(PEAK, MIN)]
942         public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 6;
943 
944         // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
945         public static final int PRIORITY_LOW_POWER_MODE = 7;
946 
947         // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
948         // higher priority voters' result is a range, it will fix the rate to a single choice.
949         // It's used to avoid refresh rate switches in certain conditions which may result in the
950         // user seeing the display flickering when the switches occur.
951         public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8;
952 
953         // High-brightness-mode may need a specific range of refresh-rates to function properly.
954         public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 9;
955 
956         // The proximity sensor needs the refresh rate to be locked in order to function, so this is
957         // set to a high priority.
958         public static final int PRIORITY_PROXIMITY = 10;
959 
960         // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
961         // to function, so this needs to be the highest priority of all votes.
962         public static final int PRIORITY_UDFPS = 11;
963 
964         // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
965         // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
966 
967         public static final int MIN_PRIORITY = PRIORITY_DEFAULT_REFRESH_RATE;
968         public static final int MAX_PRIORITY = PRIORITY_UDFPS;
969 
970         // The cutoff for the app request refresh rate range. Votes with priorities lower than this
971         // value will not be considered when constructing the app request refresh rate range.
972         public static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
973                 PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE;
974 
975         /**
976          * A value signifying an invalid width or height in a vote.
977          */
978         public static final int INVALID_SIZE = -1;
979 
980         /**
981          * The requested width of the display in pixels, or INVALID_SIZE;
982          */
983         public final int width;
984         /**
985          * The requested height of the display in pixels, or INVALID_SIZE;
986          */
987         public final int height;
988         /**
989          * Information about the min and max refresh rate DM would like to set the display to.
990          */
991         public final RefreshRateRange refreshRateRange;
992 
993         /**
994          * Whether refresh rate switching should be disabled (i.e. the refresh rate range is
995          * a single value).
996          */
997         public final boolean disableRefreshRateSwitching;
998 
999         /**
1000          * The base mode refresh rate to be used for this display. This would be used when deciding
1001          * the base mode id.
1002          */
1003         public final float baseModeRefreshRate;
1004 
forRefreshRates(float minRefreshRate, float maxRefreshRate)1005         public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
1006             return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate,
1007                     minRefreshRate == maxRefreshRate, 0f);
1008         }
1009 
forSize(int width, int height)1010         public static Vote forSize(int width, int height) {
1011             return new Vote(width, height, 0f, Float.POSITIVE_INFINITY, false,
1012                     0f);
1013         }
1014 
forDisableRefreshRateSwitching()1015         public static Vote forDisableRefreshRateSwitching() {
1016             return new Vote(INVALID_SIZE, INVALID_SIZE, 0f, Float.POSITIVE_INFINITY, true,
1017                     0f);
1018         }
1019 
forBaseModeRefreshRate(float baseModeRefreshRate)1020         public static Vote forBaseModeRefreshRate(float baseModeRefreshRate) {
1021             return new Vote(INVALID_SIZE, INVALID_SIZE, 0f, Float.POSITIVE_INFINITY, false,
1022                     baseModeRefreshRate);
1023         }
1024 
Vote(int width, int height, float minRefreshRate, float maxRefreshRate, boolean disableRefreshRateSwitching, float baseModeRefreshRate)1025         private Vote(int width, int height,
1026                 float minRefreshRate, float maxRefreshRate,
1027                 boolean disableRefreshRateSwitching,
1028                 float baseModeRefreshRate) {
1029             this.width = width;
1030             this.height = height;
1031             this.refreshRateRange =
1032                     new RefreshRateRange(minRefreshRate, maxRefreshRate);
1033             this.disableRefreshRateSwitching = disableRefreshRateSwitching;
1034             this.baseModeRefreshRate = baseModeRefreshRate;
1035         }
1036 
priorityToString(int priority)1037         public static String priorityToString(int priority) {
1038             switch (priority) {
1039                 case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
1040                     return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE";
1041                 case PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE:
1042                     return "PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE";
1043                 case PRIORITY_APP_REQUEST_SIZE:
1044                     return "PRIORITY_APP_REQUEST_SIZE";
1045                 case PRIORITY_DEFAULT_REFRESH_RATE:
1046                     return "PRIORITY_DEFAULT_REFRESH_RATE";
1047                 case PRIORITY_FLICKER_REFRESH_RATE:
1048                     return "PRIORITY_FLICKER_REFRESH_RATE";
1049                 case PRIORITY_FLICKER_REFRESH_RATE_SWITCH:
1050                     return "PRIORITY_FLICKER_REFRESH_RATE_SWITCH";
1051                 case PRIORITY_HIGH_BRIGHTNESS_MODE:
1052                     return "PRIORITY_HIGH_BRIGHTNESS_MODE";
1053                 case PRIORITY_PROXIMITY:
1054                     return "PRIORITY_PROXIMITY";
1055                 case PRIORITY_LOW_POWER_MODE:
1056                     return "PRIORITY_LOW_POWER_MODE";
1057                 case PRIORITY_UDFPS:
1058                     return "PRIORITY_UDFPS";
1059                 case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
1060                     return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
1061                 case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
1062                     return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
1063                 default:
1064                     return Integer.toString(priority);
1065             }
1066         }
1067 
1068         @Override
toString()1069         public String toString() {
1070             return "Vote{"
1071                 + "width=" + width + ", height=" + height
1072                 + ", minRefreshRate=" + refreshRateRange.min
1073                 + ", maxRefreshRate=" + refreshRateRange.max
1074                 + ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
1075                 + ", baseModeRefreshRate=" + baseModeRefreshRate + "}";
1076         }
1077     }
1078 
1079     @VisibleForTesting
1080     final class SettingsObserver extends ContentObserver {
1081         private final Uri mPeakRefreshRateSetting =
1082                 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
1083         private final Uri mMinRefreshRateSetting =
1084                 Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE);
1085         private final Uri mLowPowerModeSetting =
1086                 Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
1087         private final Uri mMatchContentFrameRateSetting =
1088                 Settings.Secure.getUriFor(Settings.Secure.MATCH_CONTENT_FRAME_RATE);
1089 
1090         private final Context mContext;
1091         private float mDefaultPeakRefreshRate;
1092         private float mDefaultRefreshRate;
1093 
SettingsObserver(@onNull Context context, @NonNull Handler handler)1094         SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
1095             super(handler);
1096             mContext = context;
1097             mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
1098                     R.integer.config_defaultPeakRefreshRate);
1099             mDefaultRefreshRate =
1100                     (float) context.getResources().getInteger(R.integer.config_defaultRefreshRate);
1101         }
1102 
observe()1103         public void observe() {
1104             final ContentResolver cr = mContext.getContentResolver();
1105             mInjector.registerPeakRefreshRateObserver(cr, this);
1106             cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
1107                     UserHandle.USER_SYSTEM);
1108             cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
1109                     UserHandle.USER_SYSTEM);
1110             cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
1111                     this);
1112 
1113             Float deviceConfigDefaultPeakRefresh =
1114                     mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
1115             if (deviceConfigDefaultPeakRefresh != null) {
1116                 mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh;
1117             }
1118 
1119             synchronized (mLock) {
1120                 updateRefreshRateSettingLocked();
1121                 updateLowPowerModeSettingLocked();
1122                 updateModeSwitchingTypeSettingLocked();
1123             }
1124         }
1125 
setDefaultRefreshRate(float refreshRate)1126         public void setDefaultRefreshRate(float refreshRate) {
1127             synchronized (mLock) {
1128                 mDefaultRefreshRate = refreshRate;
1129                 updateRefreshRateSettingLocked();
1130             }
1131         }
1132 
onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate)1133         public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
1134             if (defaultPeakRefreshRate == null) {
1135                 defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
1136                         R.integer.config_defaultPeakRefreshRate);
1137             }
1138 
1139             if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) {
1140                 synchronized (mLock) {
1141                     mDefaultPeakRefreshRate = defaultPeakRefreshRate;
1142                     updateRefreshRateSettingLocked();
1143                 }
1144             }
1145         }
1146 
1147         @Override
onChange(boolean selfChange, Uri uri, int userId)1148         public void onChange(boolean selfChange, Uri uri, int userId) {
1149             synchronized (mLock) {
1150                 if (mPeakRefreshRateSetting.equals(uri)
1151                         || mMinRefreshRateSetting.equals(uri)) {
1152                     updateRefreshRateSettingLocked();
1153                 } else if (mLowPowerModeSetting.equals(uri)) {
1154                     updateLowPowerModeSettingLocked();
1155                 } else if (mMatchContentFrameRateSetting.equals(uri)) {
1156                     updateModeSwitchingTypeSettingLocked();
1157                 }
1158             }
1159         }
1160 
updateLowPowerModeSettingLocked()1161         private void updateLowPowerModeSettingLocked() {
1162             boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
1163                     Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
1164             final Vote vote;
1165             if (inLowPowerMode) {
1166                 vote = Vote.forRefreshRates(0f, 60f);
1167             } else {
1168                 vote = null;
1169             }
1170             updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote);
1171             mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
1172         }
1173 
updateRefreshRateSettingLocked()1174         private void updateRefreshRateSettingLocked() {
1175             final ContentResolver cr = mContext.getContentResolver();
1176             float minRefreshRate = Settings.System.getFloatForUser(cr,
1177                     Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
1178             float peakRefreshRate = Settings.System.getFloatForUser(cr,
1179                     Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId());
1180             updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
1181         }
1182 
updateRefreshRateSettingLocked( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate)1183         private void updateRefreshRateSettingLocked(
1184                 float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
1185             // TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is
1186             // used to predict if we're going to be doing frequent refresh rate switching, and if
1187             // so, enable the brightness observer. The logic here is more complicated and fragile
1188             // than necessary, and we should improve it. See b/156304339 for more info.
1189             Vote peakVote = peakRefreshRate == 0f
1190                     ? null
1191                     : Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate));
1192             updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, peakVote);
1193             updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
1194                     Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY));
1195             Vote defaultVote =
1196                     defaultRefreshRate == 0f ? null : Vote.forRefreshRates(0f, defaultRefreshRate);
1197             updateVoteLocked(Vote.PRIORITY_DEFAULT_REFRESH_RATE, defaultVote);
1198 
1199             float maxRefreshRate;
1200             if (peakRefreshRate == 0f && defaultRefreshRate == 0f) {
1201                 // We require that at least one of the peak or default refresh rate values are
1202                 // set. The brightness observer requires that we're able to predict whether or not
1203                 // we're going to do frequent refresh rate switching, and with the way the code is
1204                 // currently written, we need either a default or peak refresh rate value for that.
1205                 Slog.e(TAG, "Default and peak refresh rates are both 0. One of them should be set"
1206                         + " to a valid value.");
1207                 maxRefreshRate = minRefreshRate;
1208             } else if (peakRefreshRate == 0f) {
1209                 maxRefreshRate = defaultRefreshRate;
1210             } else if (defaultRefreshRate == 0f) {
1211                 maxRefreshRate = peakRefreshRate;
1212             } else {
1213                 maxRefreshRate = Math.min(defaultRefreshRate, peakRefreshRate);
1214             }
1215 
1216             mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, maxRefreshRate);
1217         }
1218 
updateModeSwitchingTypeSettingLocked()1219         private void updateModeSwitchingTypeSettingLocked() {
1220             final ContentResolver cr = mContext.getContentResolver();
1221             int switchingType = Settings.Secure.getIntForUser(
1222                     cr, Settings.Secure.MATCH_CONTENT_FRAME_RATE, mModeSwitchingType /*default*/,
1223                     cr.getUserId());
1224             if (switchingType != mModeSwitchingType) {
1225                 mModeSwitchingType = switchingType;
1226                 notifyDesiredDisplayModeSpecsChangedLocked();
1227             }
1228         }
1229 
dumpLocked(PrintWriter pw)1230         public void dumpLocked(PrintWriter pw) {
1231             pw.println("  SettingsObserver");
1232             pw.println("    mDefaultRefreshRate: " + mDefaultRefreshRate);
1233             pw.println("    mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate);
1234         }
1235     }
1236 
1237     final class AppRequestObserver {
1238         private final SparseArray<Display.Mode> mAppRequestedModeByDisplay;
1239         private final SparseArray<RefreshRateRange> mAppPreferredRefreshRateRangeByDisplay;
1240 
AppRequestObserver()1241         AppRequestObserver() {
1242             mAppRequestedModeByDisplay = new SparseArray<>();
1243             mAppPreferredRefreshRateRangeByDisplay = new SparseArray<>();
1244         }
1245 
setAppRequest(int displayId, int modeId, float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange)1246         public void setAppRequest(int displayId, int modeId, float requestedMinRefreshRateRange,
1247                 float requestedMaxRefreshRateRange) {
1248             synchronized (mLock) {
1249                 setAppRequestedModeLocked(displayId, modeId);
1250                 setAppPreferredRefreshRateRangeLocked(displayId, requestedMinRefreshRateRange,
1251                         requestedMaxRefreshRateRange);
1252             }
1253         }
1254 
setAppRequestedModeLocked(int displayId, int modeId)1255         private void setAppRequestedModeLocked(int displayId, int modeId) {
1256             final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId);
1257             if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
1258                 return;
1259             }
1260 
1261             final Vote baseModeRefreshRateVote;
1262             final Vote sizeVote;
1263             if (requestedMode != null) {
1264                 mAppRequestedModeByDisplay.put(displayId, requestedMode);
1265                 baseModeRefreshRateVote =
1266                         Vote.forBaseModeRefreshRate(requestedMode.getRefreshRate());
1267                 sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
1268                         requestedMode.getPhysicalHeight());
1269             } else {
1270                 mAppRequestedModeByDisplay.remove(displayId);
1271                 baseModeRefreshRateVote = null;
1272                 sizeVote = null;
1273             }
1274 
1275             updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
1276                     baseModeRefreshRateVote);
1277             updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
1278         }
1279 
setAppPreferredRefreshRateRangeLocked(int displayId, float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange)1280         private void setAppPreferredRefreshRateRangeLocked(int displayId,
1281                 float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) {
1282             final Vote vote;
1283 
1284             RefreshRateRange refreshRateRange = null;
1285             if (requestedMinRefreshRateRange > 0 || requestedMaxRefreshRateRange > 0) {
1286                 float min = requestedMinRefreshRateRange;
1287                 float max = requestedMaxRefreshRateRange > 0
1288                         ? requestedMaxRefreshRateRange : Float.POSITIVE_INFINITY;
1289                 refreshRateRange = new RefreshRateRange(min, max);
1290                 if (refreshRateRange.min == 0 && refreshRateRange.max == 0) {
1291                     // requestedMinRefreshRateRange/requestedMaxRefreshRateRange were invalid
1292                     refreshRateRange = null;
1293                 }
1294             }
1295 
1296             if (Objects.equals(refreshRateRange,
1297                     mAppPreferredRefreshRateRangeByDisplay.get(displayId))) {
1298                 return;
1299             }
1300 
1301             if (refreshRateRange != null) {
1302                 mAppPreferredRefreshRateRangeByDisplay.put(displayId, refreshRateRange);
1303                 vote = Vote.forRefreshRates(refreshRateRange.min, refreshRateRange.max);
1304             } else {
1305                 mAppPreferredRefreshRateRangeByDisplay.remove(displayId);
1306                 vote = null;
1307             }
1308             synchronized (mLock) {
1309                 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE, vote);
1310             }
1311         }
1312 
findModeByIdLocked(int displayId, int modeId)1313         private Display.Mode findModeByIdLocked(int displayId, int modeId) {
1314             Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
1315             if (modes == null) {
1316                 return null;
1317             }
1318             for (Display.Mode mode : modes) {
1319                 if (mode.getModeId() == modeId) {
1320                     return mode;
1321                 }
1322             }
1323             return null;
1324         }
1325 
dumpLocked(PrintWriter pw)1326         public void dumpLocked(PrintWriter pw) {
1327             pw.println("  AppRequestObserver");
1328             pw.println("    mAppRequestedModeByDisplay:");
1329             for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
1330                 final int id = mAppRequestedModeByDisplay.keyAt(i);
1331                 final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
1332                 pw.println("    " + id + " -> " + mode);
1333             }
1334             pw.println("    mAppPreferredRefreshRateRangeByDisplay:");
1335             for (int i = 0; i < mAppPreferredRefreshRateRangeByDisplay.size(); i++) {
1336                 final int id = mAppPreferredRefreshRateRangeByDisplay.keyAt(i);
1337                 final RefreshRateRange refreshRateRange =
1338                         mAppPreferredRefreshRateRangeByDisplay.valueAt(i);
1339                 pw.println("    " + id + " -> " + refreshRateRange);
1340             }
1341         }
1342     }
1343 
1344     private final class DisplayObserver implements DisplayManager.DisplayListener {
1345         // Note that we can never call into DisplayManager or any of the non-POD classes it
1346         // returns, while holding mLock since it may call into DMS, which might be simultaneously
1347         // calling into us already holding its own lock.
1348         private final Context mContext;
1349         private final Handler mHandler;
1350 
DisplayObserver(Context context, Handler handler)1351         DisplayObserver(Context context, Handler handler) {
1352             mContext = context;
1353             mHandler = handler;
1354         }
1355 
observe()1356         public void observe() {
1357             DisplayManager dm = mContext.getSystemService(DisplayManager.class);
1358             dm.registerDisplayListener(this, mHandler);
1359 
1360             // Populate existing displays
1361             SparseArray<Display.Mode[]> modes = new SparseArray<>();
1362             SparseArray<Display.Mode> defaultModes = new SparseArray<>();
1363             DisplayInfo info = new DisplayInfo();
1364             Display[] displays = dm.getDisplays();
1365             for (Display d : displays) {
1366                 final int displayId = d.getDisplayId();
1367                 d.getDisplayInfo(info);
1368                 modes.put(displayId, info.supportedModes);
1369                 defaultModes.put(displayId, info.getDefaultMode());
1370             }
1371             synchronized (mLock) {
1372                 final int size = modes.size();
1373                 for (int i = 0; i < size; i++) {
1374                     mSupportedModesByDisplay.put(modes.keyAt(i), modes.valueAt(i));
1375                     mDefaultModeByDisplay.put(defaultModes.keyAt(i), defaultModes.valueAt(i));
1376                 }
1377             }
1378         }
1379 
1380         @Override
onDisplayAdded(int displayId)1381         public void onDisplayAdded(int displayId) {
1382             updateDisplayModes(displayId);
1383         }
1384 
1385         @Override
onDisplayRemoved(int displayId)1386         public void onDisplayRemoved(int displayId) {
1387             synchronized (mLock) {
1388                 mSupportedModesByDisplay.remove(displayId);
1389                 mDefaultModeByDisplay.remove(displayId);
1390             }
1391         }
1392 
1393         @Override
onDisplayChanged(int displayId)1394         public void onDisplayChanged(int displayId) {
1395             updateDisplayModes(displayId);
1396             // TODO: Break the coupling between DisplayObserver and BrightnessObserver.
1397             mBrightnessObserver.onDisplayChanged(displayId);
1398         }
1399 
updateDisplayModes(int displayId)1400         private void updateDisplayModes(int displayId) {
1401             Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
1402             if (d == null) {
1403                 // We can occasionally get a display added or changed event for a display that was
1404                 // subsequently removed, which means this returns null. Check this case and bail
1405                 // out early; if it gets re-attached we'll eventually get another call back for it.
1406                 return;
1407             }
1408             DisplayInfo info = new DisplayInfo();
1409             d.getDisplayInfo(info);
1410             boolean changed = false;
1411             synchronized (mLock) {
1412                 if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) {
1413                     mSupportedModesByDisplay.put(displayId, info.supportedModes);
1414                     changed = true;
1415                 }
1416                 if (!Objects.equals(mDefaultModeByDisplay.get(displayId), info.getDefaultMode())) {
1417                     changed = true;
1418                     mDefaultModeByDisplay.put(displayId, info.getDefaultMode());
1419                 }
1420                 if (changed) {
1421                     notifyDesiredDisplayModeSpecsChangedLocked();
1422                 }
1423             }
1424         }
1425     }
1426 
1427     /**
1428      * This class manages brightness threshold for switching between 60 hz and higher refresh rate.
1429      * See more information at the definition of
1430      * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and
1431      * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
1432      */
1433     @VisibleForTesting
1434     public class BrightnessObserver extends ContentObserver {
1435         private final static int LIGHT_SENSOR_RATE_MS = 250;
1436         private int[] mLowDisplayBrightnessThresholds;
1437         private int[] mLowAmbientBrightnessThresholds;
1438         private int[] mHighDisplayBrightnessThresholds;
1439         private int[] mHighAmbientBrightnessThresholds;
1440         // valid threshold if any item from the array >= 0
1441         private boolean mShouldObserveDisplayLowChange;
1442         private boolean mShouldObserveAmbientLowChange;
1443         private boolean mShouldObserveDisplayHighChange;
1444         private boolean mShouldObserveAmbientHighChange;
1445         private boolean mLoggingEnabled;
1446 
1447         private SensorManager mSensorManager;
1448         private Sensor mLightSensor;
1449         private final LightSensorEventListener mLightSensorListener =
1450                 new LightSensorEventListener();
1451         // Take it as low brightness before valid sensor data comes
1452         private float mAmbientLux = -1.0f;
1453         private AmbientFilter mAmbientFilter;
1454         private int mBrightness = -1;
1455 
1456         private final Context mContext;
1457 
1458         // Enable light sensor only when mShouldObserveAmbientLowChange is true or
1459         // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
1460         // changeable and low power mode off. After initialization, these states will
1461         // be updated from the same handler thread.
1462         private int mDefaultDisplayState = Display.STATE_UNKNOWN;
1463         private boolean mRefreshRateChangeable = false;
1464         private boolean mLowPowerModeEnabled = false;
1465 
1466         private int mRefreshRateInLowZone;
1467         private int mRefreshRateInHighZone;
1468 
BrightnessObserver(Context context, Handler handler)1469         BrightnessObserver(Context context, Handler handler) {
1470             super(handler);
1471             mContext = context;
1472             mLowDisplayBrightnessThresholds = context.getResources().getIntArray(
1473                     R.array.config_brightnessThresholdsOfPeakRefreshRate);
1474             mLowAmbientBrightnessThresholds = context.getResources().getIntArray(
1475                     R.array.config_ambientThresholdsOfPeakRefreshRate);
1476 
1477             if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
1478                 throw new RuntimeException("display low brightness threshold array and ambient "
1479                         + "brightness threshold array have different length: "
1480                         + "displayBrightnessThresholds="
1481                         + Arrays.toString(mLowDisplayBrightnessThresholds)
1482                         + ", ambientBrightnessThresholds="
1483                         + Arrays.toString(mLowAmbientBrightnessThresholds));
1484             }
1485 
1486             mHighDisplayBrightnessThresholds = context.getResources().getIntArray(
1487                     R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
1488             mHighAmbientBrightnessThresholds = context.getResources().getIntArray(
1489                     R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
1490             if (mHighDisplayBrightnessThresholds.length
1491                     != mHighAmbientBrightnessThresholds.length) {
1492                 throw new RuntimeException("display high brightness threshold array and ambient "
1493                         + "brightness threshold array have different length: "
1494                         + "displayBrightnessThresholds="
1495                         + Arrays.toString(mHighDisplayBrightnessThresholds)
1496                         + ", ambientBrightnessThresholds="
1497                         + Arrays.toString(mHighAmbientBrightnessThresholds));
1498             }
1499             mRefreshRateInHighZone = context.getResources().getInteger(
1500                     R.integer.config_fixedRefreshRateInHighZone);
1501         }
1502 
1503         /**
1504          * @return the refresh to lock to when in a low brightness zone
1505          */
1506         @VisibleForTesting
getRefreshRateInLowZone()1507         int getRefreshRateInLowZone() {
1508             return mRefreshRateInLowZone;
1509         }
1510 
1511         /**
1512          * @return the display brightness thresholds for the low brightness zones
1513          */
1514         @VisibleForTesting
getLowDisplayBrightnessThresholds()1515         int[] getLowDisplayBrightnessThresholds() {
1516             return mLowDisplayBrightnessThresholds;
1517         }
1518 
1519         /**
1520          * @return the ambient brightness thresholds for the low brightness zones
1521          */
1522         @VisibleForTesting
getLowAmbientBrightnessThresholds()1523         int[] getLowAmbientBrightnessThresholds() {
1524             return mLowAmbientBrightnessThresholds;
1525         }
1526 
registerLightSensor(SensorManager sensorManager, Sensor lightSensor)1527         public void registerLightSensor(SensorManager sensorManager, Sensor lightSensor) {
1528             mSensorManager = sensorManager;
1529             mLightSensor = lightSensor;
1530 
1531             mSensorManager.registerListener(mLightSensorListener,
1532                     mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
1533         }
1534 
observe(SensorManager sensorManager)1535         public void observe(SensorManager sensorManager) {
1536             mSensorManager = sensorManager;
1537             final ContentResolver cr = mContext.getContentResolver();
1538             mBrightness = Settings.System.getIntForUser(cr,
1539                     Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
1540 
1541             // DeviceConfig is accessible after system ready.
1542             int[] lowDisplayBrightnessThresholds =
1543                     mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds();
1544             int[] lowAmbientBrightnessThresholds =
1545                     mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds();
1546 
1547             if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null
1548                     && lowDisplayBrightnessThresholds.length
1549                     == lowAmbientBrightnessThresholds.length) {
1550                 mLowDisplayBrightnessThresholds = lowDisplayBrightnessThresholds;
1551                 mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds;
1552             }
1553 
1554 
1555             int[] highDisplayBrightnessThresholds =
1556                     mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds();
1557             int[] highAmbientBrightnessThresholds =
1558                     mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds();
1559 
1560             if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null
1561                     && highDisplayBrightnessThresholds.length
1562                     == highAmbientBrightnessThresholds.length) {
1563                 mHighDisplayBrightnessThresholds = highDisplayBrightnessThresholds;
1564                 mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds;
1565             }
1566 
1567             mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone();
1568             mRefreshRateInHighZone = mDeviceConfigDisplaySettings.getRefreshRateInHighZone();
1569 
1570             restartObserver();
1571             mDeviceConfigDisplaySettings.startListening();
1572         }
1573 
setLoggingEnabled(boolean loggingEnabled)1574         public void setLoggingEnabled(boolean loggingEnabled) {
1575             if (mLoggingEnabled == loggingEnabled) {
1576                 return;
1577             }
1578             mLoggingEnabled = loggingEnabled;
1579             mLightSensorListener.setLoggingEnabled(loggingEnabled);
1580         }
1581 
onRefreshRateSettingChangedLocked(float min, float max)1582         public void onRefreshRateSettingChangedLocked(float min, float max) {
1583             boolean changeable = (max - min > 1f && max > 60f);
1584             if (mRefreshRateChangeable != changeable) {
1585                 mRefreshRateChangeable = changeable;
1586                 updateSensorStatus();
1587                 if (!changeable) {
1588                     // Revoke previous vote from BrightnessObserver
1589                     updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE, null);
1590                     updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, null);
1591                 }
1592             }
1593         }
1594 
onLowPowerModeEnabledLocked(boolean b)1595         public void onLowPowerModeEnabledLocked(boolean b) {
1596             if (mLowPowerModeEnabled != b) {
1597                 mLowPowerModeEnabled = b;
1598                 updateSensorStatus();
1599             }
1600         }
1601 
onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds, int[] ambientThresholds)1602         public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds,
1603                 int[] ambientThresholds) {
1604             if (displayThresholds != null && ambientThresholds != null
1605                     && displayThresholds.length == ambientThresholds.length) {
1606                 mLowDisplayBrightnessThresholds = displayThresholds;
1607                 mLowAmbientBrightnessThresholds = ambientThresholds;
1608             } else {
1609                 // Invalid or empty. Use device default.
1610                 mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray(
1611                         R.array.config_brightnessThresholdsOfPeakRefreshRate);
1612                 mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray(
1613                         R.array.config_ambientThresholdsOfPeakRefreshRate);
1614             }
1615             restartObserver();
1616         }
1617 
onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate)1618         public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) {
1619             if (refreshRate != mRefreshRateInLowZone) {
1620                 mRefreshRateInLowZone = refreshRate;
1621                 restartObserver();
1622             }
1623         }
1624 
onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds, int[] ambientThresholds)1625         public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds,
1626                 int[] ambientThresholds) {
1627             if (displayThresholds != null && ambientThresholds != null
1628                     && displayThresholds.length == ambientThresholds.length) {
1629                 mHighDisplayBrightnessThresholds = displayThresholds;
1630                 mHighAmbientBrightnessThresholds = ambientThresholds;
1631             } else {
1632                 // Invalid or empty. Use device default.
1633                 mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray(
1634                         R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
1635                 mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray(
1636                         R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
1637             }
1638             restartObserver();
1639         }
1640 
onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate)1641         public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) {
1642             if (refreshRate != mRefreshRateInHighZone) {
1643                 mRefreshRateInHighZone = refreshRate;
1644                 restartObserver();
1645             }
1646         }
1647 
dumpLocked(PrintWriter pw)1648         public void dumpLocked(PrintWriter pw) {
1649             pw.println("  BrightnessObserver");
1650             pw.println("    mAmbientLux: " + mAmbientLux);
1651             pw.println("    mBrightness: " + mBrightness);
1652             pw.println("    mDefaultDisplayState: " + mDefaultDisplayState);
1653             pw.println("    mLowPowerModeEnabled: " + mLowPowerModeEnabled);
1654             pw.println("    mRefreshRateChangeable: " + mRefreshRateChangeable);
1655             pw.println("    mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
1656             pw.println("    mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange);
1657             pw.println("    mRefreshRateInLowZone: " + mRefreshRateInLowZone);
1658 
1659             for (int d : mLowDisplayBrightnessThresholds) {
1660                 pw.println("    mDisplayLowBrightnessThreshold: " + d);
1661             }
1662 
1663             for (int d : mLowAmbientBrightnessThresholds) {
1664                 pw.println("    mAmbientLowBrightnessThreshold: " + d);
1665             }
1666 
1667             pw.println("    mShouldObserveDisplayHighChange: " + mShouldObserveDisplayHighChange);
1668             pw.println("    mShouldObserveAmbientHighChange: " + mShouldObserveAmbientHighChange);
1669             pw.println("    mRefreshRateInHighZone: " + mRefreshRateInHighZone);
1670 
1671             for (int d : mHighDisplayBrightnessThresholds) {
1672                 pw.println("    mDisplayHighBrightnessThresholds: " + d);
1673             }
1674 
1675             for (int d : mHighAmbientBrightnessThresholds) {
1676                 pw.println("    mAmbientHighBrightnessThresholds: " + d);
1677             }
1678 
1679             mLightSensorListener.dumpLocked(pw);
1680 
1681             if (mAmbientFilter != null) {
1682                 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
1683                 mAmbientFilter.dump(ipw);
1684             }
1685         }
1686 
onDisplayChanged(int displayId)1687         public void onDisplayChanged(int displayId) {
1688             if (displayId == Display.DEFAULT_DISPLAY) {
1689                 updateDefaultDisplayState();
1690             }
1691         }
1692 
1693         @Override
onChange(boolean selfChange, Uri uri, int userId)1694         public void onChange(boolean selfChange, Uri uri, int userId) {
1695             synchronized (mLock) {
1696                 final ContentResolver cr = mContext.getContentResolver();
1697                 int brightness = Settings.System.getIntForUser(cr,
1698                         Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
1699                 if (brightness != mBrightness) {
1700                     mBrightness = brightness;
1701                     onBrightnessChangedLocked();
1702                 }
1703             }
1704         }
1705 
restartObserver()1706         private void restartObserver() {
1707             final ContentResolver cr = mContext.getContentResolver();
1708 
1709             if (mRefreshRateInLowZone > 0) {
1710                 mShouldObserveDisplayLowChange = hasValidThreshold(
1711                         mLowDisplayBrightnessThresholds);
1712                 mShouldObserveAmbientLowChange = hasValidThreshold(
1713                         mLowAmbientBrightnessThresholds);
1714             } else {
1715                 mShouldObserveDisplayLowChange = false;
1716                 mShouldObserveAmbientLowChange = false;
1717             }
1718 
1719             if (mRefreshRateInHighZone > 0) {
1720                 mShouldObserveDisplayHighChange = hasValidThreshold(
1721                         mHighDisplayBrightnessThresholds);
1722                 mShouldObserveAmbientHighChange = hasValidThreshold(
1723                         mHighAmbientBrightnessThresholds);
1724             } else {
1725                 mShouldObserveDisplayHighChange = false;
1726                 mShouldObserveAmbientHighChange = false;
1727             }
1728 
1729             if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) {
1730                 // Content Service does not check if an listener has already been registered.
1731                 // To ensure only one listener is registered, force an unregistration first.
1732                 mInjector.unregisterBrightnessObserver(cr, this);
1733                 mInjector.registerBrightnessObserver(cr, this);
1734             } else {
1735                 mInjector.unregisterBrightnessObserver(cr, this);
1736             }
1737 
1738             if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
1739                 Resources resources = mContext.getResources();
1740                 String lightSensorType = resources.getString(
1741                         com.android.internal.R.string.config_displayLightSensorType);
1742 
1743                 Sensor lightSensor = null;
1744                 if (!TextUtils.isEmpty(lightSensorType)) {
1745                     List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
1746                     for (int i = 0; i < sensors.size(); i++) {
1747                         Sensor sensor = sensors.get(i);
1748                         if (lightSensorType.equals(sensor.getStringType())) {
1749                             lightSensor = sensor;
1750                             break;
1751                         }
1752                     }
1753                 }
1754 
1755                 if (lightSensor == null) {
1756                     lightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
1757                 }
1758 
1759                 if (lightSensor != null) {
1760                     final Resources res = mContext.getResources();
1761 
1762                     mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
1763                     mLightSensor = lightSensor;
1764                 }
1765             } else {
1766                 mAmbientFilter = null;
1767                 mLightSensor = null;
1768             }
1769 
1770             if (mRefreshRateChangeable) {
1771                 updateSensorStatus();
1772                 synchronized (mLock) {
1773                     onBrightnessChangedLocked();
1774                 }
1775             }
1776         }
1777 
1778         /**
1779          * Checks to see if at least one value is positive, in which case it is necessary to listen
1780          * to value changes.
1781          */
hasValidThreshold(int[] a)1782         private boolean hasValidThreshold(int[] a) {
1783             for (int d: a) {
1784                 if (d >= 0) {
1785                     return true;
1786                 }
1787             }
1788 
1789             return false;
1790         }
1791 
isInsideLowZone(int brightness, float lux)1792         private boolean isInsideLowZone(int brightness, float lux) {
1793             for (int i = 0; i < mLowDisplayBrightnessThresholds.length; i++) {
1794                 int disp = mLowDisplayBrightnessThresholds[i];
1795                 int ambi = mLowAmbientBrightnessThresholds[i];
1796 
1797                 if (disp >= 0 && ambi >= 0) {
1798                     if (brightness <= disp && lux <= ambi) {
1799                         return true;
1800                     }
1801                 } else if (disp >= 0) {
1802                     if (brightness <= disp) {
1803                         return true;
1804                     }
1805                 } else if (ambi >= 0) {
1806                     if (lux <= ambi) {
1807                         return true;
1808                     }
1809                 }
1810             }
1811 
1812             return false;
1813         }
1814 
isInsideHighZone(int brightness, float lux)1815         private boolean isInsideHighZone(int brightness, float lux) {
1816             for (int i = 0; i < mHighDisplayBrightnessThresholds.length; i++) {
1817                 int disp = mHighDisplayBrightnessThresholds[i];
1818                 int ambi = mHighAmbientBrightnessThresholds[i];
1819 
1820                 if (disp >= 0 && ambi >= 0) {
1821                     if (brightness >= disp && lux >= ambi) {
1822                         return true;
1823                     }
1824                 } else if (disp >= 0) {
1825                     if (brightness >= disp) {
1826                         return true;
1827                     }
1828                 } else if (ambi >= 0) {
1829                     if (lux >= ambi) {
1830                         return true;
1831                     }
1832                 }
1833             }
1834 
1835             return false;
1836         }
onBrightnessChangedLocked()1837         private void onBrightnessChangedLocked() {
1838             Vote refreshRateVote = null;
1839             Vote refreshRateSwitchingVote = null;
1840 
1841             if (mBrightness < 0) {
1842                 // Either the setting isn't available or we shouldn't be observing yet anyways.
1843                 // Either way, just bail out since there's nothing we can do here.
1844                 return;
1845             }
1846 
1847             boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux);
1848             if (insideLowZone) {
1849                 refreshRateVote =
1850                         Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
1851                 refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
1852             }
1853 
1854             boolean insideHighZone = hasValidHighZone()
1855                     && isInsideHighZone(mBrightness, mAmbientLux);
1856             if (insideHighZone) {
1857                 refreshRateVote =
1858                         Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
1859                 refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
1860             }
1861 
1862             if (mLoggingEnabled) {
1863                 Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " +  mAmbientLux
1864                         + ", Vote " + refreshRateVote);
1865             }
1866             updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE, refreshRateVote);
1867             updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, refreshRateSwitchingVote);
1868         }
1869 
hasValidLowZone()1870         private boolean hasValidLowZone() {
1871             return mRefreshRateInLowZone > 0
1872                     && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange);
1873         }
1874 
hasValidHighZone()1875         private boolean hasValidHighZone() {
1876             return mRefreshRateInHighZone > 0
1877                     && (mShouldObserveDisplayHighChange || mShouldObserveAmbientHighChange);
1878         }
1879 
updateDefaultDisplayState()1880         private void updateDefaultDisplayState() {
1881             Display display = mContext.getSystemService(DisplayManager.class)
1882                     .getDisplay(Display.DEFAULT_DISPLAY);
1883             if (display == null) {
1884                 return;
1885             }
1886 
1887             setDefaultDisplayState(display.getState());
1888         }
1889 
1890         @VisibleForTesting
setDefaultDisplayState(int state)1891         public void setDefaultDisplayState(int state) {
1892             if (mLoggingEnabled) {
1893                 Slog.d(TAG, "setDefaultDisplayState: mDefaultDisplayState = "
1894                         + mDefaultDisplayState + ", state = " + state);
1895             }
1896 
1897             if (mDefaultDisplayState != state) {
1898                 mDefaultDisplayState = state;
1899                 updateSensorStatus();
1900             }
1901         }
1902 
updateSensorStatus()1903         private void updateSensorStatus() {
1904             if (mSensorManager == null || mLightSensorListener == null) {
1905                 return;
1906             }
1907 
1908             if (mLoggingEnabled) {
1909                 Slog.d(TAG, "updateSensorStatus: mShouldObserveAmbientLowChange = "
1910                         + mShouldObserveAmbientLowChange + ", mShouldObserveAmbientHighChange = "
1911                         + mShouldObserveAmbientHighChange);
1912                 Slog.d(TAG, "updateSensorStatus: mLowPowerModeEnabled = "
1913                         + mLowPowerModeEnabled + ", mRefreshRateChangeable = "
1914                         + mRefreshRateChangeable);
1915             }
1916 
1917             if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
1918                      && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
1919                 mSensorManager.registerListener(mLightSensorListener,
1920                         mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
1921                 if (mLoggingEnabled) {
1922                     Slog.d(TAG, "updateSensorStatus: registerListener");
1923                 }
1924             } else {
1925                 mLightSensorListener.removeCallbacks();
1926                 mSensorManager.unregisterListener(mLightSensorListener);
1927                 if (mLoggingEnabled) {
1928                     Slog.d(TAG, "updateSensorStatus: unregisterListener");
1929                 }
1930             }
1931         }
1932 
isDeviceActive()1933         private boolean isDeviceActive() {
1934             return mDefaultDisplayState == Display.STATE_ON;
1935         }
1936 
1937         private final class LightSensorEventListener implements SensorEventListener {
1938             final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
1939             private float mLastSensorData;
1940             private long mTimestamp;
1941             private boolean mLoggingEnabled;
1942 
dumpLocked(PrintWriter pw)1943             public void dumpLocked(PrintWriter pw) {
1944                 pw.println("    mLastSensorData: " + mLastSensorData);
1945                 pw.println("    mTimestamp: " + formatTimestamp(mTimestamp));
1946             }
1947 
1948 
setLoggingEnabled(boolean loggingEnabled)1949             public void setLoggingEnabled(boolean loggingEnabled) {
1950                 if (mLoggingEnabled == loggingEnabled) {
1951                     return;
1952                 }
1953                 mLoggingEnabled = loggingEnabled;
1954             }
1955 
1956             @Override
onSensorChanged(SensorEvent event)1957             public void onSensorChanged(SensorEvent event) {
1958                 mLastSensorData = event.values[0];
1959                 if (mLoggingEnabled) {
1960                     Slog.d(TAG, "On sensor changed: " + mLastSensorData);
1961                 }
1962 
1963                 boolean lowZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
1964                         mLowAmbientBrightnessThresholds);
1965                 boolean highZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
1966                         mHighAmbientBrightnessThresholds);
1967                 if ((lowZoneChanged && mLastSensorData < mAmbientLux)
1968                         || (highZoneChanged && mLastSensorData > mAmbientLux)) {
1969                     // Easier to see flicker at lower brightness environment or high brightness
1970                     // environment. Forget the history to get immediate response.
1971                     if (mAmbientFilter != null) {
1972                         mAmbientFilter.clear();
1973                     }
1974                 }
1975 
1976                 long now = SystemClock.uptimeMillis();
1977                 mTimestamp = System.currentTimeMillis();
1978                 if (mAmbientFilter != null) {
1979                     mAmbientFilter.addValue(now, mLastSensorData);
1980                 }
1981 
1982                 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1983                 processSensorData(now);
1984 
1985                 if ((lowZoneChanged && mLastSensorData > mAmbientLux)
1986                         || (highZoneChanged && mLastSensorData < mAmbientLux)) {
1987                     // Sensor may not report new event if there is no brightness change.
1988                     // Need to keep querying the temporal filter for the latest estimation,
1989                     // until sensor readout and filter estimation are in the same zone or
1990                     // is interrupted by a new sensor event.
1991                     mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
1992                 }
1993             }
1994 
1995             @Override
onAccuracyChanged(Sensor sensor, int accuracy)1996             public void onAccuracyChanged(Sensor sensor, int accuracy) {
1997                 // Not used.
1998             }
1999 
removeCallbacks()2000             public void removeCallbacks() {
2001                 mHandler.removeCallbacks(mInjectSensorEventRunnable);
2002             }
2003 
formatTimestamp(long time)2004             private String formatTimestamp(long time) {
2005                 SimpleDateFormat dateFormat =
2006                         new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
2007                 return dateFormat.format(new Date(time));
2008             }
2009 
processSensorData(long now)2010             private void processSensorData(long now) {
2011                 if (mAmbientFilter != null) {
2012                     mAmbientLux = mAmbientFilter.getEstimate(now);
2013                 } else {
2014                     mAmbientLux = mLastSensorData;
2015                 }
2016 
2017                 synchronized (mLock) {
2018                     onBrightnessChangedLocked();
2019                 }
2020             }
2021 
isDifferentZone(float lux1, float lux2, int[] luxThresholds)2022             private boolean isDifferentZone(float lux1, float lux2, int[] luxThresholds) {
2023                 for (final float boundary : luxThresholds) {
2024                     // Test each boundary. See if the current value and the new value are at
2025                     // different sides.
2026                     if ((lux1 <= boundary && lux2 > boundary)
2027                             || (lux1 > boundary && lux2 <= boundary)) {
2028                         return true;
2029                     }
2030                 }
2031 
2032                 return false;
2033             }
2034 
2035             private final Runnable mInjectSensorEventRunnable = new Runnable() {
2036                 @Override
2037                 public void run() {
2038                     long now = SystemClock.uptimeMillis();
2039                     // No need to really inject the last event into a temporal filter.
2040                     processSensorData(now);
2041 
2042                     // Inject next event if there is a possible zone change.
2043                     if (isDifferentZone(mLastSensorData, mAmbientLux,
2044                             mLowAmbientBrightnessThresholds)
2045                             || isDifferentZone(mLastSensorData, mAmbientLux,
2046                             mHighAmbientBrightnessThresholds)) {
2047                         mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
2048                     }
2049                 }
2050             };
2051         }
2052     }
2053 
2054     private class UdfpsObserver extends IUdfpsHbmListener.Stub {
2055         private final SparseBooleanArray mLocalHbmEnabled = new SparseBooleanArray();
2056         private final SparseBooleanArray mGlobalHbmEnabled = new SparseBooleanArray();
2057 
observe()2058         public void observe() {
2059             StatusBarManagerInternal statusBar =
2060                     LocalServices.getService(StatusBarManagerInternal.class);
2061             statusBar.setUdfpsHbmListener(this);
2062         }
2063 
2064         @Override
onHbmEnabled(int hbmType, int displayId)2065         public void onHbmEnabled(int hbmType, int displayId) {
2066             synchronized (mLock) {
2067                 updateHbmStateLocked(hbmType, displayId, true /*enabled*/);
2068             }
2069         }
2070 
2071         @Override
onHbmDisabled(int hbmType, int displayId)2072         public void onHbmDisabled(int hbmType, int displayId) {
2073             synchronized (mLock) {
2074                 updateHbmStateLocked(hbmType, displayId, false /*enabled*/);
2075             }
2076         }
2077 
updateHbmStateLocked(int hbmType, int displayId, boolean enabled)2078         private void updateHbmStateLocked(int hbmType, int displayId, boolean enabled) {
2079             switch (hbmType) {
2080                 case UdfpsObserver.LOCAL_HBM:
2081                     mLocalHbmEnabled.put(displayId, enabled);
2082                     break;
2083                 case UdfpsObserver.GLOBAL_HBM:
2084                     mGlobalHbmEnabled.put(displayId, enabled);
2085                     break;
2086                 default:
2087                     Slog.w(TAG, "Unknown HBM type reported. Ignoring.");
2088                     return;
2089             }
2090             updateVoteLocked(displayId);
2091         }
2092 
updateVoteLocked(int displayId)2093         private void updateVoteLocked(int displayId) {
2094             final Vote vote;
2095             if (mGlobalHbmEnabled.get(displayId)) {
2096                 vote = Vote.forRefreshRates(60f, 60f);
2097             } else if (mLocalHbmEnabled.get(displayId)) {
2098                 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
2099                 float maxRefreshRate = 0f;
2100                 for (Display.Mode mode : modes) {
2101                     if (mode.getRefreshRate() > maxRefreshRate) {
2102                         maxRefreshRate = mode.getRefreshRate();
2103                     }
2104                 }
2105                 vote = Vote.forRefreshRates(maxRefreshRate, maxRefreshRate);
2106             } else {
2107                 vote = null;
2108             }
2109 
2110             DisplayModeDirector.this.updateVoteLocked(displayId, Vote.PRIORITY_UDFPS, vote);
2111         }
2112 
dumpLocked(PrintWriter pw)2113         void dumpLocked(PrintWriter pw) {
2114             pw.println("  UdfpsObserver");
2115             pw.println("    mLocalHbmEnabled: ");
2116             for (int i = 0; i < mLocalHbmEnabled.size(); i++) {
2117                 final int displayId = mLocalHbmEnabled.keyAt(i);
2118                 final String enabled = mLocalHbmEnabled.valueAt(i) ? "enabled" : "disabled";
2119                 pw.println("      Display " + displayId + ": " + enabled);
2120             }
2121             pw.println("    mGlobalHbmEnabled: ");
2122             for (int i = 0; i < mGlobalHbmEnabled.size(); i++) {
2123                 final int displayId = mGlobalHbmEnabled.keyAt(i);
2124                 final String enabled = mGlobalHbmEnabled.valueAt(i) ? "enabled" : "disabled";
2125                 pw.println("      Display " + displayId + ": " + enabled);
2126             }
2127 
2128         }
2129     }
2130 
2131     private static final class SensorObserver implements ProximityActiveListener,
2132             DisplayManager.DisplayListener {
2133         private final String mProximitySensorName = null;
2134         private final String mProximitySensorType = Sensor.STRING_TYPE_PROXIMITY;
2135 
2136         private final BallotBox mBallotBox;
2137         private final Context mContext;
2138         private final Injector mInjector;
2139         @GuardedBy("mSensorObserverLock")
2140         private final SparseBooleanArray mDozeStateByDisplay = new SparseBooleanArray();
2141         private final Object mSensorObserverLock = new Object();
2142 
2143         private DisplayManager mDisplayManager;
2144         private DisplayManagerInternal mDisplayManagerInternal;
2145         @GuardedBy("mSensorObserverLock")
2146         private boolean mIsProxActive = false;
2147 
SensorObserver(Context context, BallotBox ballotBox, Injector injector)2148         SensorObserver(Context context, BallotBox ballotBox, Injector injector) {
2149             mContext = context;
2150             mBallotBox = ballotBox;
2151             mInjector = injector;
2152         }
2153 
2154         @Override
onProximityActive(boolean isActive)2155         public void onProximityActive(boolean isActive) {
2156             synchronized (mSensorObserverLock) {
2157                 if (mIsProxActive != isActive) {
2158                     mIsProxActive = isActive;
2159                     recalculateVotesLocked();
2160                 }
2161             }
2162         }
2163 
observe()2164         public void observe() {
2165             mDisplayManager = mContext.getSystemService(DisplayManager.class);
2166             mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
2167 
2168             final SensorManagerInternal sensorManager =
2169                     LocalServices.getService(SensorManagerInternal.class);
2170             sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this);
2171 
2172             synchronized (mSensorObserverLock) {
2173                 for (Display d : mDisplayManager.getDisplays()) {
2174                     mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d));
2175                 }
2176             }
2177             mInjector.registerDisplayListener(this, BackgroundThread.getHandler(),
2178                     DisplayManager.EVENT_FLAG_DISPLAY_ADDED
2179                             | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
2180                             | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
2181         }
2182 
recalculateVotesLocked()2183         private void recalculateVotesLocked() {
2184             final Display[] displays = mDisplayManager.getDisplays();
2185             for (Display d : displays) {
2186                 int displayId = d.getDisplayId();
2187                 Vote vote = null;
2188                 if (mIsProxActive && !mDozeStateByDisplay.get(displayId)) {
2189                     final RefreshRateRange rate =
2190                             mDisplayManagerInternal.getRefreshRateForDisplayAndSensor(
2191                                     displayId, mProximitySensorName, mProximitySensorType);
2192                     if (rate != null) {
2193                         vote = Vote.forRefreshRates(rate.min, rate.max);
2194                     }
2195                 }
2196                 mBallotBox.vote(displayId, Vote.PRIORITY_PROXIMITY, vote);
2197             }
2198         }
2199 
dumpLocked(PrintWriter pw)2200         void dumpLocked(PrintWriter pw) {
2201             pw.println("  SensorObserver");
2202             synchronized (mSensorObserverLock) {
2203                 pw.println("    mIsProxActive=" + mIsProxActive);
2204                 pw.println("    mDozeStateByDisplay:");
2205                 for (int i = 0; i < mDozeStateByDisplay.size(); i++) {
2206                     final int id = mDozeStateByDisplay.keyAt(i);
2207                     final boolean dozed = mDozeStateByDisplay.valueAt(i);
2208                     pw.println("      " + id + " -> " + dozed);
2209                 }
2210             }
2211         }
2212 
2213         @Override
onDisplayAdded(int displayId)2214         public void onDisplayAdded(int displayId) {
2215             boolean isDozeState = mInjector.isDozeState(mDisplayManager.getDisplay(displayId));
2216             synchronized (mSensorObserverLock) {
2217                 mDozeStateByDisplay.put(displayId, isDozeState);
2218                 recalculateVotesLocked();
2219             }
2220         }
2221 
2222         @Override
onDisplayChanged(int displayId)2223         public void onDisplayChanged(int displayId) {
2224             boolean wasDozeState = mDozeStateByDisplay.get(displayId);
2225             synchronized (mSensorObserverLock) {
2226                 mDozeStateByDisplay.put(displayId,
2227                         mInjector.isDozeState(mDisplayManager.getDisplay(displayId)));
2228                 if (wasDozeState != mDozeStateByDisplay.get(displayId)) {
2229                     recalculateVotesLocked();
2230                 }
2231             }
2232         }
2233 
2234         @Override
onDisplayRemoved(int displayId)2235         public void onDisplayRemoved(int displayId) {
2236             synchronized (mSensorObserverLock) {
2237                 mDozeStateByDisplay.delete(displayId);
2238                 recalculateVotesLocked();
2239             }
2240         }
2241     }
2242 
2243     /**
2244      * Listens to DisplayManager for HBM status and applies any refresh-rate restrictions for
2245      * HBM that are associated with that display. Restrictions are retrieved from
2246      * DisplayManagerInternal but originate in the display-device-config file.
2247      */
2248     private static class HbmObserver implements DisplayManager.DisplayListener {
2249         private final BallotBox mBallotBox;
2250         private final Handler mHandler;
2251         private final SparseBooleanArray mHbmEnabled = new SparseBooleanArray();
2252         private final Injector mInjector;
2253 
2254         private DisplayManagerInternal mDisplayManagerInternal;
2255 
HbmObserver(Injector injector, BallotBox ballotBox, Handler handler)2256         HbmObserver(Injector injector, BallotBox ballotBox, Handler handler) {
2257             mInjector = injector;
2258             mBallotBox = ballotBox;
2259             mHandler = handler;
2260         }
2261 
observe()2262         public void observe() {
2263             mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
2264             mInjector.registerDisplayListener(this, mHandler,
2265                     DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
2266                     | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
2267         }
2268 
2269         @Override
onDisplayAdded(int displayId)2270         public void onDisplayAdded(int displayId) {}
2271 
2272         @Override
onDisplayRemoved(int displayId)2273         public void onDisplayRemoved(int displayId) {
2274             mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
2275         }
2276 
2277         @Override
onDisplayChanged(int displayId)2278         public void onDisplayChanged(int displayId) {
2279             final BrightnessInfo info = mInjector.getBrightnessInfo(displayId);
2280             if (info == null) {
2281                 // Display no longer there. Assume we'll get an onDisplayRemoved very soon.
2282                 return;
2283             }
2284             final boolean isHbmEnabled =
2285                     info.highBrightnessMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
2286             if (isHbmEnabled == mHbmEnabled.get(displayId)) {
2287                 // no change, ignore.
2288                 return;
2289             }
2290             Vote vote = null;
2291             mHbmEnabled.put(displayId, isHbmEnabled);
2292             if (isHbmEnabled) {
2293                 final List<RefreshRateLimitation> limits =
2294                         mDisplayManagerInternal.getRefreshRateLimitations(displayId);
2295                 for (int i = 0; limits != null && i < limits.size(); i++) {
2296                     final RefreshRateLimitation limitation = limits.get(i);
2297                     if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
2298                         vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max);
2299                         break;
2300                     }
2301                 }
2302             }
2303             mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
2304         }
2305 
dumpLocked(PrintWriter pw)2306         void dumpLocked(PrintWriter pw) {
2307             pw.println("   HbmObserver");
2308             pw.println("     mHbmEnabled: " + mHbmEnabled);
2309         }
2310     }
2311 
2312     private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
DeviceConfigDisplaySettings()2313         public DeviceConfigDisplaySettings() {
2314         }
2315 
startListening()2316         public void startListening() {
2317             mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
2318                     BackgroundThread.getExecutor(), this);
2319         }
2320 
2321         /*
2322          * Return null if no such property or wrong format (not comma separated integers).
2323          */
getLowDisplayBrightnessThresholds()2324         public int[] getLowDisplayBrightnessThresholds() {
2325             return getIntArrayProperty(
2326                     DisplayManager.DeviceConfig.
2327                             KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
2328         }
2329 
2330         /*
2331          * Return null if no such property or wrong format (not comma separated integers).
2332          */
getLowAmbientBrightnessThresholds()2333         public int[] getLowAmbientBrightnessThresholds() {
2334             return getIntArrayProperty(
2335                     DisplayManager.DeviceConfig.
2336                             KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
2337         }
2338 
getRefreshRateInLowZone()2339         public int getRefreshRateInLowZone() {
2340             int defaultRefreshRateInZone = mContext.getResources().getInteger(
2341                     R.integer.config_defaultRefreshRateInZone);
2342 
2343             int refreshRate = mDeviceConfig.getInt(
2344                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
2345                     DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
2346                     defaultRefreshRateInZone);
2347 
2348             return refreshRate;
2349         }
2350 
2351         /*
2352          * Return null if no such property or wrong format (not comma separated integers).
2353          */
getHighDisplayBrightnessThresholds()2354         public int[] getHighDisplayBrightnessThresholds() {
2355             return getIntArrayProperty(
2356                     DisplayManager.DeviceConfig
2357                             .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
2358         }
2359 
2360         /*
2361          * Return null if no such property or wrong format (not comma separated integers).
2362          */
getHighAmbientBrightnessThresholds()2363         public int[] getHighAmbientBrightnessThresholds() {
2364             return getIntArrayProperty(
2365                     DisplayManager.DeviceConfig
2366                             .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
2367         }
2368 
getRefreshRateInHighZone()2369         public int getRefreshRateInHighZone() {
2370             int defaultRefreshRateInZone = mContext.getResources().getInteger(
2371                     R.integer.config_fixedRefreshRateInHighZone);
2372 
2373             int refreshRate = mDeviceConfig.getInt(
2374                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
2375                     DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
2376                     defaultRefreshRateInZone);
2377 
2378             return refreshRate;
2379         }
2380 
2381         /*
2382          * Return null if no such property
2383          */
getDefaultPeakRefreshRate()2384         public Float getDefaultPeakRefreshRate() {
2385             float defaultPeakRefreshRate = mDeviceConfig.getFloat(
2386                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
2387                     DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
2388 
2389             if (defaultPeakRefreshRate == -1) {
2390                 return null;
2391             }
2392             return defaultPeakRefreshRate;
2393         }
2394 
2395         @Override
onPropertiesChanged(@onNull DeviceConfig.Properties properties)2396         public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
2397             Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
2398             mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
2399                     defaultPeakRefreshRate).sendToTarget();
2400 
2401             int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds();
2402             int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds();
2403             int refreshRateInLowZone = getRefreshRateInLowZone();
2404 
2405             mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
2406                     new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
2407                     .sendToTarget();
2408             mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0)
2409                     .sendToTarget();
2410 
2411             int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
2412             int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds();
2413             int refreshRateInHighZone = getRefreshRateInHighZone();
2414 
2415             mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
2416                     new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
2417                     .sendToTarget();
2418             mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0)
2419                     .sendToTarget();
2420         }
2421 
getIntArrayProperty(String prop)2422         private int[] getIntArrayProperty(String prop) {
2423             String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
2424                     null);
2425 
2426             if (strArray != null) {
2427                 return parseIntArray(strArray);
2428             }
2429 
2430             return null;
2431         }
2432 
parseIntArray(@onNull String strArray)2433         private int[] parseIntArray(@NonNull String strArray) {
2434             String[] items = strArray.split(",");
2435             int[] array = new int[items.length];
2436 
2437             try {
2438                 for (int i = 0; i < array.length; i++) {
2439                     array[i] = Integer.parseInt(items[i]);
2440                 }
2441             } catch (NumberFormatException e) {
2442                 Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
2443                 array = null;
2444             }
2445 
2446             return array;
2447         }
2448     }
2449 
2450     interface Injector {
2451         // TODO: brightnessfloat: change this to the float setting
2452         Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
2453         Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
2454 
2455         @NonNull
getDeviceConfig()2456         DeviceConfigInterface getDeviceConfig();
2457 
registerBrightnessObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)2458         void registerBrightnessObserver(@NonNull ContentResolver cr,
2459                 @NonNull ContentObserver observer);
2460 
unregisterBrightnessObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)2461         void unregisterBrightnessObserver(@NonNull ContentResolver cr,
2462                 @NonNull ContentObserver observer);
2463 
registerPeakRefreshRateObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)2464         void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
2465                 @NonNull ContentObserver observer);
2466 
registerDisplayListener(@onNull DisplayManager.DisplayListener listener, Handler handler, long flags)2467         void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
2468                 Handler handler, long flags);
2469 
getBrightnessInfo(int displayId)2470         BrightnessInfo getBrightnessInfo(int displayId);
2471 
isDozeState(Display d)2472         boolean isDozeState(Display d);
2473     }
2474 
2475     @VisibleForTesting
2476     static class RealInjector implements Injector {
2477         private final Context mContext;
2478         private DisplayManager mDisplayManager;
2479 
RealInjector(Context context)2480         RealInjector(Context context) {
2481             mContext = context;
2482         }
2483 
2484         @Override
2485         @NonNull
getDeviceConfig()2486         public DeviceConfigInterface getDeviceConfig() {
2487             return DeviceConfigInterface.REAL;
2488         }
2489 
2490         @Override
registerBrightnessObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)2491         public void registerBrightnessObserver(@NonNull ContentResolver cr,
2492                 @NonNull ContentObserver observer) {
2493             cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/,
2494                     observer, UserHandle.USER_SYSTEM);
2495         }
2496 
2497         @Override
unregisterBrightnessObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)2498         public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
2499                 @NonNull ContentObserver observer) {
2500             cr.unregisterContentObserver(observer);
2501         }
2502 
2503         @Override
registerPeakRefreshRateObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)2504         public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
2505                 @NonNull ContentObserver observer) {
2506             cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
2507                     observer, UserHandle.USER_SYSTEM);
2508         }
2509 
2510         @Override
registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler, long flags)2511         public void registerDisplayListener(DisplayManager.DisplayListener listener,
2512                 Handler handler, long flags) {
2513             getDisplayManager().registerDisplayListener(listener, handler, flags);
2514         }
2515 
2516         @Override
getBrightnessInfo(int displayId)2517         public BrightnessInfo getBrightnessInfo(int displayId) {
2518             final Display display = getDisplayManager().getDisplay(displayId);
2519             if (display != null) {
2520                 return display.getBrightnessInfo();
2521             }
2522             return null;
2523         }
2524 
2525         @Override
isDozeState(Display d)2526         public boolean isDozeState(Display d) {
2527             if (d == null) {
2528                 return false;
2529             }
2530             return Display.isDozeState(d.getState());
2531         }
2532 
getDisplayManager()2533         private DisplayManager getDisplayManager() {
2534             if (mDisplayManager == null) {
2535                 mDisplayManager = mContext.getSystemService(DisplayManager.class);
2536             }
2537             return mDisplayManager;
2538         }
2539     }
2540 
2541     interface BallotBox {
vote(int displayId, int priority, Vote vote)2542         void vote(int displayId, int priority, Vote vote);
2543     }
2544 }
2545