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