blob: 500a24282191b3e6c9d567f8335a45f28b44bf0a [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 Ling67aae3f2019-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 Ling67aae3f2019-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 Ling67aae3f2019-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 Ling67aae3f2019-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 Ling67aae3f2019-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 Ling67aae3f2019-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 Ling67aae3f2019-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 Ling67aae3f2019-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 Ling86ce8f72019-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 Ling86ce8f72019-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 Ling67aae3f2019-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 Ling86ce8f72019-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 Ling67aae3f2019-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 Ling67aae3f2019-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 Ling86ce8f72019-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 Ling86ce8f72019-08-17 18:02:46 -0700632 mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
Michael Wrighta3dab232019-02-22 16:54:21 +0000633 }
634
635 private void updateRefreshRateSettingLocked() {
Long Ling86ce8f72019-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 Ling86ce8f72019-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 Ling86ce8f72019-08-17 18:02:46 -0700645
Long Ling86ce8f72019-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);
769 }
770
771 private void updateDisplayModes(int displayId) {
772 Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
Michael Wright91c28382019-03-11 12:54:22 +0000773 if (d == null) {
774 // We can occasionally get a display added or changed event for a display that was
775 // subsequently removed, which means this returns null. Check this case and bail
776 // out early; if it gets re-attached we'll eventually get another call back for it.
777 return;
778 }
Michael Wrighta3dab232019-02-22 16:54:21 +0000779 DisplayInfo info = new DisplayInfo();
780 d.getDisplayInfo(info);
781 boolean changed = false;
782 synchronized (mLock) {
783 if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) {
784 mSupportedModesByDisplay.put(displayId, info.supportedModes);
785 changed = true;
786 }
787 if (!Objects.equals(mDefaultModeByDisplay.get(displayId), info.getDefaultMode())) {
788 changed = true;
789 mDefaultModeByDisplay.put(displayId, info.getDefaultMode());
790 }
791 if (changed) {
792 notifyAllowedModesChangedLocked();
793 }
794 }
795 }
796 }
Long Lingbc841b02019-07-03 16:43:15 -0700797
798 /**
799 * This class manages brightness threshold for switching between 60 hz and higher refresh rate.
800 * See more information at the definition of
801 * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and
802 * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
803 */
804 private class BrightnessObserver extends ContentObserver {
805 private final Uri mDisplayBrightnessSetting =
806 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
807
808 private final static int LIGHT_SENSOR_RATE_MS = 250;
Long Ling67aae3f2019-08-08 16:05:44 -0700809 private int[] mDisplayBrightnessThresholds;
810 private int[] mAmbientBrightnessThresholds;
Long Lingbc841b02019-07-03 16:43:15 -0700811 // valid threshold if any item from the array >= 0
812 private boolean mShouldObserveDisplayChange;
813 private boolean mShouldObserveAmbientChange;
814
815 private SensorManager mSensorManager;
816 private Sensor mLightSensor;
Long Lingc1ee0422019-07-26 14:23:53 -0700817 private LightSensorEventListener mLightSensorListener = new LightSensorEventListener();
Long Lingbc841b02019-07-03 16:43:15 -0700818 // Take it as low brightness before valid sensor data comes
819 private float mAmbientLux = -1.0f;
820 private AmbientFilter mAmbientFilter;
821
822 private final Context mContext;
Long Ling67aae3f2019-08-08 16:05:44 -0700823 private final ScreenStateReceiver mScreenStateReceiver;
Long Lingbc841b02019-07-03 16:43:15 -0700824
Long Ling67aae3f2019-08-08 16:05:44 -0700825 // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak
Long Ling86ce8f72019-08-17 18:02:46 -0700826 // refresh rate changeable and low power mode off. After initialization, these states will
Long Ling67aae3f2019-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 Ling86ce8f72019-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;
Long Ling67aae3f2019-08-08 16:05:44 -0700837 mScreenStateReceiver = new ScreenStateReceiver(mContext);
Long Lingbc841b02019-07-03 16:43:15 -0700838 mDisplayBrightnessThresholds = context.getResources().getIntArray(
839 R.array.config_brightnessThresholdsOfPeakRefreshRate);
840 mAmbientBrightnessThresholds = context.getResources().getIntArray(
841 R.array.config_ambientThresholdsOfPeakRefreshRate);
Long Ling67aae3f2019-08-08 16:05:44 -0700842
Long Lingbc841b02019-07-03 16:43:15 -0700843 if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) {
844 throw new RuntimeException("display brightness threshold array and ambient "
845 + "brightness threshold array have different length");
846 }
Long Lingbc841b02019-07-03 16:43:15 -0700847 }
848
849 public void observe(SensorManager sensorManager) {
Long Ling67aae3f2019-08-08 16:05:44 -0700850 mSensorManager = sensorManager;
Long Ling73936632019-08-20 15:01:14 -0700851
Long Ling67aae3f2019-08-08 16:05:44 -0700852 // DeviceConfig is accessible after system ready.
853 int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds();
854 int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds();
855
856 if (brightnessThresholds != null && ambientThresholds != null
857 && brightnessThresholds.length == ambientThresholds.length) {
858 mDisplayBrightnessThresholds = brightnessThresholds;
859 mAmbientBrightnessThresholds = ambientThresholds;
Long Lingbc841b02019-07-03 16:43:15 -0700860 }
Long Ling73936632019-08-20 15:01:14 -0700861
862 mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone();
Long Ling67aae3f2019-08-08 16:05:44 -0700863 restartObserver();
864 mDeviceConfigDisplaySettings.startListening();
Long Lingbc841b02019-07-03 16:43:15 -0700865 }
866
Long Ling86ce8f72019-08-17 18:02:46 -0700867 public void onRefreshRateSettingChangedLocked(float min, float max) {
868 boolean changeable = (max - min > 1f && max > 60f);
869 if (mRefreshRateChangeable != changeable) {
870 mRefreshRateChangeable = changeable;
Long Lingbc841b02019-07-03 16:43:15 -0700871 updateSensorStatus();
Long Ling86ce8f72019-08-17 18:02:46 -0700872 if (!changeable) {
873 // Revoke previous vote from BrightnessObserver
874 updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null);
875 }
Long Lingbc841b02019-07-03 16:43:15 -0700876 }
877 }
878
Long Ling86ce8f72019-08-17 18:02:46 -0700879 public void onLowPowerModeEnabledLocked(boolean b) {
Long Ling67aae3f2019-08-08 16:05:44 -0700880 if (mLowPowerModeEnabled != b) {
Long Lingbc841b02019-07-03 16:43:15 -0700881 mLowPowerModeEnabled = b;
882 updateSensorStatus();
883 }
884 }
885
Long Ling67aae3f2019-08-08 16:05:44 -0700886 public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds,
887 int[] ambientThresholds) {
888 if (brightnessThresholds != null && ambientThresholds != null
889 && brightnessThresholds.length == ambientThresholds.length) {
890 mDisplayBrightnessThresholds = brightnessThresholds;
891 mAmbientBrightnessThresholds = ambientThresholds;
892 } else {
893 // Invalid or empty. Use device default.
894 mDisplayBrightnessThresholds = mContext.getResources().getIntArray(
895 R.array.config_brightnessThresholdsOfPeakRefreshRate);
896 mAmbientBrightnessThresholds = mContext.getResources().getIntArray(
897 R.array.config_ambientThresholdsOfPeakRefreshRate);
898 }
899 restartObserver();
900 }
901
Long Ling73936632019-08-20 15:01:14 -0700902 public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) {
903 if (refreshRate != mRefreshRateInZone) {
904 mRefreshRateInZone = refreshRate;
905 restartObserver();
906 }
907 }
908
Long Lingbc841b02019-07-03 16:43:15 -0700909 public void dumpLocked(PrintWriter pw) {
910 pw.println(" BrightnessObserver");
Long Ling73936632019-08-20 15:01:14 -0700911 pw.println(" mRefreshRateInZone: " + mRefreshRateInZone);
Long Lingbc841b02019-07-03 16:43:15 -0700912
913 for (int d: mDisplayBrightnessThresholds) {
914 pw.println(" mDisplayBrightnessThreshold: " + d);
915 }
916
917 for (int d: mAmbientBrightnessThresholds) {
918 pw.println(" mAmbientBrightnessThreshold: " + d);
919 }
920 }
921
922 @Override
923 public void onChange(boolean selfChange, Uri uri, int userId) {
924 synchronized (mLock) {
Long Ling86ce8f72019-08-17 18:02:46 -0700925 if (mRefreshRateChangeable) {
926 onBrightnessChangedLocked();
927 }
Long Lingbc841b02019-07-03 16:43:15 -0700928 }
929 }
930
Long Ling67aae3f2019-08-08 16:05:44 -0700931 private void restartObserver() {
932 mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
933 mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
934
935 final ContentResolver cr = mContext.getContentResolver();
936 if (mShouldObserveDisplayChange) {
937 // Content Service does not check if an listener has already been registered.
938 // To ensure only one listener is registered, force an unregistration first.
939 cr.unregisterContentObserver(this);
940 cr.registerContentObserver(mDisplayBrightnessSetting,
941 false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
942 } else {
943 cr.unregisterContentObserver(this);
944 }
945
946 if (mShouldObserveAmbientChange) {
947 Resources resources = mContext.getResources();
948 String lightSensorType = resources.getString(
949 com.android.internal.R.string.config_displayLightSensorType);
950
951 Sensor lightSensor = null;
952 if (!TextUtils.isEmpty(lightSensorType)) {
953 List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
954 for (int i = 0; i < sensors.size(); i++) {
955 Sensor sensor = sensors.get(i);
956 if (lightSensorType.equals(sensor.getStringType())) {
957 lightSensor = sensor;
958 break;
959 }
960 }
961 }
962
963 if (lightSensor == null) {
964 lightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
965 }
966
967 if (lightSensor != null) {
968 final Resources res = mContext.getResources();
969
970 mAmbientFilter = DisplayWhiteBalanceFactory.createBrightnessFilter(res);
971 mLightSensor = lightSensor;
972
973 // Intent.ACTION_SCREEN_ON is not sticky. Check current screen status.
974 if (mContext.getSystemService(PowerManager.class).isInteractive()) {
975 onScreenOn(true);
976 }
977 mScreenStateReceiver.register();
978 }
979 } else {
980 mAmbientFilter = null;
981 mLightSensor = null;
982 mScreenStateReceiver.unregister();
983 }
984
Long Ling86ce8f72019-08-17 18:02:46 -0700985 if (mRefreshRateChangeable) {
986 updateSensorStatus();
987 synchronized (mLock) {
988 onBrightnessChangedLocked();
989 }
Long Ling67aae3f2019-08-08 16:05:44 -0700990 }
991 }
992
Long Lingbc841b02019-07-03 16:43:15 -0700993 /**
994 * Checks to see if at least one value is positive, in which case it is necessary to listen
995 * to value changes.
996 */
997 private boolean checkShouldObserve(int[] a) {
Long Ling73936632019-08-20 15:01:14 -0700998 if (mRefreshRateInZone <= 0) {
999 return false;
1000 }
1001
Long Lingbc841b02019-07-03 16:43:15 -07001002 for (int d: a) {
1003 if (d >= 0) {
1004 return true;
1005 }
1006 }
1007
1008 return false;
1009 }
1010
Long Ling73936632019-08-20 15:01:14 -07001011 private boolean isInsideZone(int brightness, float lux) {
1012 for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
1013 int disp = mDisplayBrightnessThresholds[i];
1014 int ambi = mAmbientBrightnessThresholds[i];
1015
1016 if (disp >= 0 && ambi >= 0) {
1017 if (brightness <= disp && mAmbientLux <= ambi) {
1018 return true;
1019 }
1020 } else if (disp >= 0) {
1021 if (brightness <= disp) {
1022 return true;
1023 }
1024 } else if (ambi >= 0) {
1025 if (mAmbientLux <= ambi) {
1026 return true;
1027 }
1028 }
1029 }
1030
1031 return false;
1032 }
1033
Long Lingbc841b02019-07-03 16:43:15 -07001034 private void onBrightnessChangedLocked() {
1035 int brightness = Settings.System.getInt(mContext.getContentResolver(),
1036 Settings.System.SCREEN_BRIGHTNESS, -1);
1037
1038 Vote vote = null;
Long Ling73936632019-08-20 15:01:14 -07001039 boolean insideZone = isInsideZone(brightness, mAmbientLux);
1040 if (insideZone) {
1041 vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone);
Long Lingbc841b02019-07-03 16:43:15 -07001042 }
1043
1044 if (DEBUG) {
1045 Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux +
Long Ling73936632019-08-20 15:01:14 -07001046 ", Vote " + vote);
Long Lingbc841b02019-07-03 16:43:15 -07001047 }
1048 updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
1049 }
1050
1051 private void onScreenOn(boolean on) {
1052 // Not check mShouldObserveAmbientChange because Screen status receiver is registered
1053 // only when it is true.
1054 if (mScreenOn != on) {
1055 mScreenOn = on;
1056 updateSensorStatus();
1057 }
1058 }
1059
1060 private void updateSensorStatus() {
1061 if (mSensorManager == null || mLightSensorListener == null) {
1062 return;
1063 }
1064
Long Ling67aae3f2019-08-08 16:05:44 -07001065 if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled
Long Ling86ce8f72019-08-17 18:02:46 -07001066 && mRefreshRateChangeable) {
Long Lingbc841b02019-07-03 16:43:15 -07001067 mSensorManager.registerListener(mLightSensorListener,
1068 mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
1069 } else {
Long Lingc1ee0422019-07-26 14:23:53 -07001070 mLightSensorListener.removeCallbacks();
Long Lingbc841b02019-07-03 16:43:15 -07001071 mSensorManager.unregisterListener(mLightSensorListener);
1072 }
1073 }
1074
Long Lingc1ee0422019-07-26 14:23:53 -07001075 private final class LightSensorEventListener implements SensorEventListener {
1076 final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
1077 private float mLastSensorData;
1078
Long Lingbc841b02019-07-03 16:43:15 -07001079 @Override
1080 public void onSensorChanged(SensorEvent event) {
Long Lingc1ee0422019-07-26 14:23:53 -07001081 mLastSensorData = event.values[0];
1082 if (DEBUG) {
1083 Slog.d(TAG, "On sensor changed: " + mLastSensorData);
1084 }
Long Lingbc841b02019-07-03 16:43:15 -07001085
Long Lingc1ee0422019-07-26 14:23:53 -07001086 boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux);
1087 if (zoneChanged && mLastSensorData < mAmbientLux) {
1088 // Easier to see flicker at lower brightness environment. Forget the history to
1089 // get immediate response.
1090 mAmbientFilter.clear();
1091 }
1092
1093 long now = SystemClock.uptimeMillis();
1094 mAmbientFilter.addValue(now, mLastSensorData);
1095
1096 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1097 processSensorData(now);
1098
1099 if (zoneChanged && mLastSensorData > mAmbientLux) {
1100 // Sensor may not report new event if there is no brightness change.
1101 // Need to keep querying the temporal filter for the latest estimation,
1102 // until enter in higher lux zone or is interrupted by a new sensor event.
1103 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
Long Lingbc841b02019-07-03 16:43:15 -07001104 }
1105 }
1106
1107 @Override
1108 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1109 // Not used.
1110 }
Long Lingc1ee0422019-07-26 14:23:53 -07001111
1112 public void removeCallbacks() {
1113 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1114 }
1115
1116 private void processSensorData(long now) {
1117 mAmbientLux = mAmbientFilter.getEstimate(now);
1118
1119 synchronized (mLock) {
1120 onBrightnessChangedLocked();
1121 }
1122 }
1123
1124 private boolean isDifferentZone(float lux1, float lux2) {
1125 for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) {
1126 final float boundary = mAmbientBrightnessThresholds[z];
1127
1128 // Test each boundary. See if the current value and the new value are at
1129 // different sides.
1130 if ((lux1 <= boundary && lux2 > boundary)
1131 || (lux1 > boundary && lux2 <= boundary)) {
1132 return true;
1133 }
1134 }
1135
1136 return false;
1137 }
1138
1139 private Runnable mInjectSensorEventRunnable = new Runnable() {
1140 @Override
1141 public void run() {
1142 long now = SystemClock.uptimeMillis();
1143 // No need to really inject the last event into a temporal filter.
1144 processSensorData(now);
1145
1146 // Inject next event if there is a possible zone change.
1147 if (isDifferentZone(mLastSensorData, mAmbientLux)) {
1148 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
1149 }
1150 }
1151 };
Long Lingbc841b02019-07-03 16:43:15 -07001152 };
1153
1154 private final class ScreenStateReceiver extends BroadcastReceiver {
Long Ling67aae3f2019-08-08 16:05:44 -07001155 final Context mContext;
Long Ling86ce8f72019-08-17 18:02:46 -07001156 boolean mRegistered;
1157
Long Lingbc841b02019-07-03 16:43:15 -07001158 public ScreenStateReceiver(Context context) {
Long Ling67aae3f2019-08-08 16:05:44 -07001159 mContext = context;
Long Lingbc841b02019-07-03 16:43:15 -07001160 }
1161
1162 @Override
1163 public void onReceive(Context context, Intent intent) {
1164 onScreenOn(Intent.ACTION_SCREEN_ON.equals(intent.getAction()));
1165 }
Long Ling67aae3f2019-08-08 16:05:44 -07001166
1167 public void register() {
Long Ling86ce8f72019-08-17 18:02:46 -07001168 if (!mRegistered) {
1169 IntentFilter filter = new IntentFilter();
1170 filter.addAction(Intent.ACTION_SCREEN_OFF);
1171 filter.addAction(Intent.ACTION_SCREEN_ON);
1172 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
1173 mContext.registerReceiver(this, filter, null, mHandler);
1174 mRegistered = true;
1175 }
Long Ling67aae3f2019-08-08 16:05:44 -07001176 }
1177
1178 public void unregister() {
Long Ling86ce8f72019-08-17 18:02:46 -07001179 if (mRegistered) {
1180 mContext.unregisterReceiver(this);
1181 mRegistered = false;
1182 }
Long Ling67aae3f2019-08-08 16:05:44 -07001183 }
Long Lingbc841b02019-07-03 16:43:15 -07001184 }
1185 }
Long Ling67aae3f2019-08-08 16:05:44 -07001186
1187 private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
Long Ling67aae3f2019-08-08 16:05:44 -07001188 public DeviceConfigDisplaySettings() {
1189 }
1190
1191 public void startListening() {
1192 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1193 BackgroundThread.getExecutor(), this);
1194 }
1195
1196 /*
1197 * Return null if no such property or wrong format (not comma separated integers).
1198 */
1199 public int[] getBrightnessThresholds() {
1200 return getIntArrayProperty(
Long Ling73936632019-08-20 15:01:14 -07001201 DisplayManager.DeviceConfig.
1202 KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS);
Long Ling67aae3f2019-08-08 16:05:44 -07001203 }
1204
1205 /*
1206 * Return null if no such property or wrong format (not comma separated integers).
1207 */
1208 public int[] getAmbientThresholds() {
1209 return getIntArrayProperty(
Long Ling73936632019-08-20 15:01:14 -07001210 DisplayManager.DeviceConfig.
1211 KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS);
Long Ling67aae3f2019-08-08 16:05:44 -07001212 }
1213
1214 /*
1215 * Return null if no such property
1216 */
1217 public Float getDefaultPeakRefreshRate() {
1218 float defaultPeakRefreshRate = DeviceConfig.getFloat(
1219 DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1220 DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
1221
1222 if (defaultPeakRefreshRate == -1) {
1223 return null;
1224 }
1225 return defaultPeakRefreshRate;
1226 }
1227
Long Ling73936632019-08-20 15:01:14 -07001228 public int getRefreshRateInZone() {
1229 int defaultRefreshRateInZone = mContext.getResources().getInteger(
1230 R.integer.config_defaultRefreshRateInZone);
1231
1232 int refreshRate = DeviceConfig.getInt(
1233 DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1234 DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
1235 defaultRefreshRateInZone);
1236
1237 return refreshRate;
1238 }
1239
Long Ling67aae3f2019-08-08 16:05:44 -07001240 @Override
1241 public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
1242 int[] brightnessThresholds = getBrightnessThresholds();
1243 int[] ambientThresholds = getAmbientThresholds();
1244 Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
Long Ling73936632019-08-20 15:01:14 -07001245 int refreshRateInZone = getRefreshRateInZone();
Long Ling67aae3f2019-08-08 16:05:44 -07001246
1247 mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED,
1248 new Pair<int[], int[]>(brightnessThresholds, ambientThresholds))
1249 .sendToTarget();
1250 mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
1251 defaultPeakRefreshRate).sendToTarget();
Long Ling73936632019-08-20 15:01:14 -07001252 mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone,
1253 0).sendToTarget();
Long Ling67aae3f2019-08-08 16:05:44 -07001254 }
1255
1256 private int[] getIntArrayProperty(String prop) {
1257 String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
1258 null);
1259
1260 if (strArray != null) {
1261 return parseIntArray(strArray);
1262 }
1263
1264 return null;
1265 }
1266
1267 private int[] parseIntArray(@NonNull String strArray) {
1268 String[] items = strArray.split(",");
1269 int[] array = new int[items.length];
1270
1271 try {
1272 for (int i = 0; i < array.length; i++) {
1273 array[i] = Integer.parseInt(items[i]);
1274 }
1275 } catch (NumberFormatException e) {
1276 Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
1277 array = null;
1278 }
1279
1280 return array;
1281 }
1282 }
1283
Michael Wrighta3dab232019-02-22 16:54:21 +00001284}