1 /* 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 17 package com.android.server.display; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.ContentResolver; 22 import android.content.Context; 23 import android.content.res.Resources; 24 import android.database.ContentObserver; 25 import android.hardware.Sensor; 26 import android.hardware.SensorEvent; 27 import android.hardware.SensorEventListener; 28 import android.hardware.SensorManager; 29 import android.hardware.display.DisplayManager; 30 import android.net.Uri; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.PowerManager; 35 import android.os.SystemClock; 36 import android.os.UserHandle; 37 import android.provider.DeviceConfig; 38 import android.provider.Settings; 39 import android.text.TextUtils; 40 import android.util.Pair; 41 import android.util.Slog; 42 import android.util.SparseArray; 43 import android.view.Display; 44 import android.view.DisplayInfo; 45 46 import com.android.internal.R; 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.os.BackgroundThread; 49 import com.android.internal.util.IndentingPrintWriter; 50 import com.android.server.display.utils.AmbientFilter; 51 import com.android.server.display.utils.AmbientFilterFactory; 52 import com.android.server.utils.DeviceConfigInterface; 53 54 import java.io.PrintWriter; 55 import java.text.SimpleDateFormat; 56 import java.util.ArrayList; 57 import java.util.Arrays; 58 import java.util.Date; 59 import java.util.List; 60 import java.util.Locale; 61 import 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 */ 67 public class DisplayModeDirector { 68 private static final String TAG = "DisplayModeDirector"; 69 private boolean mLoggingEnabled; 70 71 private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1; 72 private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2; 73 private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3; 74 private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4; 75 private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5; 76 private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6; 77 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 82 // The tolerance within which we consider something approximately equals. 83 private static final float FLOAT_TOLERANCE = 0.01f; 84 85 private final Object mLock = new Object(); 86 private final Context mContext; 87 88 private final DisplayModeDirectorHandler mHandler; 89 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; 96 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. 100 private SparseArray<SparseArray<Vote>> mVotesByDisplay; 101 // A map from the display ID to the supported modes on that display. 102 private SparseArray<Display.Mode[]> mSupportedModesByDisplay; 103 // A map from the display ID to the default mode of that display. 104 private SparseArray<Display.Mode> mDefaultModeByDisplay; 105 106 private BrightnessObserver mBrightnessObserver; 107 108 private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener; 109 DisplayModeDirector(@onNull Context context, @NonNull Handler handler)110 public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) { 111 this(context, handler, new RealInjector()); 112 } 113 DisplayModeDirector(@onNull Context context, @NonNull Handler handler, @NonNull Injector injector)114 public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler, 115 @NonNull Injector injector) { 116 mContext = context; 117 mHandler = new DisplayModeDirectorHandler(handler.getLooper()); 118 mInjector = injector; 119 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); 125 mBrightnessObserver = new BrightnessObserver(context, handler); 126 mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); 127 mDeviceConfig = injector.getDeviceConfig(); 128 } 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 */ start(SensorManager sensorManager)137 public void start(SensorManager sensorManager) { 138 mSettingsObserver.observe(); 139 mDisplayObserver.observe(); 140 mSettingsObserver.observe(); 141 mBrightnessObserver.observe(sensorManager); 142 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. 145 notifyDesiredDisplayModeSpecsChangedLocked(); 146 } 147 148 } 149 setLoggingEnabled(boolean loggingEnabled)150 public void setLoggingEnabled(boolean loggingEnabled) { 151 if (mLoggingEnabled == loggingEnabled) { 152 return; 153 } 154 mLoggingEnabled = loggingEnabled; 155 mBrightnessObserver.setLoggingEnabled(loggingEnabled); 156 } 157 158 @NonNull getVotesLocked(int displayId)159 private SparseArray<Vote> getVotesLocked(int displayId) { 160 SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId); 161 final SparseArray<Vote> votes; 162 if (displayVotes != null) { 163 votes = displayVotes.clone(); 164 } else { 165 votes = new SparseArray<>(); 166 } 167 168 SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID); 169 if (globalVotes != null) { 170 for (int i = 0; i < globalVotes.size(); i++) { 171 int priority = globalVotes.keyAt(i); 172 if (votes.indexOfKey(priority) < 0) { 173 votes.put(priority, globalVotes.valueAt(i)); 174 } 175 } 176 } 177 return votes; 178 } 179 180 private static final class VoteSummary { 181 public float minRefreshRate; 182 public float maxRefreshRate; 183 public int width; 184 public int height; 185 VoteSummary()186 VoteSummary() { 187 reset(); 188 } 189 reset()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. summarizeVotes( SparseArray<Vote> votes, int lowestConsideredPriority, VoteSummary summary)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 221 /** 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 */ 229 @NonNull getDesiredDisplayModeSpecs(int displayId)230 public DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(int displayId) { 231 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) { 236 Slog.e(TAG, 237 "Asked about unknown display, returning empty display mode specs!" 238 + "(id=" + displayId + ")"); 239 return new DesiredDisplayModeSpecs(); 240 } 241 242 int[] availableModes = new int[]{defaultMode.getModeId()}; 243 VoteSummary primarySummary = new VoteSummary(); 244 int lowestConsideredPriority = Vote.MIN_PRIORITY; 245 while (lowestConsideredPriority <= Vote.MAX_PRIORITY) { 246 summarizeVotes(votes, lowestConsideredPriority, primarySummary); 247 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. 251 if (primarySummary.height == Vote.INVALID_SIZE 252 || primarySummary.width == Vote.INVALID_SIZE) { 253 primarySummary.width = defaultMode.getPhysicalWidth(); 254 primarySummary.height = defaultMode.getPhysicalHeight(); 255 } 256 257 availableModes = filterModes(modes, primarySummary); 258 if (availableModes.length > 0) { 259 if (mLoggingEnabled) { 260 Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes) 261 + " with lowest priority considered " 262 + Vote.priorityToString(lowestConsideredPriority) 263 + " and constraints: " 264 + "width=" + primarySummary.width 265 + ", height=" + primarySummary.height 266 + ", minRefreshRate=" + primarySummary.minRefreshRate 267 + ", maxRefreshRate=" + primarySummary.maxRefreshRate); 268 } 269 break; 270 } 271 272 if (mLoggingEnabled) { 273 Slog.w(TAG, "Couldn't find available modes with lowest priority set to " 274 + Vote.priorityToString(lowestConsideredPriority) 275 + " and with the following constraints: " 276 + "width=" + primarySummary.width 277 + ", height=" + primarySummary.height 278 + ", minRefreshRate=" + primarySummary.minRefreshRate 279 + ", maxRefreshRate=" + primarySummary.maxRefreshRate); 280 } 281 282 // If we haven't found anything with the current set of votes, drop the 283 // current lowest priority vote. 284 lowestConsideredPriority++; 285 } 286 287 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); 294 if (mLoggingEnabled) { 295 Slog.i(TAG, 296 String.format("App request range: [%.0f %.0f]", 297 appRequestSummary.minRefreshRate, 298 appRequestSummary.maxRefreshRate)); 299 } 300 301 int baseModeId = defaultMode.getModeId(); 302 if (availableModes.length > 0) { 303 baseModeId = availableModes[0]; 304 } 305 // 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 307 // stored as baseModeId. 308 return new DesiredDisplayModeSpecs(baseModeId, 309 new RefreshRateRange( 310 primarySummary.minRefreshRate, primarySummary.maxRefreshRate), 311 new RefreshRateRange( 312 appRequestSummary.minRefreshRate, appRequestSummary.maxRefreshRate)); 313 } 314 } 315 filterModes(Display.Mode[] supportedModes, VoteSummary summary)316 private int[] filterModes(Display.Mode[] supportedModes, VoteSummary summary) { 317 ArrayList<Display.Mode> availableModes = new ArrayList<>(); 318 for (Display.Mode mode : supportedModes) { 319 if (mode.getPhysicalWidth() != summary.width 320 || mode.getPhysicalHeight() != summary.height) { 321 if (mLoggingEnabled) { 322 Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size" 323 + ": desiredWidth=" + summary.width 324 + ": desiredHeight=" + summary.height 325 + ": 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. 334 if (refreshRate < (summary.minRefreshRate - FLOAT_TOLERANCE) 335 || refreshRate > (summary.maxRefreshRate + FLOAT_TOLERANCE)) { 336 if (mLoggingEnabled) { 337 Slog.w(TAG, "Discarding mode " + mode.getModeId() 338 + ", outside refresh rate bounds" 339 + ": minRefreshRate=" + summary.minRefreshRate 340 + ", maxRefreshRate=" + summary.maxRefreshRate 341 + ", 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 getAppRequestObserver()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 /** 366 * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate 367 * ranges. 368 */ setDesiredDisplayModeSpecsListener( @ullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener)369 public void setDesiredDisplayModeSpecsListener( 370 @Nullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener) { 371 synchronized (mLock) { 372 mDesiredDisplayModeSpecsListener = desiredDisplayModeSpecsListener; 373 } 374 } 375 376 /** 377 * 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 getVote(int displayId, int priority)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 /** 394 * Print the object's state and debug information into the given stream. 395 * 396 * @param pw The stream to dump information to. 397 */ dump(PrintWriter pw)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); 427 mBrightnessObserver.dumpLocked(pw); 428 } 429 } 430 updateVoteLocked(int priority, Vote vote)431 private void updateVoteLocked(int priority, Vote vote) { 432 updateVoteLocked(GLOBAL_ID, priority, vote); 433 } 434 updateVoteLocked(int displayId, int priority, Vote vote)435 private void updateVoteLocked(int displayId, int priority, Vote vote) { 436 if (mLoggingEnabled) { 437 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) { 457 if (mLoggingEnabled) { 458 Slog.i(TAG, "No votes left for display " + displayId + ", removing."); 459 } 460 mVotesByDisplay.remove(displayId); 461 } 462 463 notifyDesiredDisplayModeSpecsChangedLocked(); 464 } 465 notifyDesiredDisplayModeSpecsChangedLocked()466 private void notifyDesiredDisplayModeSpecsChangedLocked() { 467 if (mDesiredDisplayModeSpecsListener != null 468 && !mHandler.hasMessages(MSG_REFRESH_RATE_RANGE_CHANGED)) { 469 // 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 471 // 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); 475 msg.sendToTarget(); 476 } 477 } 478 getOrCreateVotesByDisplay(int displayId)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 490 @VisibleForTesting injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay)491 void injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay) { 492 mSupportedModesByDisplay = supportedModesByDisplay; 493 } 494 495 @VisibleForTesting injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay)496 void injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay) { 497 mDefaultModeByDisplay = defaultModeByDisplay; 498 } 499 500 @VisibleForTesting injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay)501 void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) { 502 mVotesByDisplay = votesByDisplay; 503 } 504 505 @VisibleForTesting injectBrightnessObserver(BrightnessObserver brightnessObserver)506 void injectBrightnessObserver(BrightnessObserver brightnessObserver) { 507 mBrightnessObserver = brightnessObserver; 508 } 509 510 @VisibleForTesting getBrightnessObserver()511 BrightnessObserver getBrightnessObserver() { 512 return mBrightnessObserver; 513 } 514 515 @VisibleForTesting getSettingsObserver()516 SettingsObserver getSettingsObserver() { 517 return mSettingsObserver; 518 } 519 520 521 @VisibleForTesting getDesiredDisplayModeSpecsWithInjectedFpsSettings( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate)522 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 531 /** 532 * Listens for changes refresh rate coordination. 533 */ 534 public interface DesiredDisplayModeSpecsListener { 535 /** 536 * Called when the refresh rate range may have changed. 537 */ onDesiredDisplayModeSpecsChanged()538 void onDesiredDisplayModeSpecsChanged(); 539 } 540 541 private final class DisplayModeDirectorHandler extends Handler { DisplayModeDirectorHandler(Looper looper)542 DisplayModeDirectorHandler(Looper looper) { 543 super(looper, null, true /*async*/); 544 } 545 546 @Override handleMessage(Message msg)547 public void handleMessage(Message msg) { 548 switch (msg.what) { 549 case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED: { 550 Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj; 551 mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged( 552 thresholds.first, thresholds.second); 553 break; 554 } 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 } 578 579 case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED: 580 Float defaultPeakRefreshRate = (Float) msg.obj; 581 mSettingsObserver.onDeviceConfigDefaultPeakRefreshRateChanged( 582 defaultPeakRefreshRate); 583 break; 584 585 case MSG_REFRESH_RATE_RANGE_CHANGED: 586 DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener = 587 (DesiredDisplayModeSpecsListener) msg.obj; 588 desiredDisplayModeSpecsListener.onDesiredDisplayModeSpecsChanged(); 589 break; 590 } 591 } 592 } 593 594 /** 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 */ 601 public float min; 602 /** 603 * The highest desired refresh rate. 604 */ 605 public float max; 606 RefreshRateRange()607 public RefreshRateRange() {} 608 RefreshRateRange(float min, float max)609 public RefreshRateRange(float min, float max) { 610 if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) { 611 Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : " 612 + min + " " + max); 613 this.min = this.max = 0; 614 return; 615 } 616 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 } 622 this.min = min; 623 this.max = max; 624 } 625 626 /** 627 * Checks whether the two objects have the same values. 628 */ 629 @Override equals(Object other)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 hashCode()644 public int hashCode() { 645 return Objects.hash(min, max); 646 } 647 648 @Override toString()649 public String toString() { 650 return "(" + min + " " + max + ")"; 651 } 652 } 653 654 /** 655 * Information about the desired display mode to be set by the system. Includes the base 656 * mode ID and the primary and app request refresh rate ranges. 657 * 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. 661 */ 662 public static final class DesiredDisplayModeSpecs { 663 /** 664 * Base mode ID. This is what system defaults to for all other settings, or 665 * if the refresh rate range is not available. 666 */ 667 public int baseModeId; 668 /** 669 * The primary refresh rate range. 670 */ 671 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; 679 DesiredDisplayModeSpecs()680 public DesiredDisplayModeSpecs() { 681 primaryRefreshRateRange = new RefreshRateRange(); 682 appRequestRefreshRateRange = new RefreshRateRange(); 683 } 684 DesiredDisplayModeSpecs(int baseModeId, @NonNull RefreshRateRange primaryRefreshRateRange, @NonNull RefreshRateRange appRequestRefreshRateRange)685 public DesiredDisplayModeSpecs(int baseModeId, 686 @NonNull RefreshRateRange primaryRefreshRateRange, 687 @NonNull RefreshRateRange appRequestRefreshRateRange) { 688 this.baseModeId = baseModeId; 689 this.primaryRefreshRateRange = primaryRefreshRateRange; 690 this.appRequestRefreshRateRange = appRequestRefreshRateRange; 691 } 692 693 /** 694 * Returns a string representation of the object. 695 */ 696 @Override toString()697 public String toString() { 698 return String.format("baseModeId=%d primaryRefreshRateRange=[%.0f %.0f]" 699 + " appRequestRefreshRateRange=[%.0f %.0f]", 700 baseModeId, primaryRefreshRateRange.min, primaryRefreshRateRange.max, 701 appRequestRefreshRateRange.min, appRequestRefreshRateRange.max); 702 } 703 /** 704 * Checks whether the two objects have the same values. 705 */ 706 @Override equals(Object other)707 public boolean equals(Object other) { 708 if (other == this) { 709 return true; 710 } 711 712 if (!(other instanceof DesiredDisplayModeSpecs)) { 713 return false; 714 } 715 716 DesiredDisplayModeSpecs desiredDisplayModeSpecs = (DesiredDisplayModeSpecs) other; 717 718 if (baseModeId != desiredDisplayModeSpecs.baseModeId) { 719 return false; 720 } 721 if (!primaryRefreshRateRange.equals(desiredDisplayModeSpecs.primaryRefreshRateRange)) { 722 return false; 723 } 724 if (!appRequestRefreshRateRange.equals( 725 desiredDisplayModeSpecs.appRequestRefreshRateRange)) { 726 return false; 727 } 728 return true; 729 } 730 731 @Override hashCode()732 public int hashCode() { 733 return Objects.hash(baseModeId, primaryRefreshRateRange, appRequestRefreshRateRange); 734 } 735 736 /** 737 * Copy values from the other object. 738 */ copyFrom(DesiredDisplayModeSpecs other)739 public void copyFrom(DesiredDisplayModeSpecs other) { 740 baseModeId = other.baseModeId; 741 primaryRefreshRateRange.min = other.primaryRefreshRateRange.min; 742 primaryRefreshRateRange.max = other.primaryRefreshRateRange.max; 743 appRequestRefreshRateRange.min = other.appRequestRefreshRateRange.min; 744 appRequestRefreshRateRange.max = other.appRequestRefreshRateRange.max; 745 } 746 } 747 748 @VisibleForTesting 749 static final class Vote { 750 // 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 754 // FLICKER votes for a single refresh rate like [60,60], [90,90] or null. 755 // If the higher voters result is a range, it will fix the rate to a single choice. 756 // 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; 759 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] 762 public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2; 763 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 772 public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3; 773 public static final int PRIORITY_APP_REQUEST_SIZE = 4; 774 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)] 777 public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 5; 778 779 // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on. 780 public static final int PRIORITY_LOW_POWER_MODE = 6; 781 782 // 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. 784 785 public static final int MIN_PRIORITY = PRIORITY_DEFAULT_REFRESH_RATE; 786 public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE; 787 788 // 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 793 /** 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; 806 /** 807 * Information about the min and max refresh rate DM would like to set the display to. 808 */ 809 public final RefreshRateRange refreshRateRange; 810 forRefreshRates(float minRefreshRate, float maxRefreshRate)811 public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) { 812 return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate); 813 } 814 forSize(int width, int height)815 public static Vote forSize(int width, int height) { 816 return new Vote(width, height, 0f, Float.POSITIVE_INFINITY); 817 } 818 Vote(int width, int height, float minRefreshRate, float maxRefreshRate)819 private Vote(int width, int height, 820 float minRefreshRate, float maxRefreshRate) { 821 this.width = width; 822 this.height = height; 823 this.refreshRateRange = 824 new RefreshRateRange(minRefreshRate, maxRefreshRate); 825 } 826 priorityToString(int priority)827 public static String priorityToString(int priority) { 828 switch (priority) { 829 case PRIORITY_DEFAULT_REFRESH_RATE: 830 return "PRIORITY_DEFAULT_REFRESH_RATE"; 831 case PRIORITY_FLICKER: 832 return "PRIORITY_FLICKER"; 833 case PRIORITY_USER_SETTING_MIN_REFRESH_RATE: 834 return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE"; 835 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"; 839 case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE: 840 return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE"; 841 case PRIORITY_LOW_POWER_MODE: 842 return "PRIORITY_LOW_POWER_MODE"; 843 default: 844 return Integer.toString(priority); 845 } 846 } 847 848 @Override toString()849 public String toString() { 850 return "Vote{" 851 + "width=" + width + ", height=" + height 852 + ", minRefreshRate=" + refreshRateRange.min 853 + ", maxRefreshRate=" + refreshRateRange.max + "}"; 854 } 855 } 856 857 @VisibleForTesting 858 final class SettingsObserver extends ContentObserver { 859 private final Uri mPeakRefreshRateSetting = 860 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); 861 private final Uri mMinRefreshRateSetting = 862 Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE); 863 private final Uri mLowPowerModeSetting = 864 Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE); 865 866 private final Context mContext; 867 private float mDefaultPeakRefreshRate; 868 private float mDefaultRefreshRate; 869 SettingsObserver(@onNull Context context, @NonNull Handler handler)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); 875 mDefaultRefreshRate = 876 (float) context.getResources().getInteger(R.integer.config_defaultRefreshRate); 877 } 878 observe()879 public void observe() { 880 final ContentResolver cr = mContext.getContentResolver(); 881 mInjector.registerPeakRefreshRateObserver(cr, this); 882 cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this, 883 UserHandle.USER_SYSTEM); 884 cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this, 885 UserHandle.USER_SYSTEM); 886 887 Float deviceConfigDefaultPeakRefresh = 888 mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate(); 889 if (deviceConfigDefaultPeakRefresh != null) { 890 mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh; 891 } 892 893 synchronized (mLock) { 894 updateRefreshRateSettingLocked(); 895 updateLowPowerModeSettingLocked(); 896 } 897 } 898 setDefaultRefreshRate(float refreshRate)899 public void setDefaultRefreshRate(float refreshRate) { 900 synchronized (mLock) { 901 mDefaultRefreshRate = refreshRate; 902 updateRefreshRateSettingLocked(); 903 } 904 } 905 onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate)906 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 920 @Override onChange(boolean selfChange, Uri uri, int userId)921 public void onChange(boolean selfChange, Uri uri, int userId) { 922 synchronized (mLock) { 923 if (mPeakRefreshRateSetting.equals(uri) 924 || mMinRefreshRateSetting.equals(uri)) { 925 updateRefreshRateSettingLocked(); 926 } else if (mLowPowerModeSetting.equals(uri)) { 927 updateLowPowerModeSettingLocked(); 928 } 929 } 930 } 931 updateLowPowerModeSettingLocked()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); 942 mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode); 943 } 944 updateRefreshRateSettingLocked()945 private void updateRefreshRateSettingLocked() { 946 float minRefreshRate = Settings.System.getFloat(mContext.getContentResolver(), 947 Settings.System.MIN_REFRESH_RATE, 0f); 948 float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(), 949 Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate); 950 updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate); 951 } 952 updateRefreshRateSettingLocked( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate)953 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); 963 updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, 964 Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY)); 965 Vote defaultVote = 966 defaultRefreshRate == 0f ? null : Vote.forRefreshRates(0f, defaultRefreshRate); 967 updateVoteLocked(Vote.PRIORITY_DEFAULT_REFRESH_RATE, defaultVote); 968 969 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); 987 } 988 dumpLocked(PrintWriter pw)989 public void dumpLocked(PrintWriter pw) { 990 pw.println(" SettingsObserver"); 991 pw.println(" mDefaultRefreshRate: " + mDefaultRefreshRate); 992 pw.println(" mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate); 993 } 994 } 995 996 final class AppRequestObserver { 997 private SparseArray<Display.Mode> mAppRequestedModeByDisplay; 998 AppRequestObserver()999 AppRequestObserver() { 1000 mAppRequestedModeByDisplay = new SparseArray<>(); 1001 } 1002 setAppRequestedMode(int displayId, int modeId)1003 public void setAppRequestedMode(int displayId, int modeId) { 1004 synchronized (mLock) { 1005 setAppRequestedModeLocked(displayId, modeId); 1006 } 1007 } 1008 setAppRequestedModeLocked(int displayId, int modeId)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 } 1028 1029 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote); 1030 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote); 1031 return; 1032 } 1033 findModeByIdLocked(int displayId, int modeId)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 dumpLocked(PrintWriter pw)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 DisplayObserver(Context context, Handler handler)1065 DisplayObserver(Context context, Handler handler) { 1066 mContext = context; 1067 mHandler = handler; 1068 } 1069 observe()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 onDisplayAdded(int displayId)1095 public void onDisplayAdded(int displayId) { 1096 updateDisplayModes(displayId); 1097 } 1098 1099 @Override onDisplayRemoved(int displayId)1100 public void onDisplayRemoved(int displayId) { 1101 synchronized (mLock) { 1102 mSupportedModesByDisplay.remove(displayId); 1103 mDefaultModeByDisplay.remove(displayId); 1104 } 1105 } 1106 1107 @Override onDisplayChanged(int displayId)1108 public void onDisplayChanged(int displayId) { 1109 updateDisplayModes(displayId); 1110 // TODO: Break the coupling between DisplayObserver and BrightnessObserver. 1111 mBrightnessObserver.onDisplayChanged(displayId); 1112 } 1113 updateDisplayModes(int displayId)1114 private void updateDisplayModes(int displayId) { 1115 Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId); 1116 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 } 1122 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) { 1135 notifyDesiredDisplayModeSpecsChangedLocked(); 1136 } 1137 } 1138 } 1139 } 1140 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 */ 1147 @VisibleForTesting 1148 public class BrightnessObserver extends ContentObserver { 1149 private final static int LIGHT_SENSOR_RATE_MS = 250; 1150 private int[] mLowDisplayBrightnessThresholds; 1151 private int[] mLowAmbientBrightnessThresholds; 1152 private int[] mHighDisplayBrightnessThresholds; 1153 private int[] mHighAmbientBrightnessThresholds; 1154 // valid threshold if any item from the array >= 0 1155 private boolean mShouldObserveDisplayLowChange; 1156 private boolean mShouldObserveAmbientLowChange; 1157 private boolean mShouldObserveDisplayHighChange; 1158 private boolean mShouldObserveAmbientHighChange; 1159 private boolean mLoggingEnabled; 1160 1161 private SensorManager mSensorManager; 1162 private Sensor mLightSensor; 1163 private LightSensorEventListener mLightSensorListener = new LightSensorEventListener(); 1164 // Take it as low brightness before valid sensor data comes 1165 private float mAmbientLux = -1.0f; 1166 private AmbientFilter mAmbientFilter; 1167 private int mBrightness = -1; 1168 1169 private final Context mContext; 1170 1171 // 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 1174 // be updated from the same handler thread. 1175 private int mDefaultDisplayState = Display.STATE_UNKNOWN; 1176 private boolean mRefreshRateChangeable = false; 1177 private boolean mLowPowerModeEnabled = false; 1178 1179 private int mRefreshRateInLowZone; 1180 private int mRefreshRateInHighZone; 1181 BrightnessObserver(Context context, Handler handler)1182 BrightnessObserver(Context context, Handler handler) { 1183 super(handler); 1184 mContext = context; 1185 mLowDisplayBrightnessThresholds = context.getResources().getIntArray( 1186 R.array.config_brightnessThresholdsOfPeakRefreshRate); 1187 mLowAmbientBrightnessThresholds = context.getResources().getIntArray( 1188 R.array.config_ambientThresholdsOfPeakRefreshRate); 1189 1190 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)); 1197 } 1198 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 getRefreshRateInLowZone()1220 int getRefreshRateInLowZone() { 1221 return mRefreshRateInLowZone; 1222 } 1223 1224 /** 1225 * @return the display brightness thresholds for the low brightness zones 1226 */ 1227 @VisibleForTesting getLowDisplayBrightnessThresholds()1228 int[] getLowDisplayBrightnessThresholds() { 1229 return mLowDisplayBrightnessThresholds; 1230 } 1231 1232 /** 1233 * @return the ambient brightness thresholds for the low brightness zones 1234 */ 1235 @VisibleForTesting getLowAmbientBrightnessThresholds()1236 int[] getLowAmbientBrightnessThresholds() { 1237 return mLowAmbientBrightnessThresholds; 1238 } 1239 registerLightSensor(SensorManager sensorManager, Sensor lightSensor)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 observe(SensorManager sensorManager)1248 public void observe(SensorManager sensorManager) { 1249 mSensorManager = sensorManager; 1250 final ContentResolver cr = mContext.getContentResolver(); 1251 mBrightness = Settings.System.getIntForUser(cr, 1252 Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId()); 1253 1254 // DeviceConfig is accessible after system ready. 1255 int[] lowDisplayBrightnessThresholds = 1256 mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(); 1257 int[] lowAmbientBrightnessThresholds = 1258 mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds(); 1259 1260 if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null 1261 && lowDisplayBrightnessThresholds.length 1262 == lowAmbientBrightnessThresholds.length) { 1263 mLowDisplayBrightnessThresholds = lowDisplayBrightnessThresholds; 1264 mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds; 1265 } 1266 1267 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 1280 mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone(); 1281 mRefreshRateInHighZone = mDeviceConfigDisplaySettings.getRefreshRateInHighZone(); 1282 1283 restartObserver(); 1284 mDeviceConfigDisplaySettings.startListening(); 1285 } 1286 setLoggingEnabled(boolean loggingEnabled)1287 public void setLoggingEnabled(boolean loggingEnabled) { 1288 if (mLoggingEnabled == loggingEnabled) { 1289 return; 1290 } 1291 mLoggingEnabled = loggingEnabled; 1292 mLightSensorListener.setLoggingEnabled(loggingEnabled); 1293 } 1294 onRefreshRateSettingChangedLocked(float min, float max)1295 public void onRefreshRateSettingChangedLocked(float min, float max) { 1296 boolean changeable = (max - min > 1f && max > 60f); 1297 if (mRefreshRateChangeable != changeable) { 1298 mRefreshRateChangeable = changeable; 1299 updateSensorStatus(); 1300 if (!changeable) { 1301 // Revoke previous vote from BrightnessObserver 1302 updateVoteLocked(Vote.PRIORITY_FLICKER, null); 1303 } 1304 } 1305 } 1306 onLowPowerModeEnabledLocked(boolean b)1307 public void onLowPowerModeEnabledLocked(boolean b) { 1308 if (mLowPowerModeEnabled != b) { 1309 mLowPowerModeEnabled = b; 1310 updateSensorStatus(); 1311 } 1312 } 1313 onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds, int[] ambientThresholds)1314 public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds, 1315 int[] ambientThresholds) { 1316 if (displayThresholds != null && ambientThresholds != null 1317 && displayThresholds.length == ambientThresholds.length) { 1318 mLowDisplayBrightnessThresholds = displayThresholds; 1319 mLowAmbientBrightnessThresholds = ambientThresholds; 1320 } else { 1321 // Invalid or empty. Use device default. 1322 mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray( 1323 R.array.config_brightnessThresholdsOfPeakRefreshRate); 1324 mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray( 1325 R.array.config_ambientThresholdsOfPeakRefreshRate); 1326 } 1327 restartObserver(); 1328 } 1329 onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate)1330 public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) { 1331 if (refreshRate != mRefreshRateInLowZone) { 1332 mRefreshRateInLowZone = refreshRate; 1333 restartObserver(); 1334 } 1335 } 1336 onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds, int[] ambientThresholds)1337 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 onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate)1353 public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) { 1354 if (refreshRate != mRefreshRateInHighZone) { 1355 mRefreshRateInHighZone = refreshRate; 1356 restartObserver(); 1357 } 1358 } 1359 dumpLocked(PrintWriter pw)1360 public void dumpLocked(PrintWriter pw) { 1361 pw.println(" BrightnessObserver"); 1362 pw.println(" mAmbientLux: " + mAmbientLux); 1363 pw.println(" mBrightness: " + mBrightness); 1364 pw.println(" mDefaultDisplayState: " + mDefaultDisplayState); 1365 pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled); 1366 pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable); 1367 pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange); 1368 pw.println(" mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange); 1369 pw.println(" mRefreshRateInLowZone: " + mRefreshRateInLowZone); 1370 1371 for (int d : mLowDisplayBrightnessThresholds) { 1372 pw.println(" mDisplayLowBrightnessThreshold: " + d); 1373 } 1374 1375 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); 1389 } 1390 1391 mLightSensorListener.dumpLocked(pw); 1392 1393 if (mAmbientFilter != null) { 1394 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 1395 ipw.setIndent(" "); 1396 mAmbientFilter.dump(ipw); 1397 } 1398 } 1399 onDisplayChanged(int displayId)1400 public void onDisplayChanged(int displayId) { 1401 if (displayId == Display.DEFAULT_DISPLAY) { 1402 updateDefaultDisplayState(); 1403 } 1404 } 1405 1406 @Override onChange(boolean selfChange, Uri uri, int userId)1407 public void onChange(boolean selfChange, Uri uri, int userId) { 1408 synchronized (mLock) { 1409 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 } 1416 } 1417 } 1418 restartObserver()1419 private void restartObserver() { 1420 final ContentResolver cr = mContext.getContentResolver(); 1421 1422 if (mRefreshRateInLowZone > 0) { 1423 mShouldObserveDisplayLowChange = hasValidThreshold( 1424 mLowDisplayBrightnessThresholds); 1425 mShouldObserveAmbientLowChange = hasValidThreshold( 1426 mLowAmbientBrightnessThresholds); 1427 } else { 1428 mShouldObserveDisplayLowChange = false; 1429 mShouldObserveAmbientLowChange = false; 1430 } 1431 1432 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) { 1452 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 1475 mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res); 1476 mLightSensor = lightSensor; 1477 } 1478 } else { 1479 mAmbientFilter = null; 1480 mLightSensor = null; 1481 } 1482 1483 if (mRefreshRateChangeable) { 1484 updateSensorStatus(); 1485 synchronized (mLock) { 1486 onBrightnessChangedLocked(); 1487 } 1488 } 1489 } 1490 1491 /** 1492 * Checks to see if at least one value is positive, in which case it is necessary to listen 1493 * to value changes. 1494 */ hasValidThreshold(int[] a)1495 private boolean hasValidThreshold(int[] a) { 1496 for (int d: a) { 1497 if (d >= 0) { 1498 return true; 1499 } 1500 } 1501 1502 return false; 1503 } 1504 isInsideLowZone(int brightness, float lux)1505 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]; 1509 1510 if (disp >= 0 && ambi >= 0) { 1511 if (brightness <= disp && lux <= ambi) { 1512 return true; 1513 } 1514 } else if (disp >= 0) { 1515 if (brightness <= disp) { 1516 return true; 1517 } 1518 } else if (ambi >= 0) { 1519 if (lux <= ambi) { 1520 return true; 1521 } 1522 } 1523 } 1524 1525 return false; 1526 } 1527 isInsideHighZone(int brightness, float lux)1528 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 } onBrightnessChangedLocked()1550 private void onBrightnessChangedLocked() { 1551 Vote vote = null; 1552 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); 1568 } 1569 1570 if (mLoggingEnabled) { 1571 Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " + mAmbientLux 1572 + ", Vote " + vote); 1573 } 1574 updateVoteLocked(Vote.PRIORITY_FLICKER, vote); 1575 } 1576 hasValidLowZone()1577 private boolean hasValidLowZone() { 1578 return mRefreshRateInLowZone > 0 1579 && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange); 1580 } 1581 hasValidHighZone()1582 private boolean hasValidHighZone() { 1583 return mRefreshRateInHighZone > 0 1584 && (mShouldObserveDisplayHighChange || mShouldObserveAmbientHighChange); 1585 } 1586 updateDefaultDisplayState()1587 private void updateDefaultDisplayState() { 1588 Display display = mContext.getSystemService(DisplayManager.class) 1589 .getDisplay(Display.DEFAULT_DISPLAY); 1590 if (display == null) { 1591 return; 1592 } 1593 1594 setDefaultDisplayState(display.getState()); 1595 } 1596 1597 @VisibleForTesting setDefaultDisplayState(int state)1598 public void setDefaultDisplayState(int state) { 1599 if (mLoggingEnabled) { 1600 Slog.d(TAG, "setDefaultDisplayState: mDefaultDisplayState = " 1601 + mDefaultDisplayState + ", state = " + state); 1602 } 1603 1604 if (mDefaultDisplayState != state) { 1605 mDefaultDisplayState = state; 1606 updateSensorStatus(); 1607 } 1608 } 1609 updateSensorStatus()1610 private void updateSensorStatus() { 1611 if (mSensorManager == null || mLightSensorListener == null) { 1612 return; 1613 } 1614 1615 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 1624 if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) 1625 && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) { 1626 mSensorManager.registerListener(mLightSensorListener, 1627 mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler); 1628 if (mLoggingEnabled) { 1629 Slog.d(TAG, "updateSensorStatus: registerListener"); 1630 } 1631 } else { 1632 mLightSensorListener.removeCallbacks(); 1633 mSensorManager.unregisterListener(mLightSensorListener); 1634 if (mLoggingEnabled) { 1635 Slog.d(TAG, "updateSensorStatus: unregisterListener"); 1636 } 1637 } 1638 } 1639 isDeviceActive()1640 private boolean isDeviceActive() { 1641 return mDefaultDisplayState == Display.STATE_ON; 1642 } 1643 1644 private final class LightSensorEventListener implements SensorEventListener { 1645 final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS; 1646 private float mLastSensorData; 1647 private long mTimestamp; 1648 private boolean mLoggingEnabled; 1649 dumpLocked(PrintWriter pw)1650 public void dumpLocked(PrintWriter pw) { 1651 pw.println(" mLastSensorData: " + mLastSensorData); 1652 pw.println(" mTimestamp: " + formatTimestamp(mTimestamp)); 1653 } 1654 1655 setLoggingEnabled(boolean loggingEnabled)1656 public void setLoggingEnabled(boolean loggingEnabled) { 1657 if (mLoggingEnabled == loggingEnabled) { 1658 return; 1659 } 1660 mLoggingEnabled = loggingEnabled; 1661 } 1662 1663 @Override onSensorChanged(SensorEvent event)1664 public void onSensorChanged(SensorEvent event) { 1665 mLastSensorData = event.values[0]; 1666 if (mLoggingEnabled) { 1667 Slog.d(TAG, "On sensor changed: " + mLastSensorData); 1668 } 1669 1670 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 } 1681 } 1682 1683 long now = SystemClock.uptimeMillis(); 1684 mTimestamp = System.currentTimeMillis(); 1685 if (mAmbientFilter != null) { 1686 mAmbientFilter.addValue(now, mLastSensorData); 1687 } 1688 1689 mHandler.removeCallbacks(mInjectSensorEventRunnable); 1690 processSensorData(now); 1691 1692 if ((lowZoneChanged && mLastSensorData > mAmbientLux) 1693 || (highZoneChanged && mLastSensorData < mAmbientLux)) { 1694 // Sensor may not report new event if there is no brightness change. 1695 // Need to keep querying the temporal filter for the latest estimation, 1696 // until sensor readout and filter estimation are in the same zone or 1697 // is interrupted by a new sensor event. 1698 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS); 1699 } 1700 } 1701 1702 @Override onAccuracyChanged(Sensor sensor, int accuracy)1703 public void onAccuracyChanged(Sensor sensor, int accuracy) { 1704 // Not used. 1705 } 1706 removeCallbacks()1707 public void removeCallbacks() { 1708 mHandler.removeCallbacks(mInjectSensorEventRunnable); 1709 } 1710 formatTimestamp(long time)1711 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 processSensorData(long now)1717 private void processSensorData(long now) { 1718 if (mAmbientFilter != null) { 1719 mAmbientLux = mAmbientFilter.getEstimate(now); 1720 } else { 1721 mAmbientLux = mLastSensorData; 1722 } 1723 1724 synchronized (mLock) { 1725 onBrightnessChangedLocked(); 1726 } 1727 } 1728 isDifferentZone(float lux1, float lux2, int[] luxThresholds)1729 private boolean isDifferentZone(float lux1, float lux2, int[] luxThresholds) { 1730 for (final float boundary : luxThresholds) { 1731 // 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. 1750 if (isDifferentZone(mLastSensorData, mAmbientLux, 1751 mLowAmbientBrightnessThresholds) 1752 || isDifferentZone(mLastSensorData, mAmbientLux, 1753 mHighAmbientBrightnessThresholds)) { 1754 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS); 1755 } 1756 } 1757 }; 1758 } 1759 } 1760 1761 private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener { DeviceConfigDisplaySettings()1762 public DeviceConfigDisplaySettings() { 1763 } 1764 startListening()1765 public void startListening() { 1766 mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, 1767 BackgroundThread.getExecutor(), this); 1768 } 1769 1770 /* 1771 * Return null if no such property or wrong format (not comma separated integers). 1772 */ getLowDisplayBrightnessThresholds()1773 public int[] getLowDisplayBrightnessThresholds() { 1774 return getIntArrayProperty( 1775 DisplayManager.DeviceConfig. 1776 KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS); 1777 } 1778 1779 /* 1780 * Return null if no such property or wrong format (not comma separated integers). 1781 */ getLowAmbientBrightnessThresholds()1782 public int[] getLowAmbientBrightnessThresholds() { 1783 return getIntArrayProperty( 1784 DisplayManager.DeviceConfig. 1785 KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS); 1786 } 1787 getRefreshRateInLowZone()1788 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, 1794 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 */ getHighDisplayBrightnessThresholds()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 */ getHighAmbientBrightnessThresholds()1812 public int[] getHighAmbientBrightnessThresholds() { 1813 return getIntArrayProperty( 1814 DisplayManager.DeviceConfig 1815 .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS); 1816 } 1817 getRefreshRateInHighZone()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, 1825 defaultRefreshRateInZone); 1826 1827 return refreshRate; 1828 } 1829 1830 /* 1831 * Return null if no such property 1832 */ getDefaultPeakRefreshRate()1833 public Float getDefaultPeakRefreshRate() { 1834 float defaultPeakRefreshRate = mDeviceConfig.getFloat( 1835 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 onPropertiesChanged(@onNull DeviceConfig.Properties properties)1845 public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { 1846 Float defaultPeakRefreshRate = getDefaultPeakRefreshRate(); 1847 mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED, 1848 defaultPeakRefreshRate).sendToTarget(); 1849 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(); 1859 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(); 1869 } 1870 getIntArrayProperty(String prop)1871 private int[] getIntArrayProperty(String prop) { 1872 String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop, 1873 null); 1874 1875 if (strArray != null) { 1876 return parseIntArray(strArray); 1877 } 1878 1879 return null; 1880 } 1881 parseIntArray(@onNull String strArray)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 1899 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 getDeviceConfig()1905 DeviceConfigInterface getDeviceConfig(); 1906 registerBrightnessObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)1907 void registerBrightnessObserver(@NonNull ContentResolver cr, 1908 @NonNull ContentObserver observer); 1909 unregisterBrightnessObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)1910 void unregisterBrightnessObserver(@NonNull ContentResolver cr, 1911 @NonNull ContentObserver observer); 1912 registerPeakRefreshRateObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)1913 void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, 1914 @NonNull ContentObserver observer); 1915 } 1916 1917 @VisibleForTesting 1918 static class RealInjector implements Injector { 1919 1920 @Override 1921 @NonNull getDeviceConfig()1922 public DeviceConfigInterface getDeviceConfig() { 1923 return DeviceConfigInterface.REAL; 1924 } 1925 1926 @Override registerBrightnessObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)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 unregisterBrightnessObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)1934 public void unregisterBrightnessObserver(@NonNull ContentResolver cr, 1935 @NonNull ContentObserver observer) { 1936 cr.unregisterContentObserver(observer); 1937 } 1938 1939 @Override registerPeakRefreshRateObserver(@onNull ContentResolver cr, @NonNull ContentObserver observer)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 } 1945 } 1946 1947 } 1948