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.server.display.utils.AmbientFilter; 50 import com.android.server.display.utils.AmbientFilterFactory; 51 52 import java.io.PrintWriter; 53 import java.util.ArrayList; 54 import java.util.Arrays; 55 import java.util.List; 56 import java.util.Objects; 57 58 /** 59 * The DisplayModeDirector is responsible for determining what modes are allowed to be 60 * automatically picked by the system based on system-wide and display-specific configuration. 61 */ 62 public class DisplayModeDirector { 63 private static final String TAG = "DisplayModeDirector"; 64 private static final boolean DEBUG = false; 65 66 private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1; 67 private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2; 68 private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3; 69 private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4; 70 71 // Special ID used to indicate that given vote is to be applied globally, rather than to a 72 // specific display. 73 private static final int GLOBAL_ID = -1; 74 75 // The tolerance within which we consider something approximately equals. 76 private static final float FLOAT_TOLERANCE = 0.01f; 77 78 private final Object mLock = new Object(); 79 private final Context mContext; 80 81 private final DisplayModeDirectorHandler mHandler; 82 83 // A map from the display ID to the collection of votes and their priority. The latter takes 84 // the form of another map from the priority to the vote itself so that each priority is 85 // guaranteed to have exactly one vote, which is also easily and efficiently replaceable. 86 private SparseArray<SparseArray<Vote>> mVotesByDisplay; 87 // A map from the display ID to the supported modes on that display. 88 private SparseArray<Display.Mode[]> mSupportedModesByDisplay; 89 // A map from the display ID to the default mode of that display. 90 private SparseArray<Display.Mode> mDefaultModeByDisplay; 91 92 private final AppRequestObserver mAppRequestObserver; 93 private final SettingsObserver mSettingsObserver; 94 private final DisplayObserver mDisplayObserver; 95 private BrightnessObserver mBrightnessObserver; 96 97 private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; 98 private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener; 99 DisplayModeDirector(@onNull Context context, @NonNull Handler handler)100 public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) { 101 mContext = context; 102 mHandler = new DisplayModeDirectorHandler(handler.getLooper()); 103 mVotesByDisplay = new SparseArray<>(); 104 mSupportedModesByDisplay = new SparseArray<>(); 105 mDefaultModeByDisplay = new SparseArray<>(); 106 mAppRequestObserver = new AppRequestObserver(); 107 mSettingsObserver = new SettingsObserver(context, handler); 108 mDisplayObserver = new DisplayObserver(context, handler); 109 mBrightnessObserver = new BrightnessObserver(context, handler); 110 mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); 111 } 112 113 /** 114 * Tells the DisplayModeDirector to update allowed votes and begin observing relevant system 115 * state. 116 * 117 * This has to be deferred because the object may be constructed before the rest of the system 118 * is ready. 119 */ start(SensorManager sensorManager)120 public void start(SensorManager sensorManager) { 121 mSettingsObserver.observe(); 122 mDisplayObserver.observe(); 123 mSettingsObserver.observe(); 124 mBrightnessObserver.observe(sensorManager); 125 synchronized (mLock) { 126 // We may have a listener already registered before the call to start, so go ahead and 127 // notify them to pick up our newly initialized state. 128 notifyDesiredDisplayModeSpecsChangedLocked(); 129 } 130 131 } 132 133 @NonNull getVotesLocked(int displayId)134 private SparseArray<Vote> getVotesLocked(int displayId) { 135 SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId); 136 final SparseArray<Vote> votes; 137 if (displayVotes != null) { 138 votes = displayVotes.clone(); 139 } else { 140 votes = new SparseArray<>(); 141 } 142 143 SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID); 144 if (globalVotes != null) { 145 for (int i = 0; i < globalVotes.size(); i++) { 146 int priority = globalVotes.keyAt(i); 147 if (votes.indexOfKey(priority) < 0) { 148 votes.put(priority, globalVotes.valueAt(i)); 149 } 150 } 151 } 152 return votes; 153 } 154 155 private static final class VoteSummary { 156 public float minRefreshRate; 157 public float maxRefreshRate; 158 public int width; 159 public int height; 160 VoteSummary()161 VoteSummary() { 162 reset(); 163 } 164 reset()165 public void reset() { 166 minRefreshRate = 0f; 167 maxRefreshRate = Float.POSITIVE_INFINITY; 168 width = Vote.INVALID_SIZE; 169 height = Vote.INVALID_SIZE; 170 } 171 } 172 173 // VoteSummary is returned as an output param to cut down a bit on the number of temporary 174 // objects. summarizeVotes( SparseArray<Vote> votes, int lowestConsideredPriority, VoteSummary summary)175 private void summarizeVotes( 176 SparseArray<Vote> votes, int lowestConsideredPriority, /*out*/ VoteSummary summary) { 177 summary.reset(); 178 for (int priority = Vote.MAX_PRIORITY; priority >= lowestConsideredPriority; priority--) { 179 Vote vote = votes.get(priority); 180 if (vote == null) { 181 continue; 182 } 183 // For refresh rates, just use the tightest bounds of all the votes 184 summary.minRefreshRate = Math.max(summary.minRefreshRate, vote.refreshRateRange.min); 185 summary.maxRefreshRate = Math.min(summary.maxRefreshRate, vote.refreshRateRange.max); 186 // For display size, use only the first vote we come across (i.e. the highest 187 // priority vote that includes the width / height). 188 if (summary.height == Vote.INVALID_SIZE && summary.width == Vote.INVALID_SIZE 189 && vote.height > 0 && vote.width > 0) { 190 summary.width = vote.width; 191 summary.height = vote.height; 192 } 193 } 194 } 195 196 /** 197 * Calculates the refresh rate ranges and display modes that the system is allowed to freely 198 * switch between based on global and display-specific constraints. 199 * 200 * @param displayId The display to query for. 201 * @return The ID of the default mode the system should use, and the refresh rate range the 202 * system is allowed to switch between. 203 */ 204 @NonNull getDesiredDisplayModeSpecs(int displayId)205 public DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(int displayId) { 206 synchronized (mLock) { 207 SparseArray<Vote> votes = getVotesLocked(displayId); 208 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId); 209 Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId); 210 if (modes == null || defaultMode == null) { 211 Slog.e(TAG, 212 "Asked about unknown display, returning empty display mode specs!" 213 + "(id=" + displayId + ")"); 214 return new DesiredDisplayModeSpecs(); 215 } 216 217 int[] availableModes = new int[]{defaultMode.getModeId()}; 218 VoteSummary primarySummary = new VoteSummary(); 219 int lowestConsideredPriority = Vote.MIN_PRIORITY; 220 while (lowestConsideredPriority <= Vote.MAX_PRIORITY) { 221 summarizeVotes(votes, lowestConsideredPriority, primarySummary); 222 223 // If we don't have anything specifying the width / height of the display, just use 224 // the default width and height. We don't want these switching out from underneath 225 // us since it's a pretty disruptive behavior. 226 if (primarySummary.height == Vote.INVALID_SIZE 227 || primarySummary.width == Vote.INVALID_SIZE) { 228 primarySummary.width = defaultMode.getPhysicalWidth(); 229 primarySummary.height = defaultMode.getPhysicalHeight(); 230 } 231 232 availableModes = filterModes(modes, primarySummary); 233 if (availableModes.length > 0) { 234 if (DEBUG) { 235 Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes) 236 + " with lowest priority considered " 237 + Vote.priorityToString(lowestConsideredPriority) 238 + " and constraints: " 239 + "width=" + primarySummary.width 240 + ", height=" + primarySummary.height 241 + ", minRefreshRate=" + primarySummary.minRefreshRate 242 + ", maxRefreshRate=" + primarySummary.maxRefreshRate); 243 } 244 break; 245 } 246 247 if (DEBUG) { 248 Slog.w(TAG, "Couldn't find available modes with lowest priority set to " 249 + Vote.priorityToString(lowestConsideredPriority) 250 + " and with the following constraints: " 251 + "width=" + primarySummary.width 252 + ", height=" + primarySummary.height 253 + ", minRefreshRate=" + primarySummary.minRefreshRate 254 + ", maxRefreshRate=" + primarySummary.maxRefreshRate); 255 } 256 257 // If we haven't found anything with the current set of votes, drop the 258 // current lowest priority vote. 259 lowestConsideredPriority++; 260 } 261 262 VoteSummary appRequestSummary = new VoteSummary(); 263 summarizeVotes( 264 votes, Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, appRequestSummary); 265 appRequestSummary.minRefreshRate = 266 Math.min(appRequestSummary.minRefreshRate, primarySummary.minRefreshRate); 267 appRequestSummary.maxRefreshRate = 268 Math.max(appRequestSummary.maxRefreshRate, primarySummary.maxRefreshRate); 269 if (DEBUG) { 270 Slog.i(TAG, 271 String.format("App request range: [%.0f %.0f]", 272 appRequestSummary.minRefreshRate, 273 appRequestSummary.maxRefreshRate)); 274 } 275 276 int baseModeId = defaultMode.getModeId(); 277 if (availableModes.length > 0) { 278 baseModeId = availableModes[0]; 279 } 280 // filterModes function is going to filter the modes based on the voting system. If 281 // the application requests a given mode with preferredModeId function, it will be 282 // stored as baseModeId. 283 return new DesiredDisplayModeSpecs(baseModeId, 284 new RefreshRateRange( 285 primarySummary.minRefreshRate, primarySummary.maxRefreshRate), 286 new RefreshRateRange( 287 appRequestSummary.minRefreshRate, appRequestSummary.maxRefreshRate)); 288 } 289 } 290 filterModes(Display.Mode[] supportedModes, VoteSummary summary)291 private int[] filterModes(Display.Mode[] supportedModes, VoteSummary summary) { 292 ArrayList<Display.Mode> availableModes = new ArrayList<>(); 293 for (Display.Mode mode : supportedModes) { 294 if (mode.getPhysicalWidth() != summary.width 295 || mode.getPhysicalHeight() != summary.height) { 296 if (DEBUG) { 297 Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size" 298 + ": desiredWidth=" + summary.width 299 + ": desiredHeight=" + summary.height 300 + ": actualWidth=" + mode.getPhysicalWidth() 301 + ": actualHeight=" + mode.getPhysicalHeight()); 302 } 303 continue; 304 } 305 final float refreshRate = mode.getRefreshRate(); 306 // Some refresh rates are calculated based on frame timings, so they aren't *exactly* 307 // equal to expected refresh rate. Given that, we apply a bit of tolerance to this 308 // comparison. 309 if (refreshRate < (summary.minRefreshRate - FLOAT_TOLERANCE) 310 || refreshRate > (summary.maxRefreshRate + FLOAT_TOLERANCE)) { 311 if (DEBUG) { 312 Slog.w(TAG, "Discarding mode " + mode.getModeId() 313 + ", outside refresh rate bounds" 314 + ": minRefreshRate=" + summary.minRefreshRate 315 + ", maxRefreshRate=" + summary.maxRefreshRate 316 + ", modeRefreshRate=" + refreshRate); 317 } 318 continue; 319 } 320 availableModes.add(mode); 321 } 322 final int size = availableModes.size(); 323 int[] availableModeIds = new int[size]; 324 for (int i = 0; i < size; i++) { 325 availableModeIds[i] = availableModes.get(i).getModeId(); 326 } 327 return availableModeIds; 328 } 329 330 /** 331 * Gets the observer responsible for application display mode requests. 332 */ 333 @NonNull getAppRequestObserver()334 public AppRequestObserver getAppRequestObserver() { 335 // We don't need to lock here because mAppRequestObserver is a final field, which is 336 // guaranteed to be visible on all threads after construction. 337 return mAppRequestObserver; 338 } 339 340 /** 341 * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate 342 * ranges. 343 */ setDesiredDisplayModeSpecsListener( @ullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener)344 public void setDesiredDisplayModeSpecsListener( 345 @Nullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener) { 346 synchronized (mLock) { 347 mDesiredDisplayModeSpecsListener = desiredDisplayModeSpecsListener; 348 } 349 } 350 351 /** 352 * Print the object's state and debug information into the given stream. 353 * 354 * @param pw The stream to dump information to. 355 */ dump(PrintWriter pw)356 public void dump(PrintWriter pw) { 357 pw.println("DisplayModeDirector"); 358 synchronized (mLock) { 359 pw.println(" mSupportedModesByDisplay:"); 360 for (int i = 0; i < mSupportedModesByDisplay.size(); i++) { 361 final int id = mSupportedModesByDisplay.keyAt(i); 362 final Display.Mode[] modes = mSupportedModesByDisplay.valueAt(i); 363 pw.println(" " + id + " -> " + Arrays.toString(modes)); 364 } 365 pw.println(" mDefaultModeByDisplay:"); 366 for (int i = 0; i < mDefaultModeByDisplay.size(); i++) { 367 final int id = mDefaultModeByDisplay.keyAt(i); 368 final Display.Mode mode = mDefaultModeByDisplay.valueAt(i); 369 pw.println(" " + id + " -> " + mode); 370 } 371 pw.println(" mVotesByDisplay:"); 372 for (int i = 0; i < mVotesByDisplay.size(); i++) { 373 pw.println(" " + mVotesByDisplay.keyAt(i) + ":"); 374 SparseArray<Vote> votes = mVotesByDisplay.valueAt(i); 375 for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) { 376 Vote vote = votes.get(p); 377 if (vote == null) { 378 continue; 379 } 380 pw.println(" " + Vote.priorityToString(p) + " -> " + vote); 381 } 382 } 383 mSettingsObserver.dumpLocked(pw); 384 mAppRequestObserver.dumpLocked(pw); 385 mBrightnessObserver.dumpLocked(pw); 386 } 387 } 388 updateVoteLocked(int priority, Vote vote)389 private void updateVoteLocked(int priority, Vote vote) { 390 updateVoteLocked(GLOBAL_ID, priority, vote); 391 } 392 updateVoteLocked(int displayId, int priority, Vote vote)393 private void updateVoteLocked(int displayId, int priority, Vote vote) { 394 if (DEBUG) { 395 Slog.i(TAG, "updateVoteLocked(displayId=" + displayId 396 + ", priority=" + Vote.priorityToString(priority) 397 + ", vote=" + vote + ")"); 398 } 399 if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) { 400 Slog.w(TAG, "Received a vote with an invalid priority, ignoring:" 401 + " priority=" + Vote.priorityToString(priority) 402 + ", vote=" + vote, new Throwable()); 403 return; 404 } 405 final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId); 406 407 Vote currentVote = votes.get(priority); 408 if (vote != null) { 409 votes.put(priority, vote); 410 } else { 411 votes.remove(priority); 412 } 413 414 if (votes.size() == 0) { 415 if (DEBUG) { 416 Slog.i(TAG, "No votes left for display " + displayId + ", removing."); 417 } 418 mVotesByDisplay.remove(displayId); 419 } 420 421 notifyDesiredDisplayModeSpecsChangedLocked(); 422 } 423 notifyDesiredDisplayModeSpecsChangedLocked()424 private void notifyDesiredDisplayModeSpecsChangedLocked() { 425 if (mDesiredDisplayModeSpecsListener != null 426 && !mHandler.hasMessages(MSG_REFRESH_RATE_RANGE_CHANGED)) { 427 // We need to post this to a handler to avoid calling out while holding the lock 428 // since we know there are things that both listen for changes as well as provide 429 // information. If we did call out while holding the lock, then there's no 430 // guaranteed lock order and we run the real of risk deadlock. 431 Message msg = mHandler.obtainMessage( 432 MSG_REFRESH_RATE_RANGE_CHANGED, mDesiredDisplayModeSpecsListener); 433 msg.sendToTarget(); 434 } 435 } 436 getOrCreateVotesByDisplay(int displayId)437 private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) { 438 int index = mVotesByDisplay.indexOfKey(displayId); 439 if (mVotesByDisplay.indexOfKey(displayId) >= 0) { 440 return mVotesByDisplay.get(displayId); 441 } else { 442 SparseArray<Vote> votes = new SparseArray<>(); 443 mVotesByDisplay.put(displayId, votes); 444 return votes; 445 } 446 } 447 448 @VisibleForTesting injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay)449 void injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay) { 450 mSupportedModesByDisplay = supportedModesByDisplay; 451 } 452 453 @VisibleForTesting injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay)454 void injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay) { 455 mDefaultModeByDisplay = defaultModeByDisplay; 456 } 457 458 @VisibleForTesting injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay)459 void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) { 460 mVotesByDisplay = votesByDisplay; 461 } 462 463 @VisibleForTesting injectBrightnessObserver(BrightnessObserver brightnessObserver)464 void injectBrightnessObserver(BrightnessObserver brightnessObserver) { 465 mBrightnessObserver = brightnessObserver; 466 } 467 468 @VisibleForTesting getDesiredDisplayModeSpecsWithInjectedFpsSettings( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate)469 DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings( 470 float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) { 471 synchronized (mLock) { 472 mSettingsObserver.updateRefreshRateSettingLocked( 473 minRefreshRate, peakRefreshRate, defaultRefreshRate); 474 return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY); 475 } 476 } 477 478 /** 479 * Listens for changes refresh rate coordination. 480 */ 481 public interface DesiredDisplayModeSpecsListener { 482 /** 483 * Called when the refresh rate range may have changed. 484 */ onDesiredDisplayModeSpecsChanged()485 void onDesiredDisplayModeSpecsChanged(); 486 } 487 488 private final class DisplayModeDirectorHandler extends Handler { DisplayModeDirectorHandler(Looper looper)489 DisplayModeDirectorHandler(Looper looper) { 490 super(looper, null, true /*async*/); 491 } 492 493 @Override handleMessage(Message msg)494 public void handleMessage(Message msg) { 495 switch (msg.what) { 496 case MSG_BRIGHTNESS_THRESHOLDS_CHANGED: 497 Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj; 498 499 if (thresholds != null) { 500 mBrightnessObserver.onDeviceConfigThresholdsChanged( 501 thresholds.first, thresholds.second); 502 } else { 503 mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null); 504 } 505 break; 506 507 case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED: 508 Float defaultPeakRefreshRate = (Float) msg.obj; 509 mSettingsObserver.onDeviceConfigDefaultPeakRefreshRateChanged( 510 defaultPeakRefreshRate); 511 break; 512 513 case MSG_REFRESH_RATE_IN_ZONE_CHANGED: 514 int refreshRateInZone = msg.arg1; 515 mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged( 516 refreshRateInZone); 517 break; 518 519 case MSG_REFRESH_RATE_RANGE_CHANGED: 520 DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener = 521 (DesiredDisplayModeSpecsListener) msg.obj; 522 desiredDisplayModeSpecsListener.onDesiredDisplayModeSpecsChanged(); 523 break; 524 } 525 } 526 } 527 528 /** 529 * Information about the min and max refresh rate DM would like to set the display to. 530 */ 531 public static final class RefreshRateRange { 532 /** 533 * The lowest desired refresh rate. 534 */ 535 public float min; 536 /** 537 * The highest desired refresh rate. 538 */ 539 public float max; 540 RefreshRateRange()541 public RefreshRateRange() {} 542 RefreshRateRange(float min, float max)543 public RefreshRateRange(float min, float max) { 544 if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) { 545 Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : " 546 + min + " " + max); 547 this.min = this.max = 0; 548 return; 549 } 550 if (min > max) { 551 // Min and max are within epsilon of each other, but in the wrong order. 552 float t = min; 553 min = max; 554 max = t; 555 } 556 this.min = min; 557 this.max = max; 558 } 559 560 /** 561 * Checks whether the two objects have the same values. 562 */ 563 @Override equals(Object other)564 public boolean equals(Object other) { 565 if (other == this) { 566 return true; 567 } 568 569 if (!(other instanceof RefreshRateRange)) { 570 return false; 571 } 572 573 RefreshRateRange refreshRateRange = (RefreshRateRange) other; 574 return (min == refreshRateRange.min && max == refreshRateRange.max); 575 } 576 577 @Override hashCode()578 public int hashCode() { 579 return Objects.hash(min, max); 580 } 581 582 @Override toString()583 public String toString() { 584 return "(" + min + " " + max + ")"; 585 } 586 } 587 588 /** 589 * Information about the desired display mode to be set by the system. Includes the base 590 * mode ID and the primary and app request refresh rate ranges. 591 * 592 * We have this class in addition to SurfaceControl.DesiredDisplayConfigSpecs to make clear the 593 * distinction between the config ID / physical index that 594 * SurfaceControl.DesiredDisplayConfigSpecs uses, and the mode ID used here. 595 */ 596 public static final class DesiredDisplayModeSpecs { 597 /** 598 * Base mode ID. This is what system defaults to for all other settings, or 599 * if the refresh rate range is not available. 600 */ 601 public int baseModeId; 602 /** 603 * The primary refresh rate range. 604 */ 605 public final RefreshRateRange primaryRefreshRateRange; 606 /** 607 * The app request refresh rate range. Lower priority considerations won't be included in 608 * this range, allowing surface flinger to consider additional refresh rates for apps that 609 * call setFrameRate(). This range will be greater than or equal to the primary refresh rate 610 * range, never smaller. 611 */ 612 public final RefreshRateRange appRequestRefreshRateRange; 613 DesiredDisplayModeSpecs()614 public DesiredDisplayModeSpecs() { 615 primaryRefreshRateRange = new RefreshRateRange(); 616 appRequestRefreshRateRange = new RefreshRateRange(); 617 } 618 DesiredDisplayModeSpecs(int baseModeId, @NonNull RefreshRateRange primaryRefreshRateRange, @NonNull RefreshRateRange appRequestRefreshRateRange)619 public DesiredDisplayModeSpecs(int baseModeId, 620 @NonNull RefreshRateRange primaryRefreshRateRange, 621 @NonNull RefreshRateRange appRequestRefreshRateRange) { 622 this.baseModeId = baseModeId; 623 this.primaryRefreshRateRange = primaryRefreshRateRange; 624 this.appRequestRefreshRateRange = appRequestRefreshRateRange; 625 } 626 627 /** 628 * Returns a string representation of the object. 629 */ 630 @Override toString()631 public String toString() { 632 return String.format("baseModeId=%d primaryRefreshRateRange=[%.0f %.0f]" 633 + " appRequestRefreshRateRange=[%.0f %.0f]", 634 baseModeId, primaryRefreshRateRange.min, primaryRefreshRateRange.max, 635 appRequestRefreshRateRange.min, appRequestRefreshRateRange.max); 636 } 637 /** 638 * Checks whether the two objects have the same values. 639 */ 640 @Override equals(Object other)641 public boolean equals(Object other) { 642 if (other == this) { 643 return true; 644 } 645 646 if (!(other instanceof DesiredDisplayModeSpecs)) { 647 return false; 648 } 649 650 DesiredDisplayModeSpecs desiredDisplayModeSpecs = (DesiredDisplayModeSpecs) other; 651 652 if (baseModeId != desiredDisplayModeSpecs.baseModeId) { 653 return false; 654 } 655 if (!primaryRefreshRateRange.equals(desiredDisplayModeSpecs.primaryRefreshRateRange)) { 656 return false; 657 } 658 if (!appRequestRefreshRateRange.equals( 659 desiredDisplayModeSpecs.appRequestRefreshRateRange)) { 660 return false; 661 } 662 return true; 663 } 664 665 @Override hashCode()666 public int hashCode() { 667 return Objects.hash(baseModeId, primaryRefreshRateRange, appRequestRefreshRateRange); 668 } 669 670 /** 671 * Copy values from the other object. 672 */ copyFrom(DesiredDisplayModeSpecs other)673 public void copyFrom(DesiredDisplayModeSpecs other) { 674 baseModeId = other.baseModeId; 675 primaryRefreshRateRange.min = other.primaryRefreshRateRange.min; 676 primaryRefreshRateRange.max = other.primaryRefreshRateRange.max; 677 appRequestRefreshRateRange.min = other.appRequestRefreshRateRange.min; 678 appRequestRefreshRateRange.max = other.appRequestRefreshRateRange.max; 679 } 680 } 681 682 @VisibleForTesting 683 static final class Vote { 684 // DEFAULT_FRAME_RATE votes for [0, DEFAULT]. As the lowest priority vote, it's overridden 685 // by all other considerations. It acts to set a default frame rate for a device. 686 public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0; 687 688 // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null. 689 // If the higher voters result is a range, it will fix the rate to a single choice. 690 // It's used to avoid rate switch in certain conditions. 691 public static final int PRIORITY_LOW_BRIGHTNESS = 1; 692 693 // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate. 694 // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY] 695 public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2; 696 697 // We split the app request into different priorities in case we can satisfy one desire 698 // without the other. 699 700 // Application can specify preferred refresh rate with below attrs. 701 // @see android.view.WindowManager.LayoutParams#preferredRefreshRate 702 // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId 703 // System also forces some apps like blacklisted app to run at a lower refresh rate. 704 // @see android.R.array#config_highRefreshRateBlacklist 705 public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3; 706 public static final int PRIORITY_APP_REQUEST_SIZE = 4; 707 708 // SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest 709 // of low priority voters. It votes [0, max(PEAK, MIN)] 710 public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 5; 711 712 // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on. 713 public static final int PRIORITY_LOW_POWER_MODE = 6; 714 715 // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and 716 // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString. 717 718 public static final int MIN_PRIORITY = PRIORITY_DEFAULT_REFRESH_RATE; 719 public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE; 720 721 // The cutoff for the app request refresh rate range. Votes with priorities lower than this 722 // value will not be considered when constructing the app request refresh rate range. 723 public static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF = 724 PRIORITY_APP_REQUEST_REFRESH_RATE; 725 726 /** 727 * A value signifying an invalid width or height in a vote. 728 */ 729 public static final int INVALID_SIZE = -1; 730 731 /** 732 * The requested width of the display in pixels, or INVALID_SIZE; 733 */ 734 public final int width; 735 /** 736 * The requested height of the display in pixels, or INVALID_SIZE; 737 */ 738 public final int height; 739 /** 740 * Information about the min and max refresh rate DM would like to set the display to. 741 */ 742 public final RefreshRateRange refreshRateRange; 743 forRefreshRates(float minRefreshRate, float maxRefreshRate)744 public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) { 745 return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate); 746 } 747 forSize(int width, int height)748 public static Vote forSize(int width, int height) { 749 return new Vote(width, height, 0f, Float.POSITIVE_INFINITY); 750 } 751 Vote(int width, int height, float minRefreshRate, float maxRefreshRate)752 private Vote(int width, int height, 753 float minRefreshRate, float maxRefreshRate) { 754 this.width = width; 755 this.height = height; 756 this.refreshRateRange = 757 new RefreshRateRange(minRefreshRate, maxRefreshRate); 758 } 759 priorityToString(int priority)760 public static String priorityToString(int priority) { 761 switch (priority) { 762 case PRIORITY_DEFAULT_REFRESH_RATE: 763 return "PRIORITY_DEFAULT_REFRESH_RATE"; 764 case PRIORITY_LOW_BRIGHTNESS: 765 return "PRIORITY_LOW_BRIGHTNESS"; 766 case PRIORITY_USER_SETTING_MIN_REFRESH_RATE: 767 return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE"; 768 case PRIORITY_APP_REQUEST_REFRESH_RATE: 769 return "PRIORITY_APP_REQUEST_REFRESH_RATE"; 770 case PRIORITY_APP_REQUEST_SIZE: 771 return "PRIORITY_APP_REQUEST_SIZE"; 772 case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE: 773 return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE"; 774 case PRIORITY_LOW_POWER_MODE: 775 return "PRIORITY_LOW_POWER_MODE"; 776 default: 777 return Integer.toString(priority); 778 } 779 } 780 781 @Override toString()782 public String toString() { 783 return "Vote{" 784 + "width=" + width + ", height=" + height 785 + ", minRefreshRate=" + refreshRateRange.min 786 + ", maxRefreshRate=" + refreshRateRange.max + "}"; 787 } 788 } 789 790 private final class SettingsObserver extends ContentObserver { 791 private final Uri mPeakRefreshRateSetting = 792 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); 793 private final Uri mMinRefreshRateSetting = 794 Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE); 795 private final Uri mLowPowerModeSetting = 796 Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE); 797 798 private final Context mContext; 799 private float mDefaultPeakRefreshRate; 800 private float mDefaultRefreshRate; 801 SettingsObserver(@onNull Context context, @NonNull Handler handler)802 SettingsObserver(@NonNull Context context, @NonNull Handler handler) { 803 super(handler); 804 mContext = context; 805 mDefaultPeakRefreshRate = (float) context.getResources().getInteger( 806 R.integer.config_defaultPeakRefreshRate); 807 mDefaultRefreshRate = 808 (float) context.getResources().getInteger(R.integer.config_defaultRefreshRate); 809 } 810 observe()811 public void observe() { 812 final ContentResolver cr = mContext.getContentResolver(); 813 cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this, 814 UserHandle.USER_SYSTEM); 815 cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this, 816 UserHandle.USER_SYSTEM); 817 cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this, 818 UserHandle.USER_SYSTEM); 819 820 Float deviceConfigDefaultPeakRefresh = 821 mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate(); 822 if (deviceConfigDefaultPeakRefresh != null) { 823 mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh; 824 } 825 826 synchronized (mLock) { 827 updateRefreshRateSettingLocked(); 828 updateLowPowerModeSettingLocked(); 829 } 830 } 831 onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate)832 public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) { 833 if (defaultPeakRefreshRate == null) { 834 defaultPeakRefreshRate = (float) mContext.getResources().getInteger( 835 R.integer.config_defaultPeakRefreshRate); 836 } 837 838 if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) { 839 synchronized (mLock) { 840 mDefaultPeakRefreshRate = defaultPeakRefreshRate; 841 updateRefreshRateSettingLocked(); 842 } 843 } 844 } 845 846 @Override onChange(boolean selfChange, Uri uri, int userId)847 public void onChange(boolean selfChange, Uri uri, int userId) { 848 synchronized (mLock) { 849 if (mPeakRefreshRateSetting.equals(uri) 850 || mMinRefreshRateSetting.equals(uri)) { 851 updateRefreshRateSettingLocked(); 852 } else if (mLowPowerModeSetting.equals(uri)) { 853 updateLowPowerModeSettingLocked(); 854 } 855 } 856 } 857 updateLowPowerModeSettingLocked()858 private void updateLowPowerModeSettingLocked() { 859 boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(), 860 Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0; 861 final Vote vote; 862 if (inLowPowerMode) { 863 vote = Vote.forRefreshRates(0f, 60f); 864 } else { 865 vote = null; 866 } 867 updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote); 868 mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode); 869 } 870 updateRefreshRateSettingLocked()871 private void updateRefreshRateSettingLocked() { 872 float minRefreshRate = Settings.System.getFloat(mContext.getContentResolver(), 873 Settings.System.MIN_REFRESH_RATE, 0f); 874 float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(), 875 Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate); 876 updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate); 877 } 878 updateRefreshRateSettingLocked( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate)879 private void updateRefreshRateSettingLocked( 880 float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) { 881 // TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is 882 // used to predict if we're going to be doing frequent refresh rate switching, and if 883 // so, enable the brightness observer. The logic here is more complicated and fragile 884 // than necessary, and we should improve it. See b/156304339 for more info. 885 Vote peakVote = peakRefreshRate == 0f 886 ? null 887 : Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate)); 888 updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, peakVote); 889 updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, 890 Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY)); 891 Vote defaultVote = 892 defaultRefreshRate == 0f ? null : Vote.forRefreshRates(0f, defaultRefreshRate); 893 updateVoteLocked(Vote.PRIORITY_DEFAULT_REFRESH_RATE, defaultVote); 894 895 float maxRefreshRate; 896 if (peakRefreshRate == 0f && defaultRefreshRate == 0f) { 897 // We require that at least one of the peak or default refresh rate values are 898 // set. The brightness observer requires that we're able to predict whether or not 899 // we're going to do frequent refresh rate switching, and with the way the code is 900 // currently written, we need either a default or peak refresh rate value for that. 901 Slog.e(TAG, "Default and peak refresh rates are both 0. One of them should be set" 902 + " to a valid value."); 903 maxRefreshRate = minRefreshRate; 904 } else if (peakRefreshRate == 0f) { 905 maxRefreshRate = defaultRefreshRate; 906 } else if (defaultRefreshRate == 0f) { 907 maxRefreshRate = peakRefreshRate; 908 } else { 909 maxRefreshRate = Math.min(defaultRefreshRate, peakRefreshRate); 910 } 911 912 mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, maxRefreshRate); 913 } 914 dumpLocked(PrintWriter pw)915 public void dumpLocked(PrintWriter pw) { 916 pw.println(" SettingsObserver"); 917 pw.println(" mDefaultRefreshRate: " + mDefaultRefreshRate); 918 pw.println(" mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate); 919 } 920 } 921 922 final class AppRequestObserver { 923 private SparseArray<Display.Mode> mAppRequestedModeByDisplay; 924 AppRequestObserver()925 AppRequestObserver() { 926 mAppRequestedModeByDisplay = new SparseArray<>(); 927 } 928 setAppRequestedMode(int displayId, int modeId)929 public void setAppRequestedMode(int displayId, int modeId) { 930 synchronized (mLock) { 931 setAppRequestedModeLocked(displayId, modeId); 932 } 933 } 934 setAppRequestedModeLocked(int displayId, int modeId)935 private void setAppRequestedModeLocked(int displayId, int modeId) { 936 final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId); 937 if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) { 938 return; 939 } 940 941 final Vote refreshRateVote; 942 final Vote sizeVote; 943 if (requestedMode != null) { 944 mAppRequestedModeByDisplay.put(displayId, requestedMode); 945 float refreshRate = requestedMode.getRefreshRate(); 946 refreshRateVote = Vote.forRefreshRates(refreshRate, refreshRate); 947 sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(), 948 requestedMode.getPhysicalHeight()); 949 } else { 950 mAppRequestedModeByDisplay.remove(displayId); 951 refreshRateVote = null; 952 sizeVote = null; 953 } 954 955 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote); 956 updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote); 957 return; 958 } 959 findModeByIdLocked(int displayId, int modeId)960 private Display.Mode findModeByIdLocked(int displayId, int modeId) { 961 Display.Mode[] modes = mSupportedModesByDisplay.get(displayId); 962 if (modes == null) { 963 return null; 964 } 965 for (Display.Mode mode : modes) { 966 if (mode.getModeId() == modeId) { 967 return mode; 968 } 969 } 970 return null; 971 } 972 dumpLocked(PrintWriter pw)973 public void dumpLocked(PrintWriter pw) { 974 pw.println(" AppRequestObserver"); 975 pw.println(" mAppRequestedModeByDisplay:"); 976 for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) { 977 final int id = mAppRequestedModeByDisplay.keyAt(i); 978 final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i); 979 pw.println(" " + id + " -> " + mode); 980 } 981 } 982 } 983 984 private final class DisplayObserver implements DisplayManager.DisplayListener { 985 // Note that we can never call into DisplayManager or any of the non-POD classes it 986 // returns, while holding mLock since it may call into DMS, which might be simultaneously 987 // calling into us already holding its own lock. 988 private final Context mContext; 989 private final Handler mHandler; 990 DisplayObserver(Context context, Handler handler)991 DisplayObserver(Context context, Handler handler) { 992 mContext = context; 993 mHandler = handler; 994 } 995 observe()996 public void observe() { 997 DisplayManager dm = mContext.getSystemService(DisplayManager.class); 998 dm.registerDisplayListener(this, mHandler); 999 1000 // Populate existing displays 1001 SparseArray<Display.Mode[]> modes = new SparseArray<>(); 1002 SparseArray<Display.Mode> defaultModes = new SparseArray<>(); 1003 DisplayInfo info = new DisplayInfo(); 1004 Display[] displays = dm.getDisplays(); 1005 for (Display d : displays) { 1006 final int displayId = d.getDisplayId(); 1007 d.getDisplayInfo(info); 1008 modes.put(displayId, info.supportedModes); 1009 defaultModes.put(displayId, info.getDefaultMode()); 1010 } 1011 synchronized (mLock) { 1012 final int size = modes.size(); 1013 for (int i = 0; i < size; i++) { 1014 mSupportedModesByDisplay.put(modes.keyAt(i), modes.valueAt(i)); 1015 mDefaultModeByDisplay.put(defaultModes.keyAt(i), defaultModes.valueAt(i)); 1016 } 1017 } 1018 } 1019 1020 @Override onDisplayAdded(int displayId)1021 public void onDisplayAdded(int displayId) { 1022 updateDisplayModes(displayId); 1023 } 1024 1025 @Override onDisplayRemoved(int displayId)1026 public void onDisplayRemoved(int displayId) { 1027 synchronized (mLock) { 1028 mSupportedModesByDisplay.remove(displayId); 1029 mDefaultModeByDisplay.remove(displayId); 1030 } 1031 } 1032 1033 @Override onDisplayChanged(int displayId)1034 public void onDisplayChanged(int displayId) { 1035 updateDisplayModes(displayId); 1036 mBrightnessObserver.onDisplayChanged(displayId); 1037 } 1038 updateDisplayModes(int displayId)1039 private void updateDisplayModes(int displayId) { 1040 Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId); 1041 if (d == null) { 1042 // We can occasionally get a display added or changed event for a display that was 1043 // subsequently removed, which means this returns null. Check this case and bail 1044 // out early; if it gets re-attached we'll eventually get another call back for it. 1045 return; 1046 } 1047 DisplayInfo info = new DisplayInfo(); 1048 d.getDisplayInfo(info); 1049 boolean changed = false; 1050 synchronized (mLock) { 1051 if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) { 1052 mSupportedModesByDisplay.put(displayId, info.supportedModes); 1053 changed = true; 1054 } 1055 if (!Objects.equals(mDefaultModeByDisplay.get(displayId), info.getDefaultMode())) { 1056 changed = true; 1057 mDefaultModeByDisplay.put(displayId, info.getDefaultMode()); 1058 } 1059 if (changed) { 1060 notifyDesiredDisplayModeSpecsChangedLocked(); 1061 } 1062 } 1063 } 1064 } 1065 1066 /** 1067 * This class manages brightness threshold for switching between 60 hz and higher refresh rate. 1068 * See more information at the definition of 1069 * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and 1070 * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}. 1071 */ 1072 @VisibleForTesting 1073 public class BrightnessObserver extends ContentObserver { 1074 // TODO: brightnessfloat: change this to the float setting 1075 private final Uri mDisplayBrightnessSetting = 1076 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); 1077 private final static int LIGHT_SENSOR_RATE_MS = 250; 1078 private int[] mDisplayBrightnessThresholds; 1079 private int[] mAmbientBrightnessThresholds; 1080 // valid threshold if any item from the array >= 0 1081 private boolean mShouldObserveDisplayChange; 1082 private boolean mShouldObserveAmbientChange; 1083 1084 private SensorManager mSensorManager; 1085 private Sensor mLightSensor; 1086 private LightSensorEventListener mLightSensorListener = new LightSensorEventListener(); 1087 // Take it as low brightness before valid sensor data comes 1088 private float mAmbientLux = -1.0f; 1089 private AmbientFilter mAmbientFilter; 1090 1091 private final Context mContext; 1092 1093 // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak 1094 // refresh rate changeable and low power mode off. After initialization, these states will 1095 // be updated from the same handler thread. 1096 private boolean mScreenOn = false; 1097 private boolean mRefreshRateChangeable = false; 1098 private boolean mLowPowerModeEnabled = false; 1099 1100 private int mRefreshRateInZone; 1101 BrightnessObserver(Context context, Handler handler)1102 BrightnessObserver(Context context, Handler handler) { 1103 super(handler); 1104 mContext = context; 1105 mDisplayBrightnessThresholds = context.getResources().getIntArray( 1106 R.array.config_brightnessThresholdsOfPeakRefreshRate); 1107 mAmbientBrightnessThresholds = context.getResources().getIntArray( 1108 R.array.config_ambientThresholdsOfPeakRefreshRate); 1109 1110 if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) { 1111 throw new RuntimeException("display brightness threshold array and ambient " 1112 + "brightness threshold array have different length"); 1113 } 1114 } 1115 observe(SensorManager sensorManager)1116 public void observe(SensorManager sensorManager) { 1117 mSensorManager = sensorManager; 1118 1119 // DeviceConfig is accessible after system ready. 1120 int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds(); 1121 int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds(); 1122 1123 if (brightnessThresholds != null && ambientThresholds != null 1124 && brightnessThresholds.length == ambientThresholds.length) { 1125 mDisplayBrightnessThresholds = brightnessThresholds; 1126 mAmbientBrightnessThresholds = ambientThresholds; 1127 } 1128 1129 mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone(); 1130 restartObserver(); 1131 mDeviceConfigDisplaySettings.startListening(); 1132 } 1133 onRefreshRateSettingChangedLocked(float min, float max)1134 public void onRefreshRateSettingChangedLocked(float min, float max) { 1135 boolean changeable = (max - min > 1f && max > 60f); 1136 if (mRefreshRateChangeable != changeable) { 1137 mRefreshRateChangeable = changeable; 1138 updateSensorStatus(); 1139 if (!changeable) { 1140 // Revoke previous vote from BrightnessObserver 1141 updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null); 1142 } 1143 } 1144 } 1145 onLowPowerModeEnabledLocked(boolean b)1146 public void onLowPowerModeEnabledLocked(boolean b) { 1147 if (mLowPowerModeEnabled != b) { 1148 mLowPowerModeEnabled = b; 1149 updateSensorStatus(); 1150 } 1151 } 1152 onDeviceConfigThresholdsChanged(int[] brightnessThresholds, int[] ambientThresholds)1153 public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds, 1154 int[] ambientThresholds) { 1155 if (brightnessThresholds != null && ambientThresholds != null 1156 && brightnessThresholds.length == ambientThresholds.length) { 1157 mDisplayBrightnessThresholds = brightnessThresholds; 1158 mAmbientBrightnessThresholds = ambientThresholds; 1159 } else { 1160 // Invalid or empty. Use device default. 1161 mDisplayBrightnessThresholds = mContext.getResources().getIntArray( 1162 R.array.config_brightnessThresholdsOfPeakRefreshRate); 1163 mAmbientBrightnessThresholds = mContext.getResources().getIntArray( 1164 R.array.config_ambientThresholdsOfPeakRefreshRate); 1165 } 1166 restartObserver(); 1167 } 1168 onDeviceConfigRefreshRateInZoneChanged(int refreshRate)1169 public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) { 1170 if (refreshRate != mRefreshRateInZone) { 1171 mRefreshRateInZone = refreshRate; 1172 restartObserver(); 1173 } 1174 } 1175 dumpLocked(PrintWriter pw)1176 public void dumpLocked(PrintWriter pw) { 1177 pw.println(" BrightnessObserver"); 1178 pw.println(" mAmbientLux: " + mAmbientLux); 1179 pw.println(" mRefreshRateInZone: " + mRefreshRateInZone); 1180 1181 for (int d: mDisplayBrightnessThresholds) { 1182 pw.println(" mDisplayBrightnessThreshold: " + d); 1183 } 1184 1185 for (int d: mAmbientBrightnessThresholds) { 1186 pw.println(" mAmbientBrightnessThreshold: " + d); 1187 } 1188 1189 mLightSensorListener.dumpLocked(pw); 1190 } 1191 onDisplayChanged(int displayId)1192 public void onDisplayChanged(int displayId) { 1193 if (displayId == Display.DEFAULT_DISPLAY) { 1194 onScreenOn(isDefaultDisplayOn()); 1195 } 1196 } 1197 1198 @Override onChange(boolean selfChange, Uri uri, int userId)1199 public void onChange(boolean selfChange, Uri uri, int userId) { 1200 synchronized (mLock) { 1201 onBrightnessChangedLocked(); 1202 } 1203 } 1204 restartObserver()1205 private void restartObserver() { 1206 mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds); 1207 mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds); 1208 1209 final ContentResolver cr = mContext.getContentResolver(); 1210 if (mShouldObserveDisplayChange) { 1211 // Content Service does not check if an listener has already been registered. 1212 // To ensure only one listener is registered, force an unregistration first. 1213 cr.unregisterContentObserver(this); 1214 cr.registerContentObserver(mDisplayBrightnessSetting, 1215 false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM); 1216 } else { 1217 cr.unregisterContentObserver(this); 1218 } 1219 1220 if (mShouldObserveAmbientChange) { 1221 Resources resources = mContext.getResources(); 1222 String lightSensorType = resources.getString( 1223 com.android.internal.R.string.config_displayLightSensorType); 1224 1225 Sensor lightSensor = null; 1226 if (!TextUtils.isEmpty(lightSensorType)) { 1227 List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); 1228 for (int i = 0; i < sensors.size(); i++) { 1229 Sensor sensor = sensors.get(i); 1230 if (lightSensorType.equals(sensor.getStringType())) { 1231 lightSensor = sensor; 1232 break; 1233 } 1234 } 1235 } 1236 1237 if (lightSensor == null) { 1238 lightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); 1239 } 1240 1241 if (lightSensor != null) { 1242 final Resources res = mContext.getResources(); 1243 1244 mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res); 1245 mLightSensor = lightSensor; 1246 1247 onScreenOn(isDefaultDisplayOn()); 1248 } 1249 } else { 1250 mAmbientFilter = null; 1251 mLightSensor = null; 1252 } 1253 1254 if (mRefreshRateChangeable) { 1255 updateSensorStatus(); 1256 synchronized (mLock) { 1257 onBrightnessChangedLocked(); 1258 } 1259 } 1260 } 1261 1262 /** 1263 * Checks to see if at least one value is positive, in which case it is necessary to listen 1264 * to value changes. 1265 */ checkShouldObserve(int[] a)1266 private boolean checkShouldObserve(int[] a) { 1267 if (mRefreshRateInZone <= 0) { 1268 return false; 1269 } 1270 1271 for (int d: a) { 1272 if (d >= 0) { 1273 return true; 1274 } 1275 } 1276 1277 return false; 1278 } 1279 isInsideZone(int brightness, float lux)1280 private boolean isInsideZone(int brightness, float lux) { 1281 for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) { 1282 int disp = mDisplayBrightnessThresholds[i]; 1283 int ambi = mAmbientBrightnessThresholds[i]; 1284 1285 if (disp >= 0 && ambi >= 0) { 1286 if (brightness <= disp && mAmbientLux <= ambi) { 1287 return true; 1288 } 1289 } else if (disp >= 0) { 1290 if (brightness <= disp) { 1291 return true; 1292 } 1293 } else if (ambi >= 0) { 1294 if (mAmbientLux <= ambi) { 1295 return true; 1296 } 1297 } 1298 } 1299 1300 return false; 1301 } 1302 // TODO: brightnessfloat: make it use float not int onBrightnessChangedLocked()1303 private void onBrightnessChangedLocked() { 1304 int brightness = Settings.System.getInt(mContext.getContentResolver(), 1305 Settings.System.SCREEN_BRIGHTNESS, -1); 1306 1307 Vote vote = null; 1308 boolean insideZone = isInsideZone(brightness, mAmbientLux); 1309 if (insideZone) { 1310 vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone); 1311 } 1312 1313 if (DEBUG) { 1314 Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux + 1315 ", Vote " + vote); 1316 } 1317 updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote); 1318 } 1319 onScreenOn(boolean on)1320 private void onScreenOn(boolean on) { 1321 if (mScreenOn != on) { 1322 mScreenOn = on; 1323 updateSensorStatus(); 1324 } 1325 } 1326 updateSensorStatus()1327 private void updateSensorStatus() { 1328 if (mSensorManager == null || mLightSensorListener == null) { 1329 return; 1330 } 1331 1332 if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled 1333 && mRefreshRateChangeable) { 1334 mSensorManager.registerListener(mLightSensorListener, 1335 mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler); 1336 } else { 1337 mLightSensorListener.removeCallbacks(); 1338 mSensorManager.unregisterListener(mLightSensorListener); 1339 } 1340 } 1341 isDefaultDisplayOn()1342 private boolean isDefaultDisplayOn() { 1343 final Display display = mContext.getSystemService(DisplayManager.class) 1344 .getDisplay(Display.DEFAULT_DISPLAY); 1345 return display.getState() != Display.STATE_OFF 1346 && mContext.getSystemService(PowerManager.class).isInteractive(); 1347 } 1348 1349 private final class LightSensorEventListener implements SensorEventListener { 1350 final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS; 1351 private float mLastSensorData; 1352 dumpLocked(PrintWriter pw)1353 public void dumpLocked(PrintWriter pw) { 1354 pw.println(" mLastSensorData: " + mLastSensorData); 1355 } 1356 1357 @Override onSensorChanged(SensorEvent event)1358 public void onSensorChanged(SensorEvent event) { 1359 mLastSensorData = event.values[0]; 1360 if (DEBUG) { 1361 Slog.d(TAG, "On sensor changed: " + mLastSensorData); 1362 } 1363 1364 boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux); 1365 if (zoneChanged && mLastSensorData < mAmbientLux) { 1366 // Easier to see flicker at lower brightness environment. Forget the history to 1367 // get immediate response. 1368 mAmbientFilter.clear(); 1369 } 1370 1371 long now = SystemClock.uptimeMillis(); 1372 mAmbientFilter.addValue(now, mLastSensorData); 1373 1374 mHandler.removeCallbacks(mInjectSensorEventRunnable); 1375 processSensorData(now); 1376 1377 if (zoneChanged && mLastSensorData > mAmbientLux) { 1378 // Sensor may not report new event if there is no brightness change. 1379 // Need to keep querying the temporal filter for the latest estimation, 1380 // until enter in higher lux zone or is interrupted by a new sensor event. 1381 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS); 1382 } 1383 } 1384 1385 @Override onAccuracyChanged(Sensor sensor, int accuracy)1386 public void onAccuracyChanged(Sensor sensor, int accuracy) { 1387 // Not used. 1388 } 1389 removeCallbacks()1390 public void removeCallbacks() { 1391 mHandler.removeCallbacks(mInjectSensorEventRunnable); 1392 } 1393 processSensorData(long now)1394 private void processSensorData(long now) { 1395 mAmbientLux = mAmbientFilter.getEstimate(now); 1396 1397 synchronized (mLock) { 1398 onBrightnessChangedLocked(); 1399 } 1400 } 1401 isDifferentZone(float lux1, float lux2)1402 private boolean isDifferentZone(float lux1, float lux2) { 1403 for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) { 1404 final float boundary = mAmbientBrightnessThresholds[z]; 1405 1406 // Test each boundary. See if the current value and the new value are at 1407 // different sides. 1408 if ((lux1 <= boundary && lux2 > boundary) 1409 || (lux1 > boundary && lux2 <= boundary)) { 1410 return true; 1411 } 1412 } 1413 1414 return false; 1415 } 1416 1417 private Runnable mInjectSensorEventRunnable = new Runnable() { 1418 @Override 1419 public void run() { 1420 long now = SystemClock.uptimeMillis(); 1421 // No need to really inject the last event into a temporal filter. 1422 processSensorData(now); 1423 1424 // Inject next event if there is a possible zone change. 1425 if (isDifferentZone(mLastSensorData, mAmbientLux)) { 1426 mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS); 1427 } 1428 } 1429 }; 1430 } 1431 } 1432 1433 private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener { DeviceConfigDisplaySettings()1434 public DeviceConfigDisplaySettings() { 1435 } 1436 startListening()1437 public void startListening() { 1438 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, 1439 BackgroundThread.getExecutor(), this); 1440 } 1441 1442 /* 1443 * Return null if no such property or wrong format (not comma separated integers). 1444 */ getBrightnessThresholds()1445 public int[] getBrightnessThresholds() { 1446 return getIntArrayProperty( 1447 DisplayManager.DeviceConfig. 1448 KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS); 1449 } 1450 1451 /* 1452 * Return null if no such property or wrong format (not comma separated integers). 1453 */ getAmbientThresholds()1454 public int[] getAmbientThresholds() { 1455 return getIntArrayProperty( 1456 DisplayManager.DeviceConfig. 1457 KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS); 1458 } 1459 1460 /* 1461 * Return null if no such property 1462 */ getDefaultPeakRefreshRate()1463 public Float getDefaultPeakRefreshRate() { 1464 float defaultPeakRefreshRate = DeviceConfig.getFloat( 1465 DeviceConfig.NAMESPACE_DISPLAY_MANAGER, 1466 DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1); 1467 1468 if (defaultPeakRefreshRate == -1) { 1469 return null; 1470 } 1471 return defaultPeakRefreshRate; 1472 } 1473 getRefreshRateInZone()1474 public int getRefreshRateInZone() { 1475 int defaultRefreshRateInZone = mContext.getResources().getInteger( 1476 R.integer.config_defaultRefreshRateInZone); 1477 1478 int refreshRate = DeviceConfig.getInt( 1479 DeviceConfig.NAMESPACE_DISPLAY_MANAGER, 1480 DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE, 1481 defaultRefreshRateInZone); 1482 1483 return refreshRate; 1484 } 1485 1486 @Override onPropertiesChanged(@onNull DeviceConfig.Properties properties)1487 public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { 1488 int[] brightnessThresholds = getBrightnessThresholds(); 1489 int[] ambientThresholds = getAmbientThresholds(); 1490 Float defaultPeakRefreshRate = getDefaultPeakRefreshRate(); 1491 int refreshRateInZone = getRefreshRateInZone(); 1492 1493 mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED, 1494 new Pair<int[], int[]>(brightnessThresholds, ambientThresholds)) 1495 .sendToTarget(); 1496 mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED, 1497 defaultPeakRefreshRate).sendToTarget(); 1498 mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone, 1499 0).sendToTarget(); 1500 } 1501 getIntArrayProperty(String prop)1502 private int[] getIntArrayProperty(String prop) { 1503 String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop, 1504 null); 1505 1506 if (strArray != null) { 1507 return parseIntArray(strArray); 1508 } 1509 1510 return null; 1511 } 1512 parseIntArray(@onNull String strArray)1513 private int[] parseIntArray(@NonNull String strArray) { 1514 String[] items = strArray.split(","); 1515 int[] array = new int[items.length]; 1516 1517 try { 1518 for (int i = 0; i < array.length; i++) { 1519 array[i] = Integer.parseInt(items[i]); 1520 } 1521 } catch (NumberFormatException e) { 1522 Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e); 1523 array = null; 1524 } 1525 1526 return array; 1527 } 1528 } 1529 1530 } 1531