• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.display;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.content.res.Resources;
24 import android.database.ContentObserver;
25 import android.hardware.Sensor;
26 import android.hardware.SensorEvent;
27 import android.hardware.SensorEventListener;
28 import android.hardware.SensorManager;
29 import android.hardware.display.DisplayManager;
30 import android.net.Uri;
31 import android.os.Handler;
32 import android.os.Looper;
33 import android.os.Message;
34 import android.os.PowerManager;
35 import android.os.SystemClock;
36 import android.os.UserHandle;
37 import android.provider.DeviceConfig;
38 import android.provider.Settings;
39 import android.text.TextUtils;
40 import android.util.Pair;
41 import android.util.Slog;
42 import android.util.SparseArray;
43 import android.view.Display;
44 import android.view.DisplayInfo;
45 
46 import com.android.internal.R;
47 import com.android.internal.annotations.VisibleForTesting;
48 import com.android.internal.os.BackgroundThread;
49 import com.android.internal.util.IndentingPrintWriter;
50 import com.android.server.display.utils.AmbientFilter;
51 import com.android.server.display.utils.AmbientFilterFactory;
52 import com.android.server.utils.DeviceConfigInterface;
53 
54 import java.io.PrintWriter;
55 import java.text.SimpleDateFormat;
56 import java.util.ArrayList;
57 import java.util.Arrays;
58 import java.util.Date;
59 import java.util.List;
60 import java.util.Locale;
61 import java.util.Objects;
62 
63 /**
64  * The DisplayModeDirector is responsible for determining what modes are allowed to be
65  * automatically picked by the system based on system-wide and display-specific configuration.
66  */
67 public class DisplayModeDirector {
68     private static final String TAG = "DisplayModeDirector";
69     private boolean mLoggingEnabled;
70 
71     private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1;
72     private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
73     private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
74     private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
75     private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5;
76     private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6;
77 
78     // Special ID used to indicate that given vote is to be applied globally, rather than to a
79     // specific display.
80     private static final int GLOBAL_ID = -1;
81 
82     // The tolerance within which we consider something approximately equals.
83     private static final float FLOAT_TOLERANCE = 0.01f;
84 
85     private final Object mLock = new Object();
86     private final Context mContext;
87 
88     private final DisplayModeDirectorHandler mHandler;
89     private final Injector mInjector;
90 
91     private final AppRequestObserver mAppRequestObserver;
92     private final SettingsObserver mSettingsObserver;
93     private final DisplayObserver mDisplayObserver;
94     private final DeviceConfigInterface mDeviceConfig;
95     private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
96 
97     // A map from the display ID to the collection of votes and their priority. The latter takes
98     // the form of another map from the priority to the vote itself so that each priority is
99     // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
100     private SparseArray<SparseArray<Vote>> mVotesByDisplay;
101     // A map from the display ID to the supported modes on that display.
102     private SparseArray<Display.Mode[]> mSupportedModesByDisplay;
103     // A map from the display ID to the default mode of that display.
104     private SparseArray<Display.Mode> mDefaultModeByDisplay;
105 
106     private BrightnessObserver mBrightnessObserver;
107 
108     private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
109 
DisplayModeDirector(@onNull Context context, @NonNull Handler handler)110     public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
111         this(context, handler, new RealInjector());
112     }
113 
DisplayModeDirector(@onNull Context context, @NonNull Handler handler, @NonNull Injector injector)114     public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
115             @NonNull Injector injector) {
116         mContext = context;
117         mHandler = new DisplayModeDirectorHandler(handler.getLooper());
118         mInjector = injector;
119         mVotesByDisplay = new SparseArray<>();
120         mSupportedModesByDisplay = new SparseArray<>();
121         mDefaultModeByDisplay =  new SparseArray<>();
122         mAppRequestObserver = new AppRequestObserver();
123         mSettingsObserver = new SettingsObserver(context, handler);
124         mDisplayObserver = new DisplayObserver(context, handler);
125         mBrightnessObserver = new BrightnessObserver(context, handler);
126         mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
127         mDeviceConfig = injector.getDeviceConfig();
128     }
129 
130     /**
131      * Tells the DisplayModeDirector to update allowed votes and begin observing relevant system
132      * state.
133      *
134      * This has to be deferred because the object may be constructed before the rest of the system
135      * is ready.
136      */
start(SensorManager sensorManager)137     public void start(SensorManager sensorManager) {
138         mSettingsObserver.observe();
139         mDisplayObserver.observe();
140         mSettingsObserver.observe();
141         mBrightnessObserver.observe(sensorManager);
142         synchronized (mLock) {
143             // We may have a listener already registered before the call to start, so go ahead and
144             // notify them to pick up our newly initialized state.
145             notifyDesiredDisplayModeSpecsChangedLocked();
146         }
147 
148     }
149 
setLoggingEnabled(boolean loggingEnabled)150     public void setLoggingEnabled(boolean loggingEnabled) {
151         if (mLoggingEnabled == loggingEnabled) {
152             return;
153         }
154         mLoggingEnabled = loggingEnabled;
155         mBrightnessObserver.setLoggingEnabled(loggingEnabled);
156     }
157 
158     @NonNull
getVotesLocked(int displayId)159     private SparseArray<Vote> getVotesLocked(int displayId) {
160         SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
161         final SparseArray<Vote> votes;
162         if (displayVotes != null) {
163             votes = displayVotes.clone();
164         } else {
165             votes = new SparseArray<>();
166         }
167 
168         SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
169         if (globalVotes != null) {
170             for (int i = 0; i < globalVotes.size(); i++) {
171                 int priority = globalVotes.keyAt(i);
172                 if (votes.indexOfKey(priority) < 0) {
173                     votes.put(priority, globalVotes.valueAt(i));
174                 }
175             }
176         }
177         return votes;
178     }
179 
180     private static final class VoteSummary {
181         public float minRefreshRate;
182         public float maxRefreshRate;
183         public int width;
184         public int height;
185 
VoteSummary()186         VoteSummary() {
187             reset();
188         }
189 
reset()190         public void reset() {
191             minRefreshRate = 0f;
192             maxRefreshRate = Float.POSITIVE_INFINITY;
193             width = Vote.INVALID_SIZE;
194             height = Vote.INVALID_SIZE;
195         }
196     }
197 
198     // VoteSummary is returned as an output param to cut down a bit on the number of temporary
199     // objects.
summarizeVotes( SparseArray<Vote> votes, int lowestConsideredPriority, VoteSummary summary)200     private void summarizeVotes(
201             SparseArray<Vote> votes, int lowestConsideredPriority, /*out*/ VoteSummary summary) {
202         summary.reset();
203         for (int priority = Vote.MAX_PRIORITY; priority >= lowestConsideredPriority; priority--) {
204             Vote vote = votes.get(priority);
205             if (vote == null) {
206                 continue;
207             }
208             // For refresh rates, just use the tightest bounds of all the votes
209             summary.minRefreshRate = Math.max(summary.minRefreshRate, vote.refreshRateRange.min);
210             summary.maxRefreshRate = Math.min(summary.maxRefreshRate, vote.refreshRateRange.max);
211             // For display size, use only the first vote we come across (i.e. the highest
212             // priority vote that includes the width / height).
213             if (summary.height == Vote.INVALID_SIZE && summary.width == Vote.INVALID_SIZE
214                     && vote.height > 0 && vote.width > 0) {
215                 summary.width = vote.width;
216                 summary.height = vote.height;
217             }
218         }
219     }
220 
221     /**
222      * Calculates the refresh rate ranges and display modes that the system is allowed to freely
223      * switch between based on global and display-specific constraints.
224      *
225      * @param displayId The display to query for.
226      * @return The ID of the default mode the system should use, and the refresh rate range the
227      * system is allowed to switch between.
228      */
229     @NonNull
getDesiredDisplayModeSpecs(int displayId)230     public DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(int displayId) {
231         synchronized (mLock) {
232             SparseArray<Vote> votes = getVotesLocked(displayId);
233             Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
234             Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
235             if (modes == null || defaultMode == null) {
236                 Slog.e(TAG,
237                         "Asked about unknown display, returning empty display mode specs!"
238                                 + "(id=" + displayId + ")");
239                 return new DesiredDisplayModeSpecs();
240             }
241 
242             int[] availableModes = new int[]{defaultMode.getModeId()};
243             VoteSummary primarySummary = new VoteSummary();
244             int lowestConsideredPriority = Vote.MIN_PRIORITY;
245             while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
246                 summarizeVotes(votes, lowestConsideredPriority, primarySummary);
247 
248                 // If we don't have anything specifying the width / height of the display, just use
249                 // the default width and height. We don't want these switching out from underneath
250                 // us since it's a pretty disruptive behavior.
251                 if (primarySummary.height == Vote.INVALID_SIZE
252                         || primarySummary.width == Vote.INVALID_SIZE) {
253                     primarySummary.width = defaultMode.getPhysicalWidth();
254                     primarySummary.height = defaultMode.getPhysicalHeight();
255                 }
256 
257                 availableModes = filterModes(modes, primarySummary);
258                 if (availableModes.length > 0) {
259                     if (mLoggingEnabled) {
260                         Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
261                                 + " with lowest priority considered "
262                                 + Vote.priorityToString(lowestConsideredPriority)
263                                 + " and constraints: "
264                                 + "width=" + primarySummary.width
265                                 + ", height=" + primarySummary.height
266                                 + ", minRefreshRate=" + primarySummary.minRefreshRate
267                                 + ", maxRefreshRate=" + primarySummary.maxRefreshRate);
268                     }
269                     break;
270                 }
271 
272                 if (mLoggingEnabled) {
273                     Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
274                             + Vote.priorityToString(lowestConsideredPriority)
275                             + " and with the following constraints: "
276                             + "width=" + primarySummary.width
277                             + ", height=" + primarySummary.height
278                             + ", minRefreshRate=" + primarySummary.minRefreshRate
279                             + ", maxRefreshRate=" + primarySummary.maxRefreshRate);
280                 }
281 
282                 // If we haven't found anything with the current set of votes, drop the
283                 // current lowest priority vote.
284                 lowestConsideredPriority++;
285             }
286 
287             VoteSummary appRequestSummary = new VoteSummary();
288             summarizeVotes(
289                     votes, Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, appRequestSummary);
290             appRequestSummary.minRefreshRate =
291                     Math.min(appRequestSummary.minRefreshRate, primarySummary.minRefreshRate);
292             appRequestSummary.maxRefreshRate =
293                     Math.max(appRequestSummary.maxRefreshRate, primarySummary.maxRefreshRate);
294             if (mLoggingEnabled) {
295                 Slog.i(TAG,
296                         String.format("App request range: [%.0f %.0f]",
297                                 appRequestSummary.minRefreshRate,
298                                 appRequestSummary.maxRefreshRate));
299             }
300 
301             int baseModeId = defaultMode.getModeId();
302             if (availableModes.length > 0) {
303                 baseModeId = availableModes[0];
304             }
305             // filterModes function is going to filter the modes based on the voting system. If
306             // the application requests a given mode with preferredModeId function, it will be
307             // stored as baseModeId.
308             return new DesiredDisplayModeSpecs(baseModeId,
309                     new RefreshRateRange(
310                             primarySummary.minRefreshRate, primarySummary.maxRefreshRate),
311                     new RefreshRateRange(
312                             appRequestSummary.minRefreshRate, appRequestSummary.maxRefreshRate));
313         }
314     }
315 
filterModes(Display.Mode[] supportedModes, VoteSummary summary)316     private int[] filterModes(Display.Mode[] supportedModes, VoteSummary summary) {
317         ArrayList<Display.Mode> availableModes = new ArrayList<>();
318         for (Display.Mode mode : supportedModes) {
319             if (mode.getPhysicalWidth() != summary.width
320                     || mode.getPhysicalHeight() != summary.height) {
321                 if (mLoggingEnabled) {
322                     Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
323                             + ": desiredWidth=" + summary.width
324                             + ": desiredHeight=" + summary.height
325                             + ": actualWidth=" + mode.getPhysicalWidth()
326                             + ": actualHeight=" + mode.getPhysicalHeight());
327                 }
328                 continue;
329             }
330             final float refreshRate = mode.getRefreshRate();
331             // Some refresh rates are calculated based on frame timings, so they aren't *exactly*
332             // equal to expected refresh rate. Given that, we apply a bit of tolerance to this
333             // comparison.
334             if (refreshRate < (summary.minRefreshRate - FLOAT_TOLERANCE)
335                     || refreshRate > (summary.maxRefreshRate + FLOAT_TOLERANCE)) {
336                 if (mLoggingEnabled) {
337                     Slog.w(TAG, "Discarding mode " + mode.getModeId()
338                             + ", outside refresh rate bounds"
339                             + ": minRefreshRate=" + summary.minRefreshRate
340                             + ", maxRefreshRate=" + summary.maxRefreshRate
341                             + ", modeRefreshRate=" + refreshRate);
342                 }
343                 continue;
344             }
345             availableModes.add(mode);
346         }
347         final int size = availableModes.size();
348         int[] availableModeIds = new int[size];
349         for (int i = 0; i < size; i++) {
350             availableModeIds[i] = availableModes.get(i).getModeId();
351         }
352         return availableModeIds;
353     }
354 
355     /**
356      * Gets the observer responsible for application display mode requests.
357      */
358     @NonNull
getAppRequestObserver()359     public AppRequestObserver getAppRequestObserver() {
360         // We don't need to lock here because mAppRequestObserver is a final field, which is
361         // guaranteed to be visible on all threads after construction.
362         return mAppRequestObserver;
363     }
364 
365     /**
366      * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate
367      * ranges.
368      */
setDesiredDisplayModeSpecsListener( @ullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener)369     public void setDesiredDisplayModeSpecsListener(
370             @Nullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener) {
371         synchronized (mLock) {
372             mDesiredDisplayModeSpecsListener = desiredDisplayModeSpecsListener;
373         }
374     }
375 
376     /**
377      * Retrieve the Vote for the given display and priority. Intended only for testing purposes.
378      *
379      * @param displayId the display to query for
380      * @param priority the priority of the vote to return
381      * @return the vote corresponding to the given {@code displayId} and {@code priority},
382      *         or {@code null} if there isn't one
383      */
384     @VisibleForTesting
385     @Nullable
getVote(int displayId, int priority)386     Vote getVote(int displayId, int priority) {
387         synchronized (mLock) {
388             SparseArray<Vote> votes = getVotesLocked(displayId);
389             return votes.get(priority);
390         }
391     }
392 
393     /**
394      * Print the object's state and debug information into the given stream.
395      *
396      * @param pw The stream to dump information to.
397      */
dump(PrintWriter pw)398     public void dump(PrintWriter pw) {
399         pw.println("DisplayModeDirector");
400         synchronized (mLock) {
401             pw.println("  mSupportedModesByDisplay:");
402             for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
403                 final int id = mSupportedModesByDisplay.keyAt(i);
404                 final Display.Mode[] modes = mSupportedModesByDisplay.valueAt(i);
405                 pw.println("    " + id + " -> " + Arrays.toString(modes));
406             }
407             pw.println("  mDefaultModeByDisplay:");
408             for (int i = 0; i < mDefaultModeByDisplay.size(); i++) {
409                 final int id = mDefaultModeByDisplay.keyAt(i);
410                 final Display.Mode mode = mDefaultModeByDisplay.valueAt(i);
411                 pw.println("    " + id + " -> " + mode);
412             }
413             pw.println("  mVotesByDisplay:");
414             for (int i = 0; i < mVotesByDisplay.size(); i++) {
415                 pw.println("    " + mVotesByDisplay.keyAt(i) + ":");
416                 SparseArray<Vote> votes = mVotesByDisplay.valueAt(i);
417                 for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
418                     Vote vote = votes.get(p);
419                     if (vote == null) {
420                         continue;
421                     }
422                     pw.println("      " + Vote.priorityToString(p) + " -> " + vote);
423                 }
424             }
425             mSettingsObserver.dumpLocked(pw);
426             mAppRequestObserver.dumpLocked(pw);
427             mBrightnessObserver.dumpLocked(pw);
428         }
429     }
430 
updateVoteLocked(int priority, Vote vote)431     private void updateVoteLocked(int priority, Vote vote) {
432         updateVoteLocked(GLOBAL_ID, priority, vote);
433     }
434 
updateVoteLocked(int displayId, int priority, Vote vote)435     private void updateVoteLocked(int displayId, int priority, Vote vote) {
436         if (mLoggingEnabled) {
437             Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
438                     + ", priority=" + Vote.priorityToString(priority)
439                     + ", vote=" + vote + ")");
440         }
441         if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
442             Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
443                     + " priority=" + Vote.priorityToString(priority)
444                     + ", vote=" + vote, new Throwable());
445             return;
446         }
447         final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId);
448 
449         Vote currentVote = votes.get(priority);
450         if (vote != null) {
451             votes.put(priority, vote);
452         } else {
453             votes.remove(priority);
454         }
455 
456         if (votes.size() == 0) {
457             if (mLoggingEnabled) {
458                 Slog.i(TAG, "No votes left for display " + displayId + ", removing.");
459             }
460             mVotesByDisplay.remove(displayId);
461         }
462 
463         notifyDesiredDisplayModeSpecsChangedLocked();
464     }
465 
notifyDesiredDisplayModeSpecsChangedLocked()466     private void notifyDesiredDisplayModeSpecsChangedLocked() {
467         if (mDesiredDisplayModeSpecsListener != null
468                 && !mHandler.hasMessages(MSG_REFRESH_RATE_RANGE_CHANGED)) {
469             // We need to post this to a handler to avoid calling out while holding the lock
470             // since we know there are things that both listen for changes as well as provide
471             // information. If we did call out while holding the lock, then there's no
472             // guaranteed lock order and we run the real of risk deadlock.
473             Message msg = mHandler.obtainMessage(
474                     MSG_REFRESH_RATE_RANGE_CHANGED, mDesiredDisplayModeSpecsListener);
475             msg.sendToTarget();
476         }
477     }
478 
getOrCreateVotesByDisplay(int displayId)479     private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) {
480         int index = mVotesByDisplay.indexOfKey(displayId);
481         if (mVotesByDisplay.indexOfKey(displayId) >= 0) {
482             return mVotesByDisplay.get(displayId);
483         } else {
484             SparseArray<Vote> votes = new SparseArray<>();
485             mVotesByDisplay.put(displayId, votes);
486             return votes;
487         }
488     }
489 
490     @VisibleForTesting
injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay)491     void injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay) {
492         mSupportedModesByDisplay = supportedModesByDisplay;
493     }
494 
495     @VisibleForTesting
injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay)496     void injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay) {
497         mDefaultModeByDisplay = defaultModeByDisplay;
498     }
499 
500     @VisibleForTesting
injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay)501     void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) {
502         mVotesByDisplay = votesByDisplay;
503     }
504 
505     @VisibleForTesting
injectBrightnessObserver(BrightnessObserver brightnessObserver)506     void injectBrightnessObserver(BrightnessObserver brightnessObserver) {
507         mBrightnessObserver = brightnessObserver;
508     }
509 
510     @VisibleForTesting
getBrightnessObserver()511     BrightnessObserver getBrightnessObserver() {
512         return mBrightnessObserver;
513     }
514 
515     @VisibleForTesting
getSettingsObserver()516     SettingsObserver getSettingsObserver() {
517         return mSettingsObserver;
518     }
519 
520 
521     @VisibleForTesting
getDesiredDisplayModeSpecsWithInjectedFpsSettings( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate)522     DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
523             float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
524         synchronized (mLock) {
525             mSettingsObserver.updateRefreshRateSettingLocked(
526                     minRefreshRate, peakRefreshRate, defaultRefreshRate);
527             return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY);
528         }
529     }
530 
531     /**
532      * Listens for changes refresh rate coordination.
533      */
534     public interface DesiredDisplayModeSpecsListener {
535         /**
536          * Called when the refresh rate range may have changed.
537          */
onDesiredDisplayModeSpecsChanged()538         void onDesiredDisplayModeSpecsChanged();
539     }
540 
541     private final class DisplayModeDirectorHandler extends Handler {
DisplayModeDirectorHandler(Looper looper)542         DisplayModeDirectorHandler(Looper looper) {
543             super(looper, null, true /*async*/);
544         }
545 
546         @Override
handleMessage(Message msg)547         public void handleMessage(Message msg) {
548             switch (msg.what) {
549                 case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED: {
550                     Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
551                     mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged(
552                             thresholds.first, thresholds.second);
553                     break;
554                 }
555 
556                 case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED: {
557                     int refreshRateInZone = msg.arg1;
558                     mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(
559                             refreshRateInZone);
560                     break;
561                 }
562 
563                 case MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED: {
564                     Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
565 
566                     mBrightnessObserver.onDeviceConfigHighBrightnessThresholdsChanged(
567                             thresholds.first, thresholds.second);
568 
569                     break;
570                 }
571 
572                 case MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED: {
573                     int refreshRateInZone = msg.arg1;
574                     mBrightnessObserver.onDeviceConfigRefreshRateInHighZoneChanged(
575                             refreshRateInZone);
576                     break;
577                 }
578 
579                 case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
580                     Float defaultPeakRefreshRate = (Float) msg.obj;
581                     mSettingsObserver.onDeviceConfigDefaultPeakRefreshRateChanged(
582                             defaultPeakRefreshRate);
583                     break;
584 
585                 case MSG_REFRESH_RATE_RANGE_CHANGED:
586                     DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener =
587                             (DesiredDisplayModeSpecsListener) msg.obj;
588                     desiredDisplayModeSpecsListener.onDesiredDisplayModeSpecsChanged();
589                     break;
590             }
591         }
592     }
593 
594     /**
595      * Information about the min and max refresh rate DM would like to set the display to.
596      */
597     public static final class RefreshRateRange {
598         /**
599          * The lowest desired refresh rate.
600          */
601         public float min;
602         /**
603          * The highest desired refresh rate.
604          */
605         public float max;
606 
RefreshRateRange()607         public RefreshRateRange() {}
608 
RefreshRateRange(float min, float max)609         public RefreshRateRange(float min, float max) {
610             if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) {
611                 Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : "
612                         + min + " " + max);
613                 this.min = this.max = 0;
614                 return;
615             }
616             if (min > max) {
617                 // Min and max are within epsilon of each other, but in the wrong order.
618                 float t = min;
619                 min = max;
620                 max = t;
621             }
622             this.min = min;
623             this.max = max;
624         }
625 
626         /**
627          * Checks whether the two objects have the same values.
628          */
629         @Override
equals(Object other)630         public boolean equals(Object other) {
631             if (other == this) {
632                 return true;
633             }
634 
635             if (!(other instanceof RefreshRateRange)) {
636                 return false;
637             }
638 
639             RefreshRateRange refreshRateRange = (RefreshRateRange) other;
640             return (min == refreshRateRange.min && max == refreshRateRange.max);
641         }
642 
643         @Override
hashCode()644         public int hashCode() {
645             return Objects.hash(min, max);
646         }
647 
648         @Override
toString()649         public String toString() {
650             return "(" + min + " " + max + ")";
651         }
652     }
653 
654     /**
655      * Information about the desired display mode to be set by the system. Includes the base
656      * mode ID and the primary and app request refresh rate ranges.
657      *
658      * We have this class in addition to SurfaceControl.DesiredDisplayConfigSpecs to make clear the
659      * distinction between the config ID / physical index that
660      * SurfaceControl.DesiredDisplayConfigSpecs uses, and the mode ID used here.
661      */
662     public static final class DesiredDisplayModeSpecs {
663         /**
664          * Base mode ID. This is what system defaults to for all other settings, or
665          * if the refresh rate range is not available.
666          */
667         public int baseModeId;
668         /**
669          * The primary refresh rate range.
670          */
671         public final RefreshRateRange primaryRefreshRateRange;
672         /**
673          * The app request refresh rate range. Lower priority considerations won't be included in
674          * this range, allowing surface flinger to consider additional refresh rates for apps that
675          * call setFrameRate(). This range will be greater than or equal to the primary refresh rate
676          * range, never smaller.
677          */
678         public final RefreshRateRange appRequestRefreshRateRange;
679 
DesiredDisplayModeSpecs()680         public DesiredDisplayModeSpecs() {
681             primaryRefreshRateRange = new RefreshRateRange();
682             appRequestRefreshRateRange = new RefreshRateRange();
683         }
684 
DesiredDisplayModeSpecs(int baseModeId, @NonNull RefreshRateRange primaryRefreshRateRange, @NonNull RefreshRateRange appRequestRefreshRateRange)685         public DesiredDisplayModeSpecs(int baseModeId,
686                 @NonNull RefreshRateRange primaryRefreshRateRange,
687                 @NonNull RefreshRateRange appRequestRefreshRateRange) {
688             this.baseModeId = baseModeId;
689             this.primaryRefreshRateRange = primaryRefreshRateRange;
690             this.appRequestRefreshRateRange = appRequestRefreshRateRange;
691         }
692 
693         /**
694          * Returns a string representation of the object.
695          */
696         @Override
toString()697         public String toString() {
698             return String.format("baseModeId=%d primaryRefreshRateRange=[%.0f %.0f]"
699                             + " appRequestRefreshRateRange=[%.0f %.0f]",
700                     baseModeId, primaryRefreshRateRange.min, primaryRefreshRateRange.max,
701                     appRequestRefreshRateRange.min, appRequestRefreshRateRange.max);
702         }
703         /**
704          * Checks whether the two objects have the same values.
705          */
706         @Override
equals(Object other)707         public boolean equals(Object other) {
708             if (other == this) {
709                 return true;
710             }
711 
712             if (!(other instanceof DesiredDisplayModeSpecs)) {
713                 return false;
714             }
715 
716             DesiredDisplayModeSpecs desiredDisplayModeSpecs = (DesiredDisplayModeSpecs) other;
717 
718             if (baseModeId != desiredDisplayModeSpecs.baseModeId) {
719                 return false;
720             }
721             if (!primaryRefreshRateRange.equals(desiredDisplayModeSpecs.primaryRefreshRateRange)) {
722                 return false;
723             }
724             if (!appRequestRefreshRateRange.equals(
725                         desiredDisplayModeSpecs.appRequestRefreshRateRange)) {
726                 return false;
727             }
728             return true;
729         }
730 
731         @Override
hashCode()732         public int hashCode() {
733             return Objects.hash(baseModeId, primaryRefreshRateRange, appRequestRefreshRateRange);
734         }
735 
736         /**
737          * Copy values from the other object.
738          */
copyFrom(DesiredDisplayModeSpecs other)739         public void copyFrom(DesiredDisplayModeSpecs other) {
740             baseModeId = other.baseModeId;
741             primaryRefreshRateRange.min = other.primaryRefreshRateRange.min;
742             primaryRefreshRateRange.max = other.primaryRefreshRateRange.max;
743             appRequestRefreshRateRange.min = other.appRequestRefreshRateRange.min;
744             appRequestRefreshRateRange.max = other.appRequestRefreshRateRange.max;
745         }
746     }
747 
748     @VisibleForTesting
749     static final class Vote {
750         // DEFAULT_FRAME_RATE votes for [0, DEFAULT]. As the lowest priority vote, it's overridden
751         // by all other considerations. It acts to set a default frame rate for a device.
752         public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0;
753 
754         // FLICKER votes for a single refresh rate like [60,60], [90,90] or null.
755         // If the higher voters result is a range, it will fix the rate to a single choice.
756         // It's used to avoid refresh rate switches in certain conditions which may result in the
757         // user seeing the display flickering when the switches occur.
758         public static final int PRIORITY_FLICKER = 1;
759 
760         // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
761         // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
762         public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2;
763 
764         // We split the app request into different priorities in case we can satisfy one desire
765         // without the other.
766 
767         // Application can specify preferred refresh rate with below attrs.
768         // @see android.view.WindowManager.LayoutParams#preferredRefreshRate
769         // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
770         // System also forces some apps like blacklisted app to run at a lower refresh rate.
771         // @see android.R.array#config_highRefreshRateBlacklist
772         public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3;
773         public static final int PRIORITY_APP_REQUEST_SIZE = 4;
774 
775         // SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest
776         // of low priority voters. It votes [0, max(PEAK, MIN)]
777         public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 5;
778 
779         // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
780         public static final int PRIORITY_LOW_POWER_MODE = 6;
781 
782         // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
783         // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
784 
785         public static final int MIN_PRIORITY = PRIORITY_DEFAULT_REFRESH_RATE;
786         public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;
787 
788         // The cutoff for the app request refresh rate range. Votes with priorities lower than this
789         // value will not be considered when constructing the app request refresh rate range.
790         public static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
791                 PRIORITY_APP_REQUEST_REFRESH_RATE;
792 
793         /**
794          * A value signifying an invalid width or height in a vote.
795          */
796         public static final int INVALID_SIZE = -1;
797 
798         /**
799          * The requested width of the display in pixels, or INVALID_SIZE;
800          */
801         public final int width;
802         /**
803          * The requested height of the display in pixels, or INVALID_SIZE;
804          */
805         public final int height;
806         /**
807          * Information about the min and max refresh rate DM would like to set the display to.
808          */
809         public final RefreshRateRange refreshRateRange;
810 
forRefreshRates(float minRefreshRate, float maxRefreshRate)811         public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
812             return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate);
813         }
814 
forSize(int width, int height)815         public static Vote forSize(int width, int height) {
816             return new Vote(width, height, 0f, Float.POSITIVE_INFINITY);
817         }
818 
Vote(int width, int height, float minRefreshRate, float maxRefreshRate)819         private Vote(int width, int height,
820                 float minRefreshRate, float maxRefreshRate) {
821             this.width = width;
822             this.height = height;
823             this.refreshRateRange =
824                     new RefreshRateRange(minRefreshRate, maxRefreshRate);
825         }
826 
priorityToString(int priority)827         public static String priorityToString(int priority) {
828             switch (priority) {
829                 case PRIORITY_DEFAULT_REFRESH_RATE:
830                     return "PRIORITY_DEFAULT_REFRESH_RATE";
831                 case PRIORITY_FLICKER:
832                     return "PRIORITY_FLICKER";
833                 case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
834                     return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
835                 case PRIORITY_APP_REQUEST_REFRESH_RATE:
836                     return "PRIORITY_APP_REQUEST_REFRESH_RATE";
837                 case PRIORITY_APP_REQUEST_SIZE:
838                     return "PRIORITY_APP_REQUEST_SIZE";
839                 case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
840                     return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
841                 case PRIORITY_LOW_POWER_MODE:
842                     return "PRIORITY_LOW_POWER_MODE";
843                 default:
844                     return Integer.toString(priority);
845             }
846         }
847 
848         @Override
toString()849         public String toString() {
850             return "Vote{"
851                 + "width=" + width + ", height=" + height
852                 + ", minRefreshRate=" + refreshRateRange.min
853                 + ", maxRefreshRate=" + refreshRateRange.max + "}";
854         }
855     }
856 
857     @VisibleForTesting
858     final class SettingsObserver extends ContentObserver {
859         private final Uri mPeakRefreshRateSetting =
860                 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
861         private final Uri mMinRefreshRateSetting =
862                 Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE);
863         private final Uri mLowPowerModeSetting =
864                 Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
865 
866         private final Context mContext;
867         private float mDefaultPeakRefreshRate;
868         private float mDefaultRefreshRate;
869 
SettingsObserver(@onNull Context context, @NonNull Handler handler)870         SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
871             super(handler);
872             mContext = context;
873             mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
874                     R.integer.config_defaultPeakRefreshRate);
875             mDefaultRefreshRate =
876                     (float) context.getResources().getInteger(R.integer.config_defaultRefreshRate);
877         }
878 
observe()879         public void observe() {
880             final ContentResolver cr = mContext.getContentResolver();
881             mInjector.registerPeakRefreshRateObserver(cr, this);
882             cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
883                     UserHandle.USER_SYSTEM);
884             cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
885                     UserHandle.USER_SYSTEM);
886 
887             Float deviceConfigDefaultPeakRefresh =
888                     mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
889             if (deviceConfigDefaultPeakRefresh != null) {
890                 mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh;
891             }
892 
893             synchronized (mLock) {
894                 updateRefreshRateSettingLocked();
895                 updateLowPowerModeSettingLocked();
896             }
897         }
898 
setDefaultRefreshRate(float refreshRate)899         public void setDefaultRefreshRate(float refreshRate) {
900             synchronized (mLock) {
901                 mDefaultRefreshRate = refreshRate;
902                 updateRefreshRateSettingLocked();
903             }
904         }
905 
onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate)906         public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
907             if (defaultPeakRefreshRate == null) {
908                 defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
909                         R.integer.config_defaultPeakRefreshRate);
910             }
911 
912             if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) {
913                 synchronized (mLock) {
914                     mDefaultPeakRefreshRate = defaultPeakRefreshRate;
915                     updateRefreshRateSettingLocked();
916                 }
917             }
918         }
919 
920         @Override
onChange(boolean selfChange, Uri uri, int userId)921         public void onChange(boolean selfChange, Uri uri, int userId) {
922             synchronized (mLock) {
923                 if (mPeakRefreshRateSetting.equals(uri)
924                         || mMinRefreshRateSetting.equals(uri)) {
925                     updateRefreshRateSettingLocked();
926                 } else if (mLowPowerModeSetting.equals(uri)) {
927                     updateLowPowerModeSettingLocked();
928                 }
929             }
930         }
931 
updateLowPowerModeSettingLocked()932         private void updateLowPowerModeSettingLocked() {
933             boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
934                     Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
935             final Vote vote;
936             if (inLowPowerMode) {
937                 vote = Vote.forRefreshRates(0f, 60f);
938             } else {
939                 vote = null;
940             }
941             updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote);
942             mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
943         }
944 
updateRefreshRateSettingLocked()945         private void updateRefreshRateSettingLocked() {
946             float minRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
947                     Settings.System.MIN_REFRESH_RATE, 0f);
948             float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
949                     Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate);
950             updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
951         }
952 
updateRefreshRateSettingLocked( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate)953         private void updateRefreshRateSettingLocked(
954                 float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
955             // TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is
956             // used to predict if we're going to be doing frequent refresh rate switching, and if
957             // so, enable the brightness observer. The logic here is more complicated and fragile
958             // than necessary, and we should improve it. See b/156304339 for more info.
959             Vote peakVote = peakRefreshRate == 0f
960                     ? null
961                     : Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate));
962             updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, peakVote);
963             updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
964                     Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY));
965             Vote defaultVote =
966                     defaultRefreshRate == 0f ? null : Vote.forRefreshRates(0f, defaultRefreshRate);
967             updateVoteLocked(Vote.PRIORITY_DEFAULT_REFRESH_RATE, defaultVote);
968 
969             float maxRefreshRate;
970             if (peakRefreshRate == 0f && defaultRefreshRate == 0f) {
971                 // We require that at least one of the peak or default refresh rate values are
972                 // set. The brightness observer requires that we're able to predict whether or not
973                 // we're going to do frequent refresh rate switching, and with the way the code is
974                 // currently written, we need either a default or peak refresh rate value for that.
975                 Slog.e(TAG, "Default and peak refresh rates are both 0. One of them should be set"
976                         + " to a valid value.");
977                 maxRefreshRate = minRefreshRate;
978             } else if (peakRefreshRate == 0f) {
979                 maxRefreshRate = defaultRefreshRate;
980             } else if (defaultRefreshRate == 0f) {
981                 maxRefreshRate = peakRefreshRate;
982             } else {
983                 maxRefreshRate = Math.min(defaultRefreshRate, peakRefreshRate);
984             }
985 
986             mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, maxRefreshRate);
987         }
988 
dumpLocked(PrintWriter pw)989         public void dumpLocked(PrintWriter pw) {
990             pw.println("  SettingsObserver");
991             pw.println("    mDefaultRefreshRate: " + mDefaultRefreshRate);
992             pw.println("    mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate);
993         }
994     }
995 
996     final class AppRequestObserver {
997         private SparseArray<Display.Mode> mAppRequestedModeByDisplay;
998 
AppRequestObserver()999         AppRequestObserver() {
1000             mAppRequestedModeByDisplay = new SparseArray<>();
1001         }
1002 
setAppRequestedMode(int displayId, int modeId)1003         public void setAppRequestedMode(int displayId, int modeId) {
1004             synchronized (mLock) {
1005                 setAppRequestedModeLocked(displayId, modeId);
1006             }
1007         }
1008 
setAppRequestedModeLocked(int displayId, int modeId)1009         private void setAppRequestedModeLocked(int displayId, int modeId) {
1010             final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId);
1011             if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
1012                 return;
1013             }
1014 
1015             final Vote refreshRateVote;
1016             final Vote sizeVote;
1017             if (requestedMode != null) {
1018                 mAppRequestedModeByDisplay.put(displayId, requestedMode);
1019                 float refreshRate = requestedMode.getRefreshRate();
1020                 refreshRateVote = Vote.forRefreshRates(refreshRate, refreshRate);
1021                 sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
1022                         requestedMode.getPhysicalHeight());
1023             } else {
1024                 mAppRequestedModeByDisplay.remove(displayId);
1025                 refreshRateVote = null;
1026                 sizeVote = null;
1027             }
1028 
1029             updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote);
1030             updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
1031             return;
1032         }
1033 
findModeByIdLocked(int displayId, int modeId)1034         private Display.Mode findModeByIdLocked(int displayId, int modeId) {
1035             Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
1036             if (modes == null) {
1037                 return null;
1038             }
1039             for (Display.Mode mode : modes) {
1040                 if (mode.getModeId() == modeId) {
1041                     return mode;
1042                 }
1043             }
1044             return null;
1045         }
1046 
dumpLocked(PrintWriter pw)1047         public void dumpLocked(PrintWriter pw) {
1048             pw.println("  AppRequestObserver");
1049             pw.println("    mAppRequestedModeByDisplay:");
1050             for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
1051                 final int id = mAppRequestedModeByDisplay.keyAt(i);
1052                 final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
1053                 pw.println("    " + id + " -> " + mode);
1054             }
1055         }
1056     }
1057 
1058     private final class DisplayObserver implements DisplayManager.DisplayListener {
1059         // Note that we can never call into DisplayManager or any of the non-POD classes it
1060         // returns, while holding mLock since it may call into DMS, which might be simultaneously
1061         // calling into us already holding its own lock.
1062         private final Context mContext;
1063         private final Handler mHandler;
1064 
DisplayObserver(Context context, Handler handler)1065         DisplayObserver(Context context, Handler handler) {
1066             mContext = context;
1067             mHandler = handler;
1068         }
1069 
observe()1070         public void observe() {
1071             DisplayManager dm = mContext.getSystemService(DisplayManager.class);
1072             dm.registerDisplayListener(this, mHandler);
1073 
1074             // Populate existing displays
1075             SparseArray<Display.Mode[]> modes = new SparseArray<>();
1076             SparseArray<Display.Mode> defaultModes = new SparseArray<>();
1077             DisplayInfo info = new DisplayInfo();
1078             Display[] displays = dm.getDisplays();
1079             for (Display d : displays) {
1080                 final int displayId = d.getDisplayId();
1081                 d.getDisplayInfo(info);
1082                 modes.put(displayId, info.supportedModes);
1083                 defaultModes.put(displayId, info.getDefaultMode());
1084             }
1085             synchronized (mLock) {
1086                 final int size = modes.size();
1087                 for (int i = 0; i < size; i++) {
1088                     mSupportedModesByDisplay.put(modes.keyAt(i), modes.valueAt(i));
1089                     mDefaultModeByDisplay.put(defaultModes.keyAt(i), defaultModes.valueAt(i));
1090                 }
1091             }
1092         }
1093 
1094         @Override
onDisplayAdded(int displayId)1095         public void onDisplayAdded(int displayId) {
1096             updateDisplayModes(displayId);
1097         }
1098 
1099         @Override
onDisplayRemoved(int displayId)1100         public void onDisplayRemoved(int displayId) {
1101             synchronized (mLock) {
1102                 mSupportedModesByDisplay.remove(displayId);
1103                 mDefaultModeByDisplay.remove(displayId);
1104             }
1105         }
1106 
1107         @Override
onDisplayChanged(int displayId)1108         public void onDisplayChanged(int displayId) {
1109             updateDisplayModes(displayId);
1110             // TODO: Break the coupling between DisplayObserver and BrightnessObserver.
1111             mBrightnessObserver.onDisplayChanged(displayId);
1112         }
1113 
updateDisplayModes(int displayId)1114         private void updateDisplayModes(int displayId) {
1115             Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
1116             if (d == null) {
1117                 // We can occasionally get a display added or changed event for a display that was
1118                 // subsequently removed, which means this returns null. Check this case and bail
1119                 // out early; if it gets re-attached we'll eventually get another call back for it.
1120                 return;
1121             }
1122             DisplayInfo info = new DisplayInfo();
1123             d.getDisplayInfo(info);
1124             boolean changed = false;
1125             synchronized (mLock) {
1126                 if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) {
1127                     mSupportedModesByDisplay.put(displayId, info.supportedModes);
1128                     changed = true;
1129                 }
1130                 if (!Objects.equals(mDefaultModeByDisplay.get(displayId), info.getDefaultMode())) {
1131                     changed = true;
1132                     mDefaultModeByDisplay.put(displayId, info.getDefaultMode());
1133                 }
1134                 if (changed) {
1135                     notifyDesiredDisplayModeSpecsChangedLocked();
1136                 }
1137             }
1138         }
1139     }
1140 
1141     /**
1142      * This class manages brightness threshold for switching between 60 hz and higher refresh rate.
1143      * See more information at the definition of
1144      * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and
1145      * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
1146      */
1147     @VisibleForTesting
1148     public class BrightnessObserver extends ContentObserver {
1149         private final static int LIGHT_SENSOR_RATE_MS = 250;
1150         private int[] mLowDisplayBrightnessThresholds;
1151         private int[] mLowAmbientBrightnessThresholds;
1152         private int[] mHighDisplayBrightnessThresholds;
1153         private int[] mHighAmbientBrightnessThresholds;
1154         // valid threshold if any item from the array >= 0
1155         private boolean mShouldObserveDisplayLowChange;
1156         private boolean mShouldObserveAmbientLowChange;
1157         private boolean mShouldObserveDisplayHighChange;
1158         private boolean mShouldObserveAmbientHighChange;
1159         private boolean mLoggingEnabled;
1160 
1161         private SensorManager mSensorManager;
1162         private Sensor mLightSensor;
1163         private LightSensorEventListener mLightSensorListener = new LightSensorEventListener();
1164         // Take it as low brightness before valid sensor data comes
1165         private float mAmbientLux = -1.0f;
1166         private AmbientFilter mAmbientFilter;
1167         private int mBrightness = -1;
1168 
1169         private final Context mContext;
1170 
1171         // Enable light sensor only when mShouldObserveAmbientLowChange is true or
1172         // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
1173         // changeable and low power mode off. After initialization, these states will
1174         // be updated from the same handler thread.
1175         private int mDefaultDisplayState = Display.STATE_UNKNOWN;
1176         private boolean mRefreshRateChangeable = false;
1177         private boolean mLowPowerModeEnabled = false;
1178 
1179         private int mRefreshRateInLowZone;
1180         private int mRefreshRateInHighZone;
1181 
BrightnessObserver(Context context, Handler handler)1182         BrightnessObserver(Context context, Handler handler) {
1183             super(handler);
1184             mContext = context;
1185             mLowDisplayBrightnessThresholds = context.getResources().getIntArray(
1186                     R.array.config_brightnessThresholdsOfPeakRefreshRate);
1187             mLowAmbientBrightnessThresholds = context.getResources().getIntArray(
1188                     R.array.config_ambientThresholdsOfPeakRefreshRate);
1189 
1190             if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
1191                 throw new RuntimeException("display low brightness threshold array and ambient "
1192                         + "brightness threshold array have different length: "
1193                         + "displayBrightnessThresholds="
1194                         + Arrays.toString(mLowDisplayBrightnessThresholds)
1195                         + ", ambientBrightnessThresholds="
1196                         + Arrays.toString(mLowAmbientBrightnessThresholds));
1197             }
1198 
1199             mHighDisplayBrightnessThresholds = context.getResources().getIntArray(
1200                     R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
1201             mHighAmbientBrightnessThresholds = context.getResources().getIntArray(
1202                     R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
1203             if (mHighDisplayBrightnessThresholds.length
1204                     != mHighAmbientBrightnessThresholds.length) {
1205                 throw new RuntimeException("display high brightness threshold array and ambient "
1206                         + "brightness threshold array have different length: "
1207                         + "displayBrightnessThresholds="
1208                         + Arrays.toString(mHighDisplayBrightnessThresholds)
1209                         + ", ambientBrightnessThresholds="
1210                         + Arrays.toString(mHighAmbientBrightnessThresholds));
1211             }
1212             mRefreshRateInHighZone = context.getResources().getInteger(
1213                     R.integer.config_fixedRefreshRateInHighZone);
1214         }
1215 
1216         /**
1217          * @return the refresh to lock to when in a low brightness zone
1218          */
1219         @VisibleForTesting
getRefreshRateInLowZone()1220         int getRefreshRateInLowZone() {
1221             return mRefreshRateInLowZone;
1222         }
1223 
1224         /**
1225          * @return the display brightness thresholds for the low brightness zones
1226          */
1227         @VisibleForTesting
getLowDisplayBrightnessThresholds()1228         int[] getLowDisplayBrightnessThresholds() {
1229             return mLowDisplayBrightnessThresholds;
1230         }
1231 
1232         /**
1233          * @return the ambient brightness thresholds for the low brightness zones
1234          */
1235         @VisibleForTesting
getLowAmbientBrightnessThresholds()1236         int[] getLowAmbientBrightnessThresholds() {
1237             return mLowAmbientBrightnessThresholds;
1238         }
1239 
registerLightSensor(SensorManager sensorManager, Sensor lightSensor)1240         public void registerLightSensor(SensorManager sensorManager, Sensor lightSensor) {
1241             mSensorManager = sensorManager;
1242             mLightSensor = lightSensor;
1243 
1244             mSensorManager.registerListener(mLightSensorListener,
1245                     mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
1246         }
1247 
observe(SensorManager sensorManager)1248         public void observe(SensorManager sensorManager) {
1249             mSensorManager = sensorManager;
1250             final ContentResolver cr = mContext.getContentResolver();
1251             mBrightness = Settings.System.getIntForUser(cr,
1252                     Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
1253 
1254             // DeviceConfig is accessible after system ready.
1255             int[] lowDisplayBrightnessThresholds =
1256                     mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds();
1257             int[] lowAmbientBrightnessThresholds =
1258                     mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds();
1259 
1260             if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null
1261                     && lowDisplayBrightnessThresholds.length
1262                     == lowAmbientBrightnessThresholds.length) {
1263                 mLowDisplayBrightnessThresholds = lowDisplayBrightnessThresholds;
1264                 mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds;
1265             }
1266 
1267 
1268             int[] highDisplayBrightnessThresholds =
1269                     mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds();
1270             int[] highAmbientBrightnessThresholds =
1271                     mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds();
1272 
1273             if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null
1274                     && highDisplayBrightnessThresholds.length
1275                     == highAmbientBrightnessThresholds.length) {
1276                 mHighDisplayBrightnessThresholds = highDisplayBrightnessThresholds;
1277                 mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds;
1278             }
1279 
1280             mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone();
1281             mRefreshRateInHighZone = mDeviceConfigDisplaySettings.getRefreshRateInHighZone();
1282 
1283             restartObserver();
1284             mDeviceConfigDisplaySettings.startListening();
1285         }
1286 
setLoggingEnabled(boolean loggingEnabled)1287         public void setLoggingEnabled(boolean loggingEnabled) {
1288             if (mLoggingEnabled == loggingEnabled) {
1289                 return;
1290             }
1291             mLoggingEnabled = loggingEnabled;
1292             mLightSensorListener.setLoggingEnabled(loggingEnabled);
1293         }
1294 
onRefreshRateSettingChangedLocked(float min, float max)1295         public void onRefreshRateSettingChangedLocked(float min, float max) {
1296             boolean changeable = (max - min > 1f && max > 60f);
1297             if (mRefreshRateChangeable != changeable) {
1298                 mRefreshRateChangeable = changeable;
1299                 updateSensorStatus();
1300                 if (!changeable) {
1301                     // Revoke previous vote from BrightnessObserver
1302                     updateVoteLocked(Vote.PRIORITY_FLICKER, null);
1303                 }
1304             }
1305         }
1306 
onLowPowerModeEnabledLocked(boolean b)1307         public void onLowPowerModeEnabledLocked(boolean b) {
1308             if (mLowPowerModeEnabled != b) {
1309                 mLowPowerModeEnabled = b;
1310                 updateSensorStatus();
1311             }
1312         }
1313 
onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds, int[] ambientThresholds)1314         public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds,
1315                 int[] ambientThresholds) {
1316             if (displayThresholds != null && ambientThresholds != null
1317                     && displayThresholds.length == ambientThresholds.length) {
1318                 mLowDisplayBrightnessThresholds = displayThresholds;
1319                 mLowAmbientBrightnessThresholds = ambientThresholds;
1320             } else {
1321                 // Invalid or empty. Use device default.
1322                 mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray(
1323                         R.array.config_brightnessThresholdsOfPeakRefreshRate);
1324                 mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray(
1325                         R.array.config_ambientThresholdsOfPeakRefreshRate);
1326             }
1327             restartObserver();
1328         }
1329 
onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate)1330         public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) {
1331             if (refreshRate != mRefreshRateInLowZone) {
1332                 mRefreshRateInLowZone = refreshRate;
1333                 restartObserver();
1334             }
1335         }
1336 
onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds, int[] ambientThresholds)1337         public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds,
1338                 int[] ambientThresholds) {
1339             if (displayThresholds != null && ambientThresholds != null
1340                     && displayThresholds.length == ambientThresholds.length) {
1341                 mHighDisplayBrightnessThresholds = displayThresholds;
1342                 mHighAmbientBrightnessThresholds = ambientThresholds;
1343             } else {
1344                 // Invalid or empty. Use device default.
1345                 mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray(
1346                         R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
1347                 mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray(
1348                         R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
1349             }
1350             restartObserver();
1351         }
1352 
onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate)1353         public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) {
1354             if (refreshRate != mRefreshRateInHighZone) {
1355                 mRefreshRateInHighZone = refreshRate;
1356                 restartObserver();
1357             }
1358         }
1359 
dumpLocked(PrintWriter pw)1360         public void dumpLocked(PrintWriter pw) {
1361             pw.println("  BrightnessObserver");
1362             pw.println("    mAmbientLux: " + mAmbientLux);
1363             pw.println("    mBrightness: " + mBrightness);
1364             pw.println("    mDefaultDisplayState: " + mDefaultDisplayState);
1365             pw.println("    mLowPowerModeEnabled: " + mLowPowerModeEnabled);
1366             pw.println("    mRefreshRateChangeable: " + mRefreshRateChangeable);
1367             pw.println("    mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
1368             pw.println("    mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange);
1369             pw.println("    mRefreshRateInLowZone: " + mRefreshRateInLowZone);
1370 
1371             for (int d : mLowDisplayBrightnessThresholds) {
1372                 pw.println("    mDisplayLowBrightnessThreshold: " + d);
1373             }
1374 
1375             for (int d : mLowAmbientBrightnessThresholds) {
1376                 pw.println("    mAmbientLowBrightnessThreshold: " + d);
1377             }
1378 
1379             pw.println("    mShouldObserveDisplayHighChange: " + mShouldObserveDisplayHighChange);
1380             pw.println("    mShouldObserveAmbientHighChange: " + mShouldObserveAmbientHighChange);
1381             pw.println("    mRefreshRateInHighZone: " + mRefreshRateInHighZone);
1382 
1383             for (int d : mHighDisplayBrightnessThresholds) {
1384                 pw.println("    mDisplayHighBrightnessThresholds: " + d);
1385             }
1386 
1387             for (int d : mHighAmbientBrightnessThresholds) {
1388                 pw.println("    mAmbientHighBrightnessThresholds: " + d);
1389             }
1390 
1391             mLightSensorListener.dumpLocked(pw);
1392 
1393             if (mAmbientFilter != null) {
1394                 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
1395                 ipw.setIndent("    ");
1396                 mAmbientFilter.dump(ipw);
1397             }
1398         }
1399 
onDisplayChanged(int displayId)1400         public void onDisplayChanged(int displayId) {
1401             if (displayId == Display.DEFAULT_DISPLAY) {
1402                 updateDefaultDisplayState();
1403             }
1404         }
1405 
1406         @Override
onChange(boolean selfChange, Uri uri, int userId)1407         public void onChange(boolean selfChange, Uri uri, int userId) {
1408             synchronized (mLock) {
1409                 final ContentResolver cr = mContext.getContentResolver();
1410                 int brightness = Settings.System.getIntForUser(cr,
1411                         Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
1412                 if (brightness != mBrightness) {
1413                     mBrightness = brightness;
1414                     onBrightnessChangedLocked();
1415                 }
1416             }
1417         }
1418 
restartObserver()1419         private void restartObserver() {
1420             final ContentResolver cr = mContext.getContentResolver();
1421 
1422             if (mRefreshRateInLowZone > 0) {
1423                 mShouldObserveDisplayLowChange = hasValidThreshold(
1424                         mLowDisplayBrightnessThresholds);
1425                 mShouldObserveAmbientLowChange = hasValidThreshold(
1426                         mLowAmbientBrightnessThresholds);
1427             } else {
1428                 mShouldObserveDisplayLowChange = false;
1429                 mShouldObserveAmbientLowChange = false;
1430             }
1431 
1432             if (mRefreshRateInHighZone > 0) {
1433                 mShouldObserveDisplayHighChange = hasValidThreshold(
1434                         mHighDisplayBrightnessThresholds);
1435                 mShouldObserveAmbientHighChange = hasValidThreshold(
1436                         mHighAmbientBrightnessThresholds);
1437             } else {
1438                 mShouldObserveDisplayHighChange = false;
1439                 mShouldObserveAmbientHighChange = false;
1440             }
1441 
1442             if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) {
1443                 // Content Service does not check if an listener has already been registered.
1444                 // To ensure only one listener is registered, force an unregistration first.
1445                 mInjector.unregisterBrightnessObserver(cr, this);
1446                 mInjector.registerBrightnessObserver(cr, this);
1447             } else {
1448                 mInjector.unregisterBrightnessObserver(cr, this);
1449             }
1450 
1451             if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
1452                 Resources resources = mContext.getResources();
1453                 String lightSensorType = resources.getString(
1454                         com.android.internal.R.string.config_displayLightSensorType);
1455 
1456                 Sensor lightSensor = null;
1457                 if (!TextUtils.isEmpty(lightSensorType)) {
1458                     List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
1459                     for (int i = 0; i < sensors.size(); i++) {
1460                         Sensor sensor = sensors.get(i);
1461                         if (lightSensorType.equals(sensor.getStringType())) {
1462                             lightSensor = sensor;
1463                             break;
1464                         }
1465                     }
1466                 }
1467 
1468                 if (lightSensor == null) {
1469                     lightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
1470                 }
1471 
1472                 if (lightSensor != null) {
1473                     final Resources res = mContext.getResources();
1474 
1475                     mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
1476                     mLightSensor = lightSensor;
1477                 }
1478             } else {
1479                 mAmbientFilter = null;
1480                 mLightSensor = null;
1481             }
1482 
1483             if (mRefreshRateChangeable) {
1484                 updateSensorStatus();
1485                 synchronized (mLock) {
1486                     onBrightnessChangedLocked();
1487                 }
1488             }
1489         }
1490 
1491         /**
1492          * Checks to see if at least one value is positive, in which case it is necessary to listen
1493          * to value changes.
1494          */
hasValidThreshold(int[] a)1495         private boolean hasValidThreshold(int[] a) {
1496             for (int d: a) {
1497                 if (d >= 0) {
1498                     return true;
1499                 }
1500             }
1501 
1502             return false;
1503         }
1504 
isInsideLowZone(int brightness, float lux)1505         private boolean isInsideLowZone(int brightness, float lux) {
1506             for (int i = 0; i < mLowDisplayBrightnessThresholds.length; i++) {
1507                 int disp = mLowDisplayBrightnessThresholds[i];
1508                 int ambi = mLowAmbientBrightnessThresholds[i];
1509 
1510                 if (disp >= 0 && ambi >= 0) {
1511                     if (brightness <= disp && lux <= ambi) {
1512                         return true;
1513                     }
1514                 } else if (disp >= 0) {
1515                     if (brightness <= disp) {
1516                         return true;
1517                     }
1518                 } else if (ambi >= 0) {
1519                     if (lux <= ambi) {
1520                         return true;
1521                     }
1522                 }
1523             }
1524 
1525             return false;
1526         }
1527 
isInsideHighZone(int brightness, float lux)1528         private boolean isInsideHighZone(int brightness, float lux) {
1529             for (int i = 0; i < mHighDisplayBrightnessThresholds.length; i++) {
1530                 int disp = mHighDisplayBrightnessThresholds[i];
1531                 int ambi = mHighAmbientBrightnessThresholds[i];
1532 
1533                 if (disp >= 0 && ambi >= 0) {
1534                     if (brightness >= disp && lux >= ambi) {
1535                         return true;
1536                     }
1537                 } else if (disp >= 0) {
1538                     if (brightness >= disp) {
1539                         return true;
1540                     }
1541                 } else if (ambi >= 0) {
1542                     if (lux >= ambi) {
1543                         return true;
1544                     }
1545                 }
1546             }
1547 
1548             return false;
1549         }
onBrightnessChangedLocked()1550         private void onBrightnessChangedLocked() {
1551             Vote vote = null;
1552 
1553             if (mBrightness < 0) {
1554                 // Either the setting isn't available or we shouldn't be observing yet anyways.
1555                 // Either way, just bail out since there's nothing we can do here.
1556                 return;
1557             }
1558 
1559             boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux);
1560             if (insideLowZone) {
1561                 vote = Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
1562             }
1563 
1564             boolean insideHighZone = hasValidHighZone()
1565                     && isInsideHighZone(mBrightness, mAmbientLux);
1566             if (insideHighZone) {
1567                 vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
1568             }
1569 
1570             if (mLoggingEnabled) {
1571                 Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " +  mAmbientLux
1572                         + ", Vote " + vote);
1573             }
1574             updateVoteLocked(Vote.PRIORITY_FLICKER, vote);
1575         }
1576 
hasValidLowZone()1577         private boolean hasValidLowZone() {
1578             return mRefreshRateInLowZone > 0
1579                     && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange);
1580         }
1581 
hasValidHighZone()1582         private boolean hasValidHighZone() {
1583             return mRefreshRateInHighZone > 0
1584                     && (mShouldObserveDisplayHighChange || mShouldObserveAmbientHighChange);
1585         }
1586 
updateDefaultDisplayState()1587         private void updateDefaultDisplayState() {
1588             Display display = mContext.getSystemService(DisplayManager.class)
1589                     .getDisplay(Display.DEFAULT_DISPLAY);
1590             if (display == null) {
1591                 return;
1592             }
1593 
1594             setDefaultDisplayState(display.getState());
1595         }
1596 
1597         @VisibleForTesting
setDefaultDisplayState(int state)1598         public void setDefaultDisplayState(int state) {
1599             if (mLoggingEnabled) {
1600                 Slog.d(TAG, "setDefaultDisplayState: mDefaultDisplayState = "
1601                         + mDefaultDisplayState + ", state = " + state);
1602             }
1603 
1604             if (mDefaultDisplayState != state) {
1605                 mDefaultDisplayState = state;
1606                 updateSensorStatus();
1607             }
1608         }
1609 
updateSensorStatus()1610         private void updateSensorStatus() {
1611             if (mSensorManager == null || mLightSensorListener == null) {
1612                 return;
1613             }
1614 
1615             if (mLoggingEnabled) {
1616                 Slog.d(TAG, "updateSensorStatus: mShouldObserveAmbientLowChange = "
1617                         + mShouldObserveAmbientLowChange + ", mShouldObserveAmbientHighChange = "
1618                         + mShouldObserveAmbientHighChange);
1619                 Slog.d(TAG, "updateSensorStatus: mLowPowerModeEnabled = "
1620                         + mLowPowerModeEnabled + ", mRefreshRateChangeable = "
1621                         + mRefreshRateChangeable);
1622             }
1623 
1624             if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
1625                      && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
1626                 mSensorManager.registerListener(mLightSensorListener,
1627                         mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
1628                 if (mLoggingEnabled) {
1629                     Slog.d(TAG, "updateSensorStatus: registerListener");
1630                 }
1631             } else {
1632                 mLightSensorListener.removeCallbacks();
1633                 mSensorManager.unregisterListener(mLightSensorListener);
1634                 if (mLoggingEnabled) {
1635                     Slog.d(TAG, "updateSensorStatus: unregisterListener");
1636                 }
1637             }
1638         }
1639 
isDeviceActive()1640         private boolean isDeviceActive() {
1641             return mDefaultDisplayState == Display.STATE_ON;
1642         }
1643 
1644         private final class LightSensorEventListener implements SensorEventListener {
1645             final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
1646             private float mLastSensorData;
1647             private long mTimestamp;
1648             private boolean mLoggingEnabled;
1649 
dumpLocked(PrintWriter pw)1650             public void dumpLocked(PrintWriter pw) {
1651                 pw.println("    mLastSensorData: " + mLastSensorData);
1652                 pw.println("    mTimestamp: " + formatTimestamp(mTimestamp));
1653             }
1654 
1655 
setLoggingEnabled(boolean loggingEnabled)1656             public void setLoggingEnabled(boolean loggingEnabled) {
1657                 if (mLoggingEnabled == loggingEnabled) {
1658                     return;
1659                 }
1660                 mLoggingEnabled = loggingEnabled;
1661             }
1662 
1663             @Override
onSensorChanged(SensorEvent event)1664             public void onSensorChanged(SensorEvent event) {
1665                 mLastSensorData = event.values[0];
1666                 if (mLoggingEnabled) {
1667                     Slog.d(TAG, "On sensor changed: " + mLastSensorData);
1668                 }
1669 
1670                 boolean lowZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
1671                         mLowAmbientBrightnessThresholds);
1672                 boolean highZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
1673                         mHighAmbientBrightnessThresholds);
1674                 if ((lowZoneChanged && mLastSensorData < mAmbientLux)
1675                         || (highZoneChanged && mLastSensorData > mAmbientLux)) {
1676                     // Easier to see flicker at lower brightness environment or high brightness
1677                     // environment. Forget the history to get immediate response.
1678                     if (mAmbientFilter != null) {
1679                         mAmbientFilter.clear();
1680                     }
1681                 }
1682 
1683                 long now = SystemClock.uptimeMillis();
1684                 mTimestamp = System.currentTimeMillis();
1685                 if (mAmbientFilter != null) {
1686                     mAmbientFilter.addValue(now, mLastSensorData);
1687                 }
1688 
1689                 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1690                 processSensorData(now);
1691 
1692                 if ((lowZoneChanged && mLastSensorData > mAmbientLux)
1693                         || (highZoneChanged && mLastSensorData < mAmbientLux)) {
1694                     // Sensor may not report new event if there is no brightness change.
1695                     // Need to keep querying the temporal filter for the latest estimation,
1696                     // until sensor readout and filter estimation are in the same zone or
1697                     // is interrupted by a new sensor event.
1698                     mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
1699                 }
1700             }
1701 
1702             @Override
onAccuracyChanged(Sensor sensor, int accuracy)1703             public void onAccuracyChanged(Sensor sensor, int accuracy) {
1704                 // Not used.
1705             }
1706 
removeCallbacks()1707             public void removeCallbacks() {
1708                 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1709             }
1710 
formatTimestamp(long time)1711             private String formatTimestamp(long time) {
1712                 SimpleDateFormat dateFormat =
1713                         new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
1714                 return dateFormat.format(new Date(time));
1715             }
1716 
processSensorData(long now)1717             private void processSensorData(long now) {
1718                 if (mAmbientFilter != null) {
1719                     mAmbientLux = mAmbientFilter.getEstimate(now);
1720                 } else {
1721                     mAmbientLux = mLastSensorData;
1722                 }
1723 
1724                 synchronized (mLock) {
1725                     onBrightnessChangedLocked();
1726                 }
1727             }
1728 
isDifferentZone(float lux1, float lux2, int[] luxThresholds)1729             private boolean isDifferentZone(float lux1, float lux2, int[] luxThresholds) {
1730                 for (final float boundary : luxThresholds) {
1731                     // Test each boundary. See if the current value and the new value are at
1732                     // different sides.
1733                     if ((lux1 <= boundary && lux2 > boundary)
1734                             || (lux1 > boundary && lux2 <= boundary)) {
1735                         return true;
1736                     }
1737                 }
1738 
1739                 return false;
1740             }
1741 
1742             private Runnable mInjectSensorEventRunnable = new Runnable() {
1743                 @Override
1744                 public void run() {
1745                     long now = SystemClock.uptimeMillis();
1746                     // No need to really inject the last event into a temporal filter.
1747                     processSensorData(now);
1748 
1749                     // Inject next event if there is a possible zone change.
1750                     if (isDifferentZone(mLastSensorData, mAmbientLux,
1751                             mLowAmbientBrightnessThresholds)
1752                             || isDifferentZone(mLastSensorData, mAmbientLux,
1753                             mHighAmbientBrightnessThresholds)) {
1754                         mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
1755                     }
1756                 }
1757             };
1758         }
1759     }
1760 
1761     private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
DeviceConfigDisplaySettings()1762         public DeviceConfigDisplaySettings() {
1763         }
1764 
startListening()1765         public void startListening() {
1766             mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1767                     BackgroundThread.getExecutor(), this);
1768         }
1769 
1770         /*
1771          * Return null if no such property or wrong format (not comma separated integers).
1772          */
getLowDisplayBrightnessThresholds()1773         public int[] getLowDisplayBrightnessThresholds() {
1774             return getIntArrayProperty(
1775                     DisplayManager.DeviceConfig.
1776                             KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
1777         }
1778 
1779         /*
1780          * Return null if no such property or wrong format (not comma separated integers).
1781          */
getLowAmbientBrightnessThresholds()1782         public int[] getLowAmbientBrightnessThresholds() {
1783             return getIntArrayProperty(
1784                     DisplayManager.DeviceConfig.
1785                             KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
1786         }
1787 
getRefreshRateInLowZone()1788         public int getRefreshRateInLowZone() {
1789             int defaultRefreshRateInZone = mContext.getResources().getInteger(
1790                     R.integer.config_defaultRefreshRateInZone);
1791 
1792             int refreshRate = mDeviceConfig.getInt(
1793                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1794                     DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
1795                     defaultRefreshRateInZone);
1796 
1797             return refreshRate;
1798         }
1799 
1800         /*
1801          * Return null if no such property or wrong format (not comma separated integers).
1802          */
getHighDisplayBrightnessThresholds()1803         public int[] getHighDisplayBrightnessThresholds() {
1804             return getIntArrayProperty(
1805                     DisplayManager.DeviceConfig
1806                             .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
1807         }
1808 
1809         /*
1810          * Return null if no such property or wrong format (not comma separated integers).
1811          */
getHighAmbientBrightnessThresholds()1812         public int[] getHighAmbientBrightnessThresholds() {
1813             return getIntArrayProperty(
1814                     DisplayManager.DeviceConfig
1815                             .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
1816         }
1817 
getRefreshRateInHighZone()1818         public int getRefreshRateInHighZone() {
1819             int defaultRefreshRateInZone = mContext.getResources().getInteger(
1820                     R.integer.config_fixedRefreshRateInHighZone);
1821 
1822             int refreshRate = mDeviceConfig.getInt(
1823                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1824                     DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
1825                     defaultRefreshRateInZone);
1826 
1827             return refreshRate;
1828         }
1829 
1830         /*
1831          * Return null if no such property
1832          */
getDefaultPeakRefreshRate()1833         public Float getDefaultPeakRefreshRate() {
1834             float defaultPeakRefreshRate = mDeviceConfig.getFloat(
1835                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1836                     DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
1837 
1838             if (defaultPeakRefreshRate == -1) {
1839                 return null;
1840             }
1841             return defaultPeakRefreshRate;
1842         }
1843 
1844         @Override
onPropertiesChanged(@onNull DeviceConfig.Properties properties)1845         public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
1846             Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
1847             mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
1848                     defaultPeakRefreshRate).sendToTarget();
1849 
1850             int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds();
1851             int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds();
1852             int refreshRateInLowZone = getRefreshRateInLowZone();
1853 
1854             mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
1855                     new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
1856                     .sendToTarget();
1857             mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0)
1858                     .sendToTarget();
1859 
1860             int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
1861             int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds();
1862             int refreshRateInHighZone = getRefreshRateInHighZone();
1863 
1864             mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
1865                     new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
1866                     .sendToTarget();
1867             mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0)
1868                     .sendToTarget();
1869         }
1870 
getIntArrayProperty(String prop)1871         private int[] getIntArrayProperty(String prop) {
1872             String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
1873                     null);
1874 
1875             if (strArray != null) {
1876                 return parseIntArray(strArray);
1877             }
1878 
1879             return null;
1880         }
1881 
parseIntArray(@onNull String strArray)1882         private int[] parseIntArray(@NonNull String strArray) {
1883             String[] items = strArray.split(",");
1884             int[] array = new int[items.length];
1885 
1886             try {
1887                 for (int i = 0; i < array.length; i++) {
1888                     array[i] = Integer.parseInt(items[i]);
1889                 }
1890             } catch (NumberFormatException e) {
1891                 Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
1892                 array = null;
1893             }
1894 
1895             return array;
1896         }
1897     }
1898 
1899     interface Injector {
1900         // TODO: brightnessfloat: change this to the float setting
1901         Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
1902         Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
1903 
1904         @NonNull
getDeviceConfig()1905         DeviceConfigInterface getDeviceConfig();
1906 
registerBrightnessObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)1907         void registerBrightnessObserver(@NonNull ContentResolver cr,
1908                 @NonNull ContentObserver observer);
1909 
unregisterBrightnessObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)1910         void unregisterBrightnessObserver(@NonNull ContentResolver cr,
1911                 @NonNull ContentObserver observer);
1912 
registerPeakRefreshRateObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)1913         void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
1914                 @NonNull ContentObserver observer);
1915     }
1916 
1917     @VisibleForTesting
1918     static class RealInjector implements Injector {
1919 
1920         @Override
1921         @NonNull
getDeviceConfig()1922         public DeviceConfigInterface getDeviceConfig() {
1923             return DeviceConfigInterface.REAL;
1924         }
1925 
1926         @Override
registerBrightnessObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)1927         public void registerBrightnessObserver(@NonNull ContentResolver cr,
1928                 @NonNull ContentObserver observer) {
1929             cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/,
1930                     observer, UserHandle.USER_SYSTEM);
1931         }
1932 
1933         @Override
unregisterBrightnessObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)1934         public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
1935                 @NonNull ContentObserver observer) {
1936             cr.unregisterContentObserver(observer);
1937         }
1938 
1939         @Override
registerPeakRefreshRateObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)1940         public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
1941                 @NonNull ContentObserver observer) {
1942             cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
1943                     observer, UserHandle.USER_SYSTEM);
1944         }
1945     }
1946 
1947 }
1948