blob: 9693057778ec9de9ec03fabb5fd1eeec46998934 [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;
21import android.content.ContentResolver;
22import android.content.Context;
Long Lingbc841b02019-07-03 16:43:15 -070023import android.content.res.Resources;
Michael Wrighta3dab232019-02-22 16:54:21 +000024import android.database.ContentObserver;
Long Lingbc841b02019-07-03 16:43:15 -070025import android.hardware.Sensor;
26import android.hardware.SensorEvent;
27import android.hardware.SensorEventListener;
28import android.hardware.SensorManager;
Ana Krulec4f753aa2019-11-14 00:49:39 +010029import android.hardware.display.DisplayManager;
Michael Wrighta3dab232019-02-22 16:54:21 +000030import android.net.Uri;
31import android.os.Handler;
32import android.os.Looper;
33import android.os.Message;
Long Lingbc841b02019-07-03 16:43:15 -070034import android.os.PowerManager;
35import android.os.SystemClock;
Ana Krulec4f753aa2019-11-14 00:49:39 +010036import android.os.UserHandle;
Long Lingbb459a02019-08-08 16:05:44 -070037import android.provider.DeviceConfig;
Michael Wrighta3dab232019-02-22 16:54:21 +000038import android.provider.Settings;
Long Lingbc841b02019-07-03 16:43:15 -070039import android.text.TextUtils;
Long Lingbb459a02019-08-08 16:05:44 -070040import android.util.Pair;
Michael Wrighta3dab232019-02-22 16:54:21 +000041import android.util.Slog;
42import android.util.SparseArray;
43import android.view.Display;
44import android.view.DisplayInfo;
45
46import com.android.internal.R;
Ana Krulec4f753aa2019-11-14 00:49:39 +010047import com.android.internal.annotations.VisibleForTesting;
48import com.android.internal.os.BackgroundThread;
raylinhsu0ecd3e12021-01-15 02:43:59 +080049import com.android.internal.util.IndentingPrintWriter;
Long Linga21479a2019-07-22 15:49:02 -070050import com.android.server.display.utils.AmbientFilter;
51import com.android.server.display.utils.AmbientFilterFactory;
raylinhsu0ecd3e12021-01-15 02:43:59 +080052import com.android.server.utils.DeviceConfigInterface;
Michael Wrighta3dab232019-02-22 16:54:21 +000053
54import java.io.PrintWriter;
raylinhsu73ba11e2021-01-15 02:48:14 +080055import java.text.SimpleDateFormat;
Michael Wrighta3dab232019-02-22 16:54:21 +000056import java.util.ArrayList;
57import java.util.Arrays;
raylinhsu73ba11e2021-01-15 02:48:14 +080058import java.util.Date;
Long Lingbc841b02019-07-03 16:43:15 -070059import java.util.List;
raylinhsu73ba11e2021-01-15 02:48:14 +080060import java.util.Locale;
Michael Wrighta3dab232019-02-22 16:54:21 +000061import java.util.Objects;
62
63/**
64 * The DisplayModeDirector is responsible for determining what modes are allowed to be
65 * automatically picked by the system based on system-wide and display-specific configuration.
66 */
67public class DisplayModeDirector {
68 private static final String TAG = "DisplayModeDirector";
raylinhsuf6247e32021-01-11 11:56:27 +080069 private boolean mLoggingEnabled;
Michael Wrighta3dab232019-02-22 16:54:21 +000070
Ana Kruleca74a8642019-11-14 00:51:00 +010071 private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1;
raylinhsu0ecd3e12021-01-15 02:43:59 +080072 private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
Long Lingbb459a02019-08-08 16:05:44 -070073 private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
raylinhsu0ecd3e12021-01-15 02:43:59 +080074 private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
Michael Wright58515942021-01-15 02:47:05 +080075 private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5;
76 private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6;
Michael Wrighta3dab232019-02-22 16:54:21 +000077
78 // Special ID used to indicate that given vote is to be applied globally, rather than to a
79 // specific display.
80 private static final int GLOBAL_ID = -1;
81
Michael Wrighta3dab232019-02-22 16:54:21 +000082 // The tolerance within which we consider something approximately equals.
Marin Shalamanovfb3879f2020-01-25 02:36:10 +010083 private static final float FLOAT_TOLERANCE = 0.01f;
Michael Wrighta3dab232019-02-22 16:54:21 +000084
85 private final Object mLock = new Object();
86 private final Context mContext;
87
88 private final DisplayModeDirectorHandler mHandler;
raylinhsu0ecd3e12021-01-15 02:43:59 +080089 private final Injector mInjector;
90
91 private final AppRequestObserver mAppRequestObserver;
92 private final SettingsObserver mSettingsObserver;
93 private final DisplayObserver mDisplayObserver;
94 private final DeviceConfigInterface mDeviceConfig;
95 private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
Michael Wrighta3dab232019-02-22 16:54:21 +000096
97 // A map from the display ID to the collection of votes and their priority. The latter takes
98 // the form of another map from the priority to the vote itself so that each priority is
99 // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
Ana Krulec4f753aa2019-11-14 00:49:39 +0100100 private SparseArray<SparseArray<Vote>> mVotesByDisplay;
Michael Wrighta3dab232019-02-22 16:54:21 +0000101 // A map from the display ID to the supported modes on that display.
Ana Krulec4f753aa2019-11-14 00:49:39 +0100102 private SparseArray<Display.Mode[]> mSupportedModesByDisplay;
Michael Wrighta3dab232019-02-22 16:54:21 +0000103 // A map from the display ID to the default mode of that display.
Ana Krulec4f753aa2019-11-14 00:49:39 +0100104 private SparseArray<Display.Mode> mDefaultModeByDisplay;
Michael Wrighta3dab232019-02-22 16:54:21 +0000105
Steven Thomasd3f82682020-05-11 17:45:36 -0700106 private BrightnessObserver mBrightnessObserver;
Michael Wrighta3dab232019-02-22 16:54:21 +0000107
Ana Kruleca74a8642019-11-14 00:51:00 +0100108 private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
Michael Wrighta3dab232019-02-22 16:54:21 +0000109
110 public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
raylinhsu0ecd3e12021-01-15 02:43:59 +0800111 this(context, handler, new RealInjector());
112 }
113
114 public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
115 @NonNull Injector injector) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000116 mContext = context;
117 mHandler = new DisplayModeDirectorHandler(handler.getLooper());
raylinhsu0ecd3e12021-01-15 02:43:59 +0800118 mInjector = injector;
Michael Wrighta3dab232019-02-22 16:54:21 +0000119 mVotesByDisplay = new SparseArray<>();
120 mSupportedModesByDisplay = new SparseArray<>();
121 mDefaultModeByDisplay = new SparseArray<>();
122 mAppRequestObserver = new AppRequestObserver();
123 mSettingsObserver = new SettingsObserver(context, handler);
124 mDisplayObserver = new DisplayObserver(context, handler);
Long Lingbc841b02019-07-03 16:43:15 -0700125 mBrightnessObserver = new BrightnessObserver(context, handler);
Long Lingbb459a02019-08-08 16:05:44 -0700126 mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
raylinhsu0ecd3e12021-01-15 02:43:59 +0800127 mDeviceConfig = injector.getDeviceConfig();
Michael Wrighta3dab232019-02-22 16:54:21 +0000128 }
129
130 /**
131 * Tells the DisplayModeDirector to update allowed votes and begin observing relevant system
132 * state.
133 *
134 * This has to be deferred because the object may be constructed before the rest of the system
135 * is ready.
136 */
Long Lingbc841b02019-07-03 16:43:15 -0700137 public void start(SensorManager sensorManager) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000138 mSettingsObserver.observe();
139 mDisplayObserver.observe();
140 mSettingsObserver.observe();
Long Lingbc841b02019-07-03 16:43:15 -0700141 mBrightnessObserver.observe(sensorManager);
Michael Wrighta3dab232019-02-22 16:54:21 +0000142 synchronized (mLock) {
143 // We may have a listener already registered before the call to start, so go ahead and
144 // notify them to pick up our newly initialized state.
Ana Kruleca74a8642019-11-14 00:51:00 +0100145 notifyDesiredDisplayModeSpecsChangedLocked();
Michael Wrighta3dab232019-02-22 16:54:21 +0000146 }
Long Lingbc841b02019-07-03 16:43:15 -0700147
Michael Wrighta3dab232019-02-22 16:54:21 +0000148 }
149
raylinhsuf6247e32021-01-11 11:56:27 +0800150 public void setLoggingEnabled(boolean loggingEnabled) {
151 if (mLoggingEnabled == loggingEnabled) {
152 return;
153 }
154 mLoggingEnabled = loggingEnabled;
155 mBrightnessObserver.setLoggingEnabled(loggingEnabled);
156 }
157
Michael Wrighta3dab232019-02-22 16:54:21 +0000158 @NonNull
159 private SparseArray<Vote> getVotesLocked(int displayId) {
160 SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
161 final SparseArray<Vote> votes;
162 if (displayVotes != null) {
163 votes = displayVotes.clone();
164 } else {
165 votes = new SparseArray<>();
166 }
167
168 SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
169 if (globalVotes != null) {
170 for (int i = 0; i < globalVotes.size(); i++) {
171 int priority = globalVotes.keyAt(i);
172 if (votes.indexOfKey(priority) < 0) {
173 votes.put(priority, globalVotes.valueAt(i));
174 }
175 }
176 }
177 return votes;
178 }
179
Steven Thomas305a57c2020-04-17 12:28:36 -0700180 private static final class VoteSummary {
181 public float minRefreshRate;
182 public float maxRefreshRate;
183 public int width;
184 public int height;
185
186 VoteSummary() {
187 reset();
188 }
189
190 public void reset() {
191 minRefreshRate = 0f;
192 maxRefreshRate = Float.POSITIVE_INFINITY;
193 width = Vote.INVALID_SIZE;
194 height = Vote.INVALID_SIZE;
195 }
196 }
197
198 // VoteSummary is returned as an output param to cut down a bit on the number of temporary
199 // objects.
200 private void summarizeVotes(
201 SparseArray<Vote> votes, int lowestConsideredPriority, /*out*/ VoteSummary summary) {
202 summary.reset();
203 for (int priority = Vote.MAX_PRIORITY; priority >= lowestConsideredPriority; priority--) {
204 Vote vote = votes.get(priority);
205 if (vote == null) {
206 continue;
207 }
208 // For refresh rates, just use the tightest bounds of all the votes
209 summary.minRefreshRate = Math.max(summary.minRefreshRate, vote.refreshRateRange.min);
210 summary.maxRefreshRate = Math.min(summary.maxRefreshRate, vote.refreshRateRange.max);
211 // For display size, use only the first vote we come across (i.e. the highest
212 // priority vote that includes the width / height).
213 if (summary.height == Vote.INVALID_SIZE && summary.width == Vote.INVALID_SIZE
214 && vote.height > 0 && vote.width > 0) {
215 summary.width = vote.width;
216 summary.height = vote.height;
217 }
218 }
219 }
220
Ana Krulec4f753aa2019-11-14 00:49:39 +0100221 /**
222 * Calculates the refresh rate ranges and display modes that the system is allowed to freely
223 * switch between based on global and display-specific constraints.
224 *
225 * @param displayId The display to query for.
226 * @return The ID of the default mode the system should use, and the refresh rate range the
227 * system is allowed to switch between.
228 */
Michael Wrighta3dab232019-02-22 16:54:21 +0000229 @NonNull
Ana Kruleca74a8642019-11-14 00:51:00 +0100230 public DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(int displayId) {
Ana Krulec4f753aa2019-11-14 00:49:39 +0100231 synchronized (mLock) {
232 SparseArray<Vote> votes = getVotesLocked(displayId);
233 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
234 Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
235 if (modes == null || defaultMode == null) {
Ana Kruleca74a8642019-11-14 00:51:00 +0100236 Slog.e(TAG,
237 "Asked about unknown display, returning empty display mode specs!"
238 + "(id=" + displayId + ")");
239 return new DesiredDisplayModeSpecs();
Ana Krulec4f753aa2019-11-14 00:49:39 +0100240 }
241
242 int[] availableModes = new int[]{defaultMode.getModeId()};
Steven Thomas305a57c2020-04-17 12:28:36 -0700243 VoteSummary primarySummary = new VoteSummary();
Ana Krulec4f753aa2019-11-14 00:49:39 +0100244 int lowestConsideredPriority = Vote.MIN_PRIORITY;
245 while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
Steven Thomas305a57c2020-04-17 12:28:36 -0700246 summarizeVotes(votes, lowestConsideredPriority, primarySummary);
Ana Krulec4f753aa2019-11-14 00:49:39 +0100247
248 // If we don't have anything specifying the width / height of the display, just use
249 // the default width and height. We don't want these switching out from underneath
250 // us since it's a pretty disruptive behavior.
Steven Thomas305a57c2020-04-17 12:28:36 -0700251 if (primarySummary.height == Vote.INVALID_SIZE
252 || primarySummary.width == Vote.INVALID_SIZE) {
253 primarySummary.width = defaultMode.getPhysicalWidth();
254 primarySummary.height = defaultMode.getPhysicalHeight();
Michael Wrighta3dab232019-02-22 16:54:21 +0000255 }
Michael Wrighta3dab232019-02-22 16:54:21 +0000256
Steven Thomas305a57c2020-04-17 12:28:36 -0700257 availableModes = filterModes(modes, primarySummary);
Ana Krulec4f753aa2019-11-14 00:49:39 +0100258 if (availableModes.length > 0) {
raylinhsuf6247e32021-01-11 11:56:27 +0800259 if (mLoggingEnabled) {
Ana Krulec4f753aa2019-11-14 00:49:39 +0100260 Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
261 + " with lowest priority considered "
262 + Vote.priorityToString(lowestConsideredPriority)
263 + " and constraints: "
Steven Thomas305a57c2020-04-17 12:28:36 -0700264 + "width=" + primarySummary.width
265 + ", height=" + primarySummary.height
266 + ", minRefreshRate=" + primarySummary.minRefreshRate
267 + ", maxRefreshRate=" + primarySummary.maxRefreshRate);
Ana Krulec4f753aa2019-11-14 00:49:39 +0100268 }
269 break;
270 }
Michael Wrighta3dab232019-02-22 16:54:21 +0000271
raylinhsuf6247e32021-01-11 11:56:27 +0800272 if (mLoggingEnabled) {
Ana Krulec4f753aa2019-11-14 00:49:39 +0100273 Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
Michael Wrighta3dab232019-02-22 16:54:21 +0000274 + Vote.priorityToString(lowestConsideredPriority)
Ana Krulec4f753aa2019-11-14 00:49:39 +0100275 + " and with the following constraints: "
Steven Thomas305a57c2020-04-17 12:28:36 -0700276 + "width=" + primarySummary.width
277 + ", height=" + primarySummary.height
278 + ", minRefreshRate=" + primarySummary.minRefreshRate
279 + ", maxRefreshRate=" + primarySummary.maxRefreshRate);
Michael Wrighta3dab232019-02-22 16:54:21 +0000280 }
Ana Krulec4f753aa2019-11-14 00:49:39 +0100281
282 // If we haven't found anything with the current set of votes, drop the
283 // current lowest priority vote.
284 lowestConsideredPriority++;
Michael Wrighta3dab232019-02-22 16:54:21 +0000285 }
286
Steven Thomas305a57c2020-04-17 12:28:36 -0700287 VoteSummary appRequestSummary = new VoteSummary();
288 summarizeVotes(
289 votes, Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, appRequestSummary);
290 appRequestSummary.minRefreshRate =
291 Math.min(appRequestSummary.minRefreshRate, primarySummary.minRefreshRate);
292 appRequestSummary.maxRefreshRate =
293 Math.max(appRequestSummary.maxRefreshRate, primarySummary.maxRefreshRate);
raylinhsuf6247e32021-01-11 11:56:27 +0800294 if (mLoggingEnabled) {
Steven Thomas305a57c2020-04-17 12:28:36 -0700295 Slog.i(TAG,
296 String.format("App request range: [%.0f %.0f]",
297 appRequestSummary.minRefreshRate,
298 appRequestSummary.maxRefreshRate));
299 }
300
Steven Thomasec161942020-01-03 12:46:28 -0800301 int baseModeId = defaultMode.getModeId();
Ana Krulec4f753aa2019-11-14 00:49:39 +0100302 if (availableModes.length > 0) {
Steven Thomasec161942020-01-03 12:46:28 -0800303 baseModeId = availableModes[0];
Michael Wrighta3dab232019-02-22 16:54:21 +0000304 }
Ana Krulec4f753aa2019-11-14 00:49:39 +0100305 // filterModes function is going to filter the modes based on the voting system. If
306 // the application requests a given mode with preferredModeId function, it will be
Steven Thomasec161942020-01-03 12:46:28 -0800307 // stored as baseModeId.
Steven Thomas305a57c2020-04-17 12:28:36 -0700308 return new DesiredDisplayModeSpecs(baseModeId,
309 new RefreshRateRange(
310 primarySummary.minRefreshRate, primarySummary.maxRefreshRate),
311 new RefreshRateRange(
312 appRequestSummary.minRefreshRate, appRequestSummary.maxRefreshRate));
Michael Wrighta3dab232019-02-22 16:54:21 +0000313 }
Michael Wrighta3dab232019-02-22 16:54:21 +0000314 }
315
Steven Thomas305a57c2020-04-17 12:28:36 -0700316 private int[] filterModes(Display.Mode[] supportedModes, VoteSummary summary) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000317 ArrayList<Display.Mode> availableModes = new ArrayList<>();
318 for (Display.Mode mode : supportedModes) {
Steven Thomas305a57c2020-04-17 12:28:36 -0700319 if (mode.getPhysicalWidth() != summary.width
320 || mode.getPhysicalHeight() != summary.height) {
raylinhsuf6247e32021-01-11 11:56:27 +0800321 if (mLoggingEnabled) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000322 Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
Steven Thomas305a57c2020-04-17 12:28:36 -0700323 + ": desiredWidth=" + summary.width
324 + ": desiredHeight=" + summary.height
Michael Wrighta3dab232019-02-22 16:54:21 +0000325 + ": actualWidth=" + mode.getPhysicalWidth()
326 + ": actualHeight=" + mode.getPhysicalHeight());
327 }
328 continue;
329 }
330 final float refreshRate = mode.getRefreshRate();
331 // Some refresh rates are calculated based on frame timings, so they aren't *exactly*
332 // equal to expected refresh rate. Given that, we apply a bit of tolerance to this
333 // comparison.
Steven Thomas305a57c2020-04-17 12:28:36 -0700334 if (refreshRate < (summary.minRefreshRate - FLOAT_TOLERANCE)
335 || refreshRate > (summary.maxRefreshRate + FLOAT_TOLERANCE)) {
raylinhsuf6247e32021-01-11 11:56:27 +0800336 if (mLoggingEnabled) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000337 Slog.w(TAG, "Discarding mode " + mode.getModeId()
338 + ", outside refresh rate bounds"
Steven Thomas305a57c2020-04-17 12:28:36 -0700339 + ": minRefreshRate=" + summary.minRefreshRate
340 + ", maxRefreshRate=" + summary.maxRefreshRate
Michael Wrighta3dab232019-02-22 16:54:21 +0000341 + ", modeRefreshRate=" + refreshRate);
342 }
343 continue;
344 }
345 availableModes.add(mode);
346 }
347 final int size = availableModes.size();
348 int[] availableModeIds = new int[size];
349 for (int i = 0; i < size; i++) {
350 availableModeIds[i] = availableModes.get(i).getModeId();
351 }
352 return availableModeIds;
353 }
354
355 /**
356 * Gets the observer responsible for application display mode requests.
357 */
358 @NonNull
359 public AppRequestObserver getAppRequestObserver() {
360 // We don't need to lock here because mAppRequestObserver is a final field, which is
361 // guaranteed to be visible on all threads after construction.
362 return mAppRequestObserver;
363 }
364
365 /**
Ana Kruleca74a8642019-11-14 00:51:00 +0100366 * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate
367 * ranges.
Michael Wrighta3dab232019-02-22 16:54:21 +0000368 */
Ana Kruleca74a8642019-11-14 00:51:00 +0100369 public void setDesiredDisplayModeSpecsListener(
370 @Nullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000371 synchronized (mLock) {
Ana Kruleca74a8642019-11-14 00:51:00 +0100372 mDesiredDisplayModeSpecsListener = desiredDisplayModeSpecsListener;
Michael Wrighta3dab232019-02-22 16:54:21 +0000373 }
374 }
375
376 /**
raylinhsu0ecd3e12021-01-15 02:43:59 +0800377 * Retrieve the Vote for the given display and priority. Intended only for testing purposes.
378 *
379 * @param displayId the display to query for
380 * @param priority the priority of the vote to return
381 * @return the vote corresponding to the given {@code displayId} and {@code priority},
382 * or {@code null} if there isn't one
383 */
384 @VisibleForTesting
385 @Nullable
386 Vote getVote(int displayId, int priority) {
387 synchronized (mLock) {
388 SparseArray<Vote> votes = getVotesLocked(displayId);
389 return votes.get(priority);
390 }
391 }
392
393 /**
Michael Wrighta3dab232019-02-22 16:54:21 +0000394 * Print the object's state and debug information into the given stream.
395 *
396 * @param pw The stream to dump information to.
397 */
398 public void dump(PrintWriter pw) {
399 pw.println("DisplayModeDirector");
400 synchronized (mLock) {
401 pw.println(" mSupportedModesByDisplay:");
402 for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
403 final int id = mSupportedModesByDisplay.keyAt(i);
404 final Display.Mode[] modes = mSupportedModesByDisplay.valueAt(i);
405 pw.println(" " + id + " -> " + Arrays.toString(modes));
406 }
407 pw.println(" mDefaultModeByDisplay:");
408 for (int i = 0; i < mDefaultModeByDisplay.size(); i++) {
409 final int id = mDefaultModeByDisplay.keyAt(i);
410 final Display.Mode mode = mDefaultModeByDisplay.valueAt(i);
411 pw.println(" " + id + " -> " + mode);
412 }
413 pw.println(" mVotesByDisplay:");
414 for (int i = 0; i < mVotesByDisplay.size(); i++) {
415 pw.println(" " + mVotesByDisplay.keyAt(i) + ":");
416 SparseArray<Vote> votes = mVotesByDisplay.valueAt(i);
417 for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
418 Vote vote = votes.get(p);
419 if (vote == null) {
420 continue;
421 }
422 pw.println(" " + Vote.priorityToString(p) + " -> " + vote);
423 }
424 }
425 mSettingsObserver.dumpLocked(pw);
426 mAppRequestObserver.dumpLocked(pw);
Long Lingbc841b02019-07-03 16:43:15 -0700427 mBrightnessObserver.dumpLocked(pw);
Michael Wrighta3dab232019-02-22 16:54:21 +0000428 }
429 }
430
431 private void updateVoteLocked(int priority, Vote vote) {
432 updateVoteLocked(GLOBAL_ID, priority, vote);
433 }
434
435 private void updateVoteLocked(int displayId, int priority, Vote vote) {
raylinhsuf6247e32021-01-11 11:56:27 +0800436 if (mLoggingEnabled) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000437 Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
438 + ", priority=" + Vote.priorityToString(priority)
439 + ", vote=" + vote + ")");
440 }
441 if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
442 Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
443 + " priority=" + Vote.priorityToString(priority)
444 + ", vote=" + vote, new Throwable());
445 return;
446 }
447 final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId);
448
449 Vote currentVote = votes.get(priority);
450 if (vote != null) {
451 votes.put(priority, vote);
452 } else {
453 votes.remove(priority);
454 }
455
456 if (votes.size() == 0) {
raylinhsuf6247e32021-01-11 11:56:27 +0800457 if (mLoggingEnabled) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000458 Slog.i(TAG, "No votes left for display " + displayId + ", removing.");
459 }
460 mVotesByDisplay.remove(displayId);
461 }
462
Ana Kruleca74a8642019-11-14 00:51:00 +0100463 notifyDesiredDisplayModeSpecsChangedLocked();
Michael Wrighta3dab232019-02-22 16:54:21 +0000464 }
465
Ana Kruleca74a8642019-11-14 00:51:00 +0100466 private void notifyDesiredDisplayModeSpecsChangedLocked() {
467 if (mDesiredDisplayModeSpecsListener != null
468 && !mHandler.hasMessages(MSG_REFRESH_RATE_RANGE_CHANGED)) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000469 // We need to post this to a handler to avoid calling out while holding the lock
470 // since we know there are things that both listen for changes as well as provide
Ana Kruleca74a8642019-11-14 00:51:00 +0100471 // information. If we did call out while holding the lock, then there's no
472 // guaranteed lock order and we run the real of risk deadlock.
473 Message msg = mHandler.obtainMessage(
474 MSG_REFRESH_RATE_RANGE_CHANGED, mDesiredDisplayModeSpecsListener);
Michael Wrighta3dab232019-02-22 16:54:21 +0000475 msg.sendToTarget();
476 }
477 }
478
479 private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) {
480 int index = mVotesByDisplay.indexOfKey(displayId);
481 if (mVotesByDisplay.indexOfKey(displayId) >= 0) {
482 return mVotesByDisplay.get(displayId);
483 } else {
484 SparseArray<Vote> votes = new SparseArray<>();
485 mVotesByDisplay.put(displayId, votes);
486 return votes;
487 }
488 }
489
Ana Krulec4f753aa2019-11-14 00:49:39 +0100490 @VisibleForTesting
491 void injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay) {
492 mSupportedModesByDisplay = supportedModesByDisplay;
493 }
494
495 @VisibleForTesting
496 void injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay) {
497 mDefaultModeByDisplay = defaultModeByDisplay;
498 }
499
500 @VisibleForTesting
501 void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) {
502 mVotesByDisplay = votesByDisplay;
503 }
504
Steven Thomasd3f82682020-05-11 17:45:36 -0700505 @VisibleForTesting
506 void injectBrightnessObserver(BrightnessObserver brightnessObserver) {
507 mBrightnessObserver = brightnessObserver;
508 }
509
510 @VisibleForTesting
raylinhsu0ecd3e12021-01-15 02:43:59 +0800511 BrightnessObserver getBrightnessObserver() {
512 return mBrightnessObserver;
513 }
514
515 @VisibleForTesting
516 SettingsObserver getSettingsObserver() {
517 return mSettingsObserver;
518 }
519
520
521 @VisibleForTesting
Steven Thomasd3f82682020-05-11 17:45:36 -0700522 DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
523 float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
524 synchronized (mLock) {
525 mSettingsObserver.updateRefreshRateSettingLocked(
526 minRefreshRate, peakRefreshRate, defaultRefreshRate);
527 return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY);
528 }
529 }
530
Michael Wrighta3dab232019-02-22 16:54:21 +0000531 /**
Ana Kruleca74a8642019-11-14 00:51:00 +0100532 * Listens for changes refresh rate coordination.
Michael Wrighta3dab232019-02-22 16:54:21 +0000533 */
Ana Kruleca74a8642019-11-14 00:51:00 +0100534 public interface DesiredDisplayModeSpecsListener {
Michael Wrighta3dab232019-02-22 16:54:21 +0000535 /**
Ana Kruleca74a8642019-11-14 00:51:00 +0100536 * Called when the refresh rate range may have changed.
Michael Wrighta3dab232019-02-22 16:54:21 +0000537 */
Ana Kruleca74a8642019-11-14 00:51:00 +0100538 void onDesiredDisplayModeSpecsChanged();
Michael Wrighta3dab232019-02-22 16:54:21 +0000539 }
540
Long Lingbb459a02019-08-08 16:05:44 -0700541 private final class DisplayModeDirectorHandler extends Handler {
Michael Wrighta3dab232019-02-22 16:54:21 +0000542 DisplayModeDirectorHandler(Looper looper) {
543 super(looper, null, true /*async*/);
544 }
545
546 @Override
547 public void handleMessage(Message msg) {
548 switch (msg.what) {
Michael Wright58515942021-01-15 02:47:05 +0800549 case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED: {
Long Lingbb459a02019-08-08 16:05:44 -0700550 Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
raylinhsu0ecd3e12021-01-15 02:43:59 +0800551 mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged(
552 thresholds.first, thresholds.second);
Long Lingbb459a02019-08-08 16:05:44 -0700553 break;
Michael Wright58515942021-01-15 02:47:05 +0800554 }
555
556 case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED: {
557 int refreshRateInZone = msg.arg1;
558 mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(
559 refreshRateInZone);
560 break;
561 }
562
563 case MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED: {
564 Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
565
566 mBrightnessObserver.onDeviceConfigHighBrightnessThresholdsChanged(
567 thresholds.first, thresholds.second);
568
569 break;
570 }
571
572 case MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED: {
573 int refreshRateInZone = msg.arg1;
574 mBrightnessObserver.onDeviceConfigRefreshRateInHighZoneChanged(
575 refreshRateInZone);
576 break;
577 }
Long Lingbb459a02019-08-08 16:05:44 -0700578
579 case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
580 Float defaultPeakRefreshRate = (Float) msg.obj;
581 mSettingsObserver.onDeviceConfigDefaultPeakRefreshRateChanged(
582 defaultPeakRefreshRate);
583 break;
Long Ling73936632019-08-20 15:01:14 -0700584
Ana Kruleca74a8642019-11-14 00:51:00 +0100585 case MSG_REFRESH_RATE_RANGE_CHANGED:
586 DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener =
587 (DesiredDisplayModeSpecsListener) msg.obj;
588 desiredDisplayModeSpecsListener.onDesiredDisplayModeSpecsChanged();
589 break;
Michael Wrighta3dab232019-02-22 16:54:21 +0000590 }
591 }
592 }
593
Ana Krulec4f753aa2019-11-14 00:49:39 +0100594 /**
595 * Information about the min and max refresh rate DM would like to set the display to.
596 */
597 public static final class RefreshRateRange {
598 /**
599 * The lowest desired refresh rate.
600 */
Ana Kruleca74a8642019-11-14 00:51:00 +0100601 public float min;
Ana Krulec4f753aa2019-11-14 00:49:39 +0100602 /**
603 * The highest desired refresh rate.
604 */
Ana Kruleca74a8642019-11-14 00:51:00 +0100605 public float max;
606
607 public RefreshRateRange() {}
Ana Krulec4f753aa2019-11-14 00:49:39 +0100608
609 public RefreshRateRange(float min, float max) {
Marin Shalamanovfb3879f2020-01-25 02:36:10 +0100610 if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) {
Ana Krulec4f753aa2019-11-14 00:49:39 +0100611 Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : "
612 + min + " " + max);
613 this.min = this.max = 0;
614 return;
615 }
Marin Shalamanovfb3879f2020-01-25 02:36:10 +0100616 if (min > max) {
617 // Min and max are within epsilon of each other, but in the wrong order.
618 float t = min;
619 min = max;
620 max = t;
621 }
Ana Krulec4f753aa2019-11-14 00:49:39 +0100622 this.min = min;
623 this.max = max;
624 }
625
626 /**
627 * Checks whether the two objects have the same values.
628 */
629 @Override
630 public boolean equals(Object other) {
631 if (other == this) {
632 return true;
633 }
634
635 if (!(other instanceof RefreshRateRange)) {
636 return false;
637 }
638
639 RefreshRateRange refreshRateRange = (RefreshRateRange) other;
640 return (min == refreshRateRange.min && max == refreshRateRange.max);
641 }
642
643 @Override
644 public int hashCode() {
645 return Objects.hash(min, max);
646 }
647
648 @Override
649 public String toString() {
650 return "(" + min + " " + max + ")";
651 }
652 }
653
654 /**
Steven Thomasec161942020-01-03 12:46:28 -0800655 * Information about the desired display mode to be set by the system. Includes the base
Steven Thomas305a57c2020-04-17 12:28:36 -0700656 * mode ID and the primary and app request refresh rate ranges.
Ana Kruleca74a8642019-11-14 00:51:00 +0100657 *
658 * We have this class in addition to SurfaceControl.DesiredDisplayConfigSpecs to make clear the
659 * distinction between the config ID / physical index that
660 * SurfaceControl.DesiredDisplayConfigSpecs uses, and the mode ID used here.
Ana Krulec4f753aa2019-11-14 00:49:39 +0100661 */
Ana Kruleca74a8642019-11-14 00:51:00 +0100662 public static final class DesiredDisplayModeSpecs {
Ana Krulec4f753aa2019-11-14 00:49:39 +0100663 /**
Steven Thomasec161942020-01-03 12:46:28 -0800664 * Base mode ID. This is what system defaults to for all other settings, or
Ana Krulec4f753aa2019-11-14 00:49:39 +0100665 * if the refresh rate range is not available.
666 */
Steven Thomasec161942020-01-03 12:46:28 -0800667 public int baseModeId;
Ana Krulec4f753aa2019-11-14 00:49:39 +0100668 /**
Steven Thomas305a57c2020-04-17 12:28:36 -0700669 * The primary refresh rate range.
Ana Krulec4f753aa2019-11-14 00:49:39 +0100670 */
Steven Thomas305a57c2020-04-17 12:28:36 -0700671 public final RefreshRateRange primaryRefreshRateRange;
672 /**
673 * The app request refresh rate range. Lower priority considerations won't be included in
674 * this range, allowing surface flinger to consider additional refresh rates for apps that
675 * call setFrameRate(). This range will be greater than or equal to the primary refresh rate
676 * range, never smaller.
677 */
678 public final RefreshRateRange appRequestRefreshRateRange;
Ana Krulec4f753aa2019-11-14 00:49:39 +0100679
Ana Kruleca74a8642019-11-14 00:51:00 +0100680 public DesiredDisplayModeSpecs() {
Steven Thomas305a57c2020-04-17 12:28:36 -0700681 primaryRefreshRateRange = new RefreshRateRange();
682 appRequestRefreshRateRange = new RefreshRateRange();
Ana Kruleca74a8642019-11-14 00:51:00 +0100683 }
684
Steven Thomas305a57c2020-04-17 12:28:36 -0700685 public DesiredDisplayModeSpecs(int baseModeId,
686 @NonNull RefreshRateRange primaryRefreshRateRange,
687 @NonNull RefreshRateRange appRequestRefreshRateRange) {
Steven Thomasec161942020-01-03 12:46:28 -0800688 this.baseModeId = baseModeId;
Steven Thomas305a57c2020-04-17 12:28:36 -0700689 this.primaryRefreshRateRange = primaryRefreshRateRange;
690 this.appRequestRefreshRateRange = appRequestRefreshRateRange;
Ana Krulec4f753aa2019-11-14 00:49:39 +0100691 }
692
693 /**
694 * Returns a string representation of the object.
695 */
696 @Override
697 public String toString() {
Steven Thomas305a57c2020-04-17 12:28:36 -0700698 return String.format("baseModeId=%d primaryRefreshRateRange=[%.0f %.0f]"
699 + " appRequestRefreshRateRange=[%.0f %.0f]",
700 baseModeId, primaryRefreshRateRange.min, primaryRefreshRateRange.max,
701 appRequestRefreshRateRange.min, appRequestRefreshRateRange.max);
Ana Krulec4f753aa2019-11-14 00:49:39 +0100702 }
703 /**
704 * Checks whether the two objects have the same values.
705 */
706 @Override
707 public boolean equals(Object other) {
708 if (other == this) {
709 return true;
710 }
711
Ana Kruleca74a8642019-11-14 00:51:00 +0100712 if (!(other instanceof DesiredDisplayModeSpecs)) {
Ana Krulec4f753aa2019-11-14 00:49:39 +0100713 return false;
714 }
715
Ana Kruleca74a8642019-11-14 00:51:00 +0100716 DesiredDisplayModeSpecs desiredDisplayModeSpecs = (DesiredDisplayModeSpecs) other;
Ana Krulec4f753aa2019-11-14 00:49:39 +0100717
Steven Thomasec161942020-01-03 12:46:28 -0800718 if (baseModeId != desiredDisplayModeSpecs.baseModeId) {
Ana Krulec4f753aa2019-11-14 00:49:39 +0100719 return false;
720 }
Steven Thomas305a57c2020-04-17 12:28:36 -0700721 if (!primaryRefreshRateRange.equals(desiredDisplayModeSpecs.primaryRefreshRateRange)) {
722 return false;
723 }
724 if (!appRequestRefreshRateRange.equals(
725 desiredDisplayModeSpecs.appRequestRefreshRateRange)) {
Ana Krulec4f753aa2019-11-14 00:49:39 +0100726 return false;
727 }
728 return true;
729 }
730
731 @Override
732 public int hashCode() {
Steven Thomas305a57c2020-04-17 12:28:36 -0700733 return Objects.hash(baseModeId, primaryRefreshRateRange, appRequestRefreshRateRange);
Ana Krulec4f753aa2019-11-14 00:49:39 +0100734 }
Ana Kruleca74a8642019-11-14 00:51:00 +0100735
736 /**
737 * Copy values from the other object.
738 */
739 public void copyFrom(DesiredDisplayModeSpecs other) {
Steven Thomasec161942020-01-03 12:46:28 -0800740 baseModeId = other.baseModeId;
Steven Thomas305a57c2020-04-17 12:28:36 -0700741 primaryRefreshRateRange.min = other.primaryRefreshRateRange.min;
742 primaryRefreshRateRange.max = other.primaryRefreshRateRange.max;
743 appRequestRefreshRateRange.min = other.appRequestRefreshRateRange.min;
744 appRequestRefreshRateRange.max = other.appRequestRefreshRateRange.max;
Ana Kruleca74a8642019-11-14 00:51:00 +0100745 }
Ana Krulec4f753aa2019-11-14 00:49:39 +0100746 }
747
748 @VisibleForTesting
749 static final class Vote {
Steven Thomasd3f82682020-05-11 17:45:36 -0700750 // DEFAULT_FRAME_RATE votes for [0, DEFAULT]. As the lowest priority vote, it's overridden
751 // by all other considerations. It acts to set a default frame rate for a device.
752 public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0;
753
raylinhsu0ecd3e12021-01-15 02:43:59 +0800754 // FLICKER votes for a single refresh rate like [60,60], [90,90] or null.
Long Ling73936632019-08-20 15:01:14 -0700755 // If the higher voters result is a range, it will fix the rate to a single choice.
raylinhsu0ecd3e12021-01-15 02:43:59 +0800756 // It's used to avoid refresh rate switches in certain conditions which may result in the
757 // user seeing the display flickering when the switches occur.
758 public static final int PRIORITY_FLICKER = 1;
Long Ling73936632019-08-20 15:01:14 -0700759
760 // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
761 // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
Steven Thomasd3f82682020-05-11 17:45:36 -0700762 public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2;
Long Ling73936632019-08-20 15:01:14 -0700763
764 // We split the app request into different priorities in case we can satisfy one desire
765 // without the other.
766
767 // Application can specify preferred refresh rate with below attrs.
768 // @see android.view.WindowManager.LayoutParams#preferredRefreshRate
769 // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
770 // System also forces some apps like blacklisted app to run at a lower refresh rate.
771 // @see android.R.array#config_highRefreshRateBlacklist
Steven Thomasd3f82682020-05-11 17:45:36 -0700772 public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3;
773 public static final int PRIORITY_APP_REQUEST_SIZE = 4;
Long Ling73936632019-08-20 15:01:14 -0700774
775 // SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest
776 // of low priority voters. It votes [0, max(PEAK, MIN)]
Steven Thomasd3f82682020-05-11 17:45:36 -0700777 public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 5;
Long Ling73936632019-08-20 15:01:14 -0700778
779 // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
Steven Thomasd3f82682020-05-11 17:45:36 -0700780 public static final int PRIORITY_LOW_POWER_MODE = 6;
Michael Wrighta3dab232019-02-22 16:54:21 +0000781
Steven Thomas305a57c2020-04-17 12:28:36 -0700782 // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
783 // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
Michael Wrighta3dab232019-02-22 16:54:21 +0000784
Steven Thomasd3f82682020-05-11 17:45:36 -0700785 public static final int MIN_PRIORITY = PRIORITY_DEFAULT_REFRESH_RATE;
Michael Wrighta3dab232019-02-22 16:54:21 +0000786 public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;
787
Steven Thomas305a57c2020-04-17 12:28:36 -0700788 // The cutoff for the app request refresh rate range. Votes with priorities lower than this
789 // value will not be considered when constructing the app request refresh rate range.
790 public static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
791 PRIORITY_APP_REQUEST_REFRESH_RATE;
792
Michael Wrighta3dab232019-02-22 16:54:21 +0000793 /**
794 * A value signifying an invalid width or height in a vote.
795 */
796 public static final int INVALID_SIZE = -1;
797
798 /**
799 * The requested width of the display in pixels, or INVALID_SIZE;
800 */
801 public final int width;
802 /**
803 * The requested height of the display in pixels, or INVALID_SIZE;
804 */
805 public final int height;
Michael Wrighta3dab232019-02-22 16:54:21 +0000806 /**
Ana Krulec4f753aa2019-11-14 00:49:39 +0100807 * Information about the min and max refresh rate DM would like to set the display to.
Michael Wrighta3dab232019-02-22 16:54:21 +0000808 */
Ana Krulec4f753aa2019-11-14 00:49:39 +0100809 public final RefreshRateRange refreshRateRange;
Michael Wrighta3dab232019-02-22 16:54:21 +0000810
811 public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
812 return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate);
813 }
814
815 public static Vote forSize(int width, int height) {
816 return new Vote(width, height, 0f, Float.POSITIVE_INFINITY);
817 }
818
819 private Vote(int width, int height,
820 float minRefreshRate, float maxRefreshRate) {
821 this.width = width;
822 this.height = height;
Ana Krulec4f753aa2019-11-14 00:49:39 +0100823 this.refreshRateRange =
824 new RefreshRateRange(minRefreshRate, maxRefreshRate);
Michael Wrighta3dab232019-02-22 16:54:21 +0000825 }
826
827 public static String priorityToString(int priority) {
828 switch (priority) {
Steven Thomasd3f82682020-05-11 17:45:36 -0700829 case PRIORITY_DEFAULT_REFRESH_RATE:
830 return "PRIORITY_DEFAULT_REFRESH_RATE";
raylinhsu0ecd3e12021-01-15 02:43:59 +0800831 case PRIORITY_FLICKER:
832 return "PRIORITY_FLICKER";
Long Ling73936632019-08-20 15:01:14 -0700833 case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
834 return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
Michael Wrighta3dab232019-02-22 16:54:21 +0000835 case PRIORITY_APP_REQUEST_REFRESH_RATE:
836 return "PRIORITY_APP_REQUEST_REFRESH_RATE";
837 case PRIORITY_APP_REQUEST_SIZE:
838 return "PRIORITY_APP_REQUEST_SIZE";
Long Ling73936632019-08-20 15:01:14 -0700839 case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
840 return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
Michael Wrighta3dab232019-02-22 16:54:21 +0000841 case PRIORITY_LOW_POWER_MODE:
842 return "PRIORITY_LOW_POWER_MODE";
843 default:
844 return Integer.toString(priority);
845 }
846 }
847
848 @Override
849 public String toString() {
850 return "Vote{"
Ana Krulec4f753aa2019-11-14 00:49:39 +0100851 + "width=" + width + ", height=" + height
852 + ", minRefreshRate=" + refreshRateRange.min
853 + ", maxRefreshRate=" + refreshRateRange.max + "}";
Michael Wrighta3dab232019-02-22 16:54:21 +0000854 }
855 }
856
raylinhsu0ecd3e12021-01-15 02:43:59 +0800857 @VisibleForTesting
858 final class SettingsObserver extends ContentObserver {
Long Ling3b58eff2019-08-17 18:02:46 -0700859 private final Uri mPeakRefreshRateSetting =
Michael Wrighta3dab232019-02-22 16:54:21 +0000860 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
Long Ling3b58eff2019-08-17 18:02:46 -0700861 private final Uri mMinRefreshRateSetting =
862 Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE);
Michael Wrighta3dab232019-02-22 16:54:21 +0000863 private final Uri mLowPowerModeSetting =
864 Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
865
866 private final Context mContext;
Long Lingbb459a02019-08-08 16:05:44 -0700867 private float mDefaultPeakRefreshRate;
Steven Thomasd3f82682020-05-11 17:45:36 -0700868 private float mDefaultRefreshRate;
Michael Wrighta3dab232019-02-22 16:54:21 +0000869
870 SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
871 super(handler);
872 mContext = context;
873 mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
874 R.integer.config_defaultPeakRefreshRate);
Steven Thomasd3f82682020-05-11 17:45:36 -0700875 mDefaultRefreshRate =
876 (float) context.getResources().getInteger(R.integer.config_defaultRefreshRate);
Michael Wrighta3dab232019-02-22 16:54:21 +0000877 }
878
879 public void observe() {
880 final ContentResolver cr = mContext.getContentResolver();
raylinhsu0ecd3e12021-01-15 02:43:59 +0800881 mInjector.registerPeakRefreshRateObserver(cr, this);
Long Ling3b58eff2019-08-17 18:02:46 -0700882 cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
Michael Wrighta3dab232019-02-22 16:54:21 +0000883 UserHandle.USER_SYSTEM);
884 cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
885 UserHandle.USER_SYSTEM);
Long Lingbb459a02019-08-08 16:05:44 -0700886
887 Float deviceConfigDefaultPeakRefresh =
888 mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
889 if (deviceConfigDefaultPeakRefresh != null) {
890 mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh;
891 }
892
Michael Wrighta3dab232019-02-22 16:54:21 +0000893 synchronized (mLock) {
894 updateRefreshRateSettingLocked();
895 updateLowPowerModeSettingLocked();
896 }
897 }
898
raylinhsu0ecd3e12021-01-15 02:43:59 +0800899 public void setDefaultRefreshRate(float refreshRate) {
900 synchronized (mLock) {
901 mDefaultRefreshRate = refreshRate;
902 updateRefreshRateSettingLocked();
903 }
904 }
905
Long Lingbb459a02019-08-08 16:05:44 -0700906 public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
907 if (defaultPeakRefreshRate == null) {
908 defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
909 R.integer.config_defaultPeakRefreshRate);
910 }
911
912 if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) {
913 synchronized (mLock) {
914 mDefaultPeakRefreshRate = defaultPeakRefreshRate;
915 updateRefreshRateSettingLocked();
916 }
917 }
918 }
919
Michael Wrighta3dab232019-02-22 16:54:21 +0000920 @Override
921 public void onChange(boolean selfChange, Uri uri, int userId) {
922 synchronized (mLock) {
Long Ling3b58eff2019-08-17 18:02:46 -0700923 if (mPeakRefreshRateSetting.equals(uri)
924 || mMinRefreshRateSetting.equals(uri)) {
Michael Wrighta3dab232019-02-22 16:54:21 +0000925 updateRefreshRateSettingLocked();
926 } else if (mLowPowerModeSetting.equals(uri)) {
927 updateLowPowerModeSettingLocked();
928 }
929 }
930 }
931
932 private void updateLowPowerModeSettingLocked() {
933 boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
934 Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
935 final Vote vote;
936 if (inLowPowerMode) {
937 vote = Vote.forRefreshRates(0f, 60f);
938 } else {
939 vote = null;
940 }
941 updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote);
Long Ling3b58eff2019-08-17 18:02:46 -0700942 mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
Michael Wrighta3dab232019-02-22 16:54:21 +0000943 }
944
945 private void updateRefreshRateSettingLocked() {
Long Ling3b58eff2019-08-17 18:02:46 -0700946 float minRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
947 Settings.System.MIN_REFRESH_RATE, 0f);
Michael Wrighta3dab232019-02-22 16:54:21 +0000948 float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
Adrian Salido41cc1862019-04-25 19:34:37 -0700949 Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate);
Steven Thomasd3f82682020-05-11 17:45:36 -0700950 updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
951 }
Long Ling3b58eff2019-08-17 18:02:46 -0700952
Steven Thomasd3f82682020-05-11 17:45:36 -0700953 private void updateRefreshRateSettingLocked(
954 float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
955 // TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is
956 // used to predict if we're going to be doing frequent refresh rate switching, and if
957 // so, enable the brightness observer. The logic here is more complicated and fragile
958 // than necessary, and we should improve it. See b/156304339 for more info.
959 Vote peakVote = peakRefreshRate == 0f
960 ? null
961 : Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate));
962 updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, peakVote);
Long Ling73936632019-08-20 15:01:14 -0700963 updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
964 Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY));
Steven Thomasd3f82682020-05-11 17:45:36 -0700965 Vote defaultVote =
966 defaultRefreshRate == 0f ? null : Vote.forRefreshRates(0f, defaultRefreshRate);
967 updateVoteLocked(Vote.PRIORITY_DEFAULT_REFRESH_RATE, defaultVote);
Long Ling3b58eff2019-08-17 18:02:46 -0700968
Steven Thomasd3f82682020-05-11 17:45:36 -0700969 float maxRefreshRate;
970 if (peakRefreshRate == 0f && defaultRefreshRate == 0f) {
971 // We require that at least one of the peak or default refresh rate values are
972 // set. The brightness observer requires that we're able to predict whether or not
973 // we're going to do frequent refresh rate switching, and with the way the code is
974 // currently written, we need either a default or peak refresh rate value for that.
975 Slog.e(TAG, "Default and peak refresh rates are both 0. One of them should be set"
976 + " to a valid value.");
977 maxRefreshRate = minRefreshRate;
978 } else if (peakRefreshRate == 0f) {
979 maxRefreshRate = defaultRefreshRate;
980 } else if (defaultRefreshRate == 0f) {
981 maxRefreshRate = peakRefreshRate;
982 } else {
983 maxRefreshRate = Math.min(defaultRefreshRate, peakRefreshRate);
984 }
985
986 mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, maxRefreshRate);
Long Ling741bf5f2019-04-15 14:08:49 -0700987 }
988
Michael Wrighta3dab232019-02-22 16:54:21 +0000989 public void dumpLocked(PrintWriter pw) {
990 pw.println(" SettingsObserver");
Steven Thomasd3f82682020-05-11 17:45:36 -0700991 pw.println(" mDefaultRefreshRate: " + mDefaultRefreshRate);
Michael Wrighta3dab232019-02-22 16:54:21 +0000992 pw.println(" mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate);
993 }
994 }
995
996 final class AppRequestObserver {
997 private SparseArray<Display.Mode> mAppRequestedModeByDisplay;
998
999 AppRequestObserver() {
1000 mAppRequestedModeByDisplay = new SparseArray<>();
1001 }
1002
1003 public void setAppRequestedMode(int displayId, int modeId) {
1004 synchronized (mLock) {
1005 setAppRequestedModeLocked(displayId, modeId);
1006 }
1007 }
1008
1009 private void setAppRequestedModeLocked(int displayId, int modeId) {
1010 final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId);
1011 if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
1012 return;
1013 }
1014
1015 final Vote refreshRateVote;
1016 final Vote sizeVote;
1017 if (requestedMode != null) {
1018 mAppRequestedModeByDisplay.put(displayId, requestedMode);
1019 float refreshRate = requestedMode.getRefreshRate();
1020 refreshRateVote = Vote.forRefreshRates(refreshRate, refreshRate);
1021 sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
1022 requestedMode.getPhysicalHeight());
1023 } else {
1024 mAppRequestedModeByDisplay.remove(displayId);
1025 refreshRateVote = null;
1026 sizeVote = null;
1027 }
Long Ling73936632019-08-20 15:01:14 -07001028
Michael Wrighta3dab232019-02-22 16:54:21 +00001029 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote);
1030 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
1031 return;
1032 }
1033
1034 private Display.Mode findModeByIdLocked(int displayId, int modeId) {
1035 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
1036 if (modes == null) {
1037 return null;
1038 }
1039 for (Display.Mode mode : modes) {
1040 if (mode.getModeId() == modeId) {
1041 return mode;
1042 }
1043 }
1044 return null;
1045 }
1046
1047 public void dumpLocked(PrintWriter pw) {
1048 pw.println(" AppRequestObserver");
1049 pw.println(" mAppRequestedModeByDisplay:");
1050 for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
1051 final int id = mAppRequestedModeByDisplay.keyAt(i);
1052 final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
1053 pw.println(" " + id + " -> " + mode);
1054 }
1055 }
1056 }
1057
1058 private final class DisplayObserver implements DisplayManager.DisplayListener {
1059 // Note that we can never call into DisplayManager or any of the non-POD classes it
1060 // returns, while holding mLock since it may call into DMS, which might be simultaneously
1061 // calling into us already holding its own lock.
1062 private final Context mContext;
1063 private final Handler mHandler;
1064
1065 DisplayObserver(Context context, Handler handler) {
1066 mContext = context;
1067 mHandler = handler;
1068 }
1069
1070 public void observe() {
1071 DisplayManager dm = mContext.getSystemService(DisplayManager.class);
1072 dm.registerDisplayListener(this, mHandler);
1073
1074 // Populate existing displays
1075 SparseArray<Display.Mode[]> modes = new SparseArray<>();
1076 SparseArray<Display.Mode> defaultModes = new SparseArray<>();
1077 DisplayInfo info = new DisplayInfo();
1078 Display[] displays = dm.getDisplays();
1079 for (Display d : displays) {
1080 final int displayId = d.getDisplayId();
1081 d.getDisplayInfo(info);
1082 modes.put(displayId, info.supportedModes);
1083 defaultModes.put(displayId, info.getDefaultMode());
1084 }
1085 synchronized (mLock) {
1086 final int size = modes.size();
1087 for (int i = 0; i < size; i++) {
1088 mSupportedModesByDisplay.put(modes.keyAt(i), modes.valueAt(i));
1089 mDefaultModeByDisplay.put(defaultModes.keyAt(i), defaultModes.valueAt(i));
1090 }
1091 }
1092 }
1093
1094 @Override
1095 public void onDisplayAdded(int displayId) {
1096 updateDisplayModes(displayId);
1097 }
1098
1099 @Override
1100 public void onDisplayRemoved(int displayId) {
1101 synchronized (mLock) {
1102 mSupportedModesByDisplay.remove(displayId);
1103 mDefaultModeByDisplay.remove(displayId);
1104 }
1105 }
1106
1107 @Override
1108 public void onDisplayChanged(int displayId) {
1109 updateDisplayModes(displayId);
raylinhsu0ecd3e12021-01-15 02:43:59 +08001110 // TODO: Break the coupling between DisplayObserver and BrightnessObserver.
Long Lingf3afe7d2019-08-05 12:19:42 -07001111 mBrightnessObserver.onDisplayChanged(displayId);
Michael Wrighta3dab232019-02-22 16:54:21 +00001112 }
1113
1114 private void updateDisplayModes(int displayId) {
1115 Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
Michael Wright91c28382019-03-11 12:54:22 +00001116 if (d == null) {
1117 // We can occasionally get a display added or changed event for a display that was
1118 // subsequently removed, which means this returns null. Check this case and bail
1119 // out early; if it gets re-attached we'll eventually get another call back for it.
1120 return;
1121 }
Michael Wrighta3dab232019-02-22 16:54:21 +00001122 DisplayInfo info = new DisplayInfo();
1123 d.getDisplayInfo(info);
1124 boolean changed = false;
1125 synchronized (mLock) {
1126 if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) {
1127 mSupportedModesByDisplay.put(displayId, info.supportedModes);
1128 changed = true;
1129 }
1130 if (!Objects.equals(mDefaultModeByDisplay.get(displayId), info.getDefaultMode())) {
1131 changed = true;
1132 mDefaultModeByDisplay.put(displayId, info.getDefaultMode());
1133 }
1134 if (changed) {
Ana Kruleca74a8642019-11-14 00:51:00 +01001135 notifyDesiredDisplayModeSpecsChangedLocked();
Michael Wrighta3dab232019-02-22 16:54:21 +00001136 }
1137 }
1138 }
1139 }
Long Lingbc841b02019-07-03 16:43:15 -07001140
1141 /**
1142 * This class manages brightness threshold for switching between 60 hz and higher refresh rate.
1143 * See more information at the definition of
1144 * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and
1145 * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
1146 */
Steven Thomasd3f82682020-05-11 17:45:36 -07001147 @VisibleForTesting
1148 public class BrightnessObserver extends ContentObserver {
Long Lingbc841b02019-07-03 16:43:15 -07001149 private final static int LIGHT_SENSOR_RATE_MS = 250;
raylinhsu0ecd3e12021-01-15 02:43:59 +08001150 private int[] mLowDisplayBrightnessThresholds;
1151 private int[] mLowAmbientBrightnessThresholds;
1152 private int[] mHighDisplayBrightnessThresholds;
1153 private int[] mHighAmbientBrightnessThresholds;
Long Lingbc841b02019-07-03 16:43:15 -07001154 // valid threshold if any item from the array >= 0
raylinhsu0ecd3e12021-01-15 02:43:59 +08001155 private boolean mShouldObserveDisplayLowChange;
1156 private boolean mShouldObserveAmbientLowChange;
1157 private boolean mShouldObserveDisplayHighChange;
1158 private boolean mShouldObserveAmbientHighChange;
raylinhsuf6247e32021-01-11 11:56:27 +08001159 private boolean mLoggingEnabled;
Long Lingbc841b02019-07-03 16:43:15 -07001160
1161 private SensorManager mSensorManager;
1162 private Sensor mLightSensor;
Long Lingc1ee0422019-07-26 14:23:53 -07001163 private LightSensorEventListener mLightSensorListener = new LightSensorEventListener();
Long Lingbc841b02019-07-03 16:43:15 -07001164 // Take it as low brightness before valid sensor data comes
1165 private float mAmbientLux = -1.0f;
1166 private AmbientFilter mAmbientFilter;
raylinhsu0ecd3e12021-01-15 02:43:59 +08001167 private int mBrightness = -1;
Long Lingbc841b02019-07-03 16:43:15 -07001168
1169 private final Context mContext;
Long Lingbb459a02019-08-08 16:05:44 -07001170
raylinhsu0ecd3e12021-01-15 02:43:59 +08001171 // Enable light sensor only when mShouldObserveAmbientLowChange is true or
1172 // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
1173 // changeable and low power mode off. After initialization, these states will
Long Lingbb459a02019-08-08 16:05:44 -07001174 // be updated from the same handler thread.
raylinhsu73ba11e2021-01-15 02:48:14 +08001175 private int mDefaultDisplayState = Display.STATE_UNKNOWN;
Long Ling3b58eff2019-08-17 18:02:46 -07001176 private boolean mRefreshRateChangeable = false;
Long Lingbc841b02019-07-03 16:43:15 -07001177 private boolean mLowPowerModeEnabled = false;
1178
raylinhsu0ecd3e12021-01-15 02:43:59 +08001179 private int mRefreshRateInLowZone;
1180 private int mRefreshRateInHighZone;
Long Ling73936632019-08-20 15:01:14 -07001181
Long Lingbc841b02019-07-03 16:43:15 -07001182 BrightnessObserver(Context context, Handler handler) {
1183 super(handler);
1184 mContext = context;
raylinhsu0ecd3e12021-01-15 02:43:59 +08001185 mLowDisplayBrightnessThresholds = context.getResources().getIntArray(
Long Lingbc841b02019-07-03 16:43:15 -07001186 R.array.config_brightnessThresholdsOfPeakRefreshRate);
raylinhsu0ecd3e12021-01-15 02:43:59 +08001187 mLowAmbientBrightnessThresholds = context.getResources().getIntArray(
Long Lingbc841b02019-07-03 16:43:15 -07001188 R.array.config_ambientThresholdsOfPeakRefreshRate);
Long Lingbb459a02019-08-08 16:05:44 -07001189
raylinhsu0ecd3e12021-01-15 02:43:59 +08001190 if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
1191 throw new RuntimeException("display low brightness threshold array and ambient "
1192 + "brightness threshold array have different length: "
1193 + "displayBrightnessThresholds="
1194 + Arrays.toString(mLowDisplayBrightnessThresholds)
1195 + ", ambientBrightnessThresholds="
1196 + Arrays.toString(mLowAmbientBrightnessThresholds));
Long Lingbc841b02019-07-03 16:43:15 -07001197 }
raylinhsu0ecd3e12021-01-15 02:43:59 +08001198
1199 mHighDisplayBrightnessThresholds = context.getResources().getIntArray(
1200 R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
1201 mHighAmbientBrightnessThresholds = context.getResources().getIntArray(
1202 R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
1203 if (mHighDisplayBrightnessThresholds.length
1204 != mHighAmbientBrightnessThresholds.length) {
1205 throw new RuntimeException("display high brightness threshold array and ambient "
1206 + "brightness threshold array have different length: "
1207 + "displayBrightnessThresholds="
1208 + Arrays.toString(mHighDisplayBrightnessThresholds)
1209 + ", ambientBrightnessThresholds="
1210 + Arrays.toString(mHighAmbientBrightnessThresholds));
1211 }
1212 mRefreshRateInHighZone = context.getResources().getInteger(
1213 R.integer.config_fixedRefreshRateInHighZone);
1214 }
1215
1216 /**
1217 * @return the refresh to lock to when in a low brightness zone
1218 */
1219 @VisibleForTesting
1220 int getRefreshRateInLowZone() {
1221 return mRefreshRateInLowZone;
1222 }
1223
1224 /**
1225 * @return the display brightness thresholds for the low brightness zones
1226 */
1227 @VisibleForTesting
1228 int[] getLowDisplayBrightnessThresholds() {
1229 return mLowDisplayBrightnessThresholds;
1230 }
1231
1232 /**
1233 * @return the ambient brightness thresholds for the low brightness zones
1234 */
1235 @VisibleForTesting
1236 int[] getLowAmbientBrightnessThresholds() {
1237 return mLowAmbientBrightnessThresholds;
1238 }
1239
1240 public void registerLightSensor(SensorManager sensorManager, Sensor lightSensor) {
1241 mSensorManager = sensorManager;
1242 mLightSensor = lightSensor;
1243
1244 mSensorManager.registerListener(mLightSensorListener,
1245 mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
1246 }
1247
Long Lingbc841b02019-07-03 16:43:15 -07001248 public void observe(SensorManager sensorManager) {
Long Lingbb459a02019-08-08 16:05:44 -07001249 mSensorManager = sensorManager;
raylinhsu0ecd3e12021-01-15 02:43:59 +08001250 final ContentResolver cr = mContext.getContentResolver();
1251 mBrightness = Settings.System.getIntForUser(cr,
1252 Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
Long Ling73936632019-08-20 15:01:14 -07001253
Long Lingbb459a02019-08-08 16:05:44 -07001254 // DeviceConfig is accessible after system ready.
raylinhsu0ecd3e12021-01-15 02:43:59 +08001255 int[] lowDisplayBrightnessThresholds =
1256 mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds();
1257 int[] lowAmbientBrightnessThresholds =
1258 mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds();
Long Lingbb459a02019-08-08 16:05:44 -07001259
raylinhsu0ecd3e12021-01-15 02:43:59 +08001260 if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null
1261 && lowDisplayBrightnessThresholds.length
1262 == lowAmbientBrightnessThresholds.length) {
1263 mLowDisplayBrightnessThresholds = lowDisplayBrightnessThresholds;
1264 mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds;
Long Lingbc841b02019-07-03 16:43:15 -07001265 }
Long Ling73936632019-08-20 15:01:14 -07001266
Michael Wright58515942021-01-15 02:47:05 +08001267
1268 int[] highDisplayBrightnessThresholds =
1269 mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds();
1270 int[] highAmbientBrightnessThresholds =
1271 mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds();
1272
1273 if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null
1274 && highDisplayBrightnessThresholds.length
1275 == highAmbientBrightnessThresholds.length) {
1276 mHighDisplayBrightnessThresholds = highDisplayBrightnessThresholds;
1277 mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds;
1278 }
1279
raylinhsu0ecd3e12021-01-15 02:43:59 +08001280 mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone();
Michael Wright58515942021-01-15 02:47:05 +08001281 mRefreshRateInHighZone = mDeviceConfigDisplaySettings.getRefreshRateInHighZone();
1282
Long Lingbb459a02019-08-08 16:05:44 -07001283 restartObserver();
1284 mDeviceConfigDisplaySettings.startListening();
Long Lingbc841b02019-07-03 16:43:15 -07001285 }
1286
raylinhsuf6247e32021-01-11 11:56:27 +08001287 public void setLoggingEnabled(boolean loggingEnabled) {
1288 if (mLoggingEnabled == loggingEnabled) {
1289 return;
1290 }
1291 mLoggingEnabled = loggingEnabled;
1292 mLightSensorListener.setLoggingEnabled(loggingEnabled);
1293 }
1294
Long Ling3b58eff2019-08-17 18:02:46 -07001295 public void onRefreshRateSettingChangedLocked(float min, float max) {
1296 boolean changeable = (max - min > 1f && max > 60f);
1297 if (mRefreshRateChangeable != changeable) {
1298 mRefreshRateChangeable = changeable;
Long Lingbc841b02019-07-03 16:43:15 -07001299 updateSensorStatus();
Long Ling3b58eff2019-08-17 18:02:46 -07001300 if (!changeable) {
1301 // Revoke previous vote from BrightnessObserver
raylinhsu0ecd3e12021-01-15 02:43:59 +08001302 updateVoteLocked(Vote.PRIORITY_FLICKER, null);
Long Ling3b58eff2019-08-17 18:02:46 -07001303 }
Long Lingbc841b02019-07-03 16:43:15 -07001304 }
1305 }
1306
Long Ling3b58eff2019-08-17 18:02:46 -07001307 public void onLowPowerModeEnabledLocked(boolean b) {
Long Lingbb459a02019-08-08 16:05:44 -07001308 if (mLowPowerModeEnabled != b) {
Long Lingbc841b02019-07-03 16:43:15 -07001309 mLowPowerModeEnabled = b;
1310 updateSensorStatus();
1311 }
1312 }
1313
raylinhsu0ecd3e12021-01-15 02:43:59 +08001314 public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds,
Long Lingbb459a02019-08-08 16:05:44 -07001315 int[] ambientThresholds) {
raylinhsu0ecd3e12021-01-15 02:43:59 +08001316 if (displayThresholds != null && ambientThresholds != null
1317 && displayThresholds.length == ambientThresholds.length) {
1318 mLowDisplayBrightnessThresholds = displayThresholds;
1319 mLowAmbientBrightnessThresholds = ambientThresholds;
Long Lingbb459a02019-08-08 16:05:44 -07001320 } else {
1321 // Invalid or empty. Use device default.
raylinhsu0ecd3e12021-01-15 02:43:59 +08001322 mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray(
Long Lingbb459a02019-08-08 16:05:44 -07001323 R.array.config_brightnessThresholdsOfPeakRefreshRate);
raylinhsu0ecd3e12021-01-15 02:43:59 +08001324 mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray(
Long Lingbb459a02019-08-08 16:05:44 -07001325 R.array.config_ambientThresholdsOfPeakRefreshRate);
Long Lingf3afe7d2019-08-05 12:19:42 -07001326 }
Long Lingbb459a02019-08-08 16:05:44 -07001327 restartObserver();
Long Lingf3afe7d2019-08-05 12:19:42 -07001328 }
1329
raylinhsu0ecd3e12021-01-15 02:43:59 +08001330 public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) {
1331 if (refreshRate != mRefreshRateInLowZone) {
1332 mRefreshRateInLowZone = refreshRate;
Long Ling73936632019-08-20 15:01:14 -07001333 restartObserver();
1334 }
1335 }
1336
Michael Wright58515942021-01-15 02:47:05 +08001337 public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds,
1338 int[] ambientThresholds) {
1339 if (displayThresholds != null && ambientThresholds != null
1340 && displayThresholds.length == ambientThresholds.length) {
1341 mHighDisplayBrightnessThresholds = displayThresholds;
1342 mHighAmbientBrightnessThresholds = ambientThresholds;
1343 } else {
1344 // Invalid or empty. Use device default.
1345 mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray(
1346 R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
1347 mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray(
1348 R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
1349 }
1350 restartObserver();
1351 }
1352
1353 public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) {
1354 if (refreshRate != mRefreshRateInHighZone) {
1355 mRefreshRateInHighZone = refreshRate;
1356 restartObserver();
1357 }
1358 }
1359
Long Lingbc841b02019-07-03 16:43:15 -07001360 public void dumpLocked(PrintWriter pw) {
1361 pw.println(" BrightnessObserver");
Long Lingffcd75f2020-01-27 14:46:54 -08001362 pw.println(" mAmbientLux: " + mAmbientLux);
raylinhsu0ecd3e12021-01-15 02:43:59 +08001363 pw.println(" mBrightness: " + mBrightness);
raylinhsu73ba11e2021-01-15 02:48:14 +08001364 pw.println(" mDefaultDisplayState: " + mDefaultDisplayState);
raylinhsu0ecd3e12021-01-15 02:43:59 +08001365 pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled);
1366 pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable);
1367 pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
1368 pw.println(" mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange);
1369 pw.println(" mRefreshRateInLowZone: " + mRefreshRateInLowZone);
Long Lingbc841b02019-07-03 16:43:15 -07001370
raylinhsu0ecd3e12021-01-15 02:43:59 +08001371 for (int d : mLowDisplayBrightnessThresholds) {
1372 pw.println(" mDisplayLowBrightnessThreshold: " + d);
Long Lingbc841b02019-07-03 16:43:15 -07001373 }
1374
raylinhsu0ecd3e12021-01-15 02:43:59 +08001375 for (int d : mLowAmbientBrightnessThresholds) {
1376 pw.println(" mAmbientLowBrightnessThreshold: " + d);
1377 }
1378
1379 pw.println(" mShouldObserveDisplayHighChange: " + mShouldObserveDisplayHighChange);
1380 pw.println(" mShouldObserveAmbientHighChange: " + mShouldObserveAmbientHighChange);
1381 pw.println(" mRefreshRateInHighZone: " + mRefreshRateInHighZone);
1382
1383 for (int d : mHighDisplayBrightnessThresholds) {
1384 pw.println(" mDisplayHighBrightnessThresholds: " + d);
1385 }
1386
1387 for (int d : mHighAmbientBrightnessThresholds) {
1388 pw.println(" mAmbientHighBrightnessThresholds: " + d);
Long Lingbc841b02019-07-03 16:43:15 -07001389 }
Long Lingffcd75f2020-01-27 14:46:54 -08001390
1391 mLightSensorListener.dumpLocked(pw);
raylinhsu0ecd3e12021-01-15 02:43:59 +08001392
1393 if (mAmbientFilter != null) {
1394 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
1395 ipw.setIndent(" ");
1396 mAmbientFilter.dump(ipw);
1397 }
Long Lingbc841b02019-07-03 16:43:15 -07001398 }
1399
Long Lingbb459a02019-08-08 16:05:44 -07001400 public void onDisplayChanged(int displayId) {
1401 if (displayId == Display.DEFAULT_DISPLAY) {
raylinhsu0ecd3e12021-01-15 02:43:59 +08001402 updateDefaultDisplayState();
Long Lingbb459a02019-08-08 16:05:44 -07001403 }
1404 }
1405
Long Lingbc841b02019-07-03 16:43:15 -07001406 @Override
1407 public void onChange(boolean selfChange, Uri uri, int userId) {
1408 synchronized (mLock) {
raylinhsu0ecd3e12021-01-15 02:43:59 +08001409 final ContentResolver cr = mContext.getContentResolver();
1410 int brightness = Settings.System.getIntForUser(cr,
1411 Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
1412 if (brightness != mBrightness) {
1413 mBrightness = brightness;
1414 onBrightnessChangedLocked();
1415 }
Long Lingbc841b02019-07-03 16:43:15 -07001416 }
1417 }
1418
Long Lingbb459a02019-08-08 16:05:44 -07001419 private void restartObserver() {
Long Lingbb459a02019-08-08 16:05:44 -07001420 final ContentResolver cr = mContext.getContentResolver();
raylinhsu0ecd3e12021-01-15 02:43:59 +08001421
1422 if (mRefreshRateInLowZone > 0) {
1423 mShouldObserveDisplayLowChange = hasValidThreshold(
1424 mLowDisplayBrightnessThresholds);
1425 mShouldObserveAmbientLowChange = hasValidThreshold(
1426 mLowAmbientBrightnessThresholds);
raylinhsu4a0a8962020-10-19 11:24:51 +08001427 } else {
raylinhsu0ecd3e12021-01-15 02:43:59 +08001428 mShouldObserveDisplayLowChange = false;
1429 mShouldObserveAmbientLowChange = false;
raylinhsu4a0a8962020-10-19 11:24:51 +08001430 }
1431
raylinhsu0ecd3e12021-01-15 02:43:59 +08001432 if (mRefreshRateInHighZone > 0) {
1433 mShouldObserveDisplayHighChange = hasValidThreshold(
1434 mHighDisplayBrightnessThresholds);
1435 mShouldObserveAmbientHighChange = hasValidThreshold(
1436 mHighAmbientBrightnessThresholds);
1437 } else {
1438 mShouldObserveDisplayHighChange = false;
1439 mShouldObserveAmbientHighChange = false;
1440 }
1441
1442 if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) {
1443 // Content Service does not check if an listener has already been registered.
1444 // To ensure only one listener is registered, force an unregistration first.
1445 mInjector.unregisterBrightnessObserver(cr, this);
1446 mInjector.registerBrightnessObserver(cr, this);
1447 } else {
1448 mInjector.unregisterBrightnessObserver(cr, this);
1449 }
1450
1451 if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
Long Lingbb459a02019-08-08 16:05:44 -07001452 Resources resources = mContext.getResources();
1453 String lightSensorType = resources.getString(
1454 com.android.internal.R.string.config_displayLightSensorType);
1455
1456 Sensor lightSensor = null;
1457 if (!TextUtils.isEmpty(lightSensorType)) {
1458 List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
1459 for (int i = 0; i < sensors.size(); i++) {
1460 Sensor sensor = sensors.get(i);
1461 if (lightSensorType.equals(sensor.getStringType())) {
1462 lightSensor = sensor;
1463 break;
1464 }
1465 }
1466 }
1467
1468 if (lightSensor == null) {
1469 lightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
1470 }
1471
1472 if (lightSensor != null) {
1473 final Resources res = mContext.getResources();
1474
Long Linga21479a2019-07-22 15:49:02 -07001475 mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
Long Lingbb459a02019-08-08 16:05:44 -07001476 mLightSensor = lightSensor;
Long Lingbb459a02019-08-08 16:05:44 -07001477 }
1478 } else {
1479 mAmbientFilter = null;
1480 mLightSensor = null;
1481 }
1482
Long Ling3b58eff2019-08-17 18:02:46 -07001483 if (mRefreshRateChangeable) {
1484 updateSensorStatus();
1485 synchronized (mLock) {
1486 onBrightnessChangedLocked();
1487 }
Long Lingbb459a02019-08-08 16:05:44 -07001488 }
1489 }
1490
Long Lingbc841b02019-07-03 16:43:15 -07001491 /**
1492 * Checks to see if at least one value is positive, in which case it is necessary to listen
1493 * to value changes.
1494 */
raylinhsu0ecd3e12021-01-15 02:43:59 +08001495 private boolean hasValidThreshold(int[] a) {
Long Lingbc841b02019-07-03 16:43:15 -07001496 for (int d: a) {
1497 if (d >= 0) {
1498 return true;
1499 }
1500 }
1501
1502 return false;
1503 }
1504
raylinhsu0ecd3e12021-01-15 02:43:59 +08001505 private boolean isInsideLowZone(int brightness, float lux) {
1506 for (int i = 0; i < mLowDisplayBrightnessThresholds.length; i++) {
1507 int disp = mLowDisplayBrightnessThresholds[i];
1508 int ambi = mLowAmbientBrightnessThresholds[i];
Long Ling73936632019-08-20 15:01:14 -07001509
1510 if (disp >= 0 && ambi >= 0) {
raylinhsu0ecd3e12021-01-15 02:43:59 +08001511 if (brightness <= disp && lux <= ambi) {
Long Ling73936632019-08-20 15:01:14 -07001512 return true;
1513 }
1514 } else if (disp >= 0) {
1515 if (brightness <= disp) {
1516 return true;
1517 }
1518 } else if (ambi >= 0) {
raylinhsu0ecd3e12021-01-15 02:43:59 +08001519 if (lux <= ambi) {
Long Ling73936632019-08-20 15:01:14 -07001520 return true;
1521 }
1522 }
1523 }
1524
1525 return false;
1526 }
raylinhsub4bf83a2021-01-13 13:27:25 +08001527
raylinhsu0ecd3e12021-01-15 02:43:59 +08001528 private boolean isInsideHighZone(int brightness, float lux) {
1529 for (int i = 0; i < mHighDisplayBrightnessThresholds.length; i++) {
1530 int disp = mHighDisplayBrightnessThresholds[i];
1531 int ambi = mHighAmbientBrightnessThresholds[i];
1532
1533 if (disp >= 0 && ambi >= 0) {
1534 if (brightness >= disp && lux >= ambi) {
1535 return true;
1536 }
1537 } else if (disp >= 0) {
1538 if (brightness >= disp) {
1539 return true;
1540 }
1541 } else if (ambi >= 0) {
1542 if (lux >= ambi) {
1543 return true;
1544 }
1545 }
1546 }
1547
1548 return false;
1549 }
1550 private void onBrightnessChangedLocked() {
Long Lingbc841b02019-07-03 16:43:15 -07001551 Vote vote = null;
raylinhsu0ecd3e12021-01-15 02:43:59 +08001552
1553 if (mBrightness < 0) {
1554 // Either the setting isn't available or we shouldn't be observing yet anyways.
1555 // Either way, just bail out since there's nothing we can do here.
1556 return;
1557 }
1558
1559 boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux);
1560 if (insideLowZone) {
1561 vote = Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
1562 }
1563
1564 boolean insideHighZone = hasValidHighZone()
1565 && isInsideHighZone(mBrightness, mAmbientLux);
1566 if (insideHighZone) {
1567 vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
Long Lingbc841b02019-07-03 16:43:15 -07001568 }
1569
raylinhsuf6247e32021-01-11 11:56:27 +08001570 if (mLoggingEnabled) {
raylinhsu0ecd3e12021-01-15 02:43:59 +08001571 Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " + mAmbientLux
1572 + ", Vote " + vote);
Long Lingbc841b02019-07-03 16:43:15 -07001573 }
raylinhsu0ecd3e12021-01-15 02:43:59 +08001574 updateVoteLocked(Vote.PRIORITY_FLICKER, vote);
Long Lingbc841b02019-07-03 16:43:15 -07001575 }
1576
raylinhsu0ecd3e12021-01-15 02:43:59 +08001577 private boolean hasValidLowZone() {
1578 return mRefreshRateInLowZone > 0
1579 && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange);
1580 }
1581
1582 private boolean hasValidHighZone() {
1583 return mRefreshRateInHighZone > 0
1584 && (mShouldObserveDisplayHighChange || mShouldObserveAmbientHighChange);
1585 }
1586
1587 private void updateDefaultDisplayState() {
1588 Display display = mContext.getSystemService(DisplayManager.class)
1589 .getDisplay(Display.DEFAULT_DISPLAY);
raylinhsu73ba11e2021-01-15 02:48:14 +08001590 if (display == null) {
1591 return;
1592 }
1593
1594 setDefaultDisplayState(display.getState());
raylinhsu0ecd3e12021-01-15 02:43:59 +08001595 }
1596
1597 @VisibleForTesting
raylinhsu73ba11e2021-01-15 02:48:14 +08001598 public void setDefaultDisplayState(int state) {
raylinhsuf6247e32021-01-11 11:56:27 +08001599 if (mLoggingEnabled) {
1600 Slog.d(TAG, "setDefaultDisplayState: mDefaultDisplayState = "
1601 + mDefaultDisplayState + ", state = " + state);
1602 }
1603
raylinhsu73ba11e2021-01-15 02:48:14 +08001604 if (mDefaultDisplayState != state) {
1605 mDefaultDisplayState = state;
Long Lingbc841b02019-07-03 16:43:15 -07001606 updateSensorStatus();
1607 }
1608 }
1609
1610 private void updateSensorStatus() {
1611 if (mSensorManager == null || mLightSensorListener == null) {
1612 return;
1613 }
1614
raylinhsuf6247e32021-01-11 11:56:27 +08001615 if (mLoggingEnabled) {
1616 Slog.d(TAG, "updateSensorStatus: mShouldObserveAmbientLowChange = "
1617 + mShouldObserveAmbientLowChange + ", mShouldObserveAmbientHighChange = "
1618 + mShouldObserveAmbientHighChange);
1619 Slog.d(TAG, "updateSensorStatus: mLowPowerModeEnabled = "
1620 + mLowPowerModeEnabled + ", mRefreshRateChangeable = "
1621 + mRefreshRateChangeable);
1622 }
1623
raylinhsu0ecd3e12021-01-15 02:43:59 +08001624 if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
1625 && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
Long Lingbc841b02019-07-03 16:43:15 -07001626 mSensorManager.registerListener(mLightSensorListener,
1627 mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
raylinhsuf6247e32021-01-11 11:56:27 +08001628 if (mLoggingEnabled) {
1629 Slog.d(TAG, "updateSensorStatus: registerListener");
1630 }
Long Lingbc841b02019-07-03 16:43:15 -07001631 } else {
Long Lingc1ee0422019-07-26 14:23:53 -07001632 mLightSensorListener.removeCallbacks();
Long Lingbc841b02019-07-03 16:43:15 -07001633 mSensorManager.unregisterListener(mLightSensorListener);
raylinhsuf6247e32021-01-11 11:56:27 +08001634 if (mLoggingEnabled) {
1635 Slog.d(TAG, "updateSensorStatus: unregisterListener");
1636 }
Long Lingbc841b02019-07-03 16:43:15 -07001637 }
1638 }
1639
raylinhsu0ecd3e12021-01-15 02:43:59 +08001640 private boolean isDeviceActive() {
raylinhsuf63122d2021-01-22 11:41:05 +08001641 return mDefaultDisplayState == Display.STATE_ON;
Long Lingf3afe7d2019-08-05 12:19:42 -07001642 }
1643
Long Lingc1ee0422019-07-26 14:23:53 -07001644 private final class LightSensorEventListener implements SensorEventListener {
1645 final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
1646 private float mLastSensorData;
raylinhsu73ba11e2021-01-15 02:48:14 +08001647 private long mTimestamp;
raylinhsuf6247e32021-01-11 11:56:27 +08001648 private boolean mLoggingEnabled;
Long Lingc1ee0422019-07-26 14:23:53 -07001649
Long Lingffcd75f2020-01-27 14:46:54 -08001650 public void dumpLocked(PrintWriter pw) {
1651 pw.println(" mLastSensorData: " + mLastSensorData);
raylinhsu73ba11e2021-01-15 02:48:14 +08001652 pw.println(" mTimestamp: " + formatTimestamp(mTimestamp));
Long Lingffcd75f2020-01-27 14:46:54 -08001653 }
1654
raylinhsuf6247e32021-01-11 11:56:27 +08001655
1656 public void setLoggingEnabled(boolean loggingEnabled) {
1657 if (mLoggingEnabled == loggingEnabled) {
1658 return;
1659 }
1660 mLoggingEnabled = loggingEnabled;
1661 }
1662
Long Lingbc841b02019-07-03 16:43:15 -07001663 @Override
1664 public void onSensorChanged(SensorEvent event) {
Long Lingc1ee0422019-07-26 14:23:53 -07001665 mLastSensorData = event.values[0];
raylinhsuf6247e32021-01-11 11:56:27 +08001666 if (mLoggingEnabled) {
Long Lingc1ee0422019-07-26 14:23:53 -07001667 Slog.d(TAG, "On sensor changed: " + mLastSensorData);
1668 }
Long Lingbc841b02019-07-03 16:43:15 -07001669
raylinhsu0ecd3e12021-01-15 02:43:59 +08001670 boolean lowZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
1671 mLowAmbientBrightnessThresholds);
1672 boolean highZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
1673 mHighAmbientBrightnessThresholds);
1674 if ((lowZoneChanged && mLastSensorData < mAmbientLux)
1675 || (highZoneChanged && mLastSensorData > mAmbientLux)) {
1676 // Easier to see flicker at lower brightness environment or high brightness
1677 // environment. Forget the history to get immediate response.
1678 if (mAmbientFilter != null) {
1679 mAmbientFilter.clear();
1680 }
Long Lingc1ee0422019-07-26 14:23:53 -07001681 }
1682
1683 long now = SystemClock.uptimeMillis();
raylinhsu73ba11e2021-01-15 02:48:14 +08001684 mTimestamp = System.currentTimeMillis();
raylinhsu0ecd3e12021-01-15 02:43:59 +08001685 if (mAmbientFilter != null) {
1686 mAmbientFilter.addValue(now, mLastSensorData);
1687 }
Long Lingc1ee0422019-07-26 14:23:53 -07001688
1689 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1690 processSensorData(now);
1691
raylinhsu0ecd3e12021-01-15 02:43:59 +08001692 if ((lowZoneChanged && mLastSensorData > mAmbientLux)
1693 || (highZoneChanged && mLastSensorData < mAmbientLux)) {
Long Lingc1ee0422019-07-26 14:23:53 -07001694 // Sensor may not report new event if there is no brightness change.
1695 // Need to keep querying the temporal filter for the latest estimation,
raylinhsu0ecd3e12021-01-15 02:43:59 +08001696 // until sensor readout and filter estimation are in the same zone or
1697 // is interrupted by a new sensor event.
Long Lingc1ee0422019-07-26 14:23:53 -07001698 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
Long Lingbc841b02019-07-03 16:43:15 -07001699 }
1700 }
1701
1702 @Override
1703 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1704 // Not used.
1705 }
Long Lingc1ee0422019-07-26 14:23:53 -07001706
1707 public void removeCallbacks() {
1708 mHandler.removeCallbacks(mInjectSensorEventRunnable);
1709 }
1710
raylinhsu73ba11e2021-01-15 02:48:14 +08001711 private String formatTimestamp(long time) {
1712 SimpleDateFormat dateFormat =
1713 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
1714 return dateFormat.format(new Date(time));
1715 }
1716
Long Lingc1ee0422019-07-26 14:23:53 -07001717 private void processSensorData(long now) {
raylinhsu0ecd3e12021-01-15 02:43:59 +08001718 if (mAmbientFilter != null) {
1719 mAmbientLux = mAmbientFilter.getEstimate(now);
1720 } else {
1721 mAmbientLux = mLastSensorData;
1722 }
Long Lingc1ee0422019-07-26 14:23:53 -07001723
1724 synchronized (mLock) {
1725 onBrightnessChangedLocked();
1726 }
1727 }
1728
raylinhsu0ecd3e12021-01-15 02:43:59 +08001729 private boolean isDifferentZone(float lux1, float lux2, int[] luxThresholds) {
1730 for (final float boundary : luxThresholds) {
Long Lingc1ee0422019-07-26 14:23:53 -07001731 // Test each boundary. See if the current value and the new value are at
1732 // different sides.
1733 if ((lux1 <= boundary && lux2 > boundary)
1734 || (lux1 > boundary && lux2 <= boundary)) {
1735 return true;
1736 }
1737 }
1738
1739 return false;
1740 }
1741
1742 private Runnable mInjectSensorEventRunnable = new Runnable() {
1743 @Override
1744 public void run() {
1745 long now = SystemClock.uptimeMillis();
1746 // No need to really inject the last event into a temporal filter.
1747 processSensorData(now);
1748
1749 // Inject next event if there is a possible zone change.
raylinhsu0ecd3e12021-01-15 02:43:59 +08001750 if (isDifferentZone(mLastSensorData, mAmbientLux,
1751 mLowAmbientBrightnessThresholds)
1752 || isDifferentZone(mLastSensorData, mAmbientLux,
1753 mHighAmbientBrightnessThresholds)) {
Long Lingc1ee0422019-07-26 14:23:53 -07001754 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
1755 }
1756 }
1757 };
Long Lingbb459a02019-08-08 16:05:44 -07001758 }
Long Lingbc841b02019-07-03 16:43:15 -07001759 }
Long Lingbb459a02019-08-08 16:05:44 -07001760
1761 private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
Long Lingbb459a02019-08-08 16:05:44 -07001762 public DeviceConfigDisplaySettings() {
1763 }
1764
1765 public void startListening() {
raylinhsu0ecd3e12021-01-15 02:43:59 +08001766 mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
Long Lingbb459a02019-08-08 16:05:44 -07001767 BackgroundThread.getExecutor(), this);
1768 }
1769
1770 /*
1771 * Return null if no such property or wrong format (not comma separated integers).
1772 */
raylinhsu0ecd3e12021-01-15 02:43:59 +08001773 public int[] getLowDisplayBrightnessThresholds() {
Long Lingbb459a02019-08-08 16:05:44 -07001774 return getIntArrayProperty(
Long Ling73936632019-08-20 15:01:14 -07001775 DisplayManager.DeviceConfig.
Michael Wright58515942021-01-15 02:47:05 +08001776 KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
Long Lingbb459a02019-08-08 16:05:44 -07001777 }
1778
1779 /*
1780 * Return null if no such property or wrong format (not comma separated integers).
1781 */
raylinhsu0ecd3e12021-01-15 02:43:59 +08001782 public int[] getLowAmbientBrightnessThresholds() {
Long Lingbb459a02019-08-08 16:05:44 -07001783 return getIntArrayProperty(
Long Ling73936632019-08-20 15:01:14 -07001784 DisplayManager.DeviceConfig.
Michael Wright58515942021-01-15 02:47:05 +08001785 KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
Long Lingbb459a02019-08-08 16:05:44 -07001786 }
1787
raylinhsu0ecd3e12021-01-15 02:43:59 +08001788 public int getRefreshRateInLowZone() {
1789 int defaultRefreshRateInZone = mContext.getResources().getInteger(
1790 R.integer.config_defaultRefreshRateInZone);
1791
1792 int refreshRate = mDeviceConfig.getInt(
1793 DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
Michael Wright58515942021-01-15 02:47:05 +08001794 DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
1795 defaultRefreshRateInZone);
1796
1797 return refreshRate;
1798 }
1799
1800 /*
1801 * Return null if no such property or wrong format (not comma separated integers).
1802 */
1803 public int[] getHighDisplayBrightnessThresholds() {
1804 return getIntArrayProperty(
1805 DisplayManager.DeviceConfig
1806 .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
1807 }
1808
1809 /*
1810 * Return null if no such property or wrong format (not comma separated integers).
1811 */
1812 public int[] getHighAmbientBrightnessThresholds() {
1813 return getIntArrayProperty(
1814 DisplayManager.DeviceConfig
1815 .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
1816 }
1817
1818 public int getRefreshRateInHighZone() {
1819 int defaultRefreshRateInZone = mContext.getResources().getInteger(
1820 R.integer.config_fixedRefreshRateInHighZone);
1821
1822 int refreshRate = mDeviceConfig.getInt(
1823 DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1824 DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
raylinhsu0ecd3e12021-01-15 02:43:59 +08001825 defaultRefreshRateInZone);
1826
1827 return refreshRate;
1828 }
1829
Long Lingbb459a02019-08-08 16:05:44 -07001830 /*
1831 * Return null if no such property
1832 */
1833 public Float getDefaultPeakRefreshRate() {
raylinhsu0ecd3e12021-01-15 02:43:59 +08001834 float defaultPeakRefreshRate = mDeviceConfig.getFloat(
Long Lingbb459a02019-08-08 16:05:44 -07001835 DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
1836 DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
1837
1838 if (defaultPeakRefreshRate == -1) {
1839 return null;
1840 }
1841 return defaultPeakRefreshRate;
1842 }
1843
1844 @Override
1845 public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
Long Lingbb459a02019-08-08 16:05:44 -07001846 Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
Long Lingbb459a02019-08-08 16:05:44 -07001847 mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
1848 defaultPeakRefreshRate).sendToTarget();
raylinhsu0ecd3e12021-01-15 02:43:59 +08001849
1850 int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds();
1851 int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds();
1852 int refreshRateInLowZone = getRefreshRateInLowZone();
1853
1854 mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
1855 new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
1856 .sendToTarget();
1857 mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0)
1858 .sendToTarget();
Michael Wright58515942021-01-15 02:47:05 +08001859
1860 int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
1861 int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds();
1862 int refreshRateInHighZone = getRefreshRateInHighZone();
1863
1864 mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
1865 new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
1866 .sendToTarget();
1867 mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0)
1868 .sendToTarget();
Long Lingbb459a02019-08-08 16:05:44 -07001869 }
1870
1871 private int[] getIntArrayProperty(String prop) {
raylinhsu0ecd3e12021-01-15 02:43:59 +08001872 String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
Long Lingbb459a02019-08-08 16:05:44 -07001873 null);
1874
1875 if (strArray != null) {
1876 return parseIntArray(strArray);
1877 }
1878
1879 return null;
1880 }
1881
1882 private int[] parseIntArray(@NonNull String strArray) {
1883 String[] items = strArray.split(",");
1884 int[] array = new int[items.length];
1885
1886 try {
1887 for (int i = 0; i < array.length; i++) {
1888 array[i] = Integer.parseInt(items[i]);
1889 }
1890 } catch (NumberFormatException e) {
1891 Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
1892 array = null;
1893 }
1894
1895 return array;
1896 }
1897 }
1898
raylinhsu0ecd3e12021-01-15 02:43:59 +08001899 interface Injector {
1900 // TODO: brightnessfloat: change this to the float setting
1901 Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
1902 Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
1903
1904 @NonNull
1905 DeviceConfigInterface getDeviceConfig();
1906
1907 void registerBrightnessObserver(@NonNull ContentResolver cr,
1908 @NonNull ContentObserver observer);
1909
1910 void unregisterBrightnessObserver(@NonNull ContentResolver cr,
1911 @NonNull ContentObserver observer);
1912
1913 void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
1914 @NonNull ContentObserver observer);
raylinhsu0ecd3e12021-01-15 02:43:59 +08001915 }
1916
1917 @VisibleForTesting
1918 static class RealInjector implements Injector {
1919
1920 @Override
1921 @NonNull
1922 public DeviceConfigInterface getDeviceConfig() {
1923 return DeviceConfigInterface.REAL;
1924 }
1925
1926 @Override
1927 public void registerBrightnessObserver(@NonNull ContentResolver cr,
1928 @NonNull ContentObserver observer) {
1929 cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/,
1930 observer, UserHandle.USER_SYSTEM);
1931 }
1932
1933 @Override
1934 public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
1935 @NonNull ContentObserver observer) {
1936 cr.unregisterContentObserver(observer);
1937 }
1938
1939 @Override
1940 public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
1941 @NonNull ContentObserver observer) {
1942 cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
1943 observer, UserHandle.USER_SYSTEM);
1944 }
raylinhsu0ecd3e12021-01-15 02:43:59 +08001945 }
1946
Michael Wrighta3dab232019-02-22 16:54:21 +00001947}