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