blob: 78a48dac6fb5654d8171e3b325434667c48a148c [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;
Michael Wrighta3dab232019-02-22 16:54:21 +000041import android.provider.Settings;
Long Lingbc841b02019-07-03 16:43:15 -070042import android.text.TextUtils;
Michael Wrighta3dab232019-02-22 16:54:21 +000043import android.util.Slog;
44import android.util.SparseArray;
45import android.view.Display;
46import android.view.DisplayInfo;
47
48import com.android.internal.R;
Long Lingbc841b02019-07-03 16:43:15 -070049import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
50import com.android.server.display.whitebalance.AmbientFilter;
Michael Wrighta3dab232019-02-22 16:54:21 +000051
52import java.io.PrintWriter;
53import java.util.ArrayList;
54import java.util.Arrays;
Long Lingbc841b02019-07-03 16:43:15 -070055import java.util.List;
Michael Wrighta3dab232019-02-22 16:54:21 +000056import java.util.Objects;
57
58/**
59 * The DisplayModeDirector is responsible for determining what modes are allowed to be
60 * automatically picked by the system based on system-wide and display-specific configuration.
61 */
62public class DisplayModeDirector {
63 private static final String TAG = "DisplayModeDirector";
64 private static final boolean DEBUG = false;
65
66 private static final int MSG_ALLOWED_MODES_CHANGED = 1;
67
68 // Special ID used to indicate that given vote is to be applied globally, rather than to a
69 // specific display.
70 private static final int GLOBAL_ID = -1;
71
Michael Wrighta3dab232019-02-22 16:54:21 +000072 // The tolerance within which we consider something approximately equals.
73 private static final float EPSILON = 0.001f;
74
75 private final Object mLock = new Object();
76 private final Context mContext;
77
78 private final DisplayModeDirectorHandler mHandler;
79
80 // A map from the display ID to the collection of votes and their priority. The latter takes
81 // the form of another map from the priority to the vote itself so that each priority is
82 // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
83 private final SparseArray<SparseArray<Vote>> mVotesByDisplay;
84 // A map from the display ID to the supported modes on that display.
85 private final SparseArray<Display.Mode[]> mSupportedModesByDisplay;
86 // A map from the display ID to the default mode of that display.
87 private final SparseArray<Display.Mode> mDefaultModeByDisplay;
88
89 private final AppRequestObserver mAppRequestObserver;
90 private final SettingsObserver mSettingsObserver;
91 private final DisplayObserver mDisplayObserver;
Long Lingbc841b02019-07-03 16:43:15 -070092 private final BrightnessObserver mBrightnessObserver;
Michael Wrighta3dab232019-02-22 16:54:21 +000093
94 private Listener mListener;
95
96 public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
97 mContext = context;
98 mHandler = new DisplayModeDirectorHandler(handler.getLooper());
99 mVotesByDisplay = new SparseArray<>();
100 mSupportedModesByDisplay = new SparseArray<>();
101 mDefaultModeByDisplay = new SparseArray<>();
102 mAppRequestObserver = new AppRequestObserver();
103 mSettingsObserver = new SettingsObserver(context, handler);
104 mDisplayObserver = new DisplayObserver(context, handler);
Long Lingbc841b02019-07-03 16:43:15 -0700105 mBrightnessObserver = new BrightnessObserver(context, handler);
106
Michael Wrighta3dab232019-02-22 16:54:21 +0000107 }
108
109 /**
110 * Tells the DisplayModeDirector to update allowed votes and begin observing relevant system
111 * state.
112 *
113 * This has to be deferred because the object may be constructed before the rest of the system
114 * is ready.
115 */
Long Lingbc841b02019-07-03 16:43:15 -0700116 public void start(SensorManager sensorManager) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000117 mSettingsObserver.observe();
118 mDisplayObserver.observe();
119 mSettingsObserver.observe();
Long Lingbc841b02019-07-03 16:43:15 -0700120 mBrightnessObserver.observe(sensorManager);
Michael Wrighta3dab232019-02-22 16:54:21 +0000121 synchronized (mLock) {
122 // We may have a listener already registered before the call to start, so go ahead and
123 // notify them to pick up our newly initialized state.
124 notifyAllowedModesChangedLocked();
125 }
Long Lingbc841b02019-07-03 16:43:15 -0700126
Michael Wrighta3dab232019-02-22 16:54:21 +0000127 }
128
129 /**
130 * Calculates the modes the system is allowed to freely switch between based on global and
131 * display-specific constraints.
132 *
133 * @param displayId The display to query for.
134 * @return The IDs of the modes the system is allowed to freely switch between.
135 */
136 @NonNull
137 public int[] getAllowedModes(int displayId) {
138 synchronized (mLock) {
139 SparseArray<Vote> votes = getVotesLocked(displayId);
140 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
141 Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
142 if (modes == null || defaultMode == null) {
143 Slog.e(TAG, "Asked about unknown display, returning empty allowed set! (id="
144 + displayId + ")");
145 return new int[0];
146 }
147 return getAllowedModesLocked(votes, modes, defaultMode);
148 }
149 }
150
151 @NonNull
152 private SparseArray<Vote> getVotesLocked(int displayId) {
153 SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
154 final SparseArray<Vote> votes;
155 if (displayVotes != null) {
156 votes = displayVotes.clone();
157 } else {
158 votes = new SparseArray<>();
159 }
160
161 SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
162 if (globalVotes != null) {
163 for (int i = 0; i < globalVotes.size(); i++) {
164 int priority = globalVotes.keyAt(i);
165 if (votes.indexOfKey(priority) < 0) {
166 votes.put(priority, globalVotes.valueAt(i));
167 }
168 }
169 }
170 return votes;
171 }
172
173 @NonNull
174 private int[] getAllowedModesLocked(@NonNull SparseArray<Vote> votes,
175 @NonNull Display.Mode[] modes, @NonNull Display.Mode defaultMode) {
176 int lowestConsideredPriority = Vote.MIN_PRIORITY;
177 while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
178 float minRefreshRate = 0f;
179 float maxRefreshRate = Float.POSITIVE_INFINITY;
180 int height = Vote.INVALID_SIZE;
181 int width = Vote.INVALID_SIZE;
182
183 for (int priority = Vote.MAX_PRIORITY;
184 priority >= lowestConsideredPriority;
185 priority--) {
186 Vote vote = votes.get(priority);
187 if (vote == null) {
188 continue;
189 }
190 // For refresh rates, just use the tightest bounds of all the votes
191 minRefreshRate = Math.max(minRefreshRate, vote.minRefreshRate);
192 maxRefreshRate = Math.min(maxRefreshRate, vote.maxRefreshRate);
193 // For display size, use only the first vote we come across (i.e. the highest
194 // priority vote that includes the width / height).
195 if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE
196 && vote.height > 0 && vote.width > 0) {
197 width = vote.width;
198 height = vote.height;
199 }
200 }
201
202 // If we don't have anything specifying the width / height of the display, just use the
203 // default width and height. We don't want these switching out from underneath us since
204 // it's a pretty disruptive behavior.
205 if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) {
206 width = defaultMode.getPhysicalWidth();
207 height = defaultMode.getPhysicalHeight();
208 }
209
210 int[] availableModes =
211 filterModes(modes, width, height, minRefreshRate, maxRefreshRate);
212 if (availableModes.length > 0) {
213 if (DEBUG) {
214 Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
215 + " with lowest priority considered "
216 + Vote.priorityToString(lowestConsideredPriority)
217 + " and constraints: "
218 + "width=" + width
219 + ", height=" + height
220 + ", minRefreshRate=" + minRefreshRate
221 + ", maxRefreshRate=" + maxRefreshRate);
222 }
223 return availableModes;
224 }
225
226 if (DEBUG) {
227 Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
228 + Vote.priorityToString(lowestConsideredPriority)
229 + " and with the following constraints: "
230 + "width=" + width
231 + ", height=" + height
232 + ", minRefreshRate=" + minRefreshRate
233 + ", maxRefreshRate=" + maxRefreshRate);
234 }
235 // If we haven't found anything with the current set of votes, drop the current lowest
236 // priority vote.
237 lowestConsideredPriority++;
238 }
239
240 // If we still haven't found anything that matches our current set of votes, just fall back
241 // to the default mode.
242 return new int[] { defaultMode.getModeId() };
243 }
244
245 private int[] filterModes(Display.Mode[] supportedModes,
246 int width, int height, float minRefreshRate, float maxRefreshRate) {
247 ArrayList<Display.Mode> availableModes = new ArrayList<>();
248 for (Display.Mode mode : supportedModes) {
249 if (mode.getPhysicalWidth() != width || mode.getPhysicalHeight() != height) {
250 if (DEBUG) {
251 Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
252 + ": desiredWidth=" + width
253 + ": desiredHeight=" + height
254 + ": actualWidth=" + mode.getPhysicalWidth()
255 + ": actualHeight=" + mode.getPhysicalHeight());
256 }
257 continue;
258 }
259 final float refreshRate = mode.getRefreshRate();
260 // Some refresh rates are calculated based on frame timings, so they aren't *exactly*
261 // equal to expected refresh rate. Given that, we apply a bit of tolerance to this
262 // comparison.
263 if (refreshRate < (minRefreshRate - EPSILON)
264 || refreshRate > (maxRefreshRate + EPSILON)) {
265 if (DEBUG) {
266 Slog.w(TAG, "Discarding mode " + mode.getModeId()
267 + ", outside refresh rate bounds"
268 + ": minRefreshRate=" + minRefreshRate
269 + ", maxRefreshRate=" + maxRefreshRate
270 + ", modeRefreshRate=" + refreshRate);
271 }
272 continue;
273 }
274 availableModes.add(mode);
275 }
276 final int size = availableModes.size();
277 int[] availableModeIds = new int[size];
278 for (int i = 0; i < size; i++) {
279 availableModeIds[i] = availableModes.get(i).getModeId();
280 }
281 return availableModeIds;
282 }
283
284 /**
285 * Gets the observer responsible for application display mode requests.
286 */
287 @NonNull
288 public AppRequestObserver getAppRequestObserver() {
289 // We don't need to lock here because mAppRequestObserver is a final field, which is
290 // guaranteed to be visible on all threads after construction.
291 return mAppRequestObserver;
292 }
293
294 /**
295 * Sets the listener for changes to allowed display modes.
296 */
297 public void setListener(@Nullable Listener listener) {
298 synchronized (mLock) {
299 mListener = listener;
300 }
301 }
302
303 /**
304 * Print the object's state and debug information into the given stream.
305 *
306 * @param pw The stream to dump information to.
307 */
308 public void dump(PrintWriter pw) {
309 pw.println("DisplayModeDirector");
310 synchronized (mLock) {
311 pw.println(" mSupportedModesByDisplay:");
312 for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
313 final int id = mSupportedModesByDisplay.keyAt(i);
314 final Display.Mode[] modes = mSupportedModesByDisplay.valueAt(i);
315 pw.println(" " + id + " -> " + Arrays.toString(modes));
316 }
317 pw.println(" mDefaultModeByDisplay:");
318 for (int i = 0; i < mDefaultModeByDisplay.size(); i++) {
319 final int id = mDefaultModeByDisplay.keyAt(i);
320 final Display.Mode mode = mDefaultModeByDisplay.valueAt(i);
321 pw.println(" " + id + " -> " + mode);
322 }
323 pw.println(" mVotesByDisplay:");
324 for (int i = 0; i < mVotesByDisplay.size(); i++) {
325 pw.println(" " + mVotesByDisplay.keyAt(i) + ":");
326 SparseArray<Vote> votes = mVotesByDisplay.valueAt(i);
327 for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
328 Vote vote = votes.get(p);
329 if (vote == null) {
330 continue;
331 }
332 pw.println(" " + Vote.priorityToString(p) + " -> " + vote);
333 }
334 }
335 mSettingsObserver.dumpLocked(pw);
336 mAppRequestObserver.dumpLocked(pw);
Long Lingbc841b02019-07-03 16:43:15 -0700337 mBrightnessObserver.dumpLocked(pw);
Michael Wrighta3dab232019-02-22 16:54:21 +0000338 }
339 }
340
341 private void updateVoteLocked(int priority, Vote vote) {
342 updateVoteLocked(GLOBAL_ID, priority, vote);
343 }
344
345 private void updateVoteLocked(int displayId, int priority, Vote vote) {
346 if (DEBUG) {
347 Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
348 + ", priority=" + Vote.priorityToString(priority)
349 + ", vote=" + vote + ")");
350 }
351 if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
352 Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
353 + " priority=" + Vote.priorityToString(priority)
354 + ", vote=" + vote, new Throwable());
355 return;
356 }
357 final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId);
358
359 Vote currentVote = votes.get(priority);
360 if (vote != null) {
361 votes.put(priority, vote);
362 } else {
363 votes.remove(priority);
364 }
365
366 if (votes.size() == 0) {
367 if (DEBUG) {
368 Slog.i(TAG, "No votes left for display " + displayId + ", removing.");
369 }
370 mVotesByDisplay.remove(displayId);
371 }
372
373 notifyAllowedModesChangedLocked();
374 }
375
376 private void notifyAllowedModesChangedLocked() {
377 if (mListener != null && !mHandler.hasMessages(MSG_ALLOWED_MODES_CHANGED)) {
378 // We need to post this to a handler to avoid calling out while holding the lock
379 // since we know there are things that both listen for changes as well as provide
380 // information. If we did call out while holding the lock, then there's no guaranteed
381 // lock order and we run the real of risk deadlock.
382 Message msg = mHandler.obtainMessage(MSG_ALLOWED_MODES_CHANGED, mListener);
383 msg.sendToTarget();
384 }
385 }
386
387 private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) {
388 int index = mVotesByDisplay.indexOfKey(displayId);
389 if (mVotesByDisplay.indexOfKey(displayId) >= 0) {
390 return mVotesByDisplay.get(displayId);
391 } else {
392 SparseArray<Vote> votes = new SparseArray<>();
393 mVotesByDisplay.put(displayId, votes);
394 return votes;
395 }
396 }
397
398 /**
399 * Listens for changes to display mode coordination.
400 */
401 public interface Listener {
402 /**
403 * Called when the allowed display modes may have changed.
404 */
405 void onAllowedDisplayModesChanged();
406 }
407
408 private static final class DisplayModeDirectorHandler extends Handler {
409 DisplayModeDirectorHandler(Looper looper) {
410 super(looper, null, true /*async*/);
411 }
412
413 @Override
414 public void handleMessage(Message msg) {
415 switch (msg.what) {
416 case MSG_ALLOWED_MODES_CHANGED:
417 Listener listener = (Listener) msg.obj;
418 listener.onAllowedDisplayModesChanged();
419 break;
420 }
421 }
422 }
423
424 private static final class Vote {
Michael Wrighta3dab232019-02-22 16:54:21 +0000425 // We split the app request into two priorities in case we can satisfy one desire without
426 // the other.
Ady Abrahamce743b72019-06-11 19:16:12 -0700427 public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 0;
428 public static final int PRIORITY_APP_REQUEST_SIZE = 1;
429 public static final int PRIORITY_USER_SETTING_REFRESH_RATE = 2;
Long Ling741bf5f2019-04-15 14:08:49 -0700430 public static final int PRIORITY_LOW_BRIGHTNESS = 3;
431 public static final int PRIORITY_LOW_POWER_MODE = 4;
Michael Wrighta3dab232019-02-22 16:54:21 +0000432
433 // Whenever a new priority is added, remember to update MIN_PRIORITY and/or MAX_PRIORITY as
434 // appropriate, as well as priorityToString.
435
Ady Abrahamce743b72019-06-11 19:16:12 -0700436 public static final int MIN_PRIORITY = PRIORITY_APP_REQUEST_REFRESH_RATE;
Michael Wrighta3dab232019-02-22 16:54:21 +0000437 public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;
438
439 /**
440 * A value signifying an invalid width or height in a vote.
441 */
442 public static final int INVALID_SIZE = -1;
443
444 /**
445 * The requested width of the display in pixels, or INVALID_SIZE;
446 */
447 public final int width;
448 /**
449 * The requested height of the display in pixels, or INVALID_SIZE;
450 */
451 public final int height;
452
453 /**
454 * The lowest desired refresh rate.
455 */
456 public final float minRefreshRate;
457 /**
458 * The highest desired refresh rate.
459 */
460 public final float maxRefreshRate;
461
462 public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
463 return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate);
464 }
465
466 public static Vote forSize(int width, int height) {
467 return new Vote(width, height, 0f, Float.POSITIVE_INFINITY);
468 }
469
470 private Vote(int width, int height,
471 float minRefreshRate, float maxRefreshRate) {
472 this.width = width;
473 this.height = height;
474 this.minRefreshRate = minRefreshRate;
475 this.maxRefreshRate = maxRefreshRate;
476 }
477
478 public static String priorityToString(int priority) {
479 switch (priority) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000480 case PRIORITY_APP_REQUEST_REFRESH_RATE:
481 return "PRIORITY_APP_REQUEST_REFRESH_RATE";
482 case PRIORITY_APP_REQUEST_SIZE:
483 return "PRIORITY_APP_REQUEST_SIZE";
Ady Abrahamce743b72019-06-11 19:16:12 -0700484 case PRIORITY_USER_SETTING_REFRESH_RATE:
485 return "PRIORITY_USER_SETTING_REFRESH_RATE";
Michael Wrighta3dab232019-02-22 16:54:21 +0000486 case PRIORITY_LOW_POWER_MODE:
487 return "PRIORITY_LOW_POWER_MODE";
488 default:
489 return Integer.toString(priority);
490 }
491 }
492
493 @Override
494 public String toString() {
495 return "Vote{"
496 + "width=" + width
497 + ", height=" + height
498 + ", minRefreshRate=" + minRefreshRate
499 + ", maxRefreshRate=" + maxRefreshRate
500 + "}";
501 }
502 }
503
504 private final class SettingsObserver extends ContentObserver {
505 private final Uri mRefreshRateSetting =
506 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
507 private final Uri mLowPowerModeSetting =
508 Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
509
510 private final Context mContext;
511 private final float mDefaultPeakRefreshRate;
512
513 SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
514 super(handler);
515 mContext = context;
516 mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
517 R.integer.config_defaultPeakRefreshRate);
518 }
519
520 public void observe() {
521 final ContentResolver cr = mContext.getContentResolver();
522 cr.registerContentObserver(mRefreshRateSetting, false /*notifyDescendants*/, this,
523 UserHandle.USER_SYSTEM);
524 cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
525 UserHandle.USER_SYSTEM);
526 synchronized (mLock) {
527 updateRefreshRateSettingLocked();
528 updateLowPowerModeSettingLocked();
529 }
530 }
531
532 @Override
533 public void onChange(boolean selfChange, Uri uri, int userId) {
534 synchronized (mLock) {
535 if (mRefreshRateSetting.equals(uri)) {
536 updateRefreshRateSettingLocked();
537 } else if (mLowPowerModeSetting.equals(uri)) {
538 updateLowPowerModeSettingLocked();
539 }
540 }
541 }
542
543 private void updateLowPowerModeSettingLocked() {
544 boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
545 Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
546 final Vote vote;
547 if (inLowPowerMode) {
548 vote = Vote.forRefreshRates(0f, 60f);
549 } else {
550 vote = null;
551 }
552 updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote);
Long Lingbc841b02019-07-03 16:43:15 -0700553 mBrightnessObserver.onLowPowerModeEnabled(inLowPowerMode);
Michael Wrighta3dab232019-02-22 16:54:21 +0000554 }
555
556 private void updateRefreshRateSettingLocked() {
557 float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
Adrian Salido41cc1862019-04-25 19:34:37 -0700558 Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate);
Michael Wrighta3dab232019-02-22 16:54:21 +0000559 Vote vote = Vote.forRefreshRates(0f, peakRefreshRate);
Ady Abrahamce743b72019-06-11 19:16:12 -0700560 updateVoteLocked(Vote.PRIORITY_USER_SETTING_REFRESH_RATE, vote);
Long Lingbc841b02019-07-03 16:43:15 -0700561 mBrightnessObserver.onPeakRefreshRateEnabled(peakRefreshRate > 60f);
Long Ling741bf5f2019-04-15 14:08:49 -0700562 }
563
Michael Wrighta3dab232019-02-22 16:54:21 +0000564 public void dumpLocked(PrintWriter pw) {
565 pw.println(" SettingsObserver");
566 pw.println(" mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate);
567 }
568 }
569
570 final class AppRequestObserver {
571 private SparseArray<Display.Mode> mAppRequestedModeByDisplay;
572
573 AppRequestObserver() {
574 mAppRequestedModeByDisplay = new SparseArray<>();
575 }
576
577 public void setAppRequestedMode(int displayId, int modeId) {
578 synchronized (mLock) {
579 setAppRequestedModeLocked(displayId, modeId);
580 }
581 }
582
583 private void setAppRequestedModeLocked(int displayId, int modeId) {
584 final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId);
585 if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
586 return;
587 }
588
589 final Vote refreshRateVote;
590 final Vote sizeVote;
591 if (requestedMode != null) {
592 mAppRequestedModeByDisplay.put(displayId, requestedMode);
593 float refreshRate = requestedMode.getRefreshRate();
594 refreshRateVote = Vote.forRefreshRates(refreshRate, refreshRate);
595 sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
596 requestedMode.getPhysicalHeight());
597 } else {
598 mAppRequestedModeByDisplay.remove(displayId);
599 refreshRateVote = null;
600 sizeVote = null;
601 }
602 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote);
603 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
604 return;
605 }
606
607 private Display.Mode findModeByIdLocked(int displayId, int modeId) {
608 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
609 if (modes == null) {
610 return null;
611 }
612 for (Display.Mode mode : modes) {
613 if (mode.getModeId() == modeId) {
614 return mode;
615 }
616 }
617 return null;
618 }
619
620 public void dumpLocked(PrintWriter pw) {
621 pw.println(" AppRequestObserver");
622 pw.println(" mAppRequestedModeByDisplay:");
623 for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
624 final int id = mAppRequestedModeByDisplay.keyAt(i);
625 final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
626 pw.println(" " + id + " -> " + mode);
627 }
628 }
629 }
630
631 private final class DisplayObserver implements DisplayManager.DisplayListener {
632 // Note that we can never call into DisplayManager or any of the non-POD classes it
633 // returns, while holding mLock since it may call into DMS, which might be simultaneously
634 // calling into us already holding its own lock.
635 private final Context mContext;
636 private final Handler mHandler;
637
638 DisplayObserver(Context context, Handler handler) {
639 mContext = context;
640 mHandler = handler;
641 }
642
643 public void observe() {
644 DisplayManager dm = mContext.getSystemService(DisplayManager.class);
645 dm.registerDisplayListener(this, mHandler);
646
647 // Populate existing displays
648 SparseArray<Display.Mode[]> modes = new SparseArray<>();
649 SparseArray<Display.Mode> defaultModes = new SparseArray<>();
650 DisplayInfo info = new DisplayInfo();
651 Display[] displays = dm.getDisplays();
652 for (Display d : displays) {
653 final int displayId = d.getDisplayId();
654 d.getDisplayInfo(info);
655 modes.put(displayId, info.supportedModes);
656 defaultModes.put(displayId, info.getDefaultMode());
657 }
658 synchronized (mLock) {
659 final int size = modes.size();
660 for (int i = 0; i < size; i++) {
661 mSupportedModesByDisplay.put(modes.keyAt(i), modes.valueAt(i));
662 mDefaultModeByDisplay.put(defaultModes.keyAt(i), defaultModes.valueAt(i));
663 }
664 }
665 }
666
667 @Override
668 public void onDisplayAdded(int displayId) {
669 updateDisplayModes(displayId);
670 }
671
672 @Override
673 public void onDisplayRemoved(int displayId) {
674 synchronized (mLock) {
675 mSupportedModesByDisplay.remove(displayId);
676 mDefaultModeByDisplay.remove(displayId);
677 }
678 }
679
680 @Override
681 public void onDisplayChanged(int displayId) {
682 updateDisplayModes(displayId);
683 }
684
685 private void updateDisplayModes(int displayId) {
686 Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
Michael Wright91c28382019-03-11 12:54:22 +0000687 if (d == null) {
688 // We can occasionally get a display added or changed event for a display that was
689 // subsequently removed, which means this returns null. Check this case and bail
690 // out early; if it gets re-attached we'll eventually get another call back for it.
691 return;
692 }
Michael Wrighta3dab232019-02-22 16:54:21 +0000693 DisplayInfo info = new DisplayInfo();
694 d.getDisplayInfo(info);
695 boolean changed = false;
696 synchronized (mLock) {
697 if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) {
698 mSupportedModesByDisplay.put(displayId, info.supportedModes);
699 changed = true;
700 }
701 if (!Objects.equals(mDefaultModeByDisplay.get(displayId), info.getDefaultMode())) {
702 changed = true;
703 mDefaultModeByDisplay.put(displayId, info.getDefaultMode());
704 }
705 if (changed) {
706 notifyAllowedModesChangedLocked();
707 }
708 }
709 }
710 }
Long Lingbc841b02019-07-03 16:43:15 -0700711
712 /**
713 * This class manages brightness threshold for switching between 60 hz and higher refresh rate.
714 * See more information at the definition of
715 * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and
716 * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
717 */
718 private class BrightnessObserver extends ContentObserver {
719 private final Uri mDisplayBrightnessSetting =
720 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
721
722 private final static int LIGHT_SENSOR_RATE_MS = 250;
723 private final int[] mDisplayBrightnessThresholds;
724 private final int[] mAmbientBrightnessThresholds;
725 // valid threshold if any item from the array >= 0
726 private boolean mShouldObserveDisplayChange;
727 private boolean mShouldObserveAmbientChange;
728
729 private SensorManager mSensorManager;
730 private Sensor mLightSensor;
731 // Take it as low brightness before valid sensor data comes
732 private float mAmbientLux = -1.0f;
733 private AmbientFilter mAmbientFilter;
734
735 private final Context mContext;
736 private ScreenStateReceiver mScreenStateReceiver;
737
738 // Enable light sensor only when screen is on, peak refresh rate enabled and low power mode
739 // off. After initialization, these states will be updated from the same handler thread.
740 private boolean mScreenOn = false;
741 private boolean mPeakRefreshRateEnabled = false;
742 private boolean mLowPowerModeEnabled = false;
743
744 BrightnessObserver(Context context, Handler handler) {
745 super(handler);
746 mContext = context;
747 mDisplayBrightnessThresholds = context.getResources().getIntArray(
748 R.array.config_brightnessThresholdsOfPeakRefreshRate);
749 mAmbientBrightnessThresholds = context.getResources().getIntArray(
750 R.array.config_ambientThresholdsOfPeakRefreshRate);
751 if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) {
752 throw new RuntimeException("display brightness threshold array and ambient "
753 + "brightness threshold array have different length");
754 }
755
756 mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
757 mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
758 }
759
760 public void observe(SensorManager sensorManager) {
761 if (mShouldObserveDisplayChange) {
762 final ContentResolver cr = mContext.getContentResolver();
763 cr.registerContentObserver(mDisplayBrightnessSetting,
764 false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
765 }
766
767 if (mShouldObserveAmbientChange) {
768 Resources resources = mContext.getResources();
769 String lightSensorType = resources.getString(
770 com.android.internal.R.string.config_displayLightSensorType);
771
772 Sensor lightSensor = null;
773 if (!TextUtils.isEmpty(lightSensorType)) {
774 List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
775 for (int i = 0; i < sensors.size(); i++) {
776 Sensor sensor = sensors.get(i);
777 if (lightSensorType.equals(sensor.getStringType())) {
778 lightSensor = sensor;
779 break;
780 }
781 }
782 }
783
784 if (lightSensor == null) {
785 lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
786 }
787
788 if (lightSensor != null) {
789 final Resources res = mContext.getResources();
790
791 mAmbientFilter = DisplayWhiteBalanceFactory.createBrightnessFilter(res);
792 mSensorManager = sensorManager;
793 mLightSensor = lightSensor;
794
795 // Intent.ACTION_SCREEN_ON is not sticky. Check current screen status.
796 if (mContext.getSystemService(PowerManager.class).isInteractive()) {
797 onScreenOn(true);
798 }
799 mScreenStateReceiver = new ScreenStateReceiver(mContext);
800 }
801 }
802
803 if (mShouldObserveDisplayChange || mShouldObserveAmbientChange) {
804 synchronized (mLock) {
805 onBrightnessChangedLocked();
806 }
807 }
808 }
809
810 public void onPeakRefreshRateEnabled(boolean b) {
811 if (mShouldObserveAmbientChange && mPeakRefreshRateEnabled != b) {
812 mPeakRefreshRateEnabled = b;
813 updateSensorStatus();
814 }
815 }
816
817 public void onLowPowerModeEnabled(boolean b) {
818 if (mShouldObserveAmbientChange && mLowPowerModeEnabled != b) {
819 mLowPowerModeEnabled = b;
820 updateSensorStatus();
821 }
822 }
823
824 public void dumpLocked(PrintWriter pw) {
825 pw.println(" BrightnessObserver");
826
827 for (int d: mDisplayBrightnessThresholds) {
828 pw.println(" mDisplayBrightnessThreshold: " + d);
829 }
830
831 for (int d: mAmbientBrightnessThresholds) {
832 pw.println(" mAmbientBrightnessThreshold: " + d);
833 }
834 }
835
836 @Override
837 public void onChange(boolean selfChange, Uri uri, int userId) {
838 synchronized (mLock) {
839 onBrightnessChangedLocked();
840 }
841 }
842
843 /**
844 * Checks to see if at least one value is positive, in which case it is necessary to listen
845 * to value changes.
846 */
847 private boolean checkShouldObserve(int[] a) {
848 for (int d: a) {
849 if (d >= 0) {
850 return true;
851 }
852 }
853
854 return false;
855 }
856
857 private void onBrightnessChangedLocked() {
858 int brightness = Settings.System.getInt(mContext.getContentResolver(),
859 Settings.System.SCREEN_BRIGHTNESS, -1);
860
861 Vote vote = null;
862 for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
863 int disp = mDisplayBrightnessThresholds[i];
864 int ambi = mAmbientBrightnessThresholds[i];
865
866 if (disp >= 0 && ambi >= 0) {
867 if (brightness <= disp && mAmbientLux <= ambi) {
868 vote = Vote.forRefreshRates(0f, 60f);
869 }
870 } else if (disp >= 0) {
871 if (brightness <= disp) {
872 vote = Vote.forRefreshRates(0f, 60f);
873 }
874 } else if (ambi >= 0) {
875 if (mAmbientLux <= ambi) {
876 vote = Vote.forRefreshRates(0f, 60f);
877 }
878 }
879
880 if (vote != null) {
881 break;
882 }
883 }
884
885 if (DEBUG) {
886 Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux +
887 (vote != null ? " 60hz only" : " no refresh rate limit"));
888 }
889 updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
890 }
891
892 private void onScreenOn(boolean on) {
893 // Not check mShouldObserveAmbientChange because Screen status receiver is registered
894 // only when it is true.
895 if (mScreenOn != on) {
896 mScreenOn = on;
897 updateSensorStatus();
898 }
899 }
900
901 private void updateSensorStatus() {
902 if (mSensorManager == null || mLightSensorListener == null) {
903 return;
904 }
905
906 if (mScreenOn && !mLowPowerModeEnabled && mPeakRefreshRateEnabled) {
907 mSensorManager.registerListener(mLightSensorListener,
908 mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
909 } else {
910 mSensorManager.unregisterListener(mLightSensorListener);
911 }
912 }
913
914 private final SensorEventListener mLightSensorListener = new SensorEventListener() {
915 @Override
916 public void onSensorChanged(SensorEvent event) {
917 long now = SystemClock.uptimeMillis();
918 mAmbientFilter.addValue(now, event.values[0]);
919 mAmbientLux = mAmbientFilter.getEstimate(now);
920
921 synchronized (mLock) {
922 onBrightnessChangedLocked();
923 }
924 }
925
926 @Override
927 public void onAccuracyChanged(Sensor sensor, int accuracy) {
928 // Not used.
929 }
930 };
931
932 private final class ScreenStateReceiver extends BroadcastReceiver {
933 public ScreenStateReceiver(Context context) {
934 IntentFilter filter = new IntentFilter();
935 filter.addAction(Intent.ACTION_SCREEN_OFF);
936 filter.addAction(Intent.ACTION_SCREEN_ON);
937 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
938 context.registerReceiver(this, filter, null, mHandler);
939 }
940
941 @Override
942 public void onReceive(Context context, Intent intent) {
943 onScreenOn(Intent.ACTION_SCREEN_ON.equals(intent.getAction()));
944 }
945 }
946 }
Michael Wrighta3dab232019-02-22 16:54:21 +0000947}