blob: 1fc0db3ff7cb0f760fd2ad96f0d4981ee30a3e60 [file] [log] [blame]
Michael Wrighta3dab232019-02-22 16:54:21 +00001/*
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
17package com.android.server.display;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
Long Lingbc841b02019-07-03 16:43:15 -070021import android.content.BroadcastReceiver;
Michael Wrighta3dab232019-02-22 16:54:21 +000022import android.content.ContentResolver;
23import android.content.Context;
Long Lingbc841b02019-07-03 16:43:15 -070024import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.res.Resources;
Michael Wrighta3dab232019-02-22 16:54:21 +000027import android.database.ContentObserver;
28import android.hardware.display.DisplayManager;
Long Lingbc841b02019-07-03 16:43:15 -070029import android.hardware.Sensor;
30import android.hardware.SensorEvent;
31import android.hardware.SensorEventListener;
32import android.hardware.SensorManager;
33
Michael Wrighta3dab232019-02-22 16:54:21 +000034import android.net.Uri;
35import android.os.Handler;
36import android.os.Looper;
37import android.os.Message;
38import android.os.UserHandle;
Long Lingbc841b02019-07-03 16:43:15 -070039import android.os.PowerManager;
40import android.os.SystemClock;
Long Lingbb459a02019-08-08 16:05:44 -070041import android.provider.DeviceConfig;
Michael Wrighta3dab232019-02-22 16:54:21 +000042import android.provider.Settings;
Long Lingbc841b02019-07-03 16:43:15 -070043import android.text.TextUtils;
Long Lingbb459a02019-08-08 16:05:44 -070044import android.util.Pair;
Michael Wrighta3dab232019-02-22 16:54:21 +000045import android.util.Slog;
46import android.util.SparseArray;
47import android.view.Display;
48import android.view.DisplayInfo;
49
Long Lingbb459a02019-08-08 16:05:44 -070050import com.android.internal.os.BackgroundThread;
Michael Wrighta3dab232019-02-22 16:54:21 +000051import com.android.internal.R;
Long Lingbc841b02019-07-03 16:43:15 -070052import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
53import com.android.server.display.whitebalance.AmbientFilter;
Michael Wrighta3dab232019-02-22 16:54:21 +000054
55import java.io.PrintWriter;
56import java.util.ArrayList;
57import java.util.Arrays;
Long Lingbc841b02019-07-03 16:43:15 -070058import java.util.List;
Michael Wrighta3dab232019-02-22 16:54:21 +000059import java.util.Objects;
60
61/**
62 * The DisplayModeDirector is responsible for determining what modes are allowed to be
63 * automatically picked by the system based on system-wide and display-specific configuration.
64 */
65public class DisplayModeDirector {
66 private static final String TAG = "DisplayModeDirector";
67 private static final boolean DEBUG = false;
68
69 private static final int MSG_ALLOWED_MODES_CHANGED = 1;
Long Lingbb459a02019-08-08 16:05:44 -070070 private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
71 private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
Long Ling73936632019-08-20 15:01:14 -070072 private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4;
Michael Wrighta3dab232019-02-22 16:54:21 +000073
74 // Special ID used to indicate that given vote is to be applied globally, rather than to a
75 // specific display.
76 private static final int GLOBAL_ID = -1;
77
Michael Wrighta3dab232019-02-22 16:54:21 +000078 // The tolerance within which we consider something approximately equals.
79 private static final float EPSILON = 0.001f;
80
81 private final Object mLock = new Object();
82 private final Context mContext;
83
84 private final DisplayModeDirectorHandler mHandler;
85
86 // A map from the display ID to the collection of votes and their priority. The latter takes
87 // the form of another map from the priority to the vote itself so that each priority is
88 // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
89 private final SparseArray<SparseArray<Vote>> mVotesByDisplay;
90 // A map from the display ID to the supported modes on that display.
91 private final SparseArray<Display.Mode[]> mSupportedModesByDisplay;
92 // A map from the display ID to the default mode of that display.
93 private final SparseArray<Display.Mode> mDefaultModeByDisplay;
94
95 private final AppRequestObserver mAppRequestObserver;
96 private final SettingsObserver mSettingsObserver;
97 private final DisplayObserver mDisplayObserver;
Long Lingbc841b02019-07-03 16:43:15 -070098 private final BrightnessObserver mBrightnessObserver;
Michael Wrighta3dab232019-02-22 16:54:21 +000099
Long Lingbb459a02019-08-08 16:05:44 -0700100 private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
Michael Wrighta3dab232019-02-22 16:54:21 +0000101 private Listener mListener;
102
103 public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
104 mContext = context;
105 mHandler = new DisplayModeDirectorHandler(handler.getLooper());
106 mVotesByDisplay = new SparseArray<>();
107 mSupportedModesByDisplay = new SparseArray<>();
108 mDefaultModeByDisplay = new SparseArray<>();
109 mAppRequestObserver = new AppRequestObserver();
110 mSettingsObserver = new SettingsObserver(context, handler);
111 mDisplayObserver = new DisplayObserver(context, handler);
Long Lingbc841b02019-07-03 16:43:15 -0700112 mBrightnessObserver = new BrightnessObserver(context, handler);
Long Lingbb459a02019-08-08 16:05:44 -0700113 mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
Michael Wrighta3dab232019-02-22 16:54:21 +0000114 }
115
116 /**
117 * Tells the DisplayModeDirector to update allowed votes and begin observing relevant system
118 * state.
119 *
120 * This has to be deferred because the object may be constructed before the rest of the system
121 * is ready.
122 */
Long Lingbc841b02019-07-03 16:43:15 -0700123 public void start(SensorManager sensorManager) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000124 mSettingsObserver.observe();
125 mDisplayObserver.observe();
126 mSettingsObserver.observe();
Long Lingbc841b02019-07-03 16:43:15 -0700127 mBrightnessObserver.observe(sensorManager);
Michael Wrighta3dab232019-02-22 16:54:21 +0000128 synchronized (mLock) {
129 // We may have a listener already registered before the call to start, so go ahead and
130 // notify them to pick up our newly initialized state.
131 notifyAllowedModesChangedLocked();
132 }
Long Lingbc841b02019-07-03 16:43:15 -0700133
Michael Wrighta3dab232019-02-22 16:54:21 +0000134 }
135
136 /**
137 * Calculates the modes the system is allowed to freely switch between based on global and
138 * display-specific constraints.
139 *
140 * @param displayId The display to query for.
141 * @return The IDs of the modes the system is allowed to freely switch between.
142 */
143 @NonNull
144 public int[] getAllowedModes(int displayId) {
145 synchronized (mLock) {
146 SparseArray<Vote> votes = getVotesLocked(displayId);
147 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
148 Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
149 if (modes == null || defaultMode == null) {
150 Slog.e(TAG, "Asked about unknown display, returning empty allowed set! (id="
151 + displayId + ")");
152 return new int[0];
153 }
154 return getAllowedModesLocked(votes, modes, defaultMode);
155 }
156 }
157
158 @NonNull
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 @NonNull
181 private int[] getAllowedModesLocked(@NonNull SparseArray<Vote> votes,
182 @NonNull Display.Mode[] modes, @NonNull Display.Mode defaultMode) {
183 int lowestConsideredPriority = Vote.MIN_PRIORITY;
184 while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
185 float minRefreshRate = 0f;
186 float maxRefreshRate = Float.POSITIVE_INFINITY;
187 int height = Vote.INVALID_SIZE;
188 int width = Vote.INVALID_SIZE;
189
190 for (int priority = Vote.MAX_PRIORITY;
191 priority >= lowestConsideredPriority;
192 priority--) {
193 Vote vote = votes.get(priority);
194 if (vote == null) {
195 continue;
196 }
197 // For refresh rates, just use the tightest bounds of all the votes
198 minRefreshRate = Math.max(minRefreshRate, vote.minRefreshRate);
199 maxRefreshRate = Math.min(maxRefreshRate, vote.maxRefreshRate);
200 // For display size, use only the first vote we come across (i.e. the highest
201 // priority vote that includes the width / height).
202 if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE
203 && vote.height > 0 && vote.width > 0) {
204 width = vote.width;
205 height = vote.height;
206 }
207 }
208
209 // If we don't have anything specifying the width / height of the display, just use the
210 // default width and height. We don't want these switching out from underneath us since
211 // it's a pretty disruptive behavior.
212 if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) {
213 width = defaultMode.getPhysicalWidth();
214 height = defaultMode.getPhysicalHeight();
215 }
216
217 int[] availableModes =
218 filterModes(modes, width, height, minRefreshRate, maxRefreshRate);
219 if (availableModes.length > 0) {
220 if (DEBUG) {
221 Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
222 + " with lowest priority considered "
223 + Vote.priorityToString(lowestConsideredPriority)
224 + " and constraints: "
225 + "width=" + width
226 + ", height=" + height
227 + ", minRefreshRate=" + minRefreshRate
228 + ", maxRefreshRate=" + maxRefreshRate);
229 }
230 return availableModes;
231 }
232
233 if (DEBUG) {
234 Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
235 + Vote.priorityToString(lowestConsideredPriority)
236 + " and with the following constraints: "
237 + "width=" + width
238 + ", height=" + height
239 + ", minRefreshRate=" + minRefreshRate
240 + ", maxRefreshRate=" + maxRefreshRate);
241 }
242 // If we haven't found anything with the current set of votes, drop the current lowest
243 // priority vote.
244 lowestConsideredPriority++;
245 }
246
247 // If we still haven't found anything that matches our current set of votes, just fall back
248 // to the default mode.
249 return new int[] { defaultMode.getModeId() };
250 }
251
252 private int[] filterModes(Display.Mode[] supportedModes,
253 int width, int height, float minRefreshRate, float maxRefreshRate) {
254 ArrayList<Display.Mode> availableModes = new ArrayList<>();
255 for (Display.Mode mode : supportedModes) {
256 if (mode.getPhysicalWidth() != width || mode.getPhysicalHeight() != height) {
257 if (DEBUG) {
258 Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
259 + ": desiredWidth=" + width
260 + ": desiredHeight=" + height
261 + ": actualWidth=" + mode.getPhysicalWidth()
262 + ": actualHeight=" + mode.getPhysicalHeight());
263 }
264 continue;
265 }
266 final float refreshRate = mode.getRefreshRate();
267 // Some refresh rates are calculated based on frame timings, so they aren't *exactly*
268 // equal to expected refresh rate. Given that, we apply a bit of tolerance to this
269 // comparison.
270 if (refreshRate < (minRefreshRate - EPSILON)
271 || refreshRate > (maxRefreshRate + EPSILON)) {
272 if (DEBUG) {
273 Slog.w(TAG, "Discarding mode " + mode.getModeId()
274 + ", outside refresh rate bounds"
275 + ": minRefreshRate=" + minRefreshRate
276 + ", maxRefreshRate=" + maxRefreshRate
277 + ", modeRefreshRate=" + refreshRate);
278 }
279 continue;
280 }
281 availableModes.add(mode);
282 }
283 final int size = availableModes.size();
284 int[] availableModeIds = new int[size];
285 for (int i = 0; i < size; i++) {
286 availableModeIds[i] = availableModes.get(i).getModeId();
287 }
288 return availableModeIds;
289 }
290
291 /**
292 * Gets the observer responsible for application display mode requests.
293 */
294 @NonNull
295 public AppRequestObserver getAppRequestObserver() {
296 // We don't need to lock here because mAppRequestObserver is a final field, which is
297 // guaranteed to be visible on all threads after construction.
298 return mAppRequestObserver;
299 }
300
301 /**
302 * Sets the listener for changes to allowed display modes.
303 */
304 public void setListener(@Nullable Listener listener) {
305 synchronized (mLock) {
306 mListener = listener;
307 }
308 }
309
310 /**
311 * Print the object's state and debug information into the given stream.
312 *
313 * @param pw The stream to dump information to.
314 */
315 public void dump(PrintWriter pw) {
316 pw.println("DisplayModeDirector");
317 synchronized (mLock) {
318 pw.println(" mSupportedModesByDisplay:");
319 for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
320 final int id = mSupportedModesByDisplay.keyAt(i);
321 final Display.Mode[] modes = mSupportedModesByDisplay.valueAt(i);
322 pw.println(" " + id + " -> " + Arrays.toString(modes));
323 }
324 pw.println(" mDefaultModeByDisplay:");
325 for (int i = 0; i < mDefaultModeByDisplay.size(); i++) {
326 final int id = mDefaultModeByDisplay.keyAt(i);
327 final Display.Mode mode = mDefaultModeByDisplay.valueAt(i);
328 pw.println(" " + id + " -> " + mode);
329 }
330 pw.println(" mVotesByDisplay:");
331 for (int i = 0; i < mVotesByDisplay.size(); i++) {
332 pw.println(" " + mVotesByDisplay.keyAt(i) + ":");
333 SparseArray<Vote> votes = mVotesByDisplay.valueAt(i);
334 for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
335 Vote vote = votes.get(p);
336 if (vote == null) {
337 continue;
338 }
339 pw.println(" " + Vote.priorityToString(p) + " -> " + vote);
340 }
341 }
342 mSettingsObserver.dumpLocked(pw);
343 mAppRequestObserver.dumpLocked(pw);
Long Lingbc841b02019-07-03 16:43:15 -0700344 mBrightnessObserver.dumpLocked(pw);
Michael Wrighta3dab232019-02-22 16:54:21 +0000345 }
346 }
347
348 private void updateVoteLocked(int priority, Vote vote) {
349 updateVoteLocked(GLOBAL_ID, priority, vote);
350 }
351
352 private void updateVoteLocked(int displayId, int priority, Vote vote) {
353 if (DEBUG) {
354 Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
355 + ", priority=" + Vote.priorityToString(priority)
356 + ", vote=" + vote + ")");
357 }
358 if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
359 Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
360 + " priority=" + Vote.priorityToString(priority)
361 + ", vote=" + vote, new Throwable());
362 return;
363 }
364 final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId);
365
366 Vote currentVote = votes.get(priority);
367 if (vote != null) {
368 votes.put(priority, vote);
369 } else {
370 votes.remove(priority);
371 }
372
373 if (votes.size() == 0) {
374 if (DEBUG) {
375 Slog.i(TAG, "No votes left for display " + displayId + ", removing.");
376 }
377 mVotesByDisplay.remove(displayId);
378 }
379
380 notifyAllowedModesChangedLocked();
381 }
382
383 private void notifyAllowedModesChangedLocked() {
384 if (mListener != null && !mHandler.hasMessages(MSG_ALLOWED_MODES_CHANGED)) {
385 // We need to post this to a handler to avoid calling out while holding the lock
386 // since we know there are things that both listen for changes as well as provide
387 // information. If we did call out while holding the lock, then there's no guaranteed
388 // lock order and we run the real of risk deadlock.
389 Message msg = mHandler.obtainMessage(MSG_ALLOWED_MODES_CHANGED, mListener);
390 msg.sendToTarget();
391 }
392 }
393
394 private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) {
395 int index = mVotesByDisplay.indexOfKey(displayId);
396 if (mVotesByDisplay.indexOfKey(displayId) >= 0) {
397 return mVotesByDisplay.get(displayId);
398 } else {
399 SparseArray<Vote> votes = new SparseArray<>();
400 mVotesByDisplay.put(displayId, votes);
401 return votes;
402 }
403 }
404
405 /**
406 * Listens for changes to display mode coordination.
407 */
408 public interface Listener {
409 /**
410 * Called when the allowed display modes may have changed.
411 */
412 void onAllowedDisplayModesChanged();
413 }
414
Long Lingbb459a02019-08-08 16:05:44 -0700415 private final class DisplayModeDirectorHandler extends Handler {
Michael Wrighta3dab232019-02-22 16:54:21 +0000416 DisplayModeDirectorHandler(Looper looper) {
417 super(looper, null, true /*async*/);
418 }
419
420 @Override
421 public void handleMessage(Message msg) {
422 switch (msg.what) {
423 case MSG_ALLOWED_MODES_CHANGED:
424 Listener listener = (Listener) msg.obj;
425 listener.onAllowedDisplayModesChanged();
426 break;
Long Lingbb459a02019-08-08 16:05:44 -0700427
428 case MSG_BRIGHTNESS_THRESHOLDS_CHANGED:
429 Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
430
431 if (thresholds != null) {
432 mBrightnessObserver.onDeviceConfigThresholdsChanged(
433 thresholds.first, thresholds.second);
434 } else {
435 mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null);
436 }
437 break;
438
439 case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
440 Float defaultPeakRefreshRate = (Float) msg.obj;
441 mSettingsObserver.onDeviceConfigDefaultPeakRefreshRateChanged(
442 defaultPeakRefreshRate);
443 break;
Long Ling73936632019-08-20 15:01:14 -0700444
445 case MSG_REFRESH_RATE_IN_ZONE_CHANGED:
446 int refreshRateInZone = msg.arg1;
447 mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged(
448 refreshRateInZone);
449 break;
Michael Wrighta3dab232019-02-22 16:54:21 +0000450 }
451 }
452 }
453
454 private static final class Vote {
Long Ling73936632019-08-20 15:01:14 -0700455 // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
456 // If the higher voters result is a range, it will fix the rate to a single choice.
457 // It's used to avoid rate switch in certain conditions.
458 public static final int PRIORITY_LOW_BRIGHTNESS = 0;
459
460 // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
461 // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
462 public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 1;
463
464 // We split the app request into different priorities in case we can satisfy one desire
465 // without the other.
466
467 // Application can specify preferred refresh rate with below attrs.
468 // @see android.view.WindowManager.LayoutParams#preferredRefreshRate
469 // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
470 // System also forces some apps like blacklisted app to run at a lower refresh rate.
471 // @see android.R.array#config_highRefreshRateBlacklist
472 public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 2;
473 public static final int PRIORITY_APP_REQUEST_SIZE = 3;
474
475 // SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest
476 // of low priority voters. It votes [0, max(PEAK, MIN)]
477 public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 4;
478
479 // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
480 public static final int PRIORITY_LOW_POWER_MODE = 5;
Michael Wrighta3dab232019-02-22 16:54:21 +0000481
482 // Whenever a new priority is added, remember to update MIN_PRIORITY and/or MAX_PRIORITY as
483 // appropriate, as well as priorityToString.
484
Long Ling73936632019-08-20 15:01:14 -0700485 public static final int MIN_PRIORITY = PRIORITY_LOW_BRIGHTNESS;
Michael Wrighta3dab232019-02-22 16:54:21 +0000486 public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;
487
488 /**
489 * A value signifying an invalid width or height in a vote.
490 */
491 public static final int INVALID_SIZE = -1;
492
493 /**
494 * The requested width of the display in pixels, or INVALID_SIZE;
495 */
496 public final int width;
497 /**
498 * The requested height of the display in pixels, or INVALID_SIZE;
499 */
500 public final int height;
501
502 /**
503 * The lowest desired refresh rate.
504 */
505 public final float minRefreshRate;
506 /**
507 * The highest desired refresh rate.
508 */
509 public final float maxRefreshRate;
510
511 public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
512 return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate);
513 }
514
515 public static Vote forSize(int width, int height) {
516 return new Vote(width, height, 0f, Float.POSITIVE_INFINITY);
517 }
518
519 private Vote(int width, int height,
520 float minRefreshRate, float maxRefreshRate) {
521 this.width = width;
522 this.height = height;
523 this.minRefreshRate = minRefreshRate;
524 this.maxRefreshRate = maxRefreshRate;
525 }
526
527 public static String priorityToString(int priority) {
528 switch (priority) {
Long Ling73936632019-08-20 15:01:14 -0700529 case PRIORITY_LOW_BRIGHTNESS:
530 return "PRIORITY_LOW_BRIGHTNESS";
531 case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
532 return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
Michael Wrighta3dab232019-02-22 16:54:21 +0000533 case PRIORITY_APP_REQUEST_REFRESH_RATE:
534 return "PRIORITY_APP_REQUEST_REFRESH_RATE";
535 case PRIORITY_APP_REQUEST_SIZE:
536 return "PRIORITY_APP_REQUEST_SIZE";
Long Ling73936632019-08-20 15:01:14 -0700537 case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
538 return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
Michael Wrighta3dab232019-02-22 16:54:21 +0000539 case PRIORITY_LOW_POWER_MODE:
540 return "PRIORITY_LOW_POWER_MODE";
541 default:
542 return Integer.toString(priority);
543 }
544 }
545
546 @Override
547 public String toString() {
548 return "Vote{"
549 + "width=" + width
550 + ", height=" + height
551 + ", minRefreshRate=" + minRefreshRate
552 + ", maxRefreshRate=" + maxRefreshRate
553 + "}";
554 }
555 }
556
557 private final class SettingsObserver extends ContentObserver {
Long Ling3b58eff2019-08-17 18:02:46 -0700558 private final Uri mPeakRefreshRateSetting =
Michael Wrighta3dab232019-02-22 16:54:21 +0000559 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
Long Ling3b58eff2019-08-17 18:02:46 -0700560 private final Uri mMinRefreshRateSetting =
561 Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE);
Michael Wrighta3dab232019-02-22 16:54:21 +0000562 private final Uri mLowPowerModeSetting =
563 Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
564
565 private final Context mContext;
Long Lingbb459a02019-08-08 16:05:44 -0700566 private float mDefaultPeakRefreshRate;
Michael Wrighta3dab232019-02-22 16:54:21 +0000567
568 SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
569 super(handler);
570 mContext = context;
571 mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
572 R.integer.config_defaultPeakRefreshRate);
573 }
574
575 public void observe() {
576 final ContentResolver cr = mContext.getContentResolver();
Long Ling3b58eff2019-08-17 18:02:46 -0700577 cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this,
578 UserHandle.USER_SYSTEM);
579 cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
Michael Wrighta3dab232019-02-22 16:54:21 +0000580 UserHandle.USER_SYSTEM);
581 cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
582 UserHandle.USER_SYSTEM);
Long Lingbb459a02019-08-08 16:05:44 -0700583
584 Float deviceConfigDefaultPeakRefresh =
585 mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
586 if (deviceConfigDefaultPeakRefresh != null) {
587 mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh;
588 }
589
Michael Wrighta3dab232019-02-22 16:54:21 +0000590 synchronized (mLock) {
591 updateRefreshRateSettingLocked();
592 updateLowPowerModeSettingLocked();
593 }
594 }
595
Long Lingbb459a02019-08-08 16:05:44 -0700596 public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
597 if (defaultPeakRefreshRate == null) {
598 defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
599 R.integer.config_defaultPeakRefreshRate);
600 }
601
602 if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) {
603 synchronized (mLock) {
604 mDefaultPeakRefreshRate = defaultPeakRefreshRate;
605 updateRefreshRateSettingLocked();
606 }
607 }
608 }
609
Michael Wrighta3dab232019-02-22 16:54:21 +0000610 @Override
611 public void onChange(boolean selfChange, Uri uri, int userId) {
612 synchronized (mLock) {
Long Ling3b58eff2019-08-17 18:02:46 -0700613 if (mPeakRefreshRateSetting.equals(uri)
614 || mMinRefreshRateSetting.equals(uri)) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000615 updateRefreshRateSettingLocked();
616 } else if (mLowPowerModeSetting.equals(uri)) {
617 updateLowPowerModeSettingLocked();
618 }
619 }
620 }
621
622 private void updateLowPowerModeSettingLocked() {
623 boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
624 Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
625 final Vote vote;
626 if (inLowPowerMode) {
627 vote = Vote.forRefreshRates(0f, 60f);
628 } else {
629 vote = null;
630 }
631 updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote);
Long Ling3b58eff2019-08-17 18:02:46 -0700632 mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
Michael Wrighta3dab232019-02-22 16:54:21 +0000633 }
634
635 private void updateRefreshRateSettingLocked() {
Long Ling3b58eff2019-08-17 18:02:46 -0700636 float minRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
637 Settings.System.MIN_REFRESH_RATE, 0f);
Michael Wrighta3dab232019-02-22 16:54:21 +0000638 float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
Adrian Salido41cc1862019-04-25 19:34:37 -0700639 Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate);
Long Ling3b58eff2019-08-17 18:02:46 -0700640
Long Ling73936632019-08-20 15:01:14 -0700641 updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE,
642 Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate)));
643 updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
644 Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY));
Long Ling3b58eff2019-08-17 18:02:46 -0700645
Long Ling3b58eff2019-08-17 18:02:46 -0700646 mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, peakRefreshRate);
Long Ling741bf5f2019-04-15 14:08:49 -0700647 }
648
Michael Wrighta3dab232019-02-22 16:54:21 +0000649 public void dumpLocked(PrintWriter pw) {
650 pw.println(" SettingsObserver");
651 pw.println(" mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate);
652 }
653 }
654
655 final class AppRequestObserver {
656 private SparseArray<Display.Mode> mAppRequestedModeByDisplay;
657
658 AppRequestObserver() {
659 mAppRequestedModeByDisplay = new SparseArray<>();
660 }
661
662 public void setAppRequestedMode(int displayId, int modeId) {
663 synchronized (mLock) {
664 setAppRequestedModeLocked(displayId, modeId);
665 }
666 }
667
668 private void setAppRequestedModeLocked(int displayId, int modeId) {
669 final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId);
670 if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
671 return;
672 }
673
674 final Vote refreshRateVote;
675 final Vote sizeVote;
676 if (requestedMode != null) {
677 mAppRequestedModeByDisplay.put(displayId, requestedMode);
678 float refreshRate = requestedMode.getRefreshRate();
679 refreshRateVote = Vote.forRefreshRates(refreshRate, refreshRate);
680 sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
681 requestedMode.getPhysicalHeight());
682 } else {
683 mAppRequestedModeByDisplay.remove(displayId);
684 refreshRateVote = null;
685 sizeVote = null;
686 }
Long Ling73936632019-08-20 15:01:14 -0700687
Michael Wrighta3dab232019-02-22 16:54:21 +0000688 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote);
689 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
690 return;
691 }
692
693 private Display.Mode findModeByIdLocked(int displayId, int modeId) {
694 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
695 if (modes == null) {
696 return null;
697 }
698 for (Display.Mode mode : modes) {
699 if (mode.getModeId() == modeId) {
700 return mode;
701 }
702 }
703 return null;
704 }
705
706 public void dumpLocked(PrintWriter pw) {
707 pw.println(" AppRequestObserver");
708 pw.println(" mAppRequestedModeByDisplay:");
709 for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
710 final int id = mAppRequestedModeByDisplay.keyAt(i);
711 final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
712 pw.println(" " + id + " -> " + mode);
713 }
714 }
715 }
716
717 private final class DisplayObserver implements DisplayManager.DisplayListener {
718 // Note that we can never call into DisplayManager or any of the non-POD classes it
719 // returns, while holding mLock since it may call into DMS, which might be simultaneously
720 // calling into us already holding its own lock.
721 private final Context mContext;
722 private final Handler mHandler;
723
724 DisplayObserver(Context context, Handler handler) {
725 mContext = context;
726 mHandler = handler;
727 }
728
729 public void observe() {
730 DisplayManager dm = mContext.getSystemService(DisplayManager.class);
731 dm.registerDisplayListener(this, mHandler);
732
733 // Populate existing displays
734 SparseArray<Display.Mode[]> modes = new SparseArray<>();
735 SparseArray<Display.Mode> defaultModes = new SparseArray<>();
736 DisplayInfo info = new DisplayInfo();
737 Display[] displays = dm.getDisplays();
738 for (Display d : displays) {
739 final int displayId = d.getDisplayId();
740 d.getDisplayInfo(info);
741 modes.put(displayId, info.supportedModes);
742 defaultModes.put(displayId, info.getDefaultMode());
743 }
744 synchronized (mLock) {
745 final int size = modes.size();
746 for (int i = 0; i < size; i++) {
747 mSupportedModesByDisplay.put(modes.keyAt(i), modes.valueAt(i));
748 mDefaultModeByDisplay.put(defaultModes.keyAt(i), defaultModes.valueAt(i));
749 }
750 }
751 }
752
753 @Override
754 public void onDisplayAdded(int displayId) {
755 updateDisplayModes(displayId);
756 }
757
758 @Override
759 public void onDisplayRemoved(int displayId) {
760 synchronized (mLock) {
761 mSupportedModesByDisplay.remove(displayId);
762 mDefaultModeByDisplay.remove(displayId);
763 }
764 }
765
766 @Override
767 public void onDisplayChanged(int displayId) {
768 updateDisplayModes(displayId);
Long Lingf3afe7d2019-08-05 12:19:42 -0700769 mBrightnessObserver.onDisplayChanged(displayId);
Michael Wrighta3dab232019-02-22 16:54:21 +0000770 }
771
772 private void updateDisplayModes(int displayId) {
773 Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
Michael Wright91c28382019-03-11 12:54:22 +0000774 if (d == null) {
775 // We can occasionally get a display added or changed event for a display that was
776 // subsequently removed, which means this returns null. Check this case and bail
777 // out early; if it gets re-attached we'll eventually get another call back for it.
778 return;
779 }
Michael Wrighta3dab232019-02-22 16:54:21 +0000780 DisplayInfo info = new DisplayInfo();
781 d.getDisplayInfo(info);
782 boolean changed = false;
783 synchronized (mLock) {
784 if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) {
785 mSupportedModesByDisplay.put(displayId, info.supportedModes);
786 changed = true;
787 }
788 if (!Objects.equals(mDefaultModeByDisplay.get(displayId), info.getDefaultMode())) {
789 changed = true;
790 mDefaultModeByDisplay.put(displayId, info.getDefaultMode());
791 }
792 if (changed) {
793 notifyAllowedModesChangedLocked();
794 }
795 }
796 }
797 }
Long Lingbc841b02019-07-03 16:43:15 -0700798
799 /**
800 * This class manages brightness threshold for switching between 60 hz and higher refresh rate.
801 * See more information at the definition of
802 * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and
803 * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
804 */
805 private class BrightnessObserver extends ContentObserver {
806 private final Uri mDisplayBrightnessSetting =
807 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
808
809 private final static int LIGHT_SENSOR_RATE_MS = 250;
Long Lingbb459a02019-08-08 16:05:44 -0700810 private int[] mDisplayBrightnessThresholds;
811 private int[] mAmbientBrightnessThresholds;
Long Lingbc841b02019-07-03 16:43:15 -0700812 // valid threshold if any item from the array >= 0
813 private boolean mShouldObserveDisplayChange;
814 private boolean mShouldObserveAmbientChange;
815
816 private SensorManager mSensorManager;
817 private Sensor mLightSensor;
Long Lingc1ee0422019-07-26 14:23:53 -0700818 private LightSensorEventListener mLightSensorListener = new LightSensorEventListener();
Long Lingbc841b02019-07-03 16:43:15 -0700819 // Take it as low brightness before valid sensor data comes
820 private float mAmbientLux = -1.0f;
821 private AmbientFilter mAmbientFilter;
822
823 private final Context mContext;
Long Lingbb459a02019-08-08 16:05:44 -0700824
825 // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak
Long Ling3b58eff2019-08-17 18:02:46 -0700826 // refresh rate changeable and low power mode off. After initialization, these states will
Long Lingbb459a02019-08-08 16:05:44 -0700827 // be updated from the same handler thread.
Long Lingbc841b02019-07-03 16:43:15 -0700828 private boolean mScreenOn = false;
Long Ling3b58eff2019-08-17 18:02:46 -0700829 private boolean mRefreshRateChangeable = false;
Long Lingbc841b02019-07-03 16:43:15 -0700830 private boolean mLowPowerModeEnabled = false;
831
Long Ling73936632019-08-20 15:01:14 -0700832 private int mRefreshRateInZone;
833
Long Lingbc841b02019-07-03 16:43:15 -0700834 BrightnessObserver(Context context, Handler handler) {
835 super(handler);
836 mContext = context;
837 mDisplayBrightnessThresholds = context.getResources().getIntArray(
838 R.array.config_brightnessThresholdsOfPeakRefreshRate);
839 mAmbientBrightnessThresholds = context.getResources().getIntArray(
840 R.array.config_ambientThresholdsOfPeakRefreshRate);
Long Lingbb459a02019-08-08 16:05:44 -0700841
Long Lingbc841b02019-07-03 16:43:15 -0700842 if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) {
843 throw new RuntimeException("display brightness threshold array and ambient "
844 + "brightness threshold array have different length");
845 }
Long Lingbc841b02019-07-03 16:43:15 -0700846 }
847
848 public void observe(SensorManager sensorManager) {
Long Lingbb459a02019-08-08 16:05:44 -0700849 mSensorManager = sensorManager;
Long Ling73936632019-08-20 15:01:14 -0700850
Long Lingbb459a02019-08-08 16:05:44 -0700851 // DeviceConfig is accessible after system ready.
852 int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds();
853 int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds();
854
855 if (brightnessThresholds != null && ambientThresholds != null
856 && brightnessThresholds.length == ambientThresholds.length) {
857 mDisplayBrightnessThresholds = brightnessThresholds;
858 mAmbientBrightnessThresholds = ambientThresholds;
Long Lingbc841b02019-07-03 16:43:15 -0700859 }
Long Ling73936632019-08-20 15:01:14 -0700860
861 mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone();
Long Lingbb459a02019-08-08 16:05:44 -0700862 restartObserver();
863 mDeviceConfigDisplaySettings.startListening();
Long Lingbc841b02019-07-03 16:43:15 -0700864 }
865
Long Ling3b58eff2019-08-17 18:02:46 -0700866 public void onRefreshRateSettingChangedLocked(float min, float max) {
867 boolean changeable = (max - min > 1f && max > 60f);
868 if (mRefreshRateChangeable != changeable) {
869 mRefreshRateChangeable = changeable;
Long Lingbc841b02019-07-03 16:43:15 -0700870 updateSensorStatus();
Long Ling3b58eff2019-08-17 18:02:46 -0700871 if (!changeable) {
872 // Revoke previous vote from BrightnessObserver
873 updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null);
874 }
Long Lingbc841b02019-07-03 16:43:15 -0700875 }
876 }
877
Long Ling3b58eff2019-08-17 18:02:46 -0700878 public void onLowPowerModeEnabledLocked(boolean b) {
Long Lingbb459a02019-08-08 16:05:44 -0700879 if (mLowPowerModeEnabled != b) {
Long Lingbc841b02019-07-03 16:43:15 -0700880 mLowPowerModeEnabled = b;
881 updateSensorStatus();
882 }
883 }
884
Long Lingbb459a02019-08-08 16:05:44 -0700885 public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds,
886 int[] ambientThresholds) {
887 if (brightnessThresholds != null && ambientThresholds != null
888 && brightnessThresholds.length == ambientThresholds.length) {
889 mDisplayBrightnessThresholds = brightnessThresholds;
890 mAmbientBrightnessThresholds = ambientThresholds;
891 } else {
892 // Invalid or empty. Use device default.
893 mDisplayBrightnessThresholds = mContext.getResources().getIntArray(
894 R.array.config_brightnessThresholdsOfPeakRefreshRate);
895 mAmbientBrightnessThresholds = mContext.getResources().getIntArray(
896 R.array.config_ambientThresholdsOfPeakRefreshRate);
Long Lingf3afe7d2019-08-05 12:19:42 -0700897 }
Long Lingbb459a02019-08-08 16:05:44 -0700898 restartObserver();
Long Lingf3afe7d2019-08-05 12:19:42 -0700899 }
900
Long Ling73936632019-08-20 15:01:14 -0700901 public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) {
902 if (refreshRate != mRefreshRateInZone) {
903 mRefreshRateInZone = refreshRate;
904 restartObserver();
905 }
906 }
907
Long Lingbc841b02019-07-03 16:43:15 -0700908 public void dumpLocked(PrintWriter pw) {
909 pw.println(" BrightnessObserver");
Long Ling73936632019-08-20 15:01:14 -0700910 pw.println(" mRefreshRateInZone: " + mRefreshRateInZone);
Long Lingbc841b02019-07-03 16:43:15 -0700911
912 for (int d: mDisplayBrightnessThresholds) {
913 pw.println(" mDisplayBrightnessThreshold: " + d);
914 }
915
916 for (int d: mAmbientBrightnessThresholds) {
917 pw.println(" mAmbientBrightnessThreshold: " + d);
918 }
919 }
920
Long Lingbb459a02019-08-08 16:05:44 -0700921 public void onDisplayChanged(int displayId) {
922 if (displayId == Display.DEFAULT_DISPLAY) {
923 onScreenOn(isDefaultDisplayOn());
924 }
925 }
926
Long Lingbc841b02019-07-03 16:43:15 -0700927 @Override
928 public void onChange(boolean selfChange, Uri uri, int userId) {
929 synchronized (mLock) {
930 onBrightnessChangedLocked();
931 }
932 }
933
Long Lingbb459a02019-08-08 16:05:44 -0700934 private void restartObserver() {
935 mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
936 mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
937
938 final ContentResolver cr = mContext.getContentResolver();
939 if (mShouldObserveDisplayChange) {
940 // Content Service does not check if an listener has already been registered.
941 // To ensure only one listener is registered, force an unregistration first.
942 cr.unregisterContentObserver(this);
943 cr.registerContentObserver(mDisplayBrightnessSetting,
944 false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
945 } else {
946 cr.unregisterContentObserver(this);
947 }
948
949 if (mShouldObserveAmbientChange) {
950 Resources resources = mContext.getResources();
951 String lightSensorType = resources.getString(
952 com.android.internal.R.string.config_displayLightSensorType);
953
954 Sensor lightSensor = null;
955 if (!TextUtils.isEmpty(lightSensorType)) {
956 List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
957 for (int i = 0; i < sensors.size(); i++) {
958 Sensor sensor = sensors.get(i);
959 if (lightSensorType.equals(sensor.getStringType())) {
960 lightSensor = sensor;
961 break;
962 }
963 }
964 }
965
966 if (lightSensor == null) {
967 lightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
968 }
969
970 if (lightSensor != null) {
971 final Resources res = mContext.getResources();
972
973 mAmbientFilter = DisplayWhiteBalanceFactory.createBrightnessFilter(res);
974 mLightSensor = lightSensor;
975
976 onScreenOn(isDefaultDisplayOn());
977 }
978 } else {
979 mAmbientFilter = null;
980 mLightSensor = null;
981 }
982
Long Ling3b58eff2019-08-17 18:02:46 -0700983 if (mRefreshRateChangeable) {
984 updateSensorStatus();
985 synchronized (mLock) {
986 onBrightnessChangedLocked();
987 }
Long Lingbb459a02019-08-08 16:05:44 -0700988 }
989 }
990
Long Lingbc841b02019-07-03 16:43:15 -0700991 /**
992 * Checks to see if at least one value is positive, in which case it is necessary to listen
993 * to value changes.
994 */
995 private boolean checkShouldObserve(int[] a) {
Long Ling73936632019-08-20 15:01:14 -0700996 if (mRefreshRateInZone <= 0) {
997 return false;
998 }
999
Long Lingbc841b02019-07-03 16:43:15 -07001000 for (int d: a) {
1001 if (d >= 0) {
1002 return true;
1003 }
1004 }
1005
1006 return false;
1007 }
1008
Long Ling73936632019-08-20 15:01:14 -07001009 private boolean isInsideZone(int brightness, float lux) {
1010 for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
1011 int disp = mDisplayBrightnessThresholds[i];
1012 int ambi = mAmbientBrightnessThresholds[i];
1013
1014 if (disp >= 0 && ambi >= 0) {
1015 if (brightness <= disp && mAmbientLux <= ambi) {
1016 return true;
1017 }
1018 } else if (disp >= 0) {
1019 if (brightness <= disp) {
1020 return true;
1021 }
1022 } else if (ambi >= 0) {
1023 if (mAmbientLux <= ambi) {
1024 return true;
1025 }
1026 }
1027 }
1028
1029 return false;
1030 }
1031
Long Lingbc841b02019-07-03 16:43:15 -07001032 private void onBrightnessChangedLocked() {
1033 int brightness = Settings.System.getInt(mContext.getContentResolver(),
1034 Settings.System.SCREEN_BRIGHTNESS, -1);
1035
1036 Vote vote = null;
Long Ling73936632019-08-20 15:01:14 -07001037 boolean insideZone = isInsideZone(brightness, mAmbientLux);
1038 if (insideZone) {
1039 vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone);
Long Lingbc841b02019-07-03 16:43:15 -07001040 }
1041
1042 if (DEBUG) {
1043 Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux +
Long Ling73936632019-08-20 15:01:14 -07001044 ", Vote " + vote);
Long Lingbc841b02019-07-03 16:43:15 -07001045 }
1046 updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
1047 }
1048
1049 private void onScreenOn(boolean on) {
Long Lingbc841b02019-07-03 16:43:15 -07001050 if (mScreenOn != on) {
1051 mScreenOn = on;
1052 updateSensorStatus();
1053 }
1054 }
1055
1056 private void updateSensorStatus() {
1057 if (mSensorManager == null || mLightSensorListener == null) {
1058 return;
1059 }
1060
Long Lingbb459a02019-08-08 16:05:44 -07001061 if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled
Long Ling3b58eff2019-08-17 18:02:46 -07001062 && mRefreshRateChangeable) {
Long Lingbc841b02019-07-03 16:43:15 -07001063 mSensorManager.registerListener(mLightSensorListener,
1064 mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
1065 } else {
Long Lingc1ee0422019-07-26 14:23:53 -07001066 mLightSensorListener.removeCallbacks();
Long Lingbc841b02019-07-03 16:43:15 -07001067 mSensorManager.unregisterListener(mLightSensorListener);
1068 }
1069 }
1070
Long Lingf3afe7d2019-08-05 12:19:42 -07001071 private boolean isDefaultDisplayOn() {
1072 final Display display = mContext.getSystemService(DisplayManager.class)
1073 .getDisplay(Display.DEFAULT_DISPLAY);
1074 return display.getState() != Display.STATE_OFF
1075 && mContext.getSystemService(PowerManager.class).isInteractive();
1076 }
1077
Long Lingc1ee0422019-07-26 14:23:53 -07001078 private final class LightSensorEventListener implements SensorEventListener {
1079 final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
1080 private float mLastSensorData;
1081
Long Lingbc841b02019-07-03 16:43:15 -07001082 @Override
1083 public void onSensorChanged(SensorEvent event) {
Long Lingc1ee0422019-07-26 14:23:53 -07001084 mLastSensorData = event.values[0];
1085 if (DEBUG) {
1086 Slog.d(TAG, "On sensor changed: " + mLastSensorData);
1087 }
Long Lingbc841b02019-07-03 16:43:15 -07001088
Long Lingc1ee0422019-07-26 14:23:53 -07001089 boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux);
1090 if (zoneChanged && mLastSensorData < mAmbientLux) {
1091 // Easier to see flicker at lower brightness environment. Forget the history to
1092 // get immediate response.
1093 mAmbientFilter.clear();
1094 }
1095
1096 long now = SystemClock.uptimeMillis();
1097 mAmbientFilter.addValue(now, mLastSensorData);
1098
1099 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1100 processSensorData(now);
1101
1102 if (zoneChanged && mLastSensorData > mAmbientLux) {
1103 // Sensor may not report new event if there is no brightness change.
1104 // Need to keep querying the temporal filter for the latest estimation,
1105 // until enter in higher lux zone or is interrupted by a new sensor event.
1106 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
Long Lingbc841b02019-07-03 16:43:15 -07001107 }
1108 }
1109
1110 @Override
1111 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1112 // Not used.
1113 }
Long Lingc1ee0422019-07-26 14:23:53 -07001114
1115 public void removeCallbacks() {
1116 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1117 }
1118
1119 private void processSensorData(long now) {
1120 mAmbientLux = mAmbientFilter.getEstimate(now);
1121
1122 synchronized (mLock) {
1123 onBrightnessChangedLocked();
1124 }
1125 }
1126
1127 private boolean isDifferentZone(float lux1, float lux2) {
1128 for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) {
1129 final float boundary = mAmbientBrightnessThresholds[z];
1130
1131 // Test each boundary. See if the current value and the new value are at
1132 // different sides.
1133 if ((lux1 <= boundary && lux2 > boundary)
1134 || (lux1 > boundary && lux2 <= boundary)) {
1135 return true;
1136 }
1137 }
1138
1139 return false;
1140 }
1141
1142 private Runnable mInjectSensorEventRunnable = new Runnable() {
1143 @Override
1144 public void run() {
1145 long now = SystemClock.uptimeMillis();
1146 // No need to really inject the last event into a temporal filter.
1147 processSensorData(now);
1148
1149 // Inject next event if there is a possible zone change.
1150 if (isDifferentZone(mLastSensorData, mAmbientLux)) {
1151 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
1152 }
1153 }
1154 };
Long Lingbb459a02019-08-08 16:05:44 -07001155 }
Long Lingbc841b02019-07-03 16:43:15 -07001156 }
Long Lingbb459a02019-08-08 16:05:44 -07001157
1158 private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
Long Lingbb459a02019-08-08 16:05:44 -07001159 public DeviceConfigDisplaySettings() {
1160 }
1161
1162 public void startListening() {
1163 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1164 BackgroundThread.getExecutor(), this);
1165 }
1166
1167 /*
1168 * Return null if no such property or wrong format (not comma separated integers).
1169 */
1170 public int[] getBrightnessThresholds() {
1171 return getIntArrayProperty(
Long Ling73936632019-08-20 15:01:14 -07001172 DisplayManager.DeviceConfig.
1173 KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS);
Long Lingbb459a02019-08-08 16:05:44 -07001174 }
1175
1176 /*
1177 * Return null if no such property or wrong format (not comma separated integers).
1178 */
1179 public int[] getAmbientThresholds() {
1180 return getIntArrayProperty(
Long Ling73936632019-08-20 15:01:14 -07001181 DisplayManager.DeviceConfig.
1182 KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS);
Long Lingbb459a02019-08-08 16:05:44 -07001183 }
1184
1185 /*
1186 * Return null if no such property
1187 */
1188 public Float getDefaultPeakRefreshRate() {
1189 float defaultPeakRefreshRate = DeviceConfig.getFloat(
1190 DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1191 DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
1192
1193 if (defaultPeakRefreshRate == -1) {
1194 return null;
1195 }
1196 return defaultPeakRefreshRate;
1197 }
1198
Long Ling73936632019-08-20 15:01:14 -07001199 public int getRefreshRateInZone() {
1200 int defaultRefreshRateInZone = mContext.getResources().getInteger(
1201 R.integer.config_defaultRefreshRateInZone);
1202
1203 int refreshRate = DeviceConfig.getInt(
1204 DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1205 DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
1206 defaultRefreshRateInZone);
1207
1208 return refreshRate;
1209 }
1210
Long Lingbb459a02019-08-08 16:05:44 -07001211 @Override
1212 public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
1213 int[] brightnessThresholds = getBrightnessThresholds();
1214 int[] ambientThresholds = getAmbientThresholds();
1215 Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
Long Ling73936632019-08-20 15:01:14 -07001216 int refreshRateInZone = getRefreshRateInZone();
Long Lingbb459a02019-08-08 16:05:44 -07001217
1218 mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED,
1219 new Pair<int[], int[]>(brightnessThresholds, ambientThresholds))
1220 .sendToTarget();
1221 mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
1222 defaultPeakRefreshRate).sendToTarget();
Long Ling73936632019-08-20 15:01:14 -07001223 mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone,
1224 0).sendToTarget();
Long Lingbb459a02019-08-08 16:05:44 -07001225 }
1226
1227 private int[] getIntArrayProperty(String prop) {
1228 String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
1229 null);
1230
1231 if (strArray != null) {
1232 return parseIntArray(strArray);
1233 }
1234
1235 return null;
1236 }
1237
1238 private int[] parseIntArray(@NonNull String strArray) {
1239 String[] items = strArray.split(",");
1240 int[] array = new int[items.length];
1241
1242 try {
1243 for (int i = 0; i < array.length; i++) {
1244 array[i] = Integer.parseInt(items[i]);
1245 }
1246 } catch (NumberFormatException e) {
1247 Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
1248 array = null;
1249 }
1250
1251 return array;
1252 }
1253 }
1254
Michael Wrighta3dab232019-02-22 16:54:21 +00001255}