• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.display.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                     // Revoke previous vote from BrightnessObserver
1816                     mVotesStorage.updateGlobalVote(Vote.PRIORITY_FLICKER_REFRESH_RATE, null);
1817                     mVotesStorage.updateGlobalVote(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, null);
1818                 }
1819             }
1820         }
1821 
onLowPowerModeEnabledLocked(boolean b)1822         private void onLowPowerModeEnabledLocked(boolean b) {
1823             if (mLowPowerModeEnabled != b) {
1824                 mLowPowerModeEnabled = b;
1825                 updateSensorStatus();
1826             }
1827         }
1828 
onDeviceConfigLowBrightnessThresholdsChanged(float[] displayThresholds, float[] ambientThresholds)1829         private void onDeviceConfigLowBrightnessThresholdsChanged(float[] displayThresholds,
1830                 float[] ambientThresholds) {
1831             if (displayThresholds != null && ambientThresholds != null
1832                     && displayThresholds.length == ambientThresholds.length) {
1833                 mLowDisplayBrightnessThresholds = displayThresholds;
1834                 mLowAmbientBrightnessThresholds = ambientThresholds;
1835             } else {
1836                 DisplayDeviceConfig displayDeviceConfig;
1837                 synchronized (mLock) {
1838                     displayDeviceConfig = mDefaultDisplayDeviceConfig;
1839                 }
1840                 mLowDisplayBrightnessThresholds = loadBrightnessThresholds(
1841                         () -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
1842                         () -> displayDeviceConfig.getLowDisplayBrightnessThresholds(),
1843                         R.array.config_brightnessThresholdsOfPeakRefreshRate,
1844                         displayDeviceConfig, /* attemptReadFromFeatureParams= */ false,
1845                         DeviceConfigParsingUtils::displayBrightnessThresholdsIntToFloat);
1846                 mLowAmbientBrightnessThresholds = loadBrightnessThresholds(
1847                         () -> mConfigParameterProvider.getLowAmbientBrightnessThresholds(),
1848                         () -> displayDeviceConfig.getLowAmbientBrightnessThresholds(),
1849                         R.array.config_ambientThresholdsOfPeakRefreshRate,
1850                         displayDeviceConfig, /* attemptReadFromFeatureParams= */ false,
1851                         DeviceConfigParsingUtils::ambientBrightnessThresholdsIntToFloat);
1852             }
1853             restartObserver();
1854         }
1855 
1856         /**
1857          * Used to reload the lower blocking zone refresh rate in case of changes in the
1858          * DeviceConfig properties.
1859          */
onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate)1860         public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) {
1861             if (refreshRate == -1) {
1862                 // Given there is no value available in DeviceConfig, lets not attempt loading it
1863                 // from there.
1864                 synchronized (mLock) {
1865                     loadRefreshRateInLowZone(mDefaultDisplayDeviceConfig,
1866                             /* attemptReadFromFeatureParams= */ false);
1867                 }
1868                 restartObserver();
1869             } else if (refreshRate != mRefreshRateInLowZone) {
1870                 mRefreshRateInLowZone = refreshRate;
1871                 restartObserver();
1872             }
1873         }
1874 
onDeviceConfigHighBrightnessThresholdsChanged(float[] displayThresholds, float[] ambientThresholds)1875         private void onDeviceConfigHighBrightnessThresholdsChanged(float[] displayThresholds,
1876                 float[] ambientThresholds) {
1877             if (displayThresholds != null && ambientThresholds != null
1878                     && displayThresholds.length == ambientThresholds.length) {
1879                 mHighDisplayBrightnessThresholds = displayThresholds;
1880                 mHighAmbientBrightnessThresholds = ambientThresholds;
1881             } else {
1882                 DisplayDeviceConfig displayDeviceConfig;
1883                 synchronized (mLock) {
1884                     displayDeviceConfig = mDefaultDisplayDeviceConfig;
1885                 }
1886                 mHighDisplayBrightnessThresholds = loadBrightnessThresholds(
1887                         () -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
1888                         () -> displayDeviceConfig.getHighDisplayBrightnessThresholds(),
1889                         R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate,
1890                         displayDeviceConfig, /* attemptReadFromFeatureParams= */ false,
1891                         DeviceConfigParsingUtils::displayBrightnessThresholdsIntToFloat);
1892                 mHighAmbientBrightnessThresholds = loadBrightnessThresholds(
1893                         () -> mConfigParameterProvider.getHighAmbientBrightnessThresholds(),
1894                         () -> displayDeviceConfig.getHighAmbientBrightnessThresholds(),
1895                         R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate,
1896                         displayDeviceConfig, /* attemptReadFromFeatureParams= */ false,
1897                         DeviceConfigParsingUtils::ambientBrightnessThresholdsIntToFloat);
1898             }
1899             restartObserver();
1900         }
1901 
1902         /**
1903          * Used to reload the higher blocking zone refresh rate in case of changes in the
1904          * DeviceConfig properties.
1905          */
onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate)1906         public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) {
1907             if (refreshRate == -1) {
1908                 // Given there is no value available in DeviceConfig, lets not attempt loading it
1909                 // from there.
1910                 synchronized (mLock) {
1911                     loadRefreshRateInHighZone(mDefaultDisplayDeviceConfig,
1912                             /* attemptReadFromFeatureParams= */ false);
1913                 }
1914                 restartObserver();
1915             } else if (refreshRate != mRefreshRateInHighZone) {
1916                 mRefreshRateInHighZone = refreshRate;
1917                 restartObserver();
1918             }
1919         }
1920 
dumpLocked(PrintWriter pw)1921         void dumpLocked(PrintWriter pw) {
1922             pw.println("  BrightnessObserver");
1923             pw.println("    mAmbientLux: " + mAmbientLux);
1924             pw.println("    mBrightness: " + mBrightness);
1925             pw.println("    mDefaultDisplayState: " + mDefaultDisplayState);
1926             pw.println("    mLowPowerModeEnabled: " + mLowPowerModeEnabled);
1927             pw.println("    mRefreshRateChangeable: " + mRefreshRateChangeable);
1928             pw.println("    mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
1929             pw.println("    mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange);
1930             pw.println("    mRefreshRateInLowZone: " + mRefreshRateInLowZone);
1931 
1932             for (float d : mLowDisplayBrightnessThresholds) {
1933                 pw.println("    mDisplayLowBrightnessThreshold: " + d);
1934             }
1935 
1936             for (float d : mLowAmbientBrightnessThresholds) {
1937                 pw.println("    mAmbientLowBrightnessThreshold: " + d);
1938             }
1939 
1940             pw.println("    mShouldObserveDisplayHighChange: " + mShouldObserveDisplayHighChange);
1941             pw.println("    mShouldObserveAmbientHighChange: " + mShouldObserveAmbientHighChange);
1942             pw.println("    mRefreshRateInHighZone: " + mRefreshRateInHighZone);
1943 
1944             for (float d : mHighDisplayBrightnessThresholds) {
1945                 pw.println("    mDisplayHighBrightnessThresholds: " + d);
1946             }
1947 
1948             for (float d : mHighAmbientBrightnessThresholds) {
1949                 pw.println("    mAmbientHighBrightnessThresholds: " + d);
1950             }
1951 
1952             pw.println("    mRegisteredLightSensor: " + mRegisteredLightSensor);
1953             pw.println("    mLightSensor: " + mLightSensor);
1954             pw.println("    mLightSensorName: " + mLightSensorName);
1955             pw.println("    mLightSensorType: " + mLightSensorType);
1956             mLightSensorListener.dumpLocked(pw);
1957 
1958             if (mAmbientFilter != null) {
1959                 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
1960                 mAmbientFilter.dump(ipw);
1961             }
1962         }
1963 
1964         @Override
onDisplayAdded(int displayId)1965         public void onDisplayAdded(int displayId) {}
1966 
1967         @Override
onDisplayRemoved(int displayId)1968         public void onDisplayRemoved(int displayId) {}
1969 
1970         @Override
onDisplayChanged(int displayId)1971         public void onDisplayChanged(int displayId) {
1972             if (displayId == Display.DEFAULT_DISPLAY) {
1973                 updateDefaultDisplayState();
1974 
1975                 // We don't support multiple display blocking zones yet, so only handle
1976                 // brightness changes for the default display for now.
1977                 float brightness = getBrightness(displayId);
1978                 synchronized (mLock) {
1979                     if (!BrightnessSynchronizer.floatEquals(brightness, mBrightness)) {
1980                         mBrightness = brightness;
1981                         onBrightnessChangedLocked();
1982                     }
1983                 }
1984             }
1985         }
1986 
restartObserver()1987         private void restartObserver() {
1988             if (mRefreshRateInLowZone > 0) {
1989                 mShouldObserveDisplayLowChange = hasValidThreshold(
1990                         mLowDisplayBrightnessThresholds);
1991                 mShouldObserveAmbientLowChange = hasValidThreshold(
1992                         mLowAmbientBrightnessThresholds);
1993             } else {
1994                 mShouldObserveDisplayLowChange = false;
1995                 mShouldObserveAmbientLowChange = false;
1996             }
1997 
1998             if (mRefreshRateInHighZone > 0) {
1999                 mShouldObserveDisplayHighChange = hasValidThreshold(
2000                         mHighDisplayBrightnessThresholds);
2001                 mShouldObserveAmbientHighChange = hasValidThreshold(
2002                         mHighAmbientBrightnessThresholds);
2003             } else {
2004                 mShouldObserveDisplayHighChange = false;
2005                 mShouldObserveAmbientHighChange = false;
2006             }
2007 
2008             if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
2009                 Sensor lightSensor = getLightSensor();
2010 
2011                 if (lightSensor != null && lightSensor != mLightSensor) {
2012                     final Resources res = mContext.getResources();
2013 
2014                     mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
2015                     mLightSensor = lightSensor;
2016                 }
2017             } else {
2018                 mAmbientFilter = null;
2019                 mLightSensor = null;
2020             }
2021 
2022             updateSensorStatus();
2023             synchronized (mLock) {
2024                 onBrightnessChangedLocked();
2025             }
2026         }
2027 
reloadLightSensor(DisplayDeviceConfig displayDeviceConfig)2028         private void reloadLightSensor(DisplayDeviceConfig displayDeviceConfig) {
2029             reloadLightSensorData(displayDeviceConfig);
2030             restartObserver();
2031         }
2032 
reloadLightSensorData(DisplayDeviceConfig displayDeviceConfig)2033         private void reloadLightSensorData(DisplayDeviceConfig displayDeviceConfig) {
2034             // The displayDeviceConfig (ddc) contains display specific preferences. When loaded,
2035             // it naturally falls back to the global config.xml.
2036             if (displayDeviceConfig != null
2037                     && displayDeviceConfig.getAmbientLightSensor() != null) {
2038                 // This covers both the ddc and the config.xml fallback
2039                 mLightSensorType = displayDeviceConfig.getAmbientLightSensor().type;
2040                 mLightSensorName = displayDeviceConfig.getAmbientLightSensor().name;
2041             } else if (mLightSensorName == null && mLightSensorType == null) {
2042                 Resources resources = mContext.getResources();
2043                 mLightSensorType = resources.getString(
2044                         com.android.internal.R.string.config_displayLightSensorType);
2045                 mLightSensorName = "";
2046             }
2047         }
2048 
getLightSensor()2049         private Sensor getLightSensor() {
2050             return SensorUtils.findSensor(mSensorManager, mLightSensorType,
2051                     mLightSensorName, Sensor.TYPE_LIGHT);
2052         }
2053 
2054         /**
2055          * Checks to see if at least one value is positive, in which case it is necessary to listen
2056          * to value changes.
2057          */
hasValidThreshold(float[] a)2058         private boolean hasValidThreshold(float[] a) {
2059             for (float d: a) {
2060                 if (d >= 0) {
2061                     return true;
2062                 }
2063             }
2064 
2065             return false;
2066         }
2067 
2068         /**
2069          * Check if we're in the low zone where higher refresh rates aren't allowed to prevent
2070          * flickering.
2071          * @param brightness The brightness value or a negative value meaning that only the lux
2072          *                   threshold should be applied
2073          * @param lux The lux value. If negative, only the brightness threshold is applied
2074          * @return True if we're in the low zone
2075          */
isInsideLowZone(float brightness, float lux)2076         private boolean isInsideLowZone(float brightness, float lux) {
2077             for (int i = 0; i < mLowDisplayBrightnessThresholds.length; i++) {
2078                 float disp = mLowDisplayBrightnessThresholds[i];
2079                 float ambi = mLowAmbientBrightnessThresholds[i];
2080 
2081                 if (disp >= 0 && ambi >= 0) {
2082                     if (brightness <= disp && lux <= ambi) {
2083                         return true;
2084                     }
2085                 } else if (disp >= 0) {
2086                     if (brightness <= disp) {
2087                         return true;
2088                     }
2089                 } else if (ambi >= 0) {
2090                     if (lux <= ambi) {
2091                         return true;
2092                     }
2093                 }
2094             }
2095 
2096             return false;
2097         }
2098 
2099         /**
2100          * Check if we're in the high zone where higher refresh rates aren't allowed to prevent
2101          * flickering.
2102          * @param brightness The brightness value or a negative value meaning that only the lux
2103          *                   threshold should be applied
2104          * @param lux The lux value. If negative, only the brightness threshold is applied
2105          * @return True if we're in the high zone
2106          */
isInsideHighZone(float brightness, float lux)2107         private boolean isInsideHighZone(float brightness, float lux) {
2108             for (int i = 0; i < mHighDisplayBrightnessThresholds.length; i++) {
2109                 float disp = mHighDisplayBrightnessThresholds[i];
2110                 float ambi = mHighAmbientBrightnessThresholds[i];
2111 
2112                 if (disp >= 0 && ambi >= 0) {
2113                     if (brightness >= disp && lux >= ambi) {
2114                         return true;
2115                     }
2116                 } else if (disp >= 0) {
2117                     if (brightness >= disp) {
2118                         return true;
2119                     }
2120                 } else if (ambi >= 0) {
2121                     if (lux >= ambi) {
2122                         return true;
2123                     }
2124                 }
2125             }
2126 
2127             return false;
2128         }
2129 
onBrightnessChangedLocked()2130         private void onBrightnessChangedLocked() {
2131             if (!mRefreshRateChangeable) {
2132                 return;
2133             }
2134             Vote refreshRateVote = null;
2135             Vote refreshRateSwitchingVote = null;
2136 
2137             if (Float.isNaN(mBrightness)) {
2138                 // Either the setting isn't available or we shouldn't be observing yet anyways.
2139                 // Either way, just bail out since there's nothing we can do here.
2140                 return;
2141             }
2142 
2143             boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux);
2144             if (insideLowZone) {
2145                 refreshRateVote =
2146                         Vote.forPhysicalRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
2147                 if (mLowZoneRefreshRateForThermals != null) {
2148                     RefreshRateRange range = SkinThermalStatusObserver
2149                             .findBestMatchingRefreshRateRange(mThermalStatus,
2150                                     mLowZoneRefreshRateForThermals);
2151                     if (range != null) {
2152                         refreshRateVote =
2153                                 Vote.forPhysicalRefreshRates(range.min, range.max);
2154                     }
2155                 }
2156                 refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
2157             }
2158 
2159             boolean insideHighZone = hasValidHighZone()
2160                     && isInsideHighZone(mBrightness, mAmbientLux);
2161             if (insideHighZone) {
2162                 refreshRateVote =
2163                         Vote.forPhysicalRefreshRates(mRefreshRateInHighZone,
2164                                 mRefreshRateInHighZone);
2165                 if (mHighZoneRefreshRateForThermals != null) {
2166                     RefreshRateRange range = SkinThermalStatusObserver
2167                             .findBestMatchingRefreshRateRange(mThermalStatus,
2168                                     mHighZoneRefreshRateForThermals);
2169                     if (range != null) {
2170                         refreshRateVote =
2171                                 Vote.forPhysicalRefreshRates(range.min, range.max);
2172                     }
2173                 }
2174                 refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
2175             }
2176 
2177             if (mLoggingEnabled) {
2178                 Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " +  mAmbientLux
2179                         + ", Vote " + refreshRateVote);
2180             }
2181             mVotesStorage.updateGlobalVote(Vote.PRIORITY_FLICKER_REFRESH_RATE, refreshRateVote);
2182             mVotesStorage.updateGlobalVote(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH,
2183                     refreshRateSwitchingVote);
2184         }
2185 
hasValidLowZone()2186         private boolean hasValidLowZone() {
2187             return mRefreshRateInLowZone > 0
2188                     && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange);
2189         }
2190 
hasValidHighZone()2191         private boolean hasValidHighZone() {
2192             return mRefreshRateInHighZone > 0
2193                     && (mShouldObserveDisplayHighChange || mShouldObserveAmbientHighChange);
2194         }
2195 
updateDefaultDisplayState()2196         private void updateDefaultDisplayState() {
2197             Display display = mInjector.getDisplay(Display.DEFAULT_DISPLAY);
2198             if (display == null) {
2199                 return;
2200             }
2201 
2202             setDefaultDisplayState(display.getState());
2203         }
2204 
2205         @VisibleForTesting
setDefaultDisplayState(int state)2206         void setDefaultDisplayState(int state) {
2207             if (mLoggingEnabled) {
2208                 Slog.d(TAG, "setDefaultDisplayState: mDefaultDisplayState = "
2209                         + mDefaultDisplayState + ", state = " + state);
2210             }
2211 
2212             if (mDefaultDisplayState != state) {
2213                 mDefaultDisplayState = state;
2214                 updateSensorStatus();
2215             }
2216         }
2217 
updateSensorStatus()2218         private void updateSensorStatus() {
2219             if (mSensorManager == null || mLightSensorListener == null) {
2220                 return;
2221             }
2222 
2223             if (mLoggingEnabled) {
2224                 Slog.d(TAG, "updateSensorStatus: mShouldObserveAmbientLowChange = "
2225                         + mShouldObserveAmbientLowChange + ", mShouldObserveAmbientHighChange = "
2226                         + mShouldObserveAmbientHighChange);
2227                 Slog.d(TAG, "updateSensorStatus: mLowPowerModeEnabled = "
2228                         + mLowPowerModeEnabled + ", mRefreshRateChangeable = "
2229                         + mRefreshRateChangeable);
2230             }
2231 
2232             boolean registerForThermals = false;
2233             if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
2234                      && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
2235                 registerLightSensor();
2236                 registerForThermals = mLowZoneRefreshRateForThermals != null
2237                         || mHighZoneRefreshRateForThermals != null;
2238             } else {
2239                 unregisterSensorListener();
2240             }
2241 
2242             if (registerForThermals && !mThermalRegistered) {
2243                 mThermalRegistered = mInjector.registerThermalServiceListener(mThermalListener);
2244             } else if (!registerForThermals && mThermalRegistered) {
2245                 mInjector.unregisterThermalServiceListener(mThermalListener);
2246                 mThermalRegistered = false;
2247                 synchronized (mLock) {
2248                     mThermalStatus = Temperature.THROTTLING_NONE; // reset
2249                 }
2250             }
2251         }
2252 
registerLightSensor()2253         private void registerLightSensor() {
2254             if (mRegisteredLightSensor == mLightSensor) {
2255                 return;
2256             }
2257 
2258             if (mRegisteredLightSensor != null) {
2259                 unregisterSensorListener();
2260             }
2261 
2262             mSensorManager.registerListener(mLightSensorListener,
2263                     mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
2264             mRegisteredLightSensor = mLightSensor;
2265             if (mLoggingEnabled) {
2266                 Slog.d(TAG, "updateSensorStatus: registerListener");
2267             }
2268         }
2269 
unregisterSensorListener()2270         private void unregisterSensorListener() {
2271             mLightSensorListener.removeCallbacks();
2272             mSensorManager.unregisterListener(mLightSensorListener);
2273             mRegisteredLightSensor = null;
2274             if (mLoggingEnabled) {
2275                 Slog.d(TAG, "updateSensorStatus: unregisterListener");
2276             }
2277         }
2278 
isDeviceActive()2279         private boolean isDeviceActive() {
2280             return mDefaultDisplayState == Display.STATE_ON;
2281         }
2282 
2283         /**
2284          * Get the brightness value for a display
2285          * @param displayId The ID of the display
2286          * @return The brightness value
2287          */
getBrightness(int displayId)2288         private float getBrightness(int displayId) {
2289             final BrightnessInfo info = mInjector.getBrightnessInfo(displayId);
2290             if (info != null) {
2291                 return info.adjustedBrightness;
2292             }
2293 
2294             return BRIGHTNESS_INVALID_FLOAT;
2295         }
2296 
2297         private final class LightSensorEventListener implements SensorEventListener {
2298             private static final int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
2299             private float mLastSensorData;
2300             private long mTimestamp;
2301             private boolean mLoggingEnabled;
2302 
dumpLocked(PrintWriter pw)2303             public void dumpLocked(PrintWriter pw) {
2304                 pw.println("    mLastSensorData: " + mLastSensorData);
2305                 pw.println("    mTimestamp: " + formatTimestamp(mTimestamp));
2306             }
2307 
2308 
setLoggingEnabled(boolean loggingEnabled)2309             public void setLoggingEnabled(boolean loggingEnabled) {
2310                 if (mLoggingEnabled == loggingEnabled) {
2311                     return;
2312                 }
2313                 mLoggingEnabled = loggingEnabled;
2314             }
2315 
2316             @Override
onSensorChanged(SensorEvent event)2317             public void onSensorChanged(SensorEvent event) {
2318                 mLastSensorData = event.values[0];
2319                 if (mLoggingEnabled) {
2320                     Slog.d(TAG, "On sensor changed: " + mLastSensorData);
2321                 }
2322 
2323                 boolean lowZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
2324                         mLowAmbientBrightnessThresholds);
2325                 boolean highZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
2326                         mHighAmbientBrightnessThresholds);
2327                 if ((lowZoneChanged && mLastSensorData < mAmbientLux)
2328                         || (highZoneChanged && mLastSensorData > mAmbientLux)) {
2329                     // Easier to see flicker at lower brightness environment or high brightness
2330                     // environment. Forget the history to get immediate response.
2331                     if (mAmbientFilter != null) {
2332                         mAmbientFilter.clear();
2333                     }
2334                 }
2335 
2336                 long now = SystemClock.uptimeMillis();
2337                 mTimestamp = System.currentTimeMillis();
2338                 if (mAmbientFilter != null) {
2339                     mAmbientFilter.addValue(now, mLastSensorData);
2340                 }
2341 
2342                 mHandler.removeCallbacks(mInjectSensorEventRunnable);
2343                 processSensorData(now);
2344 
2345                 if ((lowZoneChanged && mLastSensorData > mAmbientLux)
2346                         || (highZoneChanged && mLastSensorData < mAmbientLux)) {
2347                     // Sensor may not report new event if there is no brightness change.
2348                     // Need to keep querying the temporal filter for the latest estimation,
2349                     // until sensor readout and filter estimation are in the same zone or
2350                     // is interrupted by a new sensor event.
2351                     mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
2352                 }
2353             }
2354 
2355             @Override
onAccuracyChanged(Sensor sensor, int accuracy)2356             public void onAccuracyChanged(Sensor sensor, int accuracy) {
2357                 // Not used.
2358             }
2359 
removeCallbacks()2360             public void removeCallbacks() {
2361                 mHandler.removeCallbacks(mInjectSensorEventRunnable);
2362             }
2363 
formatTimestamp(long time)2364             private String formatTimestamp(long time) {
2365                 SimpleDateFormat dateFormat =
2366                         new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
2367                 return dateFormat.format(new Date(time));
2368             }
2369 
processSensorData(long now)2370             private void processSensorData(long now) {
2371                 if (mAmbientFilter != null) {
2372                     mAmbientLux = mAmbientFilter.getEstimate(now);
2373                 } else {
2374                     mAmbientLux = mLastSensorData;
2375                 }
2376 
2377                 synchronized (mLock) {
2378                     onBrightnessChangedLocked();
2379                 }
2380             }
2381 
isDifferentZone(float lux1, float lux2, float[] luxThresholds)2382             private boolean isDifferentZone(float lux1, float lux2, float[] luxThresholds) {
2383                 for (final float boundary : luxThresholds) {
2384                     // Test each boundary. See if the current value and the new value are at
2385                     // different sides.
2386                     if ((lux1 <= boundary && lux2 > boundary)
2387                             || (lux1 > boundary && lux2 <= boundary)) {
2388                         return true;
2389                     }
2390                 }
2391 
2392                 return false;
2393             }
2394 
2395             private final Runnable mInjectSensorEventRunnable = new Runnable() {
2396                 @Override
2397                 public void run() {
2398                     long now = SystemClock.uptimeMillis();
2399                     // No need to really inject the last event into a temporal filter.
2400                     processSensorData(now);
2401 
2402                     // Inject next event if there is a possible zone change.
2403                     if (isDifferentZone(mLastSensorData, mAmbientLux,
2404                             mLowAmbientBrightnessThresholds)
2405                             || isDifferentZone(mLastSensorData, mAmbientLux,
2406                             mHighAmbientBrightnessThresholds)) {
2407                         mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
2408                     }
2409                 }
2410             };
2411         }
2412     }
2413 
2414     private class UdfpsObserver extends IUdfpsRefreshRateRequestCallback.Stub {
2415         private final SparseBooleanArray mUdfpsRefreshRateEnabled = new SparseBooleanArray();
2416         private final SparseBooleanArray mAuthenticationPossible = new SparseBooleanArray();
2417 
observe()2418         public void observe() {
2419             StatusBarManagerInternal statusBar =
2420                     LocalServices.getService(StatusBarManagerInternal.class);
2421             if (statusBar == null) {
2422                 return;
2423             }
2424 
2425             // Allow UDFPS vote by registering callback, only
2426             // if the device is configured to not ignore UDFPS vote.
2427             boolean ignoreUdfpsVote = mContext.getResources()
2428                         .getBoolean(R.bool.config_ignoreUdfpsVote);
2429             if (!ignoreUdfpsVote) {
2430                 statusBar.setUdfpsRefreshRateCallback(this);
2431             }
2432         }
2433 
2434         @Override
onRequestEnabled(int displayId)2435         public void onRequestEnabled(int displayId) {
2436             synchronized (mLock) {
2437                 mUdfpsRefreshRateEnabled.put(displayId, true);
2438                 updateVoteLocked(displayId, true, Vote.PRIORITY_UDFPS);
2439             }
2440         }
2441 
2442         @Override
onRequestDisabled(int displayId)2443         public void onRequestDisabled(int displayId) {
2444             synchronized (mLock) {
2445                 mUdfpsRefreshRateEnabled.put(displayId, false);
2446                 updateVoteLocked(displayId, false, Vote.PRIORITY_UDFPS);
2447             }
2448         }
2449 
2450         @Override
onAuthenticationPossible(int displayId, boolean isPossible)2451         public void onAuthenticationPossible(int displayId, boolean isPossible) {
2452             synchronized (mLock) {
2453                 mAuthenticationPossible.put(displayId, isPossible);
2454                 updateVoteLocked(displayId, isPossible,
2455                         Vote.PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE);
2456             }
2457         }
2458 
2459         @GuardedBy("mLock")
updateVoteLocked(int displayId, boolean enabled, int votePriority)2460         private void updateVoteLocked(int displayId, boolean enabled, int votePriority) {
2461             final Vote vote;
2462             if (enabled) {
2463                 float maxRefreshRate = DisplayModeDirector.this.getMaxRefreshRateLocked(displayId);
2464                 vote = Vote.forPhysicalRefreshRates(maxRefreshRate, maxRefreshRate);
2465             } else {
2466                 vote = null;
2467             }
2468             mVotesStorage.updateVote(displayId, votePriority, vote);
2469         }
2470 
dumpLocked(PrintWriter pw)2471         void dumpLocked(PrintWriter pw) {
2472             pw.println("  UdfpsObserver");
2473             pw.println("    mUdfpsRefreshRateEnabled: ");
2474             for (int i = 0; i < mUdfpsRefreshRateEnabled.size(); i++) {
2475                 final int displayId = mUdfpsRefreshRateEnabled.keyAt(i);
2476                 final String enabled = mUdfpsRefreshRateEnabled.valueAt(i) ? "enabled" : "disabled";
2477                 pw.println("      Display " + displayId + ": " + enabled);
2478             }
2479             pw.println("    mAuthenticationPossible: ");
2480             for (int i = 0; i < mAuthenticationPossible.size(); i++) {
2481                 final int displayId = mAuthenticationPossible.keyAt(i);
2482                 final String isPossible = mAuthenticationPossible.valueAt(i) ? "possible"
2483                         : "impossible";
2484                 pw.println("      Display " + displayId + ": " + isPossible);
2485             }
2486         }
2487     }
2488 
2489     protected static final class SensorObserver implements ProximityActiveListener,
2490             DisplayManager.DisplayListener {
2491         private final String mProximitySensorName = null;
2492         private final String mProximitySensorType = Sensor.STRING_TYPE_PROXIMITY;
2493 
2494         private final VotesStorage mVotesStorage;
2495         private final Context mContext;
2496         private final Injector mInjector;
2497         @GuardedBy("mSensorObserverLock")
2498         private final SparseBooleanArray mDozeStateByDisplay = new SparseBooleanArray();
2499         private final Object mSensorObserverLock = new Object();
2500 
2501         private DisplayManager mDisplayManager;
2502         private DisplayManagerInternal mDisplayManagerInternal;
2503         @GuardedBy("mSensorObserverLock")
2504         private boolean mIsProxActive = false;
2505 
SensorObserver(Context context, VotesStorage votesStorage, Injector injector)2506         SensorObserver(Context context, VotesStorage votesStorage, Injector injector) {
2507             mContext = context;
2508             mVotesStorage = votesStorage;
2509             mInjector = injector;
2510         }
2511 
2512         @Override
onProximityActive(boolean isActive)2513         public void onProximityActive(boolean isActive) {
2514             synchronized (mSensorObserverLock) {
2515                 if (mIsProxActive != isActive) {
2516                     mIsProxActive = isActive;
2517                     recalculateVotesLocked();
2518                 }
2519             }
2520         }
2521 
observe()2522         public void observe() {
2523             mDisplayManager = mContext.getSystemService(DisplayManager.class);
2524             mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
2525 
2526             final SensorManagerInternal sensorManager =
2527                     LocalServices.getService(SensorManagerInternal.class);
2528             sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this);
2529 
2530             synchronized (mSensorObserverLock) {
2531                 for (Display d : mInjector.getDisplays()) {
2532                     mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d));
2533                 }
2534             }
2535             mInjector.registerDisplayListener(this, BackgroundThread.getHandler(),
2536                     DisplayManager.EVENT_FLAG_DISPLAY_ADDED
2537                             | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
2538                             | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
2539         }
2540 
recalculateVotesLocked()2541         private void recalculateVotesLocked() {
2542             final Display[] displays = mInjector.getDisplays();
2543             for (Display d : displays) {
2544                 int displayId = d.getDisplayId();
2545                 Vote vote = null;
2546                 if (mIsProxActive && !mDozeStateByDisplay.get(displayId)) {
2547                     final RefreshRateRange rate =
2548                             mDisplayManagerInternal.getRefreshRateForDisplayAndSensor(
2549                                     displayId, mProximitySensorName, mProximitySensorType);
2550                     if (rate != null) {
2551                         vote = Vote.forPhysicalRefreshRates(rate.min, rate.max);
2552                     }
2553                 }
2554                 mVotesStorage.updateVote(displayId, Vote.PRIORITY_PROXIMITY, vote);
2555             }
2556         }
2557 
dump(PrintWriter pw)2558         void dump(PrintWriter pw) {
2559             pw.println("  SensorObserver");
2560             synchronized (mSensorObserverLock) {
2561                 pw.println("    mIsProxActive=" + mIsProxActive);
2562                 pw.println("    mDozeStateByDisplay:");
2563                 for (int i = 0; i < mDozeStateByDisplay.size(); i++) {
2564                     final int id = mDozeStateByDisplay.keyAt(i);
2565                     final boolean dozed = mDozeStateByDisplay.valueAt(i);
2566                     pw.println("      " + id + " -> " + dozed);
2567                 }
2568             }
2569         }
2570 
2571         @Override
onDisplayAdded(int displayId)2572         public void onDisplayAdded(int displayId) {
2573             boolean isDozeState = mInjector.isDozeState(mInjector.getDisplay(displayId));
2574             synchronized (mSensorObserverLock) {
2575                 mDozeStateByDisplay.put(displayId, isDozeState);
2576                 recalculateVotesLocked();
2577             }
2578         }
2579 
2580         @Override
onDisplayChanged(int displayId)2581         public void onDisplayChanged(int displayId) {
2582             boolean wasDozeState = mDozeStateByDisplay.get(displayId);
2583             synchronized (mSensorObserverLock) {
2584                 mDozeStateByDisplay.put(displayId,
2585                         mInjector.isDozeState(mInjector.getDisplay(displayId)));
2586                 if (wasDozeState != mDozeStateByDisplay.get(displayId)) {
2587                     recalculateVotesLocked();
2588                 }
2589             }
2590         }
2591 
2592         @Override
onDisplayRemoved(int displayId)2593         public void onDisplayRemoved(int displayId) {
2594             synchronized (mSensorObserverLock) {
2595                 mDozeStateByDisplay.delete(displayId);
2596                 recalculateVotesLocked();
2597             }
2598         }
2599     }
2600 
2601     /**
2602      * Listens to DisplayManager for HBM status and applies any refresh-rate restrictions for
2603      * HBM that are associated with that display. Restrictions are retrieved from
2604      * DisplayManagerInternal but originate in the display-device-config file.
2605      */
2606     public class HbmObserver implements DisplayManager.DisplayListener {
2607         private final VotesStorage mVotesStorage;
2608         private final Handler mHandler;
2609         private final SparseIntArray mHbmMode = new SparseIntArray();
2610         private final SparseBooleanArray mHbmActive = new SparseBooleanArray();
2611         private final Injector mInjector;
2612         private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
2613         private int mRefreshRateInHbmSunlight;
2614         private int mRefreshRateInHbmHdr;
2615 
2616         private DisplayManagerInternal mDisplayManagerInternal;
2617 
HbmObserver(Injector injector, VotesStorage votesStorage, Handler handler, DeviceConfigDisplaySettings displaySettings)2618         HbmObserver(Injector injector, VotesStorage votesStorage, Handler handler,
2619                 DeviceConfigDisplaySettings displaySettings) {
2620             mInjector = injector;
2621             mVotesStorage = votesStorage;
2622             mHandler = handler;
2623             mDeviceConfigDisplaySettings = displaySettings;
2624         }
2625 
2626         /**
2627          * Sets up the refresh rate to be used when HDR is enabled
2628          */
setupHdrRefreshRates(DisplayDeviceConfig displayDeviceConfig)2629         public void setupHdrRefreshRates(DisplayDeviceConfig displayDeviceConfig) {
2630             mRefreshRateInHbmHdr = mDeviceConfigDisplaySettings
2631                 .getRefreshRateInHbmHdr(displayDeviceConfig);
2632             mRefreshRateInHbmSunlight = mDeviceConfigDisplaySettings
2633                 .getRefreshRateInHbmSunlight(displayDeviceConfig);
2634         }
2635 
2636         /**
2637          * Sets up the HDR refresh rates, and starts observing for the changes in the display that
2638          * might impact it
2639          */
observe()2640         public void observe() {
2641             synchronized (mLock) {
2642                 setupHdrRefreshRates(mDefaultDisplayDeviceConfig);
2643             }
2644             mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
2645             mInjector.registerDisplayListener(this, mHandler,
2646                     DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
2647                     | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
2648         }
2649 
2650         /**
2651          * @return the refresh to lock to when the device is in high brightness mode for Sunlight.
2652          */
2653         @VisibleForTesting
getRefreshRateInHbmSunlight()2654         int getRefreshRateInHbmSunlight() {
2655             return mRefreshRateInHbmSunlight;
2656         }
2657 
2658         /**
2659          * @return the refresh to lock to when the device is in high brightness mode for HDR.
2660          */
2661         @VisibleForTesting
getRefreshRateInHbmHdr()2662         int getRefreshRateInHbmHdr() {
2663             return mRefreshRateInHbmHdr;
2664         }
2665 
2666         /**
2667          * Recalculates the HBM vote when the device config has been changed.
2668          */
onDeviceConfigRefreshRateInHbmSunlightChanged(int refreshRate)2669         public void onDeviceConfigRefreshRateInHbmSunlightChanged(int refreshRate) {
2670             if (refreshRate != mRefreshRateInHbmSunlight) {
2671                 mRefreshRateInHbmSunlight = refreshRate;
2672                 onDeviceConfigRefreshRateInHbmChanged();
2673             }
2674         }
2675 
2676         /**
2677          * Recalculates the HBM vote when the device config has been changed.
2678          */
onDeviceConfigRefreshRateInHbmHdrChanged(int refreshRate)2679         public void onDeviceConfigRefreshRateInHbmHdrChanged(int refreshRate) {
2680             if (refreshRate != mRefreshRateInHbmHdr) {
2681                 mRefreshRateInHbmHdr = refreshRate;
2682                 onDeviceConfigRefreshRateInHbmChanged();
2683             }
2684         }
2685 
2686         @Override
onDisplayAdded(int displayId)2687         public void onDisplayAdded(int displayId) {}
2688 
2689         @Override
onDisplayRemoved(int displayId)2690         public void onDisplayRemoved(int displayId) {
2691             mVotesStorage.updateVote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
2692             mHbmMode.delete(displayId);
2693             mHbmActive.delete(displayId);
2694         }
2695 
2696         @Override
onDisplayChanged(int displayId)2697         public void onDisplayChanged(int displayId) {
2698             final BrightnessInfo info = mInjector.getBrightnessInfo(displayId);
2699             if (info == null) {
2700                 // Display no longer there. Assume we'll get an onDisplayRemoved very soon.
2701                 return;
2702             }
2703 
2704             final int hbmMode = info.highBrightnessMode;
2705             final boolean isHbmActive = hbmMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF
2706                     && info.adjustedBrightness > info.highBrightnessTransitionPoint;
2707             if (hbmMode == mHbmMode.get(displayId)
2708                     && isHbmActive == mHbmActive.get(displayId)) {
2709                 // no change, ignore.
2710                 return;
2711             }
2712             mHbmMode.put(displayId, hbmMode);
2713             mHbmActive.put(displayId, isHbmActive);
2714             recalculateVotesForDisplay(displayId);
2715         }
2716 
onDeviceConfigRefreshRateInHbmChanged()2717         private void onDeviceConfigRefreshRateInHbmChanged() {
2718             final int[] displayIds = mHbmMode.copyKeys();
2719             if (displayIds != null) {
2720                 for (int id : displayIds) {
2721                     recalculateVotesForDisplay(id);
2722                 }
2723             }
2724         }
2725 
recalculateVotesForDisplay(int displayId)2726         private void recalculateVotesForDisplay(int displayId) {
2727             Vote vote = null;
2728             if (mHbmActive.get(displayId, false)) {
2729                 final int hbmMode =
2730                         mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
2731                 if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
2732                     // Device resource properties take priority over DisplayDeviceConfig
2733                     if (mRefreshRateInHbmSunlight > 0) {
2734                         vote = Vote.forPhysicalRefreshRates(mRefreshRateInHbmSunlight,
2735                                 mRefreshRateInHbmSunlight);
2736                     } else {
2737                         final List<RefreshRateLimitation> limits =
2738                                 mDisplayManagerInternal.getRefreshRateLimitations(displayId);
2739                         for (int i = 0; limits != null && i < limits.size(); i++) {
2740                             final RefreshRateLimitation limitation = limits.get(i);
2741                             if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
2742                                 vote = Vote.forPhysicalRefreshRates(limitation.range.min,
2743                                         limitation.range.max);
2744                                 break;
2745                             }
2746                         }
2747                     }
2748                 } else if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
2749                         && mRefreshRateInHbmHdr > 0) {
2750                     // HBM for HDR vote isn't supported through DisplayDeviceConfig yet, so look for
2751                     // a vote from Device properties
2752                     vote = Vote.forPhysicalRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr);
2753                 } else {
2754                     Slog.w(TAG, "Unexpected HBM mode " + hbmMode + " for display ID " + displayId);
2755                 }
2756 
2757             }
2758             mVotesStorage.updateVote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
2759         }
2760 
dumpLocked(PrintWriter pw)2761         void dumpLocked(PrintWriter pw) {
2762             pw.println("   HbmObserver");
2763             pw.println("     mHbmMode: " + mHbmMode);
2764             pw.println("     mHbmActive: " + mHbmActive);
2765             pw.println("     mRefreshRateInHbmSunlight: " + mRefreshRateInHbmSunlight);
2766             pw.println("     mRefreshRateInHbmHdr: " + mRefreshRateInHbmHdr);
2767         }
2768     }
2769 
2770     private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
startListening()2771         public void startListening() {
2772             mConfigParameterProvider.addOnPropertiesChangedListener(
2773                     BackgroundThread.getExecutor(), this);
2774         }
2775 
getRefreshRateInHbmHdr(DisplayDeviceConfig displayDeviceConfig)2776         private int getRefreshRateInHbmHdr(DisplayDeviceConfig displayDeviceConfig) {
2777             return getRefreshRate(
2778                     () -> mConfigParameterProvider.getRefreshRateInHbmHdr(),
2779                     () -> displayDeviceConfig.getDefaultRefreshRateInHbmHdr(),
2780                     R.integer.config_defaultRefreshRateInHbmHdr,
2781                     displayDeviceConfig
2782             );
2783         }
2784 
getRefreshRateInHbmSunlight(DisplayDeviceConfig displayDeviceConfig)2785         private int getRefreshRateInHbmSunlight(DisplayDeviceConfig displayDeviceConfig) {
2786             return getRefreshRate(
2787                     () -> mConfigParameterProvider.getRefreshRateInHbmSunlight(),
2788                     () -> displayDeviceConfig.getDefaultRefreshRateInHbmSunlight(),
2789                     R.integer.config_defaultRefreshRateInHbmSunlight,
2790                     displayDeviceConfig
2791             );
2792         }
2793 
getRefreshRate(IntSupplier fromConfigPram, IntSupplier fromDisplayDeviceConfig, @IntegerRes int configKey, DisplayDeviceConfig displayDeviceConfig)2794         private int getRefreshRate(IntSupplier fromConfigPram, IntSupplier fromDisplayDeviceConfig,
2795                 @IntegerRes int configKey, DisplayDeviceConfig displayDeviceConfig) {
2796             int refreshRate = -1;
2797             try {
2798                 refreshRate = fromConfigPram.getAsInt();
2799             } catch (NullPointerException npe) {
2800                 // Do Nothing
2801             }
2802             if (refreshRate == -1) {
2803                 refreshRate = (displayDeviceConfig == null)
2804                                 ? mContext.getResources().getInteger(configKey)
2805                                 : fromDisplayDeviceConfig.getAsInt();
2806             }
2807             return refreshRate;
2808         }
2809 
2810         @Override
onPropertiesChanged(@onNull DeviceConfig.Properties properties)2811         public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
2812             float defaultPeakRefreshRate = mConfigParameterProvider.getPeakRefreshRateDefault();
2813             mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
2814                     defaultPeakRefreshRate == -1 ? null : defaultPeakRefreshRate).sendToTarget();
2815 
2816             float[] lowDisplayBrightnessThresholds =
2817                     mConfigParameterProvider.getLowDisplayBrightnessThresholds();
2818             float[] lowAmbientBrightnessThresholds =
2819                     mConfigParameterProvider.getLowAmbientBrightnessThresholds();
2820             final int refreshRateInLowZone = mConfigParameterProvider.getRefreshRateInLowZone();
2821 
2822             mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
2823                     new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
2824                     .sendToTarget();
2825 
2826             mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone,
2827                     0).sendToTarget();
2828 
2829             float[] highDisplayBrightnessThresholds =
2830                     mConfigParameterProvider.getHighDisplayBrightnessThresholds();
2831             float[] highAmbientBrightnessThresholds =
2832                     mConfigParameterProvider.getHighAmbientBrightnessThresholds();
2833             final int refreshRateInHighZone = mConfigParameterProvider.getRefreshRateInHighZone();
2834 
2835             mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
2836                     new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
2837                     .sendToTarget();
2838 
2839             mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone,
2840                     0).sendToTarget();
2841 
2842             synchronized (mLock) {
2843                 final int refreshRateInHbmSunlight =
2844                         getRefreshRateInHbmSunlight(mDefaultDisplayDeviceConfig);
2845                 mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED,
2846                         refreshRateInHbmSunlight, 0)
2847                     .sendToTarget();
2848 
2849                 final int refreshRateInHbmHdr =
2850                         getRefreshRateInHbmHdr(mDefaultDisplayDeviceConfig);
2851                 mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED, refreshRateInHbmHdr, 0)
2852                     .sendToTarget();
2853             }
2854         }
2855     }
2856 
2857     interface Injector {
2858         Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
2859 
2860         @NonNull
getDeviceConfig()2861         DeviceConfigInterface getDeviceConfig();
2862 
registerPeakRefreshRateObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)2863         void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
2864                 @NonNull ContentObserver observer);
2865 
registerDisplayListener(@onNull DisplayManager.DisplayListener listener, Handler handler)2866         void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
2867                 Handler handler);
2868 
registerDisplayListener(@onNull DisplayManager.DisplayListener listener, Handler handler, long flags)2869         void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
2870                 Handler handler, long flags);
2871 
getDisplay(int displayId)2872         Display getDisplay(int displayId);
2873 
getDisplays()2874         Display[] getDisplays();
2875 
getDisplayInfo(int displayId, DisplayInfo displayInfo)2876         boolean getDisplayInfo(int displayId, DisplayInfo displayInfo);
2877 
getBrightnessInfo(int displayId)2878         BrightnessInfo getBrightnessInfo(int displayId);
2879 
isDozeState(Display d)2880         boolean isDozeState(Display d);
2881 
registerThermalServiceListener(IThermalEventListener listener)2882         boolean registerThermalServiceListener(IThermalEventListener listener);
unregisterThermalServiceListener(IThermalEventListener listener)2883         void unregisterThermalServiceListener(IThermalEventListener listener);
2884 
supportsFrameRateOverride()2885         boolean supportsFrameRateOverride();
2886     }
2887 
2888     @VisibleForTesting
2889     static class RealInjector implements Injector {
2890         private final Context mContext;
2891         private DisplayManager mDisplayManager;
2892 
RealInjector(Context context)2893         RealInjector(Context context) {
2894             mContext = context;
2895         }
2896 
2897         @Override
2898         @NonNull
getDeviceConfig()2899         public DeviceConfigInterface getDeviceConfig() {
2900             return DeviceConfigInterface.REAL;
2901         }
2902 
2903         @Override
registerPeakRefreshRateObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)2904         public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
2905                 @NonNull ContentObserver observer) {
2906             cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
2907                     observer, UserHandle.USER_SYSTEM);
2908         }
2909 
2910         @Override
registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler)2911         public void registerDisplayListener(DisplayManager.DisplayListener listener,
2912                 Handler handler) {
2913             getDisplayManager().registerDisplayListener(listener, handler);
2914         }
2915 
2916         @Override
registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler, long flags)2917         public void registerDisplayListener(DisplayManager.DisplayListener listener,
2918                 Handler handler, long flags) {
2919             getDisplayManager().registerDisplayListener(listener, handler, flags);
2920         }
2921 
2922         @Override
getDisplay(int displayId)2923         public Display getDisplay(int displayId) {
2924             return getDisplayManager().getDisplay(displayId);
2925         }
2926 
2927         @Override
getDisplays()2928         public Display[] getDisplays() {
2929             return getDisplayManager().getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
2930         }
2931 
2932         @Override
getDisplayInfo(int displayId, DisplayInfo displayInfo)2933         public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) {
2934             Display display = getDisplayManager().getDisplay(displayId);
2935             if (display == null) {
2936                 // We can occasionally get a display added or changed event for a display that was
2937                 // subsequently removed, which means this returns null. Check this case and bail
2938                 // out early; if it gets re-attached we'll eventually get another call back for it.
2939                 return false;
2940             }
2941             return display.getDisplayInfo(displayInfo);
2942         }
2943 
2944         @Override
getBrightnessInfo(int displayId)2945         public BrightnessInfo getBrightnessInfo(int displayId) {
2946             final Display display = getDisplayManager().getDisplay(displayId);
2947             if (display != null) {
2948                 return display.getBrightnessInfo();
2949             }
2950             return null;
2951         }
2952 
2953         @Override
isDozeState(Display d)2954         public boolean isDozeState(Display d) {
2955             if (d == null) {
2956                 return false;
2957             }
2958             return Display.isDozeState(d.getState());
2959         }
2960 
2961         @Override
registerThermalServiceListener(IThermalEventListener listener)2962         public boolean registerThermalServiceListener(IThermalEventListener listener) {
2963             IThermalService thermalService = getThermalService();
2964             if (thermalService == null) {
2965                 Slog.w(TAG, "Could not observe thermal status. Service not available");
2966                 return false;
2967             }
2968             try {
2969                 thermalService.registerThermalEventListenerWithType(listener,
2970                         Temperature.TYPE_SKIN);
2971             } catch (RemoteException e) {
2972                 Slog.e(TAG, "Failed to register thermal status listener", e);
2973                 return false;
2974             }
2975             return true;
2976         }
2977 
2978         @Override
unregisterThermalServiceListener(IThermalEventListener listener)2979         public void unregisterThermalServiceListener(IThermalEventListener listener) {
2980             IThermalService thermalService = getThermalService();
2981             if (thermalService == null) {
2982                 Slog.w(TAG, "Could not unregister thermal status. Service not available");
2983             }
2984             try {
2985                 thermalService.unregisterThermalEventListener(listener);
2986             } catch (RemoteException e) {
2987                 Slog.e(TAG, "Failed to unregister thermal status listener", e);
2988             }
2989         }
2990 
2991         @Override
supportsFrameRateOverride()2992         public boolean supportsFrameRateOverride() {
2993             return SurfaceFlingerProperties.enable_frame_rate_override().orElse(true);
2994         }
2995 
getDisplayManager()2996         private DisplayManager getDisplayManager() {
2997             if (mDisplayManager == null) {
2998                 mDisplayManager = mContext.getSystemService(DisplayManager.class);
2999             }
3000             return mDisplayManager;
3001         }
3002 
getThermalService()3003         private IThermalService getThermalService() {
3004             return IThermalService.Stub.asInterface(
3005                     ServiceManager.getService(Context.THERMAL_SERVICE));
3006         }
3007     }
3008 }
3009