blob: d24bd1aad1b1f067663aa4e247924590bdf48529 [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 Linga21479a2019-07-22 15:49:02 -070052import com.android.server.display.utils.AmbientFilter;
53import com.android.server.display.utils.AmbientFilterFactory;
Long Lingbc841b02019-07-03 16:43:15 -070054import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
Michael Wrighta3dab232019-02-22 16:54:21 +000055
56import java.io.PrintWriter;
57import java.util.ArrayList;
58import java.util.Arrays;
Long Lingbc841b02019-07-03 16:43:15 -070059import java.util.List;
Michael Wrighta3dab232019-02-22 16:54:21 +000060import java.util.Objects;
61
62/**
63 * The DisplayModeDirector is responsible for determining what modes are allowed to be
64 * automatically picked by the system based on system-wide and display-specific configuration.
65 */
66public class DisplayModeDirector {
67 private static final String TAG = "DisplayModeDirector";
68 private static final boolean DEBUG = false;
69
70 private static final int MSG_ALLOWED_MODES_CHANGED = 1;
Long Lingbb459a02019-08-08 16:05:44 -070071 private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
72 private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
Long Ling73936632019-08-20 15:01:14 -070073 private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4;
Michael Wrighta3dab232019-02-22 16:54:21 +000074
75 // Special ID used to indicate that given vote is to be applied globally, rather than to a
76 // specific display.
77 private static final int GLOBAL_ID = -1;
78
Michael Wrighta3dab232019-02-22 16:54:21 +000079 // The tolerance within which we consider something approximately equals.
80 private static final float EPSILON = 0.001f;
81
82 private final Object mLock = new Object();
83 private final Context mContext;
84
85 private final DisplayModeDirectorHandler mHandler;
86
87 // A map from the display ID to the collection of votes and their priority. The latter takes
88 // the form of another map from the priority to the vote itself so that each priority is
89 // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
90 private final SparseArray<SparseArray<Vote>> mVotesByDisplay;
91 // A map from the display ID to the supported modes on that display.
92 private final SparseArray<Display.Mode[]> mSupportedModesByDisplay;
93 // A map from the display ID to the default mode of that display.
94 private final SparseArray<Display.Mode> mDefaultModeByDisplay;
95
96 private final AppRequestObserver mAppRequestObserver;
97 private final SettingsObserver mSettingsObserver;
98 private final DisplayObserver mDisplayObserver;
Long Lingbc841b02019-07-03 16:43:15 -070099 private final BrightnessObserver mBrightnessObserver;
Michael Wrighta3dab232019-02-22 16:54:21 +0000100
Long Lingbb459a02019-08-08 16:05:44 -0700101 private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
Michael Wrighta3dab232019-02-22 16:54:21 +0000102 private Listener mListener;
103
104 public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
105 mContext = context;
106 mHandler = new DisplayModeDirectorHandler(handler.getLooper());
107 mVotesByDisplay = new SparseArray<>();
108 mSupportedModesByDisplay = new SparseArray<>();
109 mDefaultModeByDisplay = new SparseArray<>();
110 mAppRequestObserver = new AppRequestObserver();
111 mSettingsObserver = new SettingsObserver(context, handler);
112 mDisplayObserver = new DisplayObserver(context, handler);
Long Lingbc841b02019-07-03 16:43:15 -0700113 mBrightnessObserver = new BrightnessObserver(context, handler);
Long Lingbb459a02019-08-08 16:05:44 -0700114 mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
Michael Wrighta3dab232019-02-22 16:54:21 +0000115 }
116
117 /**
118 * Tells the DisplayModeDirector to update allowed votes and begin observing relevant system
119 * state.
120 *
121 * This has to be deferred because the object may be constructed before the rest of the system
122 * is ready.
123 */
Long Lingbc841b02019-07-03 16:43:15 -0700124 public void start(SensorManager sensorManager) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000125 mSettingsObserver.observe();
126 mDisplayObserver.observe();
127 mSettingsObserver.observe();
Long Lingbc841b02019-07-03 16:43:15 -0700128 mBrightnessObserver.observe(sensorManager);
Michael Wrighta3dab232019-02-22 16:54:21 +0000129 synchronized (mLock) {
130 // We may have a listener already registered before the call to start, so go ahead and
131 // notify them to pick up our newly initialized state.
132 notifyAllowedModesChangedLocked();
133 }
Long Lingbc841b02019-07-03 16:43:15 -0700134
Michael Wrighta3dab232019-02-22 16:54:21 +0000135 }
136
137 /**
138 * Calculates the modes the system is allowed to freely switch between based on global and
139 * display-specific constraints.
140 *
141 * @param displayId The display to query for.
142 * @return The IDs of the modes the system is allowed to freely switch between.
143 */
144 @NonNull
145 public int[] getAllowedModes(int displayId) {
146 synchronized (mLock) {
147 SparseArray<Vote> votes = getVotesLocked(displayId);
148 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
149 Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
150 if (modes == null || defaultMode == null) {
151 Slog.e(TAG, "Asked about unknown display, returning empty allowed set! (id="
152 + displayId + ")");
153 return new int[0];
154 }
155 return getAllowedModesLocked(votes, modes, defaultMode);
156 }
157 }
158
159 @NonNull
160 private SparseArray<Vote> getVotesLocked(int displayId) {
161 SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
162 final SparseArray<Vote> votes;
163 if (displayVotes != null) {
164 votes = displayVotes.clone();
165 } else {
166 votes = new SparseArray<>();
167 }
168
169 SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
170 if (globalVotes != null) {
171 for (int i = 0; i < globalVotes.size(); i++) {
172 int priority = globalVotes.keyAt(i);
173 if (votes.indexOfKey(priority) < 0) {
174 votes.put(priority, globalVotes.valueAt(i));
175 }
176 }
177 }
178 return votes;
179 }
180
181 @NonNull
182 private int[] getAllowedModesLocked(@NonNull SparseArray<Vote> votes,
183 @NonNull Display.Mode[] modes, @NonNull Display.Mode defaultMode) {
184 int lowestConsideredPriority = Vote.MIN_PRIORITY;
185 while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
186 float minRefreshRate = 0f;
187 float maxRefreshRate = Float.POSITIVE_INFINITY;
188 int height = Vote.INVALID_SIZE;
189 int width = Vote.INVALID_SIZE;
190
191 for (int priority = Vote.MAX_PRIORITY;
192 priority >= lowestConsideredPriority;
193 priority--) {
194 Vote vote = votes.get(priority);
195 if (vote == null) {
196 continue;
197 }
198 // For refresh rates, just use the tightest bounds of all the votes
199 minRefreshRate = Math.max(minRefreshRate, vote.minRefreshRate);
200 maxRefreshRate = Math.min(maxRefreshRate, vote.maxRefreshRate);
201 // For display size, use only the first vote we come across (i.e. the highest
202 // priority vote that includes the width / height).
203 if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE
204 && vote.height > 0 && vote.width > 0) {
205 width = vote.width;
206 height = vote.height;
207 }
208 }
209
210 // If we don't have anything specifying the width / height of the display, just use the
211 // default width and height. We don't want these switching out from underneath us since
212 // it's a pretty disruptive behavior.
213 if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) {
214 width = defaultMode.getPhysicalWidth();
215 height = defaultMode.getPhysicalHeight();
216 }
217
218 int[] availableModes =
219 filterModes(modes, width, height, minRefreshRate, maxRefreshRate);
220 if (availableModes.length > 0) {
221 if (DEBUG) {
222 Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
223 + " with lowest priority considered "
224 + Vote.priorityToString(lowestConsideredPriority)
225 + " and constraints: "
226 + "width=" + width
227 + ", height=" + height
228 + ", minRefreshRate=" + minRefreshRate
229 + ", maxRefreshRate=" + maxRefreshRate);
230 }
231 return availableModes;
232 }
233
234 if (DEBUG) {
235 Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
236 + Vote.priorityToString(lowestConsideredPriority)
237 + " and with the following constraints: "
238 + "width=" + width
239 + ", height=" + height
240 + ", minRefreshRate=" + minRefreshRate
241 + ", maxRefreshRate=" + maxRefreshRate);
242 }
243 // If we haven't found anything with the current set of votes, drop the current lowest
244 // priority vote.
245 lowestConsideredPriority++;
246 }
247
248 // If we still haven't found anything that matches our current set of votes, just fall back
249 // to the default mode.
250 return new int[] { defaultMode.getModeId() };
251 }
252
253 private int[] filterModes(Display.Mode[] supportedModes,
254 int width, int height, float minRefreshRate, float maxRefreshRate) {
255 ArrayList<Display.Mode> availableModes = new ArrayList<>();
256 for (Display.Mode mode : supportedModes) {
257 if (mode.getPhysicalWidth() != width || mode.getPhysicalHeight() != height) {
258 if (DEBUG) {
259 Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
260 + ": desiredWidth=" + width
261 + ": desiredHeight=" + height
262 + ": actualWidth=" + mode.getPhysicalWidth()
263 + ": actualHeight=" + mode.getPhysicalHeight());
264 }
265 continue;
266 }
267 final float refreshRate = mode.getRefreshRate();
268 // Some refresh rates are calculated based on frame timings, so they aren't *exactly*
269 // equal to expected refresh rate. Given that, we apply a bit of tolerance to this
270 // comparison.
271 if (refreshRate < (minRefreshRate - EPSILON)
272 || refreshRate > (maxRefreshRate + EPSILON)) {
273 if (DEBUG) {
274 Slog.w(TAG, "Discarding mode " + mode.getModeId()
275 + ", outside refresh rate bounds"
276 + ": minRefreshRate=" + minRefreshRate
277 + ", maxRefreshRate=" + maxRefreshRate
278 + ", modeRefreshRate=" + refreshRate);
279 }
280 continue;
281 }
282 availableModes.add(mode);
283 }
284 final int size = availableModes.size();
285 int[] availableModeIds = new int[size];
286 for (int i = 0; i < size; i++) {
287 availableModeIds[i] = availableModes.get(i).getModeId();
288 }
289 return availableModeIds;
290 }
291
292 /**
293 * Gets the observer responsible for application display mode requests.
294 */
295 @NonNull
296 public AppRequestObserver getAppRequestObserver() {
297 // We don't need to lock here because mAppRequestObserver is a final field, which is
298 // guaranteed to be visible on all threads after construction.
299 return mAppRequestObserver;
300 }
301
302 /**
303 * Sets the listener for changes to allowed display modes.
304 */
305 public void setListener(@Nullable Listener listener) {
306 synchronized (mLock) {
307 mListener = listener;
308 }
309 }
310
311 /**
312 * Print the object's state and debug information into the given stream.
313 *
314 * @param pw The stream to dump information to.
315 */
316 public void dump(PrintWriter pw) {
317 pw.println("DisplayModeDirector");
318 synchronized (mLock) {
319 pw.println(" mSupportedModesByDisplay:");
320 for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
321 final int id = mSupportedModesByDisplay.keyAt(i);
322 final Display.Mode[] modes = mSupportedModesByDisplay.valueAt(i);
323 pw.println(" " + id + " -> " + Arrays.toString(modes));
324 }
325 pw.println(" mDefaultModeByDisplay:");
326 for (int i = 0; i < mDefaultModeByDisplay.size(); i++) {
327 final int id = mDefaultModeByDisplay.keyAt(i);
328 final Display.Mode mode = mDefaultModeByDisplay.valueAt(i);
329 pw.println(" " + id + " -> " + mode);
330 }
331 pw.println(" mVotesByDisplay:");
332 for (int i = 0; i < mVotesByDisplay.size(); i++) {
333 pw.println(" " + mVotesByDisplay.keyAt(i) + ":");
334 SparseArray<Vote> votes = mVotesByDisplay.valueAt(i);
335 for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
336 Vote vote = votes.get(p);
337 if (vote == null) {
338 continue;
339 }
340 pw.println(" " + Vote.priorityToString(p) + " -> " + vote);
341 }
342 }
343 mSettingsObserver.dumpLocked(pw);
344 mAppRequestObserver.dumpLocked(pw);
Long Lingbc841b02019-07-03 16:43:15 -0700345 mBrightnessObserver.dumpLocked(pw);
Michael Wrighta3dab232019-02-22 16:54:21 +0000346 }
347 }
348
349 private void updateVoteLocked(int priority, Vote vote) {
350 updateVoteLocked(GLOBAL_ID, priority, vote);
351 }
352
353 private void updateVoteLocked(int displayId, int priority, Vote vote) {
354 if (DEBUG) {
355 Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
356 + ", priority=" + Vote.priorityToString(priority)
357 + ", vote=" + vote + ")");
358 }
359 if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
360 Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
361 + " priority=" + Vote.priorityToString(priority)
362 + ", vote=" + vote, new Throwable());
363 return;
364 }
365 final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId);
366
367 Vote currentVote = votes.get(priority);
368 if (vote != null) {
369 votes.put(priority, vote);
370 } else {
371 votes.remove(priority);
372 }
373
374 if (votes.size() == 0) {
375 if (DEBUG) {
376 Slog.i(TAG, "No votes left for display " + displayId + ", removing.");
377 }
378 mVotesByDisplay.remove(displayId);
379 }
380
381 notifyAllowedModesChangedLocked();
382 }
383
384 private void notifyAllowedModesChangedLocked() {
385 if (mListener != null && !mHandler.hasMessages(MSG_ALLOWED_MODES_CHANGED)) {
386 // We need to post this to a handler to avoid calling out while holding the lock
387 // since we know there are things that both listen for changes as well as provide
388 // information. If we did call out while holding the lock, then there's no guaranteed
389 // lock order and we run the real of risk deadlock.
390 Message msg = mHandler.obtainMessage(MSG_ALLOWED_MODES_CHANGED, mListener);
391 msg.sendToTarget();
392 }
393 }
394
395 private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) {
396 int index = mVotesByDisplay.indexOfKey(displayId);
397 if (mVotesByDisplay.indexOfKey(displayId) >= 0) {
398 return mVotesByDisplay.get(displayId);
399 } else {
400 SparseArray<Vote> votes = new SparseArray<>();
401 mVotesByDisplay.put(displayId, votes);
402 return votes;
403 }
404 }
405
406 /**
407 * Listens for changes to display mode coordination.
408 */
409 public interface Listener {
410 /**
411 * Called when the allowed display modes may have changed.
412 */
413 void onAllowedDisplayModesChanged();
414 }
415
Long Lingbb459a02019-08-08 16:05:44 -0700416 private final class DisplayModeDirectorHandler extends Handler {
Michael Wrighta3dab232019-02-22 16:54:21 +0000417 DisplayModeDirectorHandler(Looper looper) {
418 super(looper, null, true /*async*/);
419 }
420
421 @Override
422 public void handleMessage(Message msg) {
423 switch (msg.what) {
424 case MSG_ALLOWED_MODES_CHANGED:
425 Listener listener = (Listener) msg.obj;
426 listener.onAllowedDisplayModesChanged();
427 break;
Long Lingbb459a02019-08-08 16:05:44 -0700428
429 case MSG_BRIGHTNESS_THRESHOLDS_CHANGED:
430 Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
431
432 if (thresholds != null) {
433 mBrightnessObserver.onDeviceConfigThresholdsChanged(
434 thresholds.first, thresholds.second);
435 } else {
436 mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null);
437 }
438 break;
439
440 case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
441 Float defaultPeakRefreshRate = (Float) msg.obj;
442 mSettingsObserver.onDeviceConfigDefaultPeakRefreshRateChanged(
443 defaultPeakRefreshRate);
444 break;
Long Ling73936632019-08-20 15:01:14 -0700445
446 case MSG_REFRESH_RATE_IN_ZONE_CHANGED:
447 int refreshRateInZone = msg.arg1;
448 mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged(
449 refreshRateInZone);
450 break;
Michael Wrighta3dab232019-02-22 16:54:21 +0000451 }
452 }
453 }
454
455 private static final class Vote {
Long Ling73936632019-08-20 15:01:14 -0700456 // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
457 // If the higher voters result is a range, it will fix the rate to a single choice.
458 // It's used to avoid rate switch in certain conditions.
459 public static final int PRIORITY_LOW_BRIGHTNESS = 0;
460
461 // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
462 // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
463 public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 1;
464
465 // We split the app request into different priorities in case we can satisfy one desire
466 // without the other.
467
468 // Application can specify preferred refresh rate with below attrs.
469 // @see android.view.WindowManager.LayoutParams#preferredRefreshRate
470 // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
471 // System also forces some apps like blacklisted app to run at a lower refresh rate.
472 // @see android.R.array#config_highRefreshRateBlacklist
473 public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 2;
474 public static final int PRIORITY_APP_REQUEST_SIZE = 3;
475
476 // SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest
477 // of low priority voters. It votes [0, max(PEAK, MIN)]
478 public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 4;
479
480 // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
481 public static final int PRIORITY_LOW_POWER_MODE = 5;
Michael Wrighta3dab232019-02-22 16:54:21 +0000482
483 // Whenever a new priority is added, remember to update MIN_PRIORITY and/or MAX_PRIORITY as
484 // appropriate, as well as priorityToString.
485
Long Ling73936632019-08-20 15:01:14 -0700486 public static final int MIN_PRIORITY = PRIORITY_LOW_BRIGHTNESS;
Michael Wrighta3dab232019-02-22 16:54:21 +0000487 public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;
488
489 /**
490 * A value signifying an invalid width or height in a vote.
491 */
492 public static final int INVALID_SIZE = -1;
493
494 /**
495 * The requested width of the display in pixels, or INVALID_SIZE;
496 */
497 public final int width;
498 /**
499 * The requested height of the display in pixels, or INVALID_SIZE;
500 */
501 public final int height;
502
503 /**
504 * The lowest desired refresh rate.
505 */
506 public final float minRefreshRate;
507 /**
508 * The highest desired refresh rate.
509 */
510 public final float maxRefreshRate;
511
512 public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
513 return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate);
514 }
515
516 public static Vote forSize(int width, int height) {
517 return new Vote(width, height, 0f, Float.POSITIVE_INFINITY);
518 }
519
520 private Vote(int width, int height,
521 float minRefreshRate, float maxRefreshRate) {
522 this.width = width;
523 this.height = height;
524 this.minRefreshRate = minRefreshRate;
525 this.maxRefreshRate = maxRefreshRate;
526 }
527
528 public static String priorityToString(int priority) {
529 switch (priority) {
Long Ling73936632019-08-20 15:01:14 -0700530 case PRIORITY_LOW_BRIGHTNESS:
531 return "PRIORITY_LOW_BRIGHTNESS";
532 case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
533 return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
Michael Wrighta3dab232019-02-22 16:54:21 +0000534 case PRIORITY_APP_REQUEST_REFRESH_RATE:
535 return "PRIORITY_APP_REQUEST_REFRESH_RATE";
536 case PRIORITY_APP_REQUEST_SIZE:
537 return "PRIORITY_APP_REQUEST_SIZE";
Long Ling73936632019-08-20 15:01:14 -0700538 case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
539 return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
Michael Wrighta3dab232019-02-22 16:54:21 +0000540 case PRIORITY_LOW_POWER_MODE:
541 return "PRIORITY_LOW_POWER_MODE";
542 default:
543 return Integer.toString(priority);
544 }
545 }
546
547 @Override
548 public String toString() {
549 return "Vote{"
550 + "width=" + width
551 + ", height=" + height
552 + ", minRefreshRate=" + minRefreshRate
553 + ", maxRefreshRate=" + maxRefreshRate
554 + "}";
555 }
556 }
557
558 private final class SettingsObserver extends ContentObserver {
Long Ling3b58eff2019-08-17 18:02:46 -0700559 private final Uri mPeakRefreshRateSetting =
Michael Wrighta3dab232019-02-22 16:54:21 +0000560 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
Long Ling3b58eff2019-08-17 18:02:46 -0700561 private final Uri mMinRefreshRateSetting =
562 Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE);
Michael Wrighta3dab232019-02-22 16:54:21 +0000563 private final Uri mLowPowerModeSetting =
564 Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
565
566 private final Context mContext;
Long Lingbb459a02019-08-08 16:05:44 -0700567 private float mDefaultPeakRefreshRate;
Michael Wrighta3dab232019-02-22 16:54:21 +0000568
569 SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
570 super(handler);
571 mContext = context;
572 mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
573 R.integer.config_defaultPeakRefreshRate);
574 }
575
576 public void observe() {
577 final ContentResolver cr = mContext.getContentResolver();
Long Ling3b58eff2019-08-17 18:02:46 -0700578 cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this,
579 UserHandle.USER_SYSTEM);
580 cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
Michael Wrighta3dab232019-02-22 16:54:21 +0000581 UserHandle.USER_SYSTEM);
582 cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
583 UserHandle.USER_SYSTEM);
Long Lingbb459a02019-08-08 16:05:44 -0700584
585 Float deviceConfigDefaultPeakRefresh =
586 mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
587 if (deviceConfigDefaultPeakRefresh != null) {
588 mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh;
589 }
590
Michael Wrighta3dab232019-02-22 16:54:21 +0000591 synchronized (mLock) {
592 updateRefreshRateSettingLocked();
593 updateLowPowerModeSettingLocked();
594 }
595 }
596
Long Lingbb459a02019-08-08 16:05:44 -0700597 public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
598 if (defaultPeakRefreshRate == null) {
599 defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
600 R.integer.config_defaultPeakRefreshRate);
601 }
602
603 if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) {
604 synchronized (mLock) {
605 mDefaultPeakRefreshRate = defaultPeakRefreshRate;
606 updateRefreshRateSettingLocked();
607 }
608 }
609 }
610
Michael Wrighta3dab232019-02-22 16:54:21 +0000611 @Override
612 public void onChange(boolean selfChange, Uri uri, int userId) {
613 synchronized (mLock) {
Long Ling3b58eff2019-08-17 18:02:46 -0700614 if (mPeakRefreshRateSetting.equals(uri)
615 || mMinRefreshRateSetting.equals(uri)) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000616 updateRefreshRateSettingLocked();
617 } else if (mLowPowerModeSetting.equals(uri)) {
618 updateLowPowerModeSettingLocked();
619 }
620 }
621 }
622
623 private void updateLowPowerModeSettingLocked() {
624 boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
625 Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
626 final Vote vote;
627 if (inLowPowerMode) {
628 vote = Vote.forRefreshRates(0f, 60f);
629 } else {
630 vote = null;
631 }
632 updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote);
Long Ling3b58eff2019-08-17 18:02:46 -0700633 mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
Michael Wrighta3dab232019-02-22 16:54:21 +0000634 }
635
636 private void updateRefreshRateSettingLocked() {
Long Ling3b58eff2019-08-17 18:02:46 -0700637 float minRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
638 Settings.System.MIN_REFRESH_RATE, 0f);
Michael Wrighta3dab232019-02-22 16:54:21 +0000639 float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
Adrian Salido41cc1862019-04-25 19:34:37 -0700640 Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate);
Long Ling3b58eff2019-08-17 18:02:46 -0700641
Long Ling73936632019-08-20 15:01:14 -0700642 updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE,
643 Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate)));
644 updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
645 Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY));
Long Ling3b58eff2019-08-17 18:02:46 -0700646
Long Ling3b58eff2019-08-17 18:02:46 -0700647 mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, peakRefreshRate);
Long Ling741bf5f2019-04-15 14:08:49 -0700648 }
649
Michael Wrighta3dab232019-02-22 16:54:21 +0000650 public void dumpLocked(PrintWriter pw) {
651 pw.println(" SettingsObserver");
652 pw.println(" mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate);
653 }
654 }
655
656 final class AppRequestObserver {
657 private SparseArray<Display.Mode> mAppRequestedModeByDisplay;
658
659 AppRequestObserver() {
660 mAppRequestedModeByDisplay = new SparseArray<>();
661 }
662
663 public void setAppRequestedMode(int displayId, int modeId) {
664 synchronized (mLock) {
665 setAppRequestedModeLocked(displayId, modeId);
666 }
667 }
668
669 private void setAppRequestedModeLocked(int displayId, int modeId) {
670 final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId);
671 if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
672 return;
673 }
674
675 final Vote refreshRateVote;
676 final Vote sizeVote;
677 if (requestedMode != null) {
678 mAppRequestedModeByDisplay.put(displayId, requestedMode);
679 float refreshRate = requestedMode.getRefreshRate();
680 refreshRateVote = Vote.forRefreshRates(refreshRate, refreshRate);
681 sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
682 requestedMode.getPhysicalHeight());
683 } else {
684 mAppRequestedModeByDisplay.remove(displayId);
685 refreshRateVote = null;
686 sizeVote = null;
687 }
Long Ling73936632019-08-20 15:01:14 -0700688
Michael Wrighta3dab232019-02-22 16:54:21 +0000689 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote);
690 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
691 return;
692 }
693
694 private Display.Mode findModeByIdLocked(int displayId, int modeId) {
695 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
696 if (modes == null) {
697 return null;
698 }
699 for (Display.Mode mode : modes) {
700 if (mode.getModeId() == modeId) {
701 return mode;
702 }
703 }
704 return null;
705 }
706
707 public void dumpLocked(PrintWriter pw) {
708 pw.println(" AppRequestObserver");
709 pw.println(" mAppRequestedModeByDisplay:");
710 for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
711 final int id = mAppRequestedModeByDisplay.keyAt(i);
712 final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
713 pw.println(" " + id + " -> " + mode);
714 }
715 }
716 }
717
718 private final class DisplayObserver implements DisplayManager.DisplayListener {
719 // Note that we can never call into DisplayManager or any of the non-POD classes it
720 // returns, while holding mLock since it may call into DMS, which might be simultaneously
721 // calling into us already holding its own lock.
722 private final Context mContext;
723 private final Handler mHandler;
724
725 DisplayObserver(Context context, Handler handler) {
726 mContext = context;
727 mHandler = handler;
728 }
729
730 public void observe() {
731 DisplayManager dm = mContext.getSystemService(DisplayManager.class);
732 dm.registerDisplayListener(this, mHandler);
733
734 // Populate existing displays
735 SparseArray<Display.Mode[]> modes = new SparseArray<>();
736 SparseArray<Display.Mode> defaultModes = new SparseArray<>();
737 DisplayInfo info = new DisplayInfo();
738 Display[] displays = dm.getDisplays();
739 for (Display d : displays) {
740 final int displayId = d.getDisplayId();
741 d.getDisplayInfo(info);
742 modes.put(displayId, info.supportedModes);
743 defaultModes.put(displayId, info.getDefaultMode());
744 }
745 synchronized (mLock) {
746 final int size = modes.size();
747 for (int i = 0; i < size; i++) {
748 mSupportedModesByDisplay.put(modes.keyAt(i), modes.valueAt(i));
749 mDefaultModeByDisplay.put(defaultModes.keyAt(i), defaultModes.valueAt(i));
750 }
751 }
752 }
753
754 @Override
755 public void onDisplayAdded(int displayId) {
756 updateDisplayModes(displayId);
757 }
758
759 @Override
760 public void onDisplayRemoved(int displayId) {
761 synchronized (mLock) {
762 mSupportedModesByDisplay.remove(displayId);
763 mDefaultModeByDisplay.remove(displayId);
764 }
765 }
766
767 @Override
768 public void onDisplayChanged(int displayId) {
769 updateDisplayModes(displayId);
Long Lingf3afe7d2019-08-05 12:19:42 -0700770 mBrightnessObserver.onDisplayChanged(displayId);
Michael Wrighta3dab232019-02-22 16:54:21 +0000771 }
772
773 private void updateDisplayModes(int displayId) {
774 Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
Michael Wright91c28382019-03-11 12:54:22 +0000775 if (d == null) {
776 // We can occasionally get a display added or changed event for a display that was
777 // subsequently removed, which means this returns null. Check this case and bail
778 // out early; if it gets re-attached we'll eventually get another call back for it.
779 return;
780 }
Michael Wrighta3dab232019-02-22 16:54:21 +0000781 DisplayInfo info = new DisplayInfo();
782 d.getDisplayInfo(info);
783 boolean changed = false;
784 synchronized (mLock) {
785 if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) {
786 mSupportedModesByDisplay.put(displayId, info.supportedModes);
787 changed = true;
788 }
789 if (!Objects.equals(mDefaultModeByDisplay.get(displayId), info.getDefaultMode())) {
790 changed = true;
791 mDefaultModeByDisplay.put(displayId, info.getDefaultMode());
792 }
793 if (changed) {
794 notifyAllowedModesChangedLocked();
795 }
796 }
797 }
798 }
Long Lingbc841b02019-07-03 16:43:15 -0700799
800 /**
801 * This class manages brightness threshold for switching between 60 hz and higher refresh rate.
802 * See more information at the definition of
803 * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and
804 * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
805 */
806 private class BrightnessObserver extends ContentObserver {
807 private final Uri mDisplayBrightnessSetting =
808 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
809
810 private final static int LIGHT_SENSOR_RATE_MS = 250;
Long Lingbb459a02019-08-08 16:05:44 -0700811 private int[] mDisplayBrightnessThresholds;
812 private int[] mAmbientBrightnessThresholds;
Long Lingbc841b02019-07-03 16:43:15 -0700813 // valid threshold if any item from the array >= 0
814 private boolean mShouldObserveDisplayChange;
815 private boolean mShouldObserveAmbientChange;
816
817 private SensorManager mSensorManager;
818 private Sensor mLightSensor;
Long Lingc1ee0422019-07-26 14:23:53 -0700819 private LightSensorEventListener mLightSensorListener = new LightSensorEventListener();
Long Lingbc841b02019-07-03 16:43:15 -0700820 // Take it as low brightness before valid sensor data comes
821 private float mAmbientLux = -1.0f;
822 private AmbientFilter mAmbientFilter;
823
824 private final Context mContext;
Long Lingbb459a02019-08-08 16:05:44 -0700825
826 // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak
Long Ling3b58eff2019-08-17 18:02:46 -0700827 // refresh rate changeable and low power mode off. After initialization, these states will
Long Lingbb459a02019-08-08 16:05:44 -0700828 // be updated from the same handler thread.
Long Lingbc841b02019-07-03 16:43:15 -0700829 private boolean mScreenOn = false;
Long Ling3b58eff2019-08-17 18:02:46 -0700830 private boolean mRefreshRateChangeable = false;
Long Lingbc841b02019-07-03 16:43:15 -0700831 private boolean mLowPowerModeEnabled = false;
832
Long Ling73936632019-08-20 15:01:14 -0700833 private int mRefreshRateInZone;
834
Long Lingbc841b02019-07-03 16:43:15 -0700835 BrightnessObserver(Context context, Handler handler) {
836 super(handler);
837 mContext = context;
838 mDisplayBrightnessThresholds = context.getResources().getIntArray(
839 R.array.config_brightnessThresholdsOfPeakRefreshRate);
840 mAmbientBrightnessThresholds = context.getResources().getIntArray(
841 R.array.config_ambientThresholdsOfPeakRefreshRate);
Long Lingbb459a02019-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 Lingbb459a02019-08-08 16:05:44 -0700850 mSensorManager = sensorManager;
Long Ling73936632019-08-20 15:01:14 -0700851
Long Lingbb459a02019-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 Lingbb459a02019-08-08 16:05:44 -0700863 restartObserver();
864 mDeviceConfigDisplaySettings.startListening();
Long Lingbc841b02019-07-03 16:43:15 -0700865 }
866
Long Ling3b58eff2019-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 Ling3b58eff2019-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 Ling3b58eff2019-08-17 18:02:46 -0700879 public void onLowPowerModeEnabledLocked(boolean b) {
Long Lingbb459a02019-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 Lingbb459a02019-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);
Long Lingf3afe7d2019-08-05 12:19:42 -0700898 }
Long Lingbb459a02019-08-08 16:05:44 -0700899 restartObserver();
Long Lingf3afe7d2019-08-05 12:19:42 -0700900 }
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
Long Lingbb459a02019-08-08 16:05:44 -0700922 public void onDisplayChanged(int displayId) {
923 if (displayId == Display.DEFAULT_DISPLAY) {
924 onScreenOn(isDefaultDisplayOn());
925 }
926 }
927
Long Lingbc841b02019-07-03 16:43:15 -0700928 @Override
929 public void onChange(boolean selfChange, Uri uri, int userId) {
930 synchronized (mLock) {
931 onBrightnessChangedLocked();
932 }
933 }
934
Long Lingbb459a02019-08-08 16:05:44 -0700935 private void restartObserver() {
936 mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
937 mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
938
939 final ContentResolver cr = mContext.getContentResolver();
940 if (mShouldObserveDisplayChange) {
941 // Content Service does not check if an listener has already been registered.
942 // To ensure only one listener is registered, force an unregistration first.
943 cr.unregisterContentObserver(this);
944 cr.registerContentObserver(mDisplayBrightnessSetting,
945 false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
946 } else {
947 cr.unregisterContentObserver(this);
948 }
949
950 if (mShouldObserveAmbientChange) {
951 Resources resources = mContext.getResources();
952 String lightSensorType = resources.getString(
953 com.android.internal.R.string.config_displayLightSensorType);
954
955 Sensor lightSensor = null;
956 if (!TextUtils.isEmpty(lightSensorType)) {
957 List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
958 for (int i = 0; i < sensors.size(); i++) {
959 Sensor sensor = sensors.get(i);
960 if (lightSensorType.equals(sensor.getStringType())) {
961 lightSensor = sensor;
962 break;
963 }
964 }
965 }
966
967 if (lightSensor == null) {
968 lightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
969 }
970
971 if (lightSensor != null) {
972 final Resources res = mContext.getResources();
973
Long Linga21479a2019-07-22 15:49:02 -0700974 mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
Long Lingbb459a02019-08-08 16:05:44 -0700975 mLightSensor = lightSensor;
976
977 onScreenOn(isDefaultDisplayOn());
978 }
979 } else {
980 mAmbientFilter = null;
981 mLightSensor = null;
982 }
983
Long Ling3b58eff2019-08-17 18:02:46 -0700984 if (mRefreshRateChangeable) {
985 updateSensorStatus();
986 synchronized (mLock) {
987 onBrightnessChangedLocked();
988 }
Long Lingbb459a02019-08-08 16:05:44 -0700989 }
990 }
991
Long Lingbc841b02019-07-03 16:43:15 -0700992 /**
993 * Checks to see if at least one value is positive, in which case it is necessary to listen
994 * to value changes.
995 */
996 private boolean checkShouldObserve(int[] a) {
Long Ling73936632019-08-20 15:01:14 -0700997 if (mRefreshRateInZone <= 0) {
998 return false;
999 }
1000
Long Lingbc841b02019-07-03 16:43:15 -07001001 for (int d: a) {
1002 if (d >= 0) {
1003 return true;
1004 }
1005 }
1006
1007 return false;
1008 }
1009
Long Ling73936632019-08-20 15:01:14 -07001010 private boolean isInsideZone(int brightness, float lux) {
1011 for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
1012 int disp = mDisplayBrightnessThresholds[i];
1013 int ambi = mAmbientBrightnessThresholds[i];
1014
1015 if (disp >= 0 && ambi >= 0) {
1016 if (brightness <= disp && mAmbientLux <= ambi) {
1017 return true;
1018 }
1019 } else if (disp >= 0) {
1020 if (brightness <= disp) {
1021 return true;
1022 }
1023 } else if (ambi >= 0) {
1024 if (mAmbientLux <= ambi) {
1025 return true;
1026 }
1027 }
1028 }
1029
1030 return false;
1031 }
1032
Long Lingbc841b02019-07-03 16:43:15 -07001033 private void onBrightnessChangedLocked() {
1034 int brightness = Settings.System.getInt(mContext.getContentResolver(),
1035 Settings.System.SCREEN_BRIGHTNESS, -1);
1036
1037 Vote vote = null;
Long Ling73936632019-08-20 15:01:14 -07001038 boolean insideZone = isInsideZone(brightness, mAmbientLux);
1039 if (insideZone) {
1040 vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone);
Long Lingbc841b02019-07-03 16:43:15 -07001041 }
1042
1043 if (DEBUG) {
1044 Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux +
Long Ling73936632019-08-20 15:01:14 -07001045 ", Vote " + vote);
Long Lingbc841b02019-07-03 16:43:15 -07001046 }
1047 updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
1048 }
1049
1050 private void onScreenOn(boolean on) {
Long Lingbc841b02019-07-03 16:43:15 -07001051 if (mScreenOn != on) {
1052 mScreenOn = on;
1053 updateSensorStatus();
1054 }
1055 }
1056
1057 private void updateSensorStatus() {
1058 if (mSensorManager == null || mLightSensorListener == null) {
1059 return;
1060 }
1061
Long Lingbb459a02019-08-08 16:05:44 -07001062 if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled
Long Ling3b58eff2019-08-17 18:02:46 -07001063 && mRefreshRateChangeable) {
Long Lingbc841b02019-07-03 16:43:15 -07001064 mSensorManager.registerListener(mLightSensorListener,
1065 mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
1066 } else {
Long Lingc1ee0422019-07-26 14:23:53 -07001067 mLightSensorListener.removeCallbacks();
Long Lingbc841b02019-07-03 16:43:15 -07001068 mSensorManager.unregisterListener(mLightSensorListener);
1069 }
1070 }
1071
Long Lingf3afe7d2019-08-05 12:19:42 -07001072 private boolean isDefaultDisplayOn() {
1073 final Display display = mContext.getSystemService(DisplayManager.class)
1074 .getDisplay(Display.DEFAULT_DISPLAY);
1075 return display.getState() != Display.STATE_OFF
1076 && mContext.getSystemService(PowerManager.class).isInteractive();
1077 }
1078
Long Lingc1ee0422019-07-26 14:23:53 -07001079 private final class LightSensorEventListener implements SensorEventListener {
1080 final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
1081 private float mLastSensorData;
1082
Long Lingbc841b02019-07-03 16:43:15 -07001083 @Override
1084 public void onSensorChanged(SensorEvent event) {
Long Lingc1ee0422019-07-26 14:23:53 -07001085 mLastSensorData = event.values[0];
1086 if (DEBUG) {
1087 Slog.d(TAG, "On sensor changed: " + mLastSensorData);
1088 }
Long Lingbc841b02019-07-03 16:43:15 -07001089
Long Lingc1ee0422019-07-26 14:23:53 -07001090 boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux);
1091 if (zoneChanged && mLastSensorData < mAmbientLux) {
1092 // Easier to see flicker at lower brightness environment. Forget the history to
1093 // get immediate response.
1094 mAmbientFilter.clear();
1095 }
1096
1097 long now = SystemClock.uptimeMillis();
1098 mAmbientFilter.addValue(now, mLastSensorData);
1099
1100 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1101 processSensorData(now);
1102
1103 if (zoneChanged && mLastSensorData > mAmbientLux) {
1104 // Sensor may not report new event if there is no brightness change.
1105 // Need to keep querying the temporal filter for the latest estimation,
1106 // until enter in higher lux zone or is interrupted by a new sensor event.
1107 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
Long Lingbc841b02019-07-03 16:43:15 -07001108 }
1109 }
1110
1111 @Override
1112 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1113 // Not used.
1114 }
Long Lingc1ee0422019-07-26 14:23:53 -07001115
1116 public void removeCallbacks() {
1117 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1118 }
1119
1120 private void processSensorData(long now) {
1121 mAmbientLux = mAmbientFilter.getEstimate(now);
1122
1123 synchronized (mLock) {
1124 onBrightnessChangedLocked();
1125 }
1126 }
1127
1128 private boolean isDifferentZone(float lux1, float lux2) {
1129 for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) {
1130 final float boundary = mAmbientBrightnessThresholds[z];
1131
1132 // Test each boundary. See if the current value and the new value are at
1133 // different sides.
1134 if ((lux1 <= boundary && lux2 > boundary)
1135 || (lux1 > boundary && lux2 <= boundary)) {
1136 return true;
1137 }
1138 }
1139
1140 return false;
1141 }
1142
1143 private Runnable mInjectSensorEventRunnable = new Runnable() {
1144 @Override
1145 public void run() {
1146 long now = SystemClock.uptimeMillis();
1147 // No need to really inject the last event into a temporal filter.
1148 processSensorData(now);
1149
1150 // Inject next event if there is a possible zone change.
1151 if (isDifferentZone(mLastSensorData, mAmbientLux)) {
1152 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
1153 }
1154 }
1155 };
Long Lingbb459a02019-08-08 16:05:44 -07001156 }
Long Lingbc841b02019-07-03 16:43:15 -07001157 }
Long Lingbb459a02019-08-08 16:05:44 -07001158
1159 private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
Long Lingbb459a02019-08-08 16:05:44 -07001160 public DeviceConfigDisplaySettings() {
1161 }
1162
1163 public void startListening() {
1164 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1165 BackgroundThread.getExecutor(), this);
1166 }
1167
1168 /*
1169 * Return null if no such property or wrong format (not comma separated integers).
1170 */
1171 public int[] getBrightnessThresholds() {
1172 return getIntArrayProperty(
Long Ling73936632019-08-20 15:01:14 -07001173 DisplayManager.DeviceConfig.
1174 KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS);
Long Lingbb459a02019-08-08 16:05:44 -07001175 }
1176
1177 /*
1178 * Return null if no such property or wrong format (not comma separated integers).
1179 */
1180 public int[] getAmbientThresholds() {
1181 return getIntArrayProperty(
Long Ling73936632019-08-20 15:01:14 -07001182 DisplayManager.DeviceConfig.
1183 KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS);
Long Lingbb459a02019-08-08 16:05:44 -07001184 }
1185
1186 /*
1187 * Return null if no such property
1188 */
1189 public Float getDefaultPeakRefreshRate() {
1190 float defaultPeakRefreshRate = DeviceConfig.getFloat(
1191 DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1192 DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
1193
1194 if (defaultPeakRefreshRate == -1) {
1195 return null;
1196 }
1197 return defaultPeakRefreshRate;
1198 }
1199
Long Ling73936632019-08-20 15:01:14 -07001200 public int getRefreshRateInZone() {
1201 int defaultRefreshRateInZone = mContext.getResources().getInteger(
1202 R.integer.config_defaultRefreshRateInZone);
1203
1204 int refreshRate = DeviceConfig.getInt(
1205 DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1206 DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
1207 defaultRefreshRateInZone);
1208
1209 return refreshRate;
1210 }
1211
Long Lingbb459a02019-08-08 16:05:44 -07001212 @Override
1213 public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
1214 int[] brightnessThresholds = getBrightnessThresholds();
1215 int[] ambientThresholds = getAmbientThresholds();
1216 Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
Long Ling73936632019-08-20 15:01:14 -07001217 int refreshRateInZone = getRefreshRateInZone();
Long Lingbb459a02019-08-08 16:05:44 -07001218
1219 mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED,
1220 new Pair<int[], int[]>(brightnessThresholds, ambientThresholds))
1221 .sendToTarget();
1222 mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
1223 defaultPeakRefreshRate).sendToTarget();
Long Ling73936632019-08-20 15:01:14 -07001224 mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone,
1225 0).sendToTarget();
Long Lingbb459a02019-08-08 16:05:44 -07001226 }
1227
1228 private int[] getIntArrayProperty(String prop) {
1229 String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
1230 null);
1231
1232 if (strArray != null) {
1233 return parseIntArray(strArray);
1234 }
1235
1236 return null;
1237 }
1238
1239 private int[] parseIntArray(@NonNull String strArray) {
1240 String[] items = strArray.split(",");
1241 int[] array = new int[items.length];
1242
1243 try {
1244 for (int i = 0; i < array.length; i++) {
1245 array[i] = Integer.parseInt(items[i]);
1246 }
1247 } catch (NumberFormatException e) {
1248 Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
1249 array = null;
1250 }
1251
1252 return array;
1253 }
1254 }
1255
Michael Wrighta3dab232019-02-22 16:54:21 +00001256}