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.server.display.utils.AmbientFilter;
50 import com.android.server.display.utils.AmbientFilterFactory;
51 
52 import java.io.PrintWriter;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.List;
56 import java.util.Objects;
57 
58 /**
59  * The DisplayModeDirector is responsible for determining what modes are allowed to be
60  * automatically picked by the system based on system-wide and display-specific configuration.
61  */
62 public class DisplayModeDirector {
63     private static final String TAG = "DisplayModeDirector";
64     private static final boolean DEBUG = false;
65 
66     private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1;
67     private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
68     private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
69     private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4;
70 
71     // Special ID used to indicate that given vote is to be applied globally, rather than to a
72     // specific display.
73     private static final int GLOBAL_ID = -1;
74 
75     // The tolerance within which we consider something approximately equals.
76     private static final float FLOAT_TOLERANCE = 0.01f;
77 
78     private final Object mLock = new Object();
79     private final Context mContext;
80 
81     private final DisplayModeDirectorHandler mHandler;
82 
83     // A map from the display ID to the collection of votes and their priority. The latter takes
84     // the form of another map from the priority to the vote itself so that each priority is
85     // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
86     private SparseArray<SparseArray<Vote>> mVotesByDisplay;
87     // A map from the display ID to the supported modes on that display.
88     private SparseArray<Display.Mode[]> mSupportedModesByDisplay;
89     // A map from the display ID to the default mode of that display.
90     private SparseArray<Display.Mode> mDefaultModeByDisplay;
91 
92     private final AppRequestObserver mAppRequestObserver;
93     private final SettingsObserver mSettingsObserver;
94     private final DisplayObserver mDisplayObserver;
95     private BrightnessObserver mBrightnessObserver;
96 
97     private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
98     private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
99 
DisplayModeDirector(@onNull Context context, @NonNull Handler handler)100     public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
101         mContext = context;
102         mHandler = new DisplayModeDirectorHandler(handler.getLooper());
103         mVotesByDisplay = new SparseArray<>();
104         mSupportedModesByDisplay = new SparseArray<>();
105         mDefaultModeByDisplay =  new SparseArray<>();
106         mAppRequestObserver = new AppRequestObserver();
107         mSettingsObserver = new SettingsObserver(context, handler);
108         mDisplayObserver = new DisplayObserver(context, handler);
109         mBrightnessObserver = new BrightnessObserver(context, handler);
110         mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
111     }
112 
113     /**
114      * Tells the DisplayModeDirector to update allowed votes and begin observing relevant system
115      * state.
116      *
117      * This has to be deferred because the object may be constructed before the rest of the system
118      * is ready.
119      */
start(SensorManager sensorManager)120     public void start(SensorManager sensorManager) {
121         mSettingsObserver.observe();
122         mDisplayObserver.observe();
123         mSettingsObserver.observe();
124         mBrightnessObserver.observe(sensorManager);
125         synchronized (mLock) {
126             // We may have a listener already registered before the call to start, so go ahead and
127             // notify them to pick up our newly initialized state.
128             notifyDesiredDisplayModeSpecsChangedLocked();
129         }
130 
131     }
132 
133     @NonNull
getVotesLocked(int displayId)134     private SparseArray<Vote> getVotesLocked(int displayId) {
135         SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
136         final SparseArray<Vote> votes;
137         if (displayVotes != null) {
138             votes = displayVotes.clone();
139         } else {
140             votes = new SparseArray<>();
141         }
142 
143         SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
144         if (globalVotes != null) {
145             for (int i = 0; i < globalVotes.size(); i++) {
146                 int priority = globalVotes.keyAt(i);
147                 if (votes.indexOfKey(priority) < 0) {
148                     votes.put(priority, globalVotes.valueAt(i));
149                 }
150             }
151         }
152         return votes;
153     }
154 
155     private static final class VoteSummary {
156         public float minRefreshRate;
157         public float maxRefreshRate;
158         public int width;
159         public int height;
160 
VoteSummary()161         VoteSummary() {
162             reset();
163         }
164 
reset()165         public void reset() {
166             minRefreshRate = 0f;
167             maxRefreshRate = Float.POSITIVE_INFINITY;
168             width = Vote.INVALID_SIZE;
169             height = Vote.INVALID_SIZE;
170         }
171     }
172 
173     // VoteSummary is returned as an output param to cut down a bit on the number of temporary
174     // objects.
summarizeVotes( SparseArray<Vote> votes, int lowestConsideredPriority, VoteSummary summary)175     private void summarizeVotes(
176             SparseArray<Vote> votes, int lowestConsideredPriority, /*out*/ VoteSummary summary) {
177         summary.reset();
178         for (int priority = Vote.MAX_PRIORITY; priority >= lowestConsideredPriority; priority--) {
179             Vote vote = votes.get(priority);
180             if (vote == null) {
181                 continue;
182             }
183             // For refresh rates, just use the tightest bounds of all the votes
184             summary.minRefreshRate = Math.max(summary.minRefreshRate, vote.refreshRateRange.min);
185             summary.maxRefreshRate = Math.min(summary.maxRefreshRate, vote.refreshRateRange.max);
186             // For display size, use only the first vote we come across (i.e. the highest
187             // priority vote that includes the width / height).
188             if (summary.height == Vote.INVALID_SIZE && summary.width == Vote.INVALID_SIZE
189                     && vote.height > 0 && vote.width > 0) {
190                 summary.width = vote.width;
191                 summary.height = vote.height;
192             }
193         }
194     }
195 
196     /**
197      * Calculates the refresh rate ranges and display modes that the system is allowed to freely
198      * switch between based on global and display-specific constraints.
199      *
200      * @param displayId The display to query for.
201      * @return The ID of the default mode the system should use, and the refresh rate range the
202      * system is allowed to switch between.
203      */
204     @NonNull
getDesiredDisplayModeSpecs(int displayId)205     public DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(int displayId) {
206         synchronized (mLock) {
207             SparseArray<Vote> votes = getVotesLocked(displayId);
208             Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
209             Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
210             if (modes == null || defaultMode == null) {
211                 Slog.e(TAG,
212                         "Asked about unknown display, returning empty display mode specs!"
213                                 + "(id=" + displayId + ")");
214                 return new DesiredDisplayModeSpecs();
215             }
216 
217             int[] availableModes = new int[]{defaultMode.getModeId()};
218             VoteSummary primarySummary = new VoteSummary();
219             int lowestConsideredPriority = Vote.MIN_PRIORITY;
220             while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
221                 summarizeVotes(votes, lowestConsideredPriority, primarySummary);
222 
223                 // If we don't have anything specifying the width / height of the display, just use
224                 // the default width and height. We don't want these switching out from underneath
225                 // us since it's a pretty disruptive behavior.
226                 if (primarySummary.height == Vote.INVALID_SIZE
227                         || primarySummary.width == Vote.INVALID_SIZE) {
228                     primarySummary.width = defaultMode.getPhysicalWidth();
229                     primarySummary.height = defaultMode.getPhysicalHeight();
230                 }
231 
232                 availableModes = filterModes(modes, primarySummary);
233                 if (availableModes.length > 0) {
234                     if (DEBUG) {
235                         Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
236                                 + " with lowest priority considered "
237                                 + Vote.priorityToString(lowestConsideredPriority)
238                                 + " and constraints: "
239                                 + "width=" + primarySummary.width
240                                 + ", height=" + primarySummary.height
241                                 + ", minRefreshRate=" + primarySummary.minRefreshRate
242                                 + ", maxRefreshRate=" + primarySummary.maxRefreshRate);
243                     }
244                     break;
245                 }
246 
247                 if (DEBUG) {
248                     Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
249                             + Vote.priorityToString(lowestConsideredPriority)
250                             + " and with the following constraints: "
251                             + "width=" + primarySummary.width
252                             + ", height=" + primarySummary.height
253                             + ", minRefreshRate=" + primarySummary.minRefreshRate
254                             + ", maxRefreshRate=" + primarySummary.maxRefreshRate);
255                 }
256 
257                 // If we haven't found anything with the current set of votes, drop the
258                 // current lowest priority vote.
259                 lowestConsideredPriority++;
260             }
261 
262             VoteSummary appRequestSummary = new VoteSummary();
263             summarizeVotes(
264                     votes, Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, appRequestSummary);
265             appRequestSummary.minRefreshRate =
266                     Math.min(appRequestSummary.minRefreshRate, primarySummary.minRefreshRate);
267             appRequestSummary.maxRefreshRate =
268                     Math.max(appRequestSummary.maxRefreshRate, primarySummary.maxRefreshRate);
269             if (DEBUG) {
270                 Slog.i(TAG,
271                         String.format("App request range: [%.0f %.0f]",
272                                 appRequestSummary.minRefreshRate,
273                                 appRequestSummary.maxRefreshRate));
274             }
275 
276             int baseModeId = defaultMode.getModeId();
277             if (availableModes.length > 0) {
278                 baseModeId = availableModes[0];
279             }
280             // filterModes function is going to filter the modes based on the voting system. If
281             // the application requests a given mode with preferredModeId function, it will be
282             // stored as baseModeId.
283             return new DesiredDisplayModeSpecs(baseModeId,
284                     new RefreshRateRange(
285                             primarySummary.minRefreshRate, primarySummary.maxRefreshRate),
286                     new RefreshRateRange(
287                             appRequestSummary.minRefreshRate, appRequestSummary.maxRefreshRate));
288         }
289     }
290 
filterModes(Display.Mode[] supportedModes, VoteSummary summary)291     private int[] filterModes(Display.Mode[] supportedModes, VoteSummary summary) {
292         ArrayList<Display.Mode> availableModes = new ArrayList<>();
293         for (Display.Mode mode : supportedModes) {
294             if (mode.getPhysicalWidth() != summary.width
295                     || mode.getPhysicalHeight() != summary.height) {
296                 if (DEBUG) {
297                     Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
298                             + ": desiredWidth=" + summary.width
299                             + ": desiredHeight=" + summary.height
300                             + ": actualWidth=" + mode.getPhysicalWidth()
301                             + ": actualHeight=" + mode.getPhysicalHeight());
302                 }
303                 continue;
304             }
305             final float refreshRate = mode.getRefreshRate();
306             // Some refresh rates are calculated based on frame timings, so they aren't *exactly*
307             // equal to expected refresh rate. Given that, we apply a bit of tolerance to this
308             // comparison.
309             if (refreshRate < (summary.minRefreshRate - FLOAT_TOLERANCE)
310                     || refreshRate > (summary.maxRefreshRate + FLOAT_TOLERANCE)) {
311                 if (DEBUG) {
312                     Slog.w(TAG, "Discarding mode " + mode.getModeId()
313                             + ", outside refresh rate bounds"
314                             + ": minRefreshRate=" + summary.minRefreshRate
315                             + ", maxRefreshRate=" + summary.maxRefreshRate
316                             + ", modeRefreshRate=" + refreshRate);
317                 }
318                 continue;
319             }
320             availableModes.add(mode);
321         }
322         final int size = availableModes.size();
323         int[] availableModeIds = new int[size];
324         for (int i = 0; i < size; i++) {
325             availableModeIds[i] = availableModes.get(i).getModeId();
326         }
327         return availableModeIds;
328     }
329 
330     /**
331      * Gets the observer responsible for application display mode requests.
332      */
333     @NonNull
getAppRequestObserver()334     public AppRequestObserver getAppRequestObserver() {
335         // We don't need to lock here because mAppRequestObserver is a final field, which is
336         // guaranteed to be visible on all threads after construction.
337         return mAppRequestObserver;
338     }
339 
340     /**
341      * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate
342      * ranges.
343      */
setDesiredDisplayModeSpecsListener( @ullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener)344     public void setDesiredDisplayModeSpecsListener(
345             @Nullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener) {
346         synchronized (mLock) {
347             mDesiredDisplayModeSpecsListener = desiredDisplayModeSpecsListener;
348         }
349     }
350 
351     /**
352      * Print the object's state and debug information into the given stream.
353      *
354      * @param pw The stream to dump information to.
355      */
dump(PrintWriter pw)356     public void dump(PrintWriter pw) {
357         pw.println("DisplayModeDirector");
358         synchronized (mLock) {
359             pw.println("  mSupportedModesByDisplay:");
360             for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
361                 final int id = mSupportedModesByDisplay.keyAt(i);
362                 final Display.Mode[] modes = mSupportedModesByDisplay.valueAt(i);
363                 pw.println("    " + id + " -> " + Arrays.toString(modes));
364             }
365             pw.println("  mDefaultModeByDisplay:");
366             for (int i = 0; i < mDefaultModeByDisplay.size(); i++) {
367                 final int id = mDefaultModeByDisplay.keyAt(i);
368                 final Display.Mode mode = mDefaultModeByDisplay.valueAt(i);
369                 pw.println("    " + id + " -> " + mode);
370             }
371             pw.println("  mVotesByDisplay:");
372             for (int i = 0; i < mVotesByDisplay.size(); i++) {
373                 pw.println("    " + mVotesByDisplay.keyAt(i) + ":");
374                 SparseArray<Vote> votes = mVotesByDisplay.valueAt(i);
375                 for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
376                     Vote vote = votes.get(p);
377                     if (vote == null) {
378                         continue;
379                     }
380                     pw.println("      " + Vote.priorityToString(p) + " -> " + vote);
381                 }
382             }
383             mSettingsObserver.dumpLocked(pw);
384             mAppRequestObserver.dumpLocked(pw);
385             mBrightnessObserver.dumpLocked(pw);
386         }
387     }
388 
updateVoteLocked(int priority, Vote vote)389     private void updateVoteLocked(int priority, Vote vote) {
390         updateVoteLocked(GLOBAL_ID, priority, vote);
391     }
392 
updateVoteLocked(int displayId, int priority, Vote vote)393     private void updateVoteLocked(int displayId, int priority, Vote vote) {
394         if (DEBUG) {
395             Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
396                     + ", priority=" + Vote.priorityToString(priority)
397                     + ", vote=" + vote + ")");
398         }
399         if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
400             Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
401                     + " priority=" + Vote.priorityToString(priority)
402                     + ", vote=" + vote, new Throwable());
403             return;
404         }
405         final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId);
406 
407         Vote currentVote = votes.get(priority);
408         if (vote != null) {
409             votes.put(priority, vote);
410         } else {
411             votes.remove(priority);
412         }
413 
414         if (votes.size() == 0) {
415             if (DEBUG) {
416                 Slog.i(TAG, "No votes left for display " + displayId + ", removing.");
417             }
418             mVotesByDisplay.remove(displayId);
419         }
420 
421         notifyDesiredDisplayModeSpecsChangedLocked();
422     }
423 
notifyDesiredDisplayModeSpecsChangedLocked()424     private void notifyDesiredDisplayModeSpecsChangedLocked() {
425         if (mDesiredDisplayModeSpecsListener != null
426                 && !mHandler.hasMessages(MSG_REFRESH_RATE_RANGE_CHANGED)) {
427             // We need to post this to a handler to avoid calling out while holding the lock
428             // since we know there are things that both listen for changes as well as provide
429             // information. If we did call out while holding the lock, then there's no
430             // guaranteed lock order and we run the real of risk deadlock.
431             Message msg = mHandler.obtainMessage(
432                     MSG_REFRESH_RATE_RANGE_CHANGED, mDesiredDisplayModeSpecsListener);
433             msg.sendToTarget();
434         }
435     }
436 
getOrCreateVotesByDisplay(int displayId)437     private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) {
438         int index = mVotesByDisplay.indexOfKey(displayId);
439         if (mVotesByDisplay.indexOfKey(displayId) >= 0) {
440             return mVotesByDisplay.get(displayId);
441         } else {
442             SparseArray<Vote> votes = new SparseArray<>();
443             mVotesByDisplay.put(displayId, votes);
444             return votes;
445         }
446     }
447 
448     @VisibleForTesting
injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay)449     void injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay) {
450         mSupportedModesByDisplay = supportedModesByDisplay;
451     }
452 
453     @VisibleForTesting
injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay)454     void injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay) {
455         mDefaultModeByDisplay = defaultModeByDisplay;
456     }
457 
458     @VisibleForTesting
injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay)459     void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) {
460         mVotesByDisplay = votesByDisplay;
461     }
462 
463     @VisibleForTesting
injectBrightnessObserver(BrightnessObserver brightnessObserver)464     void injectBrightnessObserver(BrightnessObserver brightnessObserver) {
465         mBrightnessObserver = brightnessObserver;
466     }
467 
468     @VisibleForTesting
getDesiredDisplayModeSpecsWithInjectedFpsSettings( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate)469     DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
470             float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
471         synchronized (mLock) {
472             mSettingsObserver.updateRefreshRateSettingLocked(
473                     minRefreshRate, peakRefreshRate, defaultRefreshRate);
474             return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY);
475         }
476     }
477 
478     /**
479      * Listens for changes refresh rate coordination.
480      */
481     public interface DesiredDisplayModeSpecsListener {
482         /**
483          * Called when the refresh rate range may have changed.
484          */
onDesiredDisplayModeSpecsChanged()485         void onDesiredDisplayModeSpecsChanged();
486     }
487 
488     private final class DisplayModeDirectorHandler extends Handler {
DisplayModeDirectorHandler(Looper looper)489         DisplayModeDirectorHandler(Looper looper) {
490             super(looper, null, true /*async*/);
491         }
492 
493         @Override
handleMessage(Message msg)494         public void handleMessage(Message msg) {
495             switch (msg.what) {
496                 case MSG_BRIGHTNESS_THRESHOLDS_CHANGED:
497                     Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
498 
499                     if (thresholds != null) {
500                         mBrightnessObserver.onDeviceConfigThresholdsChanged(
501                                 thresholds.first, thresholds.second);
502                     } else {
503                         mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null);
504                     }
505                     break;
506 
507                 case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
508                     Float defaultPeakRefreshRate = (Float) msg.obj;
509                     mSettingsObserver.onDeviceConfigDefaultPeakRefreshRateChanged(
510                             defaultPeakRefreshRate);
511                     break;
512 
513                 case MSG_REFRESH_RATE_IN_ZONE_CHANGED:
514                     int refreshRateInZone = msg.arg1;
515                     mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged(
516                             refreshRateInZone);
517                     break;
518 
519                 case MSG_REFRESH_RATE_RANGE_CHANGED:
520                     DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener =
521                             (DesiredDisplayModeSpecsListener) msg.obj;
522                     desiredDisplayModeSpecsListener.onDesiredDisplayModeSpecsChanged();
523                     break;
524             }
525         }
526     }
527 
528     /**
529      * Information about the min and max refresh rate DM would like to set the display to.
530      */
531     public static final class RefreshRateRange {
532         /**
533          * The lowest desired refresh rate.
534          */
535         public float min;
536         /**
537          * The highest desired refresh rate.
538          */
539         public float max;
540 
RefreshRateRange()541         public RefreshRateRange() {}
542 
RefreshRateRange(float min, float max)543         public RefreshRateRange(float min, float max) {
544             if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) {
545                 Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : "
546                         + min + " " + max);
547                 this.min = this.max = 0;
548                 return;
549             }
550             if (min > max) {
551                 // Min and max are within epsilon of each other, but in the wrong order.
552                 float t = min;
553                 min = max;
554                 max = t;
555             }
556             this.min = min;
557             this.max = max;
558         }
559 
560         /**
561          * Checks whether the two objects have the same values.
562          */
563         @Override
equals(Object other)564         public boolean equals(Object other) {
565             if (other == this) {
566                 return true;
567             }
568 
569             if (!(other instanceof RefreshRateRange)) {
570                 return false;
571             }
572 
573             RefreshRateRange refreshRateRange = (RefreshRateRange) other;
574             return (min == refreshRateRange.min && max == refreshRateRange.max);
575         }
576 
577         @Override
hashCode()578         public int hashCode() {
579             return Objects.hash(min, max);
580         }
581 
582         @Override
toString()583         public String toString() {
584             return "(" + min + " " + max + ")";
585         }
586     }
587 
588     /**
589      * Information about the desired display mode to be set by the system. Includes the base
590      * mode ID and the primary and app request refresh rate ranges.
591      *
592      * We have this class in addition to SurfaceControl.DesiredDisplayConfigSpecs to make clear the
593      * distinction between the config ID / physical index that
594      * SurfaceControl.DesiredDisplayConfigSpecs uses, and the mode ID used here.
595      */
596     public static final class DesiredDisplayModeSpecs {
597         /**
598          * Base mode ID. This is what system defaults to for all other settings, or
599          * if the refresh rate range is not available.
600          */
601         public int baseModeId;
602         /**
603          * The primary refresh rate range.
604          */
605         public final RefreshRateRange primaryRefreshRateRange;
606         /**
607          * The app request refresh rate range. Lower priority considerations won't be included in
608          * this range, allowing surface flinger to consider additional refresh rates for apps that
609          * call setFrameRate(). This range will be greater than or equal to the primary refresh rate
610          * range, never smaller.
611          */
612         public final RefreshRateRange appRequestRefreshRateRange;
613 
DesiredDisplayModeSpecs()614         public DesiredDisplayModeSpecs() {
615             primaryRefreshRateRange = new RefreshRateRange();
616             appRequestRefreshRateRange = new RefreshRateRange();
617         }
618 
DesiredDisplayModeSpecs(int baseModeId, @NonNull RefreshRateRange primaryRefreshRateRange, @NonNull RefreshRateRange appRequestRefreshRateRange)619         public DesiredDisplayModeSpecs(int baseModeId,
620                 @NonNull RefreshRateRange primaryRefreshRateRange,
621                 @NonNull RefreshRateRange appRequestRefreshRateRange) {
622             this.baseModeId = baseModeId;
623             this.primaryRefreshRateRange = primaryRefreshRateRange;
624             this.appRequestRefreshRateRange = appRequestRefreshRateRange;
625         }
626 
627         /**
628          * Returns a string representation of the object.
629          */
630         @Override
toString()631         public String toString() {
632             return String.format("baseModeId=%d primaryRefreshRateRange=[%.0f %.0f]"
633                             + " appRequestRefreshRateRange=[%.0f %.0f]",
634                     baseModeId, primaryRefreshRateRange.min, primaryRefreshRateRange.max,
635                     appRequestRefreshRateRange.min, appRequestRefreshRateRange.max);
636         }
637         /**
638          * Checks whether the two objects have the same values.
639          */
640         @Override
equals(Object other)641         public boolean equals(Object other) {
642             if (other == this) {
643                 return true;
644             }
645 
646             if (!(other instanceof DesiredDisplayModeSpecs)) {
647                 return false;
648             }
649 
650             DesiredDisplayModeSpecs desiredDisplayModeSpecs = (DesiredDisplayModeSpecs) other;
651 
652             if (baseModeId != desiredDisplayModeSpecs.baseModeId) {
653                 return false;
654             }
655             if (!primaryRefreshRateRange.equals(desiredDisplayModeSpecs.primaryRefreshRateRange)) {
656                 return false;
657             }
658             if (!appRequestRefreshRateRange.equals(
659                         desiredDisplayModeSpecs.appRequestRefreshRateRange)) {
660                 return false;
661             }
662             return true;
663         }
664 
665         @Override
hashCode()666         public int hashCode() {
667             return Objects.hash(baseModeId, primaryRefreshRateRange, appRequestRefreshRateRange);
668         }
669 
670         /**
671          * Copy values from the other object.
672          */
copyFrom(DesiredDisplayModeSpecs other)673         public void copyFrom(DesiredDisplayModeSpecs other) {
674             baseModeId = other.baseModeId;
675             primaryRefreshRateRange.min = other.primaryRefreshRateRange.min;
676             primaryRefreshRateRange.max = other.primaryRefreshRateRange.max;
677             appRequestRefreshRateRange.min = other.appRequestRefreshRateRange.min;
678             appRequestRefreshRateRange.max = other.appRequestRefreshRateRange.max;
679         }
680     }
681 
682     @VisibleForTesting
683     static final class Vote {
684         // DEFAULT_FRAME_RATE votes for [0, DEFAULT]. As the lowest priority vote, it's overridden
685         // by all other considerations. It acts to set a default frame rate for a device.
686         public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0;
687 
688         // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
689         // If the higher voters result is a range, it will fix the rate to a single choice.
690         // It's used to avoid rate switch in certain conditions.
691         public static final int PRIORITY_LOW_BRIGHTNESS = 1;
692 
693         // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
694         // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
695         public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2;
696 
697         // We split the app request into different priorities in case we can satisfy one desire
698         // without the other.
699 
700         // Application can specify preferred refresh rate with below attrs.
701         // @see android.view.WindowManager.LayoutParams#preferredRefreshRate
702         // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
703         // System also forces some apps like blacklisted app to run at a lower refresh rate.
704         // @see android.R.array#config_highRefreshRateBlacklist
705         public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3;
706         public static final int PRIORITY_APP_REQUEST_SIZE = 4;
707 
708         // SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest
709         // of low priority voters. It votes [0, max(PEAK, MIN)]
710         public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 5;
711 
712         // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
713         public static final int PRIORITY_LOW_POWER_MODE = 6;
714 
715         // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
716         // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
717 
718         public static final int MIN_PRIORITY = PRIORITY_DEFAULT_REFRESH_RATE;
719         public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;
720 
721         // The cutoff for the app request refresh rate range. Votes with priorities lower than this
722         // value will not be considered when constructing the app request refresh rate range.
723         public static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
724                 PRIORITY_APP_REQUEST_REFRESH_RATE;
725 
726         /**
727          * A value signifying an invalid width or height in a vote.
728          */
729         public static final int INVALID_SIZE = -1;
730 
731         /**
732          * The requested width of the display in pixels, or INVALID_SIZE;
733          */
734         public final int width;
735         /**
736          * The requested height of the display in pixels, or INVALID_SIZE;
737          */
738         public final int height;
739         /**
740          * Information about the min and max refresh rate DM would like to set the display to.
741          */
742         public final RefreshRateRange refreshRateRange;
743 
forRefreshRates(float minRefreshRate, float maxRefreshRate)744         public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
745             return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate);
746         }
747 
forSize(int width, int height)748         public static Vote forSize(int width, int height) {
749             return new Vote(width, height, 0f, Float.POSITIVE_INFINITY);
750         }
751 
Vote(int width, int height, float minRefreshRate, float maxRefreshRate)752         private Vote(int width, int height,
753                 float minRefreshRate, float maxRefreshRate) {
754             this.width = width;
755             this.height = height;
756             this.refreshRateRange =
757                     new RefreshRateRange(minRefreshRate, maxRefreshRate);
758         }
759 
priorityToString(int priority)760         public static String priorityToString(int priority) {
761             switch (priority) {
762                 case PRIORITY_DEFAULT_REFRESH_RATE:
763                     return "PRIORITY_DEFAULT_REFRESH_RATE";
764                 case PRIORITY_LOW_BRIGHTNESS:
765                     return "PRIORITY_LOW_BRIGHTNESS";
766                 case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
767                     return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
768                 case PRIORITY_APP_REQUEST_REFRESH_RATE:
769                     return "PRIORITY_APP_REQUEST_REFRESH_RATE";
770                 case PRIORITY_APP_REQUEST_SIZE:
771                     return "PRIORITY_APP_REQUEST_SIZE";
772                 case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
773                     return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
774                 case PRIORITY_LOW_POWER_MODE:
775                     return "PRIORITY_LOW_POWER_MODE";
776                 default:
777                     return Integer.toString(priority);
778             }
779         }
780 
781         @Override
toString()782         public String toString() {
783             return "Vote{"
784                 + "width=" + width + ", height=" + height
785                 + ", minRefreshRate=" + refreshRateRange.min
786                 + ", maxRefreshRate=" + refreshRateRange.max + "}";
787         }
788     }
789 
790     private final class SettingsObserver extends ContentObserver {
791         private final Uri mPeakRefreshRateSetting =
792                 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
793         private final Uri mMinRefreshRateSetting =
794                 Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE);
795         private final Uri mLowPowerModeSetting =
796                 Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
797 
798         private final Context mContext;
799         private float mDefaultPeakRefreshRate;
800         private float mDefaultRefreshRate;
801 
SettingsObserver(@onNull Context context, @NonNull Handler handler)802         SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
803             super(handler);
804             mContext = context;
805             mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
806                     R.integer.config_defaultPeakRefreshRate);
807             mDefaultRefreshRate =
808                     (float) context.getResources().getInteger(R.integer.config_defaultRefreshRate);
809         }
810 
observe()811         public void observe() {
812             final ContentResolver cr = mContext.getContentResolver();
813             cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this,
814                     UserHandle.USER_SYSTEM);
815             cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
816                     UserHandle.USER_SYSTEM);
817             cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
818                     UserHandle.USER_SYSTEM);
819 
820             Float deviceConfigDefaultPeakRefresh =
821                     mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
822             if (deviceConfigDefaultPeakRefresh != null) {
823                 mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh;
824             }
825 
826             synchronized (mLock) {
827                 updateRefreshRateSettingLocked();
828                 updateLowPowerModeSettingLocked();
829             }
830         }
831 
onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate)832         public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
833             if (defaultPeakRefreshRate == null) {
834                 defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
835                         R.integer.config_defaultPeakRefreshRate);
836             }
837 
838             if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) {
839                 synchronized (mLock) {
840                     mDefaultPeakRefreshRate = defaultPeakRefreshRate;
841                     updateRefreshRateSettingLocked();
842                 }
843             }
844         }
845 
846         @Override
onChange(boolean selfChange, Uri uri, int userId)847         public void onChange(boolean selfChange, Uri uri, int userId) {
848             synchronized (mLock) {
849                 if (mPeakRefreshRateSetting.equals(uri)
850                         || mMinRefreshRateSetting.equals(uri)) {
851                     updateRefreshRateSettingLocked();
852                 } else if (mLowPowerModeSetting.equals(uri)) {
853                     updateLowPowerModeSettingLocked();
854                 }
855             }
856         }
857 
updateLowPowerModeSettingLocked()858         private void updateLowPowerModeSettingLocked() {
859             boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
860                     Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
861             final Vote vote;
862             if (inLowPowerMode) {
863                 vote = Vote.forRefreshRates(0f, 60f);
864             } else {
865                 vote = null;
866             }
867             updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote);
868             mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
869         }
870 
updateRefreshRateSettingLocked()871         private void updateRefreshRateSettingLocked() {
872             float minRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
873                     Settings.System.MIN_REFRESH_RATE, 0f);
874             float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
875                     Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate);
876             updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
877         }
878 
updateRefreshRateSettingLocked( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate)879         private void updateRefreshRateSettingLocked(
880                 float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
881             // TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is
882             // used to predict if we're going to be doing frequent refresh rate switching, and if
883             // so, enable the brightness observer. The logic here is more complicated and fragile
884             // than necessary, and we should improve it. See b/156304339 for more info.
885             Vote peakVote = peakRefreshRate == 0f
886                     ? null
887                     : Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate));
888             updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, peakVote);
889             updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
890                     Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY));
891             Vote defaultVote =
892                     defaultRefreshRate == 0f ? null : Vote.forRefreshRates(0f, defaultRefreshRate);
893             updateVoteLocked(Vote.PRIORITY_DEFAULT_REFRESH_RATE, defaultVote);
894 
895             float maxRefreshRate;
896             if (peakRefreshRate == 0f && defaultRefreshRate == 0f) {
897                 // We require that at least one of the peak or default refresh rate values are
898                 // set. The brightness observer requires that we're able to predict whether or not
899                 // we're going to do frequent refresh rate switching, and with the way the code is
900                 // currently written, we need either a default or peak refresh rate value for that.
901                 Slog.e(TAG, "Default and peak refresh rates are both 0. One of them should be set"
902                         + " to a valid value.");
903                 maxRefreshRate = minRefreshRate;
904             } else if (peakRefreshRate == 0f) {
905                 maxRefreshRate = defaultRefreshRate;
906             } else if (defaultRefreshRate == 0f) {
907                 maxRefreshRate = peakRefreshRate;
908             } else {
909                 maxRefreshRate = Math.min(defaultRefreshRate, peakRefreshRate);
910             }
911 
912             mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, maxRefreshRate);
913         }
914 
dumpLocked(PrintWriter pw)915         public void dumpLocked(PrintWriter pw) {
916             pw.println("  SettingsObserver");
917             pw.println("    mDefaultRefreshRate: " + mDefaultRefreshRate);
918             pw.println("    mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate);
919         }
920     }
921 
922     final class AppRequestObserver {
923         private SparseArray<Display.Mode> mAppRequestedModeByDisplay;
924 
AppRequestObserver()925         AppRequestObserver() {
926             mAppRequestedModeByDisplay = new SparseArray<>();
927         }
928 
setAppRequestedMode(int displayId, int modeId)929         public void setAppRequestedMode(int displayId, int modeId) {
930             synchronized (mLock) {
931                 setAppRequestedModeLocked(displayId, modeId);
932             }
933         }
934 
setAppRequestedModeLocked(int displayId, int modeId)935         private void setAppRequestedModeLocked(int displayId, int modeId) {
936             final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId);
937             if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
938                 return;
939             }
940 
941             final Vote refreshRateVote;
942             final Vote sizeVote;
943             if (requestedMode != null) {
944                 mAppRequestedModeByDisplay.put(displayId, requestedMode);
945                 float refreshRate = requestedMode.getRefreshRate();
946                 refreshRateVote = Vote.forRefreshRates(refreshRate, refreshRate);
947                 sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
948                         requestedMode.getPhysicalHeight());
949             } else {
950                 mAppRequestedModeByDisplay.remove(displayId);
951                 refreshRateVote = null;
952                 sizeVote = null;
953             }
954 
955             updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote);
956             updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
957             return;
958         }
959 
findModeByIdLocked(int displayId, int modeId)960         private Display.Mode findModeByIdLocked(int displayId, int modeId) {
961             Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
962             if (modes == null) {
963                 return null;
964             }
965             for (Display.Mode mode : modes) {
966                 if (mode.getModeId() == modeId) {
967                     return mode;
968                 }
969             }
970             return null;
971         }
972 
dumpLocked(PrintWriter pw)973         public void dumpLocked(PrintWriter pw) {
974             pw.println("  AppRequestObserver");
975             pw.println("    mAppRequestedModeByDisplay:");
976             for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
977                 final int id = mAppRequestedModeByDisplay.keyAt(i);
978                 final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
979                 pw.println("    " + id + " -> " + mode);
980             }
981         }
982     }
983 
984     private final class DisplayObserver implements DisplayManager.DisplayListener {
985         // Note that we can never call into DisplayManager or any of the non-POD classes it
986         // returns, while holding mLock since it may call into DMS, which might be simultaneously
987         // calling into us already holding its own lock.
988         private final Context mContext;
989         private final Handler mHandler;
990 
DisplayObserver(Context context, Handler handler)991         DisplayObserver(Context context, Handler handler) {
992             mContext = context;
993             mHandler = handler;
994         }
995 
observe()996         public void observe() {
997             DisplayManager dm = mContext.getSystemService(DisplayManager.class);
998             dm.registerDisplayListener(this, mHandler);
999 
1000             // Populate existing displays
1001             SparseArray<Display.Mode[]> modes = new SparseArray<>();
1002             SparseArray<Display.Mode> defaultModes = new SparseArray<>();
1003             DisplayInfo info = new DisplayInfo();
1004             Display[] displays = dm.getDisplays();
1005             for (Display d : displays) {
1006                 final int displayId = d.getDisplayId();
1007                 d.getDisplayInfo(info);
1008                 modes.put(displayId, info.supportedModes);
1009                 defaultModes.put(displayId, info.getDefaultMode());
1010             }
1011             synchronized (mLock) {
1012                 final int size = modes.size();
1013                 for (int i = 0; i < size; i++) {
1014                     mSupportedModesByDisplay.put(modes.keyAt(i), modes.valueAt(i));
1015                     mDefaultModeByDisplay.put(defaultModes.keyAt(i), defaultModes.valueAt(i));
1016                 }
1017             }
1018         }
1019 
1020         @Override
onDisplayAdded(int displayId)1021         public void onDisplayAdded(int displayId) {
1022             updateDisplayModes(displayId);
1023         }
1024 
1025         @Override
onDisplayRemoved(int displayId)1026         public void onDisplayRemoved(int displayId) {
1027             synchronized (mLock) {
1028                 mSupportedModesByDisplay.remove(displayId);
1029                 mDefaultModeByDisplay.remove(displayId);
1030             }
1031         }
1032 
1033         @Override
onDisplayChanged(int displayId)1034         public void onDisplayChanged(int displayId) {
1035             updateDisplayModes(displayId);
1036             mBrightnessObserver.onDisplayChanged(displayId);
1037         }
1038 
updateDisplayModes(int displayId)1039         private void updateDisplayModes(int displayId) {
1040             Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
1041             if (d == null) {
1042                 // We can occasionally get a display added or changed event for a display that was
1043                 // subsequently removed, which means this returns null. Check this case and bail
1044                 // out early; if it gets re-attached we'll eventually get another call back for it.
1045                 return;
1046             }
1047             DisplayInfo info = new DisplayInfo();
1048             d.getDisplayInfo(info);
1049             boolean changed = false;
1050             synchronized (mLock) {
1051                 if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) {
1052                     mSupportedModesByDisplay.put(displayId, info.supportedModes);
1053                     changed = true;
1054                 }
1055                 if (!Objects.equals(mDefaultModeByDisplay.get(displayId), info.getDefaultMode())) {
1056                     changed = true;
1057                     mDefaultModeByDisplay.put(displayId, info.getDefaultMode());
1058                 }
1059                 if (changed) {
1060                     notifyDesiredDisplayModeSpecsChangedLocked();
1061                 }
1062             }
1063         }
1064     }
1065 
1066     /**
1067      * This class manages brightness threshold for switching between 60 hz and higher refresh rate.
1068      * See more information at the definition of
1069      * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and
1070      * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
1071      */
1072     @VisibleForTesting
1073     public class BrightnessObserver extends ContentObserver {
1074         // TODO: brightnessfloat: change this to the float setting
1075         private final Uri mDisplayBrightnessSetting =
1076                 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
1077         private final static int LIGHT_SENSOR_RATE_MS = 250;
1078         private int[] mDisplayBrightnessThresholds;
1079         private int[] mAmbientBrightnessThresholds;
1080         // valid threshold if any item from the array >= 0
1081         private boolean mShouldObserveDisplayChange;
1082         private boolean mShouldObserveAmbientChange;
1083 
1084         private SensorManager mSensorManager;
1085         private Sensor mLightSensor;
1086         private LightSensorEventListener mLightSensorListener = new LightSensorEventListener();
1087         // Take it as low brightness before valid sensor data comes
1088         private float mAmbientLux = -1.0f;
1089         private AmbientFilter mAmbientFilter;
1090 
1091         private final Context mContext;
1092 
1093         // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak
1094         // refresh rate changeable and low power mode off. After initialization, these states will
1095         // be updated from the same handler thread.
1096         private boolean mScreenOn = false;
1097         private boolean mRefreshRateChangeable = false;
1098         private boolean mLowPowerModeEnabled = false;
1099 
1100         private int mRefreshRateInZone;
1101 
BrightnessObserver(Context context, Handler handler)1102         BrightnessObserver(Context context, Handler handler) {
1103             super(handler);
1104             mContext = context;
1105             mDisplayBrightnessThresholds = context.getResources().getIntArray(
1106                     R.array.config_brightnessThresholdsOfPeakRefreshRate);
1107             mAmbientBrightnessThresholds = context.getResources().getIntArray(
1108                     R.array.config_ambientThresholdsOfPeakRefreshRate);
1109 
1110             if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) {
1111                 throw new RuntimeException("display brightness threshold array and ambient "
1112                         + "brightness threshold array have different length");
1113             }
1114         }
1115 
observe(SensorManager sensorManager)1116         public void observe(SensorManager sensorManager) {
1117             mSensorManager = sensorManager;
1118 
1119             // DeviceConfig is accessible after system ready.
1120             int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds();
1121             int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds();
1122 
1123             if (brightnessThresholds != null && ambientThresholds != null
1124                     && brightnessThresholds.length == ambientThresholds.length) {
1125                 mDisplayBrightnessThresholds = brightnessThresholds;
1126                 mAmbientBrightnessThresholds = ambientThresholds;
1127             }
1128 
1129             mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone();
1130             restartObserver();
1131             mDeviceConfigDisplaySettings.startListening();
1132         }
1133 
onRefreshRateSettingChangedLocked(float min, float max)1134         public void onRefreshRateSettingChangedLocked(float min, float max) {
1135             boolean changeable = (max - min > 1f && max > 60f);
1136             if (mRefreshRateChangeable != changeable) {
1137                 mRefreshRateChangeable = changeable;
1138                 updateSensorStatus();
1139                 if (!changeable) {
1140                     // Revoke previous vote from BrightnessObserver
1141                     updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null);
1142                 }
1143             }
1144         }
1145 
onLowPowerModeEnabledLocked(boolean b)1146         public void onLowPowerModeEnabledLocked(boolean b) {
1147             if (mLowPowerModeEnabled != b) {
1148                 mLowPowerModeEnabled = b;
1149                 updateSensorStatus();
1150             }
1151         }
1152 
onDeviceConfigThresholdsChanged(int[] brightnessThresholds, int[] ambientThresholds)1153         public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds,
1154                 int[] ambientThresholds) {
1155             if (brightnessThresholds != null && ambientThresholds != null
1156                     && brightnessThresholds.length == ambientThresholds.length) {
1157                 mDisplayBrightnessThresholds = brightnessThresholds;
1158                 mAmbientBrightnessThresholds = ambientThresholds;
1159             } else {
1160                 // Invalid or empty. Use device default.
1161                 mDisplayBrightnessThresholds = mContext.getResources().getIntArray(
1162                         R.array.config_brightnessThresholdsOfPeakRefreshRate);
1163                 mAmbientBrightnessThresholds = mContext.getResources().getIntArray(
1164                         R.array.config_ambientThresholdsOfPeakRefreshRate);
1165             }
1166             restartObserver();
1167         }
1168 
onDeviceConfigRefreshRateInZoneChanged(int refreshRate)1169         public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) {
1170             if (refreshRate != mRefreshRateInZone) {
1171                 mRefreshRateInZone = refreshRate;
1172                 restartObserver();
1173             }
1174         }
1175 
dumpLocked(PrintWriter pw)1176         public void dumpLocked(PrintWriter pw) {
1177             pw.println("  BrightnessObserver");
1178             pw.println("    mAmbientLux: " + mAmbientLux);
1179             pw.println("    mRefreshRateInZone: " + mRefreshRateInZone);
1180 
1181             for (int d: mDisplayBrightnessThresholds) {
1182                 pw.println("    mDisplayBrightnessThreshold: " + d);
1183             }
1184 
1185             for (int d: mAmbientBrightnessThresholds) {
1186                 pw.println("    mAmbientBrightnessThreshold: " + d);
1187             }
1188 
1189             mLightSensorListener.dumpLocked(pw);
1190         }
1191 
onDisplayChanged(int displayId)1192         public void onDisplayChanged(int displayId) {
1193             if (displayId == Display.DEFAULT_DISPLAY) {
1194                 onScreenOn(isDefaultDisplayOn());
1195             }
1196         }
1197 
1198         @Override
onChange(boolean selfChange, Uri uri, int userId)1199         public void onChange(boolean selfChange, Uri uri, int userId) {
1200             synchronized (mLock) {
1201                 onBrightnessChangedLocked();
1202             }
1203         }
1204 
restartObserver()1205         private void restartObserver() {
1206             mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
1207             mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
1208 
1209             final ContentResolver cr = mContext.getContentResolver();
1210             if (mShouldObserveDisplayChange) {
1211                 // Content Service does not check if an listener has already been registered.
1212                 // To ensure only one listener is registered, force an unregistration first.
1213                 cr.unregisterContentObserver(this);
1214                 cr.registerContentObserver(mDisplayBrightnessSetting,
1215                         false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
1216             } else {
1217                 cr.unregisterContentObserver(this);
1218             }
1219 
1220             if (mShouldObserveAmbientChange) {
1221                 Resources resources = mContext.getResources();
1222                 String lightSensorType = resources.getString(
1223                         com.android.internal.R.string.config_displayLightSensorType);
1224 
1225                 Sensor lightSensor = null;
1226                 if (!TextUtils.isEmpty(lightSensorType)) {
1227                     List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
1228                     for (int i = 0; i < sensors.size(); i++) {
1229                         Sensor sensor = sensors.get(i);
1230                         if (lightSensorType.equals(sensor.getStringType())) {
1231                             lightSensor = sensor;
1232                             break;
1233                         }
1234                     }
1235                 }
1236 
1237                 if (lightSensor == null) {
1238                     lightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
1239                 }
1240 
1241                 if (lightSensor != null) {
1242                     final Resources res = mContext.getResources();
1243 
1244                     mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
1245                     mLightSensor = lightSensor;
1246 
1247                     onScreenOn(isDefaultDisplayOn());
1248                 }
1249             } else {
1250                 mAmbientFilter = null;
1251                 mLightSensor = null;
1252             }
1253 
1254             if (mRefreshRateChangeable) {
1255                 updateSensorStatus();
1256                 synchronized (mLock) {
1257                     onBrightnessChangedLocked();
1258                 }
1259             }
1260         }
1261 
1262         /**
1263          * Checks to see if at least one value is positive, in which case it is necessary to listen
1264          * to value changes.
1265          */
checkShouldObserve(int[] a)1266         private boolean checkShouldObserve(int[] a) {
1267             if (mRefreshRateInZone <= 0) {
1268                 return false;
1269             }
1270 
1271             for (int d: a) {
1272                 if (d >= 0) {
1273                     return true;
1274                 }
1275             }
1276 
1277             return false;
1278         }
1279 
isInsideZone(int brightness, float lux)1280         private boolean isInsideZone(int brightness, float lux) {
1281             for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
1282                 int disp = mDisplayBrightnessThresholds[i];
1283                 int ambi = mAmbientBrightnessThresholds[i];
1284 
1285                 if (disp >= 0 && ambi >= 0) {
1286                     if (brightness <= disp && mAmbientLux <= ambi) {
1287                         return true;
1288                     }
1289                 } else if (disp >= 0) {
1290                     if (brightness <= disp) {
1291                         return true;
1292                     }
1293                 } else if (ambi >= 0) {
1294                     if (mAmbientLux <= ambi) {
1295                         return true;
1296                     }
1297                 }
1298             }
1299 
1300             return false;
1301         }
1302         // TODO: brightnessfloat: make it use float not int
onBrightnessChangedLocked()1303         private void onBrightnessChangedLocked() {
1304             int brightness = Settings.System.getInt(mContext.getContentResolver(),
1305                     Settings.System.SCREEN_BRIGHTNESS, -1);
1306 
1307             Vote vote = null;
1308             boolean insideZone = isInsideZone(brightness, mAmbientLux);
1309             if (insideZone) {
1310                 vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone);
1311             }
1312 
1313             if (DEBUG) {
1314                 Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " +  mAmbientLux +
1315                         ", Vote " + vote);
1316             }
1317             updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
1318         }
1319 
onScreenOn(boolean on)1320         private void onScreenOn(boolean on) {
1321             if (mScreenOn != on) {
1322                 mScreenOn = on;
1323                 updateSensorStatus();
1324             }
1325         }
1326 
updateSensorStatus()1327         private void updateSensorStatus() {
1328             if (mSensorManager == null || mLightSensorListener == null) {
1329                 return;
1330             }
1331 
1332             if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled
1333                     && mRefreshRateChangeable) {
1334                 mSensorManager.registerListener(mLightSensorListener,
1335                         mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
1336             } else {
1337                 mLightSensorListener.removeCallbacks();
1338                 mSensorManager.unregisterListener(mLightSensorListener);
1339             }
1340         }
1341 
isDefaultDisplayOn()1342         private boolean isDefaultDisplayOn() {
1343             final Display display = mContext.getSystemService(DisplayManager.class)
1344                     .getDisplay(Display.DEFAULT_DISPLAY);
1345             return display.getState() != Display.STATE_OFF
1346                     && mContext.getSystemService(PowerManager.class).isInteractive();
1347         }
1348 
1349         private final class LightSensorEventListener implements SensorEventListener {
1350             final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
1351             private float mLastSensorData;
1352 
dumpLocked(PrintWriter pw)1353             public void dumpLocked(PrintWriter pw) {
1354                 pw.println("    mLastSensorData: " + mLastSensorData);
1355             }
1356 
1357             @Override
onSensorChanged(SensorEvent event)1358             public void onSensorChanged(SensorEvent event) {
1359                 mLastSensorData = event.values[0];
1360                 if (DEBUG) {
1361                     Slog.d(TAG, "On sensor changed: " + mLastSensorData);
1362                 }
1363 
1364                 boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux);
1365                 if (zoneChanged && mLastSensorData < mAmbientLux) {
1366                     // Easier to see flicker at lower brightness environment. Forget the history to
1367                     // get immediate response.
1368                     mAmbientFilter.clear();
1369                 }
1370 
1371                 long now = SystemClock.uptimeMillis();
1372                 mAmbientFilter.addValue(now, mLastSensorData);
1373 
1374                 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1375                 processSensorData(now);
1376 
1377                 if (zoneChanged && mLastSensorData > mAmbientLux) {
1378                     // Sensor may not report new event if there is no brightness change.
1379                     // Need to keep querying the temporal filter for the latest estimation,
1380                     // until enter in higher lux zone or is interrupted by a new sensor event.
1381                     mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
1382                 }
1383             }
1384 
1385             @Override
onAccuracyChanged(Sensor sensor, int accuracy)1386             public void onAccuracyChanged(Sensor sensor, int accuracy) {
1387                 // Not used.
1388             }
1389 
removeCallbacks()1390             public void removeCallbacks() {
1391                 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1392             }
1393 
processSensorData(long now)1394             private void processSensorData(long now) {
1395                 mAmbientLux = mAmbientFilter.getEstimate(now);
1396 
1397                 synchronized (mLock) {
1398                     onBrightnessChangedLocked();
1399                 }
1400             }
1401 
isDifferentZone(float lux1, float lux2)1402             private boolean isDifferentZone(float lux1, float lux2) {
1403                 for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) {
1404                     final float boundary = mAmbientBrightnessThresholds[z];
1405 
1406                     // Test each boundary. See if the current value and the new value are at
1407                     // different sides.
1408                     if ((lux1 <= boundary && lux2 > boundary)
1409                             || (lux1 > boundary && lux2 <= boundary)) {
1410                         return true;
1411                     }
1412                 }
1413 
1414                 return false;
1415             }
1416 
1417             private Runnable mInjectSensorEventRunnable = new Runnable() {
1418                 @Override
1419                 public void run() {
1420                     long now = SystemClock.uptimeMillis();
1421                     // No need to really inject the last event into a temporal filter.
1422                     processSensorData(now);
1423 
1424                     // Inject next event if there is a possible zone change.
1425                     if (isDifferentZone(mLastSensorData, mAmbientLux)) {
1426                         mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
1427                     }
1428                 }
1429             };
1430         }
1431     }
1432 
1433     private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
DeviceConfigDisplaySettings()1434         public DeviceConfigDisplaySettings() {
1435         }
1436 
startListening()1437         public void startListening() {
1438             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1439                     BackgroundThread.getExecutor(), this);
1440         }
1441 
1442         /*
1443          * Return null if no such property or wrong format (not comma separated integers).
1444          */
getBrightnessThresholds()1445         public int[] getBrightnessThresholds() {
1446             return getIntArrayProperty(
1447                     DisplayManager.DeviceConfig.
1448                             KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS);
1449         }
1450 
1451         /*
1452          * Return null if no such property or wrong format (not comma separated integers).
1453          */
getAmbientThresholds()1454         public int[] getAmbientThresholds() {
1455             return getIntArrayProperty(
1456                     DisplayManager.DeviceConfig.
1457                             KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS);
1458         }
1459 
1460         /*
1461          * Return null if no such property
1462          */
getDefaultPeakRefreshRate()1463         public Float getDefaultPeakRefreshRate() {
1464             float defaultPeakRefreshRate = DeviceConfig.getFloat(
1465                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1466                     DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
1467 
1468             if (defaultPeakRefreshRate == -1) {
1469                 return null;
1470             }
1471             return defaultPeakRefreshRate;
1472         }
1473 
getRefreshRateInZone()1474         public int getRefreshRateInZone() {
1475             int defaultRefreshRateInZone = mContext.getResources().getInteger(
1476                     R.integer.config_defaultRefreshRateInZone);
1477 
1478             int refreshRate = DeviceConfig.getInt(
1479                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1480                     DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
1481                     defaultRefreshRateInZone);
1482 
1483             return refreshRate;
1484         }
1485 
1486         @Override
onPropertiesChanged(@onNull DeviceConfig.Properties properties)1487         public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
1488             int[] brightnessThresholds = getBrightnessThresholds();
1489             int[] ambientThresholds = getAmbientThresholds();
1490             Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
1491             int refreshRateInZone = getRefreshRateInZone();
1492 
1493             mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED,
1494                     new Pair<int[], int[]>(brightnessThresholds, ambientThresholds))
1495                     .sendToTarget();
1496             mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
1497                     defaultPeakRefreshRate).sendToTarget();
1498             mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone,
1499                     0).sendToTarget();
1500         }
1501 
getIntArrayProperty(String prop)1502         private int[] getIntArrayProperty(String prop) {
1503             String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
1504                     null);
1505 
1506             if (strArray != null) {
1507                 return parseIntArray(strArray);
1508             }
1509 
1510             return null;
1511         }
1512 
parseIntArray(@onNull String strArray)1513         private int[] parseIntArray(@NonNull String strArray) {
1514             String[] items = strArray.split(",");
1515             int[] array = new int[items.length];
1516 
1517             try {
1518                 for (int i = 0; i < array.length; i++) {
1519                     array[i] = Integer.parseInt(items[i]);
1520                 }
1521             } catch (NumberFormatException e) {
1522                 Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
1523                 array = null;
1524             }
1525 
1526             return array;
1527         }
1528     }
1529 
1530 }
1531