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