blob: 97fd02f535131a18ce194e7bd6f7b7c170d4caac [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;
Michael Wrighta3dab232019-02-22 16:54:21 +000072
73 // Special ID used to indicate that given vote is to be applied globally, rather than to a
74 // specific display.
75 private static final int GLOBAL_ID = -1;
76
Michael Wrighta3dab232019-02-22 16:54:21 +000077 // The tolerance within which we consider something approximately equals.
78 private static final float EPSILON = 0.001f;
79
80 private final Object mLock = new Object();
81 private final Context mContext;
82
83 private final DisplayModeDirectorHandler mHandler;
84
85 // A map from the display ID to the collection of votes and their priority. The latter takes
86 // the form of another map from the priority to the vote itself so that each priority is
87 // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
88 private final SparseArray<SparseArray<Vote>> mVotesByDisplay;
89 // A map from the display ID to the supported modes on that display.
90 private final SparseArray<Display.Mode[]> mSupportedModesByDisplay;
91 // A map from the display ID to the default mode of that display.
92 private final SparseArray<Display.Mode> mDefaultModeByDisplay;
93
94 private final AppRequestObserver mAppRequestObserver;
95 private final SettingsObserver mSettingsObserver;
96 private final DisplayObserver mDisplayObserver;
Long Lingbc841b02019-07-03 16:43:15 -070097 private final BrightnessObserver mBrightnessObserver;
Michael Wrighta3dab232019-02-22 16:54:21 +000098
Long Ling67aae3f2019-08-08 16:05:44 -070099 private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
Michael Wrighta3dab232019-02-22 16:54:21 +0000100 private Listener mListener;
101
102 public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
103 mContext = context;
104 mHandler = new DisplayModeDirectorHandler(handler.getLooper());
105 mVotesByDisplay = new SparseArray<>();
106 mSupportedModesByDisplay = new SparseArray<>();
107 mDefaultModeByDisplay = new SparseArray<>();
108 mAppRequestObserver = new AppRequestObserver();
109 mSettingsObserver = new SettingsObserver(context, handler);
110 mDisplayObserver = new DisplayObserver(context, handler);
Long Lingbc841b02019-07-03 16:43:15 -0700111 mBrightnessObserver = new BrightnessObserver(context, handler);
Long Ling67aae3f2019-08-08 16:05:44 -0700112 mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
Michael Wrighta3dab232019-02-22 16:54:21 +0000113 }
114
115 /**
116 * Tells the DisplayModeDirector to update allowed votes and begin observing relevant system
117 * state.
118 *
119 * This has to be deferred because the object may be constructed before the rest of the system
120 * is ready.
121 */
Long Lingbc841b02019-07-03 16:43:15 -0700122 public void start(SensorManager sensorManager) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000123 mSettingsObserver.observe();
124 mDisplayObserver.observe();
125 mSettingsObserver.observe();
Long Lingbc841b02019-07-03 16:43:15 -0700126 mBrightnessObserver.observe(sensorManager);
Michael Wrighta3dab232019-02-22 16:54:21 +0000127 synchronized (mLock) {
128 // We may have a listener already registered before the call to start, so go ahead and
129 // notify them to pick up our newly initialized state.
130 notifyAllowedModesChangedLocked();
131 }
Long Lingbc841b02019-07-03 16:43:15 -0700132
Michael Wrighta3dab232019-02-22 16:54:21 +0000133 }
134
135 /**
136 * Calculates the modes the system is allowed to freely switch between based on global and
137 * display-specific constraints.
138 *
139 * @param displayId The display to query for.
140 * @return The IDs of the modes the system is allowed to freely switch between.
141 */
142 @NonNull
143 public int[] getAllowedModes(int displayId) {
144 synchronized (mLock) {
145 SparseArray<Vote> votes = getVotesLocked(displayId);
146 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
147 Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
148 if (modes == null || defaultMode == null) {
149 Slog.e(TAG, "Asked about unknown display, returning empty allowed set! (id="
150 + displayId + ")");
151 return new int[0];
152 }
153 return getAllowedModesLocked(votes, modes, defaultMode);
154 }
155 }
156
157 @NonNull
158 private SparseArray<Vote> getVotesLocked(int displayId) {
159 SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
160 final SparseArray<Vote> votes;
161 if (displayVotes != null) {
162 votes = displayVotes.clone();
163 } else {
164 votes = new SparseArray<>();
165 }
166
167 SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
168 if (globalVotes != null) {
169 for (int i = 0; i < globalVotes.size(); i++) {
170 int priority = globalVotes.keyAt(i);
171 if (votes.indexOfKey(priority) < 0) {
172 votes.put(priority, globalVotes.valueAt(i));
173 }
174 }
175 }
176 return votes;
177 }
178
179 @NonNull
180 private int[] getAllowedModesLocked(@NonNull SparseArray<Vote> votes,
181 @NonNull Display.Mode[] modes, @NonNull Display.Mode defaultMode) {
182 int lowestConsideredPriority = Vote.MIN_PRIORITY;
183 while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
184 float minRefreshRate = 0f;
185 float maxRefreshRate = Float.POSITIVE_INFINITY;
186 int height = Vote.INVALID_SIZE;
187 int width = Vote.INVALID_SIZE;
188
189 for (int priority = Vote.MAX_PRIORITY;
190 priority >= lowestConsideredPriority;
191 priority--) {
192 Vote vote = votes.get(priority);
193 if (vote == null) {
194 continue;
195 }
196 // For refresh rates, just use the tightest bounds of all the votes
197 minRefreshRate = Math.max(minRefreshRate, vote.minRefreshRate);
198 maxRefreshRate = Math.min(maxRefreshRate, vote.maxRefreshRate);
199 // For display size, use only the first vote we come across (i.e. the highest
200 // priority vote that includes the width / height).
201 if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE
202 && vote.height > 0 && vote.width > 0) {
203 width = vote.width;
204 height = vote.height;
205 }
206 }
207
208 // If we don't have anything specifying the width / height of the display, just use the
209 // default width and height. We don't want these switching out from underneath us since
210 // it's a pretty disruptive behavior.
211 if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) {
212 width = defaultMode.getPhysicalWidth();
213 height = defaultMode.getPhysicalHeight();
214 }
215
216 int[] availableModes =
217 filterModes(modes, width, height, minRefreshRate, maxRefreshRate);
218 if (availableModes.length > 0) {
219 if (DEBUG) {
220 Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
221 + " with lowest priority considered "
222 + Vote.priorityToString(lowestConsideredPriority)
223 + " and constraints: "
224 + "width=" + width
225 + ", height=" + height
226 + ", minRefreshRate=" + minRefreshRate
227 + ", maxRefreshRate=" + maxRefreshRate);
228 }
229 return availableModes;
230 }
231
232 if (DEBUG) {
233 Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
234 + Vote.priorityToString(lowestConsideredPriority)
235 + " and with the following constraints: "
236 + "width=" + width
237 + ", height=" + height
238 + ", minRefreshRate=" + minRefreshRate
239 + ", maxRefreshRate=" + maxRefreshRate);
240 }
241 // If we haven't found anything with the current set of votes, drop the current lowest
242 // priority vote.
243 lowestConsideredPriority++;
244 }
245
246 // If we still haven't found anything that matches our current set of votes, just fall back
247 // to the default mode.
248 return new int[] { defaultMode.getModeId() };
249 }
250
251 private int[] filterModes(Display.Mode[] supportedModes,
252 int width, int height, float minRefreshRate, float maxRefreshRate) {
253 ArrayList<Display.Mode> availableModes = new ArrayList<>();
254 for (Display.Mode mode : supportedModes) {
255 if (mode.getPhysicalWidth() != width || mode.getPhysicalHeight() != height) {
256 if (DEBUG) {
257 Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
258 + ": desiredWidth=" + width
259 + ": desiredHeight=" + height
260 + ": actualWidth=" + mode.getPhysicalWidth()
261 + ": actualHeight=" + mode.getPhysicalHeight());
262 }
263 continue;
264 }
265 final float refreshRate = mode.getRefreshRate();
266 // Some refresh rates are calculated based on frame timings, so they aren't *exactly*
267 // equal to expected refresh rate. Given that, we apply a bit of tolerance to this
268 // comparison.
269 if (refreshRate < (minRefreshRate - EPSILON)
270 || refreshRate > (maxRefreshRate + EPSILON)) {
271 if (DEBUG) {
272 Slog.w(TAG, "Discarding mode " + mode.getModeId()
273 + ", outside refresh rate bounds"
274 + ": minRefreshRate=" + minRefreshRate
275 + ", maxRefreshRate=" + maxRefreshRate
276 + ", modeRefreshRate=" + refreshRate);
277 }
278 continue;
279 }
280 availableModes.add(mode);
281 }
282 final int size = availableModes.size();
283 int[] availableModeIds = new int[size];
284 for (int i = 0; i < size; i++) {
285 availableModeIds[i] = availableModes.get(i).getModeId();
286 }
287 return availableModeIds;
288 }
289
290 /**
291 * Gets the observer responsible for application display mode requests.
292 */
293 @NonNull
294 public AppRequestObserver getAppRequestObserver() {
295 // We don't need to lock here because mAppRequestObserver is a final field, which is
296 // guaranteed to be visible on all threads after construction.
297 return mAppRequestObserver;
298 }
299
300 /**
301 * Sets the listener for changes to allowed display modes.
302 */
303 public void setListener(@Nullable Listener listener) {
304 synchronized (mLock) {
305 mListener = listener;
306 }
307 }
308
309 /**
310 * Print the object's state and debug information into the given stream.
311 *
312 * @param pw The stream to dump information to.
313 */
314 public void dump(PrintWriter pw) {
315 pw.println("DisplayModeDirector");
316 synchronized (mLock) {
317 pw.println(" mSupportedModesByDisplay:");
318 for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
319 final int id = mSupportedModesByDisplay.keyAt(i);
320 final Display.Mode[] modes = mSupportedModesByDisplay.valueAt(i);
321 pw.println(" " + id + " -> " + Arrays.toString(modes));
322 }
323 pw.println(" mDefaultModeByDisplay:");
324 for (int i = 0; i < mDefaultModeByDisplay.size(); i++) {
325 final int id = mDefaultModeByDisplay.keyAt(i);
326 final Display.Mode mode = mDefaultModeByDisplay.valueAt(i);
327 pw.println(" " + id + " -> " + mode);
328 }
329 pw.println(" mVotesByDisplay:");
330 for (int i = 0; i < mVotesByDisplay.size(); i++) {
331 pw.println(" " + mVotesByDisplay.keyAt(i) + ":");
332 SparseArray<Vote> votes = mVotesByDisplay.valueAt(i);
333 for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
334 Vote vote = votes.get(p);
335 if (vote == null) {
336 continue;
337 }
338 pw.println(" " + Vote.priorityToString(p) + " -> " + vote);
339 }
340 }
341 mSettingsObserver.dumpLocked(pw);
342 mAppRequestObserver.dumpLocked(pw);
Long Lingbc841b02019-07-03 16:43:15 -0700343 mBrightnessObserver.dumpLocked(pw);
Michael Wrighta3dab232019-02-22 16:54:21 +0000344 }
345 }
346
347 private void updateVoteLocked(int priority, Vote vote) {
348 updateVoteLocked(GLOBAL_ID, priority, vote);
349 }
350
351 private void updateVoteLocked(int displayId, int priority, Vote vote) {
352 if (DEBUG) {
353 Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
354 + ", priority=" + Vote.priorityToString(priority)
355 + ", vote=" + vote + ")");
356 }
357 if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
358 Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
359 + " priority=" + Vote.priorityToString(priority)
360 + ", vote=" + vote, new Throwable());
361 return;
362 }
363 final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId);
364
365 Vote currentVote = votes.get(priority);
366 if (vote != null) {
367 votes.put(priority, vote);
368 } else {
369 votes.remove(priority);
370 }
371
372 if (votes.size() == 0) {
373 if (DEBUG) {
374 Slog.i(TAG, "No votes left for display " + displayId + ", removing.");
375 }
376 mVotesByDisplay.remove(displayId);
377 }
378
379 notifyAllowedModesChangedLocked();
380 }
381
382 private void notifyAllowedModesChangedLocked() {
383 if (mListener != null && !mHandler.hasMessages(MSG_ALLOWED_MODES_CHANGED)) {
384 // We need to post this to a handler to avoid calling out while holding the lock
385 // since we know there are things that both listen for changes as well as provide
386 // information. If we did call out while holding the lock, then there's no guaranteed
387 // lock order and we run the real of risk deadlock.
388 Message msg = mHandler.obtainMessage(MSG_ALLOWED_MODES_CHANGED, mListener);
389 msg.sendToTarget();
390 }
391 }
392
393 private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) {
394 int index = mVotesByDisplay.indexOfKey(displayId);
395 if (mVotesByDisplay.indexOfKey(displayId) >= 0) {
396 return mVotesByDisplay.get(displayId);
397 } else {
398 SparseArray<Vote> votes = new SparseArray<>();
399 mVotesByDisplay.put(displayId, votes);
400 return votes;
401 }
402 }
403
404 /**
405 * Listens for changes to display mode coordination.
406 */
407 public interface Listener {
408 /**
409 * Called when the allowed display modes may have changed.
410 */
411 void onAllowedDisplayModesChanged();
412 }
413
Long Ling67aae3f2019-08-08 16:05:44 -0700414 private final class DisplayModeDirectorHandler extends Handler {
Michael Wrighta3dab232019-02-22 16:54:21 +0000415 DisplayModeDirectorHandler(Looper looper) {
416 super(looper, null, true /*async*/);
417 }
418
419 @Override
420 public void handleMessage(Message msg) {
421 switch (msg.what) {
422 case MSG_ALLOWED_MODES_CHANGED:
423 Listener listener = (Listener) msg.obj;
424 listener.onAllowedDisplayModesChanged();
425 break;
Long Ling67aae3f2019-08-08 16:05:44 -0700426
427 case MSG_BRIGHTNESS_THRESHOLDS_CHANGED:
428 Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
429
430 if (thresholds != null) {
431 mBrightnessObserver.onDeviceConfigThresholdsChanged(
432 thresholds.first, thresholds.second);
433 } else {
434 mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null);
435 }
436 break;
437
438 case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
439 Float defaultPeakRefreshRate = (Float) msg.obj;
440 mSettingsObserver.onDeviceConfigDefaultPeakRefreshRateChanged(
441 defaultPeakRefreshRate);
442 break;
Michael Wrighta3dab232019-02-22 16:54:21 +0000443 }
444 }
445 }
446
447 private static final class Vote {
Michael Wrighta3dab232019-02-22 16:54:21 +0000448 // We split the app request into two priorities in case we can satisfy one desire without
449 // the other.
Ady Abrahamce743b72019-06-11 19:16:12 -0700450 public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 0;
451 public static final int PRIORITY_APP_REQUEST_SIZE = 1;
452 public static final int PRIORITY_USER_SETTING_REFRESH_RATE = 2;
Long Ling741bf5f2019-04-15 14:08:49 -0700453 public static final int PRIORITY_LOW_BRIGHTNESS = 3;
454 public static final int PRIORITY_LOW_POWER_MODE = 4;
Michael Wrighta3dab232019-02-22 16:54:21 +0000455
456 // Whenever a new priority is added, remember to update MIN_PRIORITY and/or MAX_PRIORITY as
457 // appropriate, as well as priorityToString.
458
Ady Abrahamce743b72019-06-11 19:16:12 -0700459 public static final int MIN_PRIORITY = PRIORITY_APP_REQUEST_REFRESH_RATE;
Michael Wrighta3dab232019-02-22 16:54:21 +0000460 public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;
461
462 /**
463 * A value signifying an invalid width or height in a vote.
464 */
465 public static final int INVALID_SIZE = -1;
466
467 /**
468 * The requested width of the display in pixels, or INVALID_SIZE;
469 */
470 public final int width;
471 /**
472 * The requested height of the display in pixels, or INVALID_SIZE;
473 */
474 public final int height;
475
476 /**
477 * The lowest desired refresh rate.
478 */
479 public final float minRefreshRate;
480 /**
481 * The highest desired refresh rate.
482 */
483 public final float maxRefreshRate;
484
485 public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
486 return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate);
487 }
488
489 public static Vote forSize(int width, int height) {
490 return new Vote(width, height, 0f, Float.POSITIVE_INFINITY);
491 }
492
493 private Vote(int width, int height,
494 float minRefreshRate, float maxRefreshRate) {
495 this.width = width;
496 this.height = height;
497 this.minRefreshRate = minRefreshRate;
498 this.maxRefreshRate = maxRefreshRate;
499 }
500
501 public static String priorityToString(int priority) {
502 switch (priority) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000503 case PRIORITY_APP_REQUEST_REFRESH_RATE:
504 return "PRIORITY_APP_REQUEST_REFRESH_RATE";
505 case PRIORITY_APP_REQUEST_SIZE:
506 return "PRIORITY_APP_REQUEST_SIZE";
Ady Abrahamce743b72019-06-11 19:16:12 -0700507 case PRIORITY_USER_SETTING_REFRESH_RATE:
508 return "PRIORITY_USER_SETTING_REFRESH_RATE";
Michael Wrighta3dab232019-02-22 16:54:21 +0000509 case PRIORITY_LOW_POWER_MODE:
510 return "PRIORITY_LOW_POWER_MODE";
511 default:
512 return Integer.toString(priority);
513 }
514 }
515
516 @Override
517 public String toString() {
518 return "Vote{"
519 + "width=" + width
520 + ", height=" + height
521 + ", minRefreshRate=" + minRefreshRate
522 + ", maxRefreshRate=" + maxRefreshRate
523 + "}";
524 }
525 }
526
527 private final class SettingsObserver extends ContentObserver {
Long Ling86ce8f72019-08-17 18:02:46 -0700528 private final Uri mPeakRefreshRateSetting =
Michael Wrighta3dab232019-02-22 16:54:21 +0000529 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
Long Ling86ce8f72019-08-17 18:02:46 -0700530 private final Uri mMinRefreshRateSetting =
531 Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE);
Michael Wrighta3dab232019-02-22 16:54:21 +0000532 private final Uri mLowPowerModeSetting =
533 Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
534
535 private final Context mContext;
Long Ling67aae3f2019-08-08 16:05:44 -0700536 private float mDefaultPeakRefreshRate;
Michael Wrighta3dab232019-02-22 16:54:21 +0000537
538 SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
539 super(handler);
540 mContext = context;
541 mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
542 R.integer.config_defaultPeakRefreshRate);
543 }
544
545 public void observe() {
546 final ContentResolver cr = mContext.getContentResolver();
Long Ling86ce8f72019-08-17 18:02:46 -0700547 cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this,
548 UserHandle.USER_SYSTEM);
549 cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
Michael Wrighta3dab232019-02-22 16:54:21 +0000550 UserHandle.USER_SYSTEM);
551 cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
552 UserHandle.USER_SYSTEM);
Long Ling67aae3f2019-08-08 16:05:44 -0700553
554 Float deviceConfigDefaultPeakRefresh =
555 mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
556 if (deviceConfigDefaultPeakRefresh != null) {
557 mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh;
558 }
559
Michael Wrighta3dab232019-02-22 16:54:21 +0000560 synchronized (mLock) {
561 updateRefreshRateSettingLocked();
562 updateLowPowerModeSettingLocked();
563 }
564 }
565
Long Ling67aae3f2019-08-08 16:05:44 -0700566 public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
567 if (defaultPeakRefreshRate == null) {
568 defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
569 R.integer.config_defaultPeakRefreshRate);
570 }
571
572 if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) {
573 synchronized (mLock) {
574 mDefaultPeakRefreshRate = defaultPeakRefreshRate;
575 updateRefreshRateSettingLocked();
576 }
577 }
578 }
579
Michael Wrighta3dab232019-02-22 16:54:21 +0000580 @Override
581 public void onChange(boolean selfChange, Uri uri, int userId) {
582 synchronized (mLock) {
Long Ling86ce8f72019-08-17 18:02:46 -0700583 if (mPeakRefreshRateSetting.equals(uri)
584 || mMinRefreshRateSetting.equals(uri)) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000585 updateRefreshRateSettingLocked();
586 } else if (mLowPowerModeSetting.equals(uri)) {
587 updateLowPowerModeSettingLocked();
588 }
589 }
590 }
591
592 private void updateLowPowerModeSettingLocked() {
593 boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
594 Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
595 final Vote vote;
596 if (inLowPowerMode) {
597 vote = Vote.forRefreshRates(0f, 60f);
598 } else {
599 vote = null;
600 }
601 updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote);
Long Ling86ce8f72019-08-17 18:02:46 -0700602 mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
Michael Wrighta3dab232019-02-22 16:54:21 +0000603 }
604
605 private void updateRefreshRateSettingLocked() {
Long Ling86ce8f72019-08-17 18:02:46 -0700606 float minRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
607 Settings.System.MIN_REFRESH_RATE, 0f);
Michael Wrighta3dab232019-02-22 16:54:21 +0000608 float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
Adrian Salido41cc1862019-04-25 19:34:37 -0700609 Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate);
Long Ling86ce8f72019-08-17 18:02:46 -0700610
611 if (peakRefreshRate < minRefreshRate) {
612 peakRefreshRate = minRefreshRate;
613 }
614
615 Vote vote = Vote.forRefreshRates(minRefreshRate, peakRefreshRate);
Ady Abrahamce743b72019-06-11 19:16:12 -0700616 updateVoteLocked(Vote.PRIORITY_USER_SETTING_REFRESH_RATE, vote);
Long Ling86ce8f72019-08-17 18:02:46 -0700617 mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, peakRefreshRate);
Long Ling741bf5f2019-04-15 14:08:49 -0700618 }
619
Michael Wrighta3dab232019-02-22 16:54:21 +0000620 public void dumpLocked(PrintWriter pw) {
621 pw.println(" SettingsObserver");
622 pw.println(" mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate);
623 }
624 }
625
626 final class AppRequestObserver {
627 private SparseArray<Display.Mode> mAppRequestedModeByDisplay;
628
629 AppRequestObserver() {
630 mAppRequestedModeByDisplay = new SparseArray<>();
631 }
632
633 public void setAppRequestedMode(int displayId, int modeId) {
634 synchronized (mLock) {
635 setAppRequestedModeLocked(displayId, modeId);
636 }
637 }
638
639 private void setAppRequestedModeLocked(int displayId, int modeId) {
640 final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId);
641 if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
642 return;
643 }
644
645 final Vote refreshRateVote;
646 final Vote sizeVote;
647 if (requestedMode != null) {
648 mAppRequestedModeByDisplay.put(displayId, requestedMode);
649 float refreshRate = requestedMode.getRefreshRate();
650 refreshRateVote = Vote.forRefreshRates(refreshRate, refreshRate);
651 sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
652 requestedMode.getPhysicalHeight());
653 } else {
654 mAppRequestedModeByDisplay.remove(displayId);
655 refreshRateVote = null;
656 sizeVote = null;
657 }
658 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote);
659 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
660 return;
661 }
662
663 private Display.Mode findModeByIdLocked(int displayId, int modeId) {
664 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
665 if (modes == null) {
666 return null;
667 }
668 for (Display.Mode mode : modes) {
669 if (mode.getModeId() == modeId) {
670 return mode;
671 }
672 }
673 return null;
674 }
675
676 public void dumpLocked(PrintWriter pw) {
677 pw.println(" AppRequestObserver");
678 pw.println(" mAppRequestedModeByDisplay:");
679 for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
680 final int id = mAppRequestedModeByDisplay.keyAt(i);
681 final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
682 pw.println(" " + id + " -> " + mode);
683 }
684 }
685 }
686
687 private final class DisplayObserver implements DisplayManager.DisplayListener {
688 // Note that we can never call into DisplayManager or any of the non-POD classes it
689 // returns, while holding mLock since it may call into DMS, which might be simultaneously
690 // calling into us already holding its own lock.
691 private final Context mContext;
692 private final Handler mHandler;
693
694 DisplayObserver(Context context, Handler handler) {
695 mContext = context;
696 mHandler = handler;
697 }
698
699 public void observe() {
700 DisplayManager dm = mContext.getSystemService(DisplayManager.class);
701 dm.registerDisplayListener(this, mHandler);
702
703 // Populate existing displays
704 SparseArray<Display.Mode[]> modes = new SparseArray<>();
705 SparseArray<Display.Mode> defaultModes = new SparseArray<>();
706 DisplayInfo info = new DisplayInfo();
707 Display[] displays = dm.getDisplays();
708 for (Display d : displays) {
709 final int displayId = d.getDisplayId();
710 d.getDisplayInfo(info);
711 modes.put(displayId, info.supportedModes);
712 defaultModes.put(displayId, info.getDefaultMode());
713 }
714 synchronized (mLock) {
715 final int size = modes.size();
716 for (int i = 0; i < size; i++) {
717 mSupportedModesByDisplay.put(modes.keyAt(i), modes.valueAt(i));
718 mDefaultModeByDisplay.put(defaultModes.keyAt(i), defaultModes.valueAt(i));
719 }
720 }
721 }
722
723 @Override
724 public void onDisplayAdded(int displayId) {
725 updateDisplayModes(displayId);
726 }
727
728 @Override
729 public void onDisplayRemoved(int displayId) {
730 synchronized (mLock) {
731 mSupportedModesByDisplay.remove(displayId);
732 mDefaultModeByDisplay.remove(displayId);
733 }
734 }
735
736 @Override
737 public void onDisplayChanged(int displayId) {
738 updateDisplayModes(displayId);
739 }
740
741 private void updateDisplayModes(int displayId) {
742 Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
Michael Wright91c28382019-03-11 12:54:22 +0000743 if (d == null) {
744 // We can occasionally get a display added or changed event for a display that was
745 // subsequently removed, which means this returns null. Check this case and bail
746 // out early; if it gets re-attached we'll eventually get another call back for it.
747 return;
748 }
Michael Wrighta3dab232019-02-22 16:54:21 +0000749 DisplayInfo info = new DisplayInfo();
750 d.getDisplayInfo(info);
751 boolean changed = false;
752 synchronized (mLock) {
753 if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) {
754 mSupportedModesByDisplay.put(displayId, info.supportedModes);
755 changed = true;
756 }
757 if (!Objects.equals(mDefaultModeByDisplay.get(displayId), info.getDefaultMode())) {
758 changed = true;
759 mDefaultModeByDisplay.put(displayId, info.getDefaultMode());
760 }
761 if (changed) {
762 notifyAllowedModesChangedLocked();
763 }
764 }
765 }
766 }
Long Lingbc841b02019-07-03 16:43:15 -0700767
768 /**
769 * This class manages brightness threshold for switching between 60 hz and higher refresh rate.
770 * See more information at the definition of
771 * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and
772 * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
773 */
774 private class BrightnessObserver extends ContentObserver {
775 private final Uri mDisplayBrightnessSetting =
776 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
777
778 private final static int LIGHT_SENSOR_RATE_MS = 250;
Long Ling67aae3f2019-08-08 16:05:44 -0700779 private int[] mDisplayBrightnessThresholds;
780 private int[] mAmbientBrightnessThresholds;
Long Lingbc841b02019-07-03 16:43:15 -0700781 // valid threshold if any item from the array >= 0
782 private boolean mShouldObserveDisplayChange;
783 private boolean mShouldObserveAmbientChange;
784
785 private SensorManager mSensorManager;
786 private Sensor mLightSensor;
Long Lingc1ee0422019-07-26 14:23:53 -0700787 private LightSensorEventListener mLightSensorListener = new LightSensorEventListener();
Long Lingbc841b02019-07-03 16:43:15 -0700788 // Take it as low brightness before valid sensor data comes
789 private float mAmbientLux = -1.0f;
790 private AmbientFilter mAmbientFilter;
791
792 private final Context mContext;
Long Ling67aae3f2019-08-08 16:05:44 -0700793 private final ScreenStateReceiver mScreenStateReceiver;
Long Lingbc841b02019-07-03 16:43:15 -0700794
Long Ling67aae3f2019-08-08 16:05:44 -0700795 // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak
Long Ling86ce8f72019-08-17 18:02:46 -0700796 // refresh rate changeable and low power mode off. After initialization, these states will
Long Ling67aae3f2019-08-08 16:05:44 -0700797 // be updated from the same handler thread.
Long Lingbc841b02019-07-03 16:43:15 -0700798 private boolean mScreenOn = false;
Long Ling86ce8f72019-08-17 18:02:46 -0700799 private boolean mRefreshRateChangeable = false;
Long Lingbc841b02019-07-03 16:43:15 -0700800 private boolean mLowPowerModeEnabled = false;
801
802 BrightnessObserver(Context context, Handler handler) {
803 super(handler);
804 mContext = context;
Long Ling67aae3f2019-08-08 16:05:44 -0700805 mScreenStateReceiver = new ScreenStateReceiver(mContext);
Long Lingbc841b02019-07-03 16:43:15 -0700806 mDisplayBrightnessThresholds = context.getResources().getIntArray(
807 R.array.config_brightnessThresholdsOfPeakRefreshRate);
808 mAmbientBrightnessThresholds = context.getResources().getIntArray(
809 R.array.config_ambientThresholdsOfPeakRefreshRate);
Long Ling67aae3f2019-08-08 16:05:44 -0700810
Long Lingbc841b02019-07-03 16:43:15 -0700811 if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) {
812 throw new RuntimeException("display brightness threshold array and ambient "
813 + "brightness threshold array have different length");
814 }
Long Lingbc841b02019-07-03 16:43:15 -0700815 }
816
817 public void observe(SensorManager sensorManager) {
Long Ling67aae3f2019-08-08 16:05:44 -0700818 mSensorManager = sensorManager;
819 // DeviceConfig is accessible after system ready.
820 int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds();
821 int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds();
822
823 if (brightnessThresholds != null && ambientThresholds != null
824 && brightnessThresholds.length == ambientThresholds.length) {
825 mDisplayBrightnessThresholds = brightnessThresholds;
826 mAmbientBrightnessThresholds = ambientThresholds;
Long Lingbc841b02019-07-03 16:43:15 -0700827 }
Long Ling67aae3f2019-08-08 16:05:44 -0700828 restartObserver();
829 mDeviceConfigDisplaySettings.startListening();
Long Lingbc841b02019-07-03 16:43:15 -0700830 }
831
Long Ling86ce8f72019-08-17 18:02:46 -0700832 public void onRefreshRateSettingChangedLocked(float min, float max) {
833 boolean changeable = (max - min > 1f && max > 60f);
834 if (mRefreshRateChangeable != changeable) {
835 mRefreshRateChangeable = changeable;
Long Lingbc841b02019-07-03 16:43:15 -0700836 updateSensorStatus();
Long Ling86ce8f72019-08-17 18:02:46 -0700837 if (!changeable) {
838 // Revoke previous vote from BrightnessObserver
839 updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null);
840 }
Long Lingbc841b02019-07-03 16:43:15 -0700841 }
842 }
843
Long Ling86ce8f72019-08-17 18:02:46 -0700844 public void onLowPowerModeEnabledLocked(boolean b) {
Long Ling67aae3f2019-08-08 16:05:44 -0700845 if (mLowPowerModeEnabled != b) {
Long Lingbc841b02019-07-03 16:43:15 -0700846 mLowPowerModeEnabled = b;
847 updateSensorStatus();
848 }
849 }
850
Long Ling67aae3f2019-08-08 16:05:44 -0700851 public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds,
852 int[] ambientThresholds) {
853 if (brightnessThresholds != null && ambientThresholds != null
854 && brightnessThresholds.length == ambientThresholds.length) {
855 mDisplayBrightnessThresholds = brightnessThresholds;
856 mAmbientBrightnessThresholds = ambientThresholds;
857 } else {
858 // Invalid or empty. Use device default.
859 mDisplayBrightnessThresholds = mContext.getResources().getIntArray(
860 R.array.config_brightnessThresholdsOfPeakRefreshRate);
861 mAmbientBrightnessThresholds = mContext.getResources().getIntArray(
862 R.array.config_ambientThresholdsOfPeakRefreshRate);
863 }
864 restartObserver();
865 }
866
Long Lingbc841b02019-07-03 16:43:15 -0700867 public void dumpLocked(PrintWriter pw) {
868 pw.println(" BrightnessObserver");
869
870 for (int d: mDisplayBrightnessThresholds) {
871 pw.println(" mDisplayBrightnessThreshold: " + d);
872 }
873
874 for (int d: mAmbientBrightnessThresholds) {
875 pw.println(" mAmbientBrightnessThreshold: " + d);
876 }
877 }
878
879 @Override
880 public void onChange(boolean selfChange, Uri uri, int userId) {
881 synchronized (mLock) {
Long Ling86ce8f72019-08-17 18:02:46 -0700882 if (mRefreshRateChangeable) {
883 onBrightnessChangedLocked();
884 }
Long Lingbc841b02019-07-03 16:43:15 -0700885 }
886 }
887
Long Ling67aae3f2019-08-08 16:05:44 -0700888 private void restartObserver() {
889 mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
890 mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
891
892 final ContentResolver cr = mContext.getContentResolver();
893 if (mShouldObserveDisplayChange) {
894 // Content Service does not check if an listener has already been registered.
895 // To ensure only one listener is registered, force an unregistration first.
896 cr.unregisterContentObserver(this);
897 cr.registerContentObserver(mDisplayBrightnessSetting,
898 false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
899 } else {
900 cr.unregisterContentObserver(this);
901 }
902
903 if (mShouldObserveAmbientChange) {
904 Resources resources = mContext.getResources();
905 String lightSensorType = resources.getString(
906 com.android.internal.R.string.config_displayLightSensorType);
907
908 Sensor lightSensor = null;
909 if (!TextUtils.isEmpty(lightSensorType)) {
910 List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
911 for (int i = 0; i < sensors.size(); i++) {
912 Sensor sensor = sensors.get(i);
913 if (lightSensorType.equals(sensor.getStringType())) {
914 lightSensor = sensor;
915 break;
916 }
917 }
918 }
919
920 if (lightSensor == null) {
921 lightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
922 }
923
924 if (lightSensor != null) {
925 final Resources res = mContext.getResources();
926
927 mAmbientFilter = DisplayWhiteBalanceFactory.createBrightnessFilter(res);
928 mLightSensor = lightSensor;
929
930 // Intent.ACTION_SCREEN_ON is not sticky. Check current screen status.
931 if (mContext.getSystemService(PowerManager.class).isInteractive()) {
932 onScreenOn(true);
933 }
934 mScreenStateReceiver.register();
935 }
936 } else {
937 mAmbientFilter = null;
938 mLightSensor = null;
939 mScreenStateReceiver.unregister();
940 }
941
Long Ling86ce8f72019-08-17 18:02:46 -0700942 if (mRefreshRateChangeable) {
943 updateSensorStatus();
944 synchronized (mLock) {
945 onBrightnessChangedLocked();
946 }
Long Ling67aae3f2019-08-08 16:05:44 -0700947 }
948 }
949
Long Lingbc841b02019-07-03 16:43:15 -0700950 /**
951 * Checks to see if at least one value is positive, in which case it is necessary to listen
952 * to value changes.
953 */
954 private boolean checkShouldObserve(int[] a) {
955 for (int d: a) {
956 if (d >= 0) {
957 return true;
958 }
959 }
960
961 return false;
962 }
963
964 private void onBrightnessChangedLocked() {
965 int brightness = Settings.System.getInt(mContext.getContentResolver(),
966 Settings.System.SCREEN_BRIGHTNESS, -1);
967
968 Vote vote = null;
969 for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
970 int disp = mDisplayBrightnessThresholds[i];
971 int ambi = mAmbientBrightnessThresholds[i];
972
973 if (disp >= 0 && ambi >= 0) {
974 if (brightness <= disp && mAmbientLux <= ambi) {
975 vote = Vote.forRefreshRates(0f, 60f);
976 }
977 } else if (disp >= 0) {
978 if (brightness <= disp) {
979 vote = Vote.forRefreshRates(0f, 60f);
980 }
981 } else if (ambi >= 0) {
982 if (mAmbientLux <= ambi) {
983 vote = Vote.forRefreshRates(0f, 60f);
984 }
985 }
986
987 if (vote != null) {
988 break;
989 }
990 }
991
992 if (DEBUG) {
993 Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux +
994 (vote != null ? " 60hz only" : " no refresh rate limit"));
995 }
996 updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
997 }
998
999 private void onScreenOn(boolean on) {
1000 // Not check mShouldObserveAmbientChange because Screen status receiver is registered
1001 // only when it is true.
1002 if (mScreenOn != on) {
1003 mScreenOn = on;
1004 updateSensorStatus();
1005 }
1006 }
1007
1008 private void updateSensorStatus() {
1009 if (mSensorManager == null || mLightSensorListener == null) {
1010 return;
1011 }
1012
Long Ling67aae3f2019-08-08 16:05:44 -07001013 if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled
Long Ling86ce8f72019-08-17 18:02:46 -07001014 && mRefreshRateChangeable) {
Long Lingbc841b02019-07-03 16:43:15 -07001015 mSensorManager.registerListener(mLightSensorListener,
1016 mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
1017 } else {
Long Lingc1ee0422019-07-26 14:23:53 -07001018 mLightSensorListener.removeCallbacks();
Long Lingbc841b02019-07-03 16:43:15 -07001019 mSensorManager.unregisterListener(mLightSensorListener);
1020 }
1021 }
1022
Long Lingc1ee0422019-07-26 14:23:53 -07001023 private final class LightSensorEventListener implements SensorEventListener {
1024 final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
1025 private float mLastSensorData;
1026
Long Lingbc841b02019-07-03 16:43:15 -07001027 @Override
1028 public void onSensorChanged(SensorEvent event) {
Long Lingc1ee0422019-07-26 14:23:53 -07001029 mLastSensorData = event.values[0];
1030 if (DEBUG) {
1031 Slog.d(TAG, "On sensor changed: " + mLastSensorData);
1032 }
Long Lingbc841b02019-07-03 16:43:15 -07001033
Long Lingc1ee0422019-07-26 14:23:53 -07001034 boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux);
1035 if (zoneChanged && mLastSensorData < mAmbientLux) {
1036 // Easier to see flicker at lower brightness environment. Forget the history to
1037 // get immediate response.
1038 mAmbientFilter.clear();
1039 }
1040
1041 long now = SystemClock.uptimeMillis();
1042 mAmbientFilter.addValue(now, mLastSensorData);
1043
1044 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1045 processSensorData(now);
1046
1047 if (zoneChanged && mLastSensorData > mAmbientLux) {
1048 // Sensor may not report new event if there is no brightness change.
1049 // Need to keep querying the temporal filter for the latest estimation,
1050 // until enter in higher lux zone or is interrupted by a new sensor event.
1051 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
Long Lingbc841b02019-07-03 16:43:15 -07001052 }
1053 }
1054
1055 @Override
1056 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1057 // Not used.
1058 }
Long Lingc1ee0422019-07-26 14:23:53 -07001059
1060 public void removeCallbacks() {
1061 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1062 }
1063
1064 private void processSensorData(long now) {
1065 mAmbientLux = mAmbientFilter.getEstimate(now);
1066
1067 synchronized (mLock) {
1068 onBrightnessChangedLocked();
1069 }
1070 }
1071
1072 private boolean isDifferentZone(float lux1, float lux2) {
1073 for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) {
1074 final float boundary = mAmbientBrightnessThresholds[z];
1075
1076 // Test each boundary. See if the current value and the new value are at
1077 // different sides.
1078 if ((lux1 <= boundary && lux2 > boundary)
1079 || (lux1 > boundary && lux2 <= boundary)) {
1080 return true;
1081 }
1082 }
1083
1084 return false;
1085 }
1086
1087 private Runnable mInjectSensorEventRunnable = new Runnable() {
1088 @Override
1089 public void run() {
1090 long now = SystemClock.uptimeMillis();
1091 // No need to really inject the last event into a temporal filter.
1092 processSensorData(now);
1093
1094 // Inject next event if there is a possible zone change.
1095 if (isDifferentZone(mLastSensorData, mAmbientLux)) {
1096 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
1097 }
1098 }
1099 };
Long Lingbc841b02019-07-03 16:43:15 -07001100 };
1101
1102 private final class ScreenStateReceiver extends BroadcastReceiver {
Long Ling67aae3f2019-08-08 16:05:44 -07001103 final Context mContext;
Long Ling86ce8f72019-08-17 18:02:46 -07001104 boolean mRegistered;
1105
Long Lingbc841b02019-07-03 16:43:15 -07001106 public ScreenStateReceiver(Context context) {
Long Ling67aae3f2019-08-08 16:05:44 -07001107 mContext = context;
Long Lingbc841b02019-07-03 16:43:15 -07001108 }
1109
1110 @Override
1111 public void onReceive(Context context, Intent intent) {
1112 onScreenOn(Intent.ACTION_SCREEN_ON.equals(intent.getAction()));
1113 }
Long Ling67aae3f2019-08-08 16:05:44 -07001114
1115 public void register() {
Long Ling86ce8f72019-08-17 18:02:46 -07001116 if (!mRegistered) {
1117 IntentFilter filter = new IntentFilter();
1118 filter.addAction(Intent.ACTION_SCREEN_OFF);
1119 filter.addAction(Intent.ACTION_SCREEN_ON);
1120 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
1121 mContext.registerReceiver(this, filter, null, mHandler);
1122 mRegistered = true;
1123 }
Long Ling67aae3f2019-08-08 16:05:44 -07001124 }
1125
1126 public void unregister() {
Long Ling86ce8f72019-08-17 18:02:46 -07001127 if (mRegistered) {
1128 mContext.unregisterReceiver(this);
1129 mRegistered = false;
1130 }
Long Ling67aae3f2019-08-08 16:05:44 -07001131 }
Long Lingbc841b02019-07-03 16:43:15 -07001132 }
1133 }
Long Ling67aae3f2019-08-08 16:05:44 -07001134
1135 private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
1136
1137 public DeviceConfigDisplaySettings() {
1138 }
1139
1140 public void startListening() {
1141 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1142 BackgroundThread.getExecutor(), this);
1143 }
1144
1145 /*
1146 * Return null if no such property or wrong format (not comma separated integers).
1147 */
1148 public int[] getBrightnessThresholds() {
1149 return getIntArrayProperty(
1150 DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_BRIGHTNESS_THRESHOLDS);
1151 }
1152
1153 /*
1154 * Return null if no such property or wrong format (not comma separated integers).
1155 */
1156 public int[] getAmbientThresholds() {
1157 return getIntArrayProperty(
1158 DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_AMBIENT_THRESHOLDS);
1159 }
1160
1161 /*
1162 * Return null if no such property
1163 */
1164 public Float getDefaultPeakRefreshRate() {
1165 float defaultPeakRefreshRate = DeviceConfig.getFloat(
1166 DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1167 DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
1168
1169 if (defaultPeakRefreshRate == -1) {
1170 return null;
1171 }
1172 return defaultPeakRefreshRate;
1173 }
1174
1175 @Override
1176 public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
1177 int[] brightnessThresholds = getBrightnessThresholds();
1178 int[] ambientThresholds = getAmbientThresholds();
1179 Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
1180
1181 mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED,
1182 new Pair<int[], int[]>(brightnessThresholds, ambientThresholds))
1183 .sendToTarget();
1184 mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
1185 defaultPeakRefreshRate).sendToTarget();
1186 }
1187
1188 private int[] getIntArrayProperty(String prop) {
1189 String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
1190 null);
1191
1192 if (strArray != null) {
1193 return parseIntArray(strArray);
1194 }
1195
1196 return null;
1197 }
1198
1199 private int[] parseIntArray(@NonNull String strArray) {
1200 String[] items = strArray.split(",");
1201 int[] array = new int[items.length];
1202
1203 try {
1204 for (int i = 0; i < array.length; i++) {
1205 array[i] = Integer.parseInt(items[i]);
1206 }
1207 } catch (NumberFormatException e) {
1208 Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
1209 array = null;
1210 }
1211
1212 return array;
1213 }
1214 }
1215
Michael Wrighta3dab232019-02-22 16:54:21 +00001216}