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