1 /* 2 * Copyright (C) 2020 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.content.Context; 20 import android.database.ContentObserver; 21 import android.hardware.display.BrightnessInfo; 22 import android.net.Uri; 23 import android.os.Handler; 24 import android.os.IBinder; 25 import android.os.IThermalEventListener; 26 import android.os.IThermalService; 27 import android.os.PowerManager; 28 import android.os.RemoteException; 29 import android.os.ServiceManager; 30 import android.os.SystemClock; 31 import android.os.Temperature; 32 import android.os.UserHandle; 33 import android.provider.Settings; 34 import android.util.MathUtils; 35 import android.util.Slog; 36 import android.util.TimeUtils; 37 import android.view.SurfaceControlHdrLayerInfoListener; 38 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData; 41 import com.android.server.display.DisplayManagerService.Clock; 42 43 import java.io.PrintWriter; 44 import java.util.Iterator; 45 import java.util.LinkedList; 46 47 /** 48 * Controls the status of high-brightness mode for devices that support it. This class assumes that 49 * an instance is always created even if a device does not support high-brightness mode (HBM); in 50 * the case where it is not supported, the majority of the logic is skipped. On devices that support 51 * HBM, we keep track of the ambient lux as well as historical usage of HBM to determine when HBM is 52 * allowed and not. This class's output is simply a brightness-range maximum value (queried via 53 * {@link #getCurrentBrightnessMax}) that changes depending on whether HBM is enabled or not. 54 */ 55 class HighBrightnessModeController { 56 private static final String TAG = "HighBrightnessModeController"; 57 58 private static final boolean DEBUG = false; 59 60 private static final float HDR_PERCENT_OF_SCREEN_REQUIRED = 0.50f; 61 62 @VisibleForTesting 63 static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY; 64 65 private final float mBrightnessMin; 66 private final float mBrightnessMax; 67 private final Handler mHandler; 68 private final Runnable mHbmChangeCallback; 69 private final Runnable mRecalcRunnable; 70 private final Clock mClock; 71 private final SkinThermalStatusObserver mSkinThermalStatusObserver; 72 private final Context mContext; 73 private final SettingsObserver mSettingsObserver; 74 private final Injector mInjector; 75 76 private HdrListener mHdrListener; 77 private HighBrightnessModeData mHbmData; 78 private IBinder mRegisteredDisplayToken; 79 80 private boolean mIsInAllowedAmbientRange = false; 81 private boolean mIsTimeAvailable = false; 82 private boolean mIsAutoBrightnessEnabled = false; 83 private float mBrightness; 84 private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; 85 private boolean mIsHdrLayerPresent = false; 86 private boolean mIsThermalStatusWithinLimit = true; 87 private boolean mIsBlockedByLowPowerMode = false; 88 private int mWidth; 89 private int mHeight; 90 private float mAmbientLux; 91 92 /** 93 * If HBM is currently running, this is the start time for the current HBM session. 94 */ 95 private long mRunningStartTimeMillis = -1; 96 97 /** 98 * List of previous HBM-events ordered from most recent to least recent. 99 * Meant to store only the events that fall into the most recent 100 * {@link mHbmData.timeWindowMillis}. 101 */ 102 private LinkedList<HbmEvent> mEvents = new LinkedList<>(); 103 HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken, float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData, Runnable hbmChangeCallback, Context context)104 HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken, 105 float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData, 106 Runnable hbmChangeCallback, Context context) { 107 this(new Injector(), handler, width, height, displayToken, brightnessMin, brightnessMax, 108 hbmData, hbmChangeCallback, context); 109 } 110 111 @VisibleForTesting HighBrightnessModeController(Injector injector, Handler handler, int width, int height, IBinder displayToken, float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData, Runnable hbmChangeCallback, Context context)112 HighBrightnessModeController(Injector injector, Handler handler, int width, int height, 113 IBinder displayToken, float brightnessMin, float brightnessMax, 114 HighBrightnessModeData hbmData, Runnable hbmChangeCallback, 115 Context context) { 116 mInjector = injector; 117 mContext = context; 118 mClock = injector.getClock(); 119 mHandler = handler; 120 mBrightness = brightnessMin; 121 mBrightnessMin = brightnessMin; 122 mBrightnessMax = brightnessMax; 123 mHbmChangeCallback = hbmChangeCallback; 124 mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler); 125 mSettingsObserver = new SettingsObserver(mHandler); 126 mRecalcRunnable = this::recalculateTimeAllowance; 127 mHdrListener = new HdrListener(); 128 129 resetHbmData(width, height, displayToken, hbmData); 130 } 131 setAutoBrightnessEnabled(boolean isEnabled)132 void setAutoBrightnessEnabled(boolean isEnabled) { 133 if (!deviceSupportsHbm() || isEnabled == mIsAutoBrightnessEnabled) { 134 return; 135 } 136 if (DEBUG) { 137 Slog.d(TAG, "setAutoBrightnessEnabled( " + isEnabled + " )"); 138 } 139 mIsAutoBrightnessEnabled = isEnabled; 140 mIsInAllowedAmbientRange = false; // reset when auto-brightness switches 141 recalculateTimeAllowance(); 142 } 143 getCurrentBrightnessMin()144 float getCurrentBrightnessMin() { 145 return mBrightnessMin; 146 } 147 getCurrentBrightnessMax()148 float getCurrentBrightnessMax() { 149 if (!deviceSupportsHbm() || isCurrentlyAllowed()) { 150 // Either the device doesn't support HBM, or HBM range is currently allowed (device 151 // it in a high-lux environment). In either case, return the highest brightness 152 // level supported by the device. 153 return mBrightnessMax; 154 } else { 155 // Hbm is not allowed, only allow up to the brightness where we 156 // transition to high brightness mode. 157 return mHbmData.transitionPoint; 158 } 159 } 160 getNormalBrightnessMax()161 float getNormalBrightnessMax() { 162 return deviceSupportsHbm() ? mHbmData.transitionPoint : mBrightnessMax; 163 } 164 getHdrBrightnessValue()165 float getHdrBrightnessValue() { 166 // For HDR brightness, we take the current brightness and scale it to the max. The reason 167 // we do this is because we want brightness to go to HBM max when it would normally go 168 // to normal max, meaning it should not wait to go to 10000 lux (or whatever the transition 169 // point happens to be) in order to go full HDR. Likewise, HDR on manual brightness should 170 // automatically scale the brightness without forcing the user to adjust to higher values. 171 return MathUtils.map(getCurrentBrightnessMin(), getCurrentBrightnessMax(), 172 mBrightnessMin, mBrightnessMax, mBrightness); 173 } 174 onAmbientLuxChange(float ambientLux)175 void onAmbientLuxChange(float ambientLux) { 176 mAmbientLux = ambientLux; 177 if (!deviceSupportsHbm() || !mIsAutoBrightnessEnabled) { 178 return; 179 } 180 181 final boolean isHighLux = (ambientLux >= mHbmData.minimumLux); 182 if (isHighLux != mIsInAllowedAmbientRange) { 183 mIsInAllowedAmbientRange = isHighLux; 184 recalculateTimeAllowance(); 185 } 186 } 187 onBrightnessChanged(float brightness)188 void onBrightnessChanged(float brightness) { 189 if (!deviceSupportsHbm()) { 190 return; 191 } 192 mBrightness = brightness; 193 194 // If we are starting or ending a high brightness mode session, store the current 195 // session in mRunningStartTimeMillis, or the old one in mEvents. 196 final boolean wasHbmDrainingAvailableTime = mRunningStartTimeMillis != -1; 197 final boolean shouldHbmDrainAvailableTime = mBrightness > mHbmData.transitionPoint 198 && !mIsHdrLayerPresent; 199 if (wasHbmDrainingAvailableTime != shouldHbmDrainAvailableTime) { 200 final long currentTime = mClock.uptimeMillis(); 201 if (shouldHbmDrainAvailableTime) { 202 mRunningStartTimeMillis = currentTime; 203 } else { 204 mEvents.addFirst(new HbmEvent(mRunningStartTimeMillis, currentTime)); 205 mRunningStartTimeMillis = -1; 206 207 if (DEBUG) { 208 Slog.d(TAG, "New HBM event: " + mEvents.getFirst()); 209 } 210 } 211 } 212 213 recalculateTimeAllowance(); 214 } 215 getHighBrightnessMode()216 int getHighBrightnessMode() { 217 return mHbmMode; 218 } 219 getTransitionPoint()220 float getTransitionPoint() { 221 if (deviceSupportsHbm()) { 222 return mHbmData.transitionPoint; 223 } else { 224 return HBM_TRANSITION_POINT_INVALID; 225 } 226 } 227 stop()228 void stop() { 229 registerHdrListener(null /*displayToken*/); 230 mSkinThermalStatusObserver.stopObserving(); 231 mSettingsObserver.stopObserving(); 232 } 233 resetHbmData(int width, int height, IBinder displayToken, HighBrightnessModeData hbmData)234 void resetHbmData(int width, int height, IBinder displayToken, HighBrightnessModeData hbmData) { 235 mWidth = width; 236 mHeight = height; 237 mHbmData = hbmData; 238 239 unregisterHdrListener(); 240 mSkinThermalStatusObserver.stopObserving(); 241 mSettingsObserver.stopObserving(); 242 if (deviceSupportsHbm()) { 243 registerHdrListener(displayToken); 244 recalculateTimeAllowance(); 245 if (mHbmData.thermalStatusLimit > PowerManager.THERMAL_STATUS_NONE) { 246 mIsThermalStatusWithinLimit = true; 247 mSkinThermalStatusObserver.startObserving(); 248 } 249 if (!mHbmData.allowInLowPowerMode) { 250 mIsBlockedByLowPowerMode = false; 251 mSettingsObserver.startObserving(); 252 } 253 } 254 } 255 dump(PrintWriter pw)256 void dump(PrintWriter pw) { 257 mHandler.runWithScissors(() -> dumpLocal(pw), 1000); 258 } 259 260 @VisibleForTesting getHdrListener()261 HdrListener getHdrListener() { 262 return mHdrListener; 263 } 264 dumpLocal(PrintWriter pw)265 private void dumpLocal(PrintWriter pw) { 266 pw.println("HighBrightnessModeController:"); 267 pw.println(" mBrightness=" + mBrightness); 268 pw.println(" mCurrentMin=" + getCurrentBrightnessMin()); 269 pw.println(" mCurrentMax=" + getCurrentBrightnessMax()); 270 pw.println(" mHbmMode=" + BrightnessInfo.hbmToString(mHbmMode) 271 + (mHbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR 272 ? "(" + getHdrBrightnessValue() + ")" : "")); 273 pw.println(" mHbmData=" + mHbmData); 274 pw.println(" mAmbientLux=" + mAmbientLux 275 + (mIsAutoBrightnessEnabled ? "" : " (old/invalid)")); 276 pw.println(" mIsInAllowedAmbientRange=" + mIsInAllowedAmbientRange); 277 pw.println(" mIsAutoBrightnessEnabled=" + mIsAutoBrightnessEnabled); 278 pw.println(" mIsHdrLayerPresent=" + mIsHdrLayerPresent); 279 pw.println(" mBrightnessMin=" + mBrightnessMin); 280 pw.println(" mBrightnessMax=" + mBrightnessMax); 281 pw.println(" remainingTime=" + calculateRemainingTime(mClock.uptimeMillis())); 282 pw.println(" mIsTimeAvailable= " + mIsTimeAvailable); 283 pw.println(" mRunningStartTimeMillis=" + TimeUtils.formatUptime(mRunningStartTimeMillis)); 284 pw.println(" mIsThermalStatusWithinLimit=" + mIsThermalStatusWithinLimit); 285 pw.println(" mIsBlockedByLowPowerMode=" + mIsBlockedByLowPowerMode); 286 pw.println(" width*height=" + mWidth + "*" + mHeight); 287 pw.println(" mEvents="); 288 final long currentTime = mClock.uptimeMillis(); 289 long lastStartTime = currentTime; 290 if (mRunningStartTimeMillis != -1) { 291 lastStartTime = dumpHbmEvent(pw, new HbmEvent(mRunningStartTimeMillis, currentTime)); 292 } 293 for (HbmEvent event : mEvents) { 294 if (lastStartTime > event.endTimeMillis) { 295 pw.println(" event: [normal brightness]: " 296 + TimeUtils.formatDuration(lastStartTime - event.endTimeMillis)); 297 } 298 lastStartTime = dumpHbmEvent(pw, event); 299 } 300 301 mSkinThermalStatusObserver.dump(pw); 302 } 303 dumpHbmEvent(PrintWriter pw, HbmEvent event)304 private long dumpHbmEvent(PrintWriter pw, HbmEvent event) { 305 final long duration = event.endTimeMillis - event.startTimeMillis; 306 pw.println(" event: [" 307 + TimeUtils.formatUptime(event.startTimeMillis) + ", " 308 + TimeUtils.formatUptime(event.endTimeMillis) + "] (" 309 + TimeUtils.formatDuration(duration) + ")"); 310 return event.startTimeMillis; 311 } 312 isCurrentlyAllowed()313 private boolean isCurrentlyAllowed() { 314 // Returns true if HBM is allowed (above the ambient lux threshold) and there's still 315 // time within the current window for additional HBM usage. We return false if there is an 316 // HDR layer because we don't want the brightness MAX to change for HDR, which has its 317 // brightness scaled in a different way than sunlight HBM that doesn't require changing 318 // the MAX. HDR also needs to work under manual brightness which never adjusts the 319 // brightness maximum; so we implement HDR-HBM in a way that doesn't adjust the max. 320 // See {@link #getHdrBrightnessValue}. 321 return !mIsHdrLayerPresent 322 && (mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange 323 && mIsThermalStatusWithinLimit && !mIsBlockedByLowPowerMode); 324 } 325 deviceSupportsHbm()326 private boolean deviceSupportsHbm() { 327 return mHbmData != null; 328 } 329 calculateRemainingTime(long currentTime)330 private long calculateRemainingTime(long currentTime) { 331 if (!deviceSupportsHbm()) { 332 return 0; 333 } 334 335 long timeAlreadyUsed = 0; 336 337 // First, lets see how much time we've taken for any currently running 338 // session of HBM. 339 if (mRunningStartTimeMillis > 0) { 340 if (mRunningStartTimeMillis > currentTime) { 341 Slog.e(TAG, "Start time set to the future. curr: " + currentTime 342 + ", start: " + mRunningStartTimeMillis); 343 mRunningStartTimeMillis = currentTime; 344 } 345 timeAlreadyUsed = currentTime - mRunningStartTimeMillis; 346 } 347 348 if (DEBUG) { 349 Slog.d(TAG, "Time already used after current session: " + timeAlreadyUsed); 350 } 351 352 // Next, lets iterate through the history of previous sessions and add those times. 353 final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis; 354 Iterator<HbmEvent> it = mEvents.iterator(); 355 while (it.hasNext()) { 356 final HbmEvent event = it.next(); 357 358 // If this event ended before the current Timing window, discard forever and ever. 359 if (event.endTimeMillis < windowstartTimeMillis) { 360 it.remove(); 361 continue; 362 } 363 364 final long startTimeMillis = Math.max(event.startTimeMillis, windowstartTimeMillis); 365 timeAlreadyUsed += event.endTimeMillis - startTimeMillis; 366 } 367 368 if (DEBUG) { 369 Slog.d(TAG, "Time already used after all sessions: " + timeAlreadyUsed); 370 } 371 372 return Math.max(0, mHbmData.timeMaxMillis - timeAlreadyUsed); 373 } 374 375 /** 376 * Recalculates the allowable HBM time. 377 */ recalculateTimeAllowance()378 private void recalculateTimeAllowance() { 379 final long currentTime = mClock.uptimeMillis(); 380 final long remainingTime = calculateRemainingTime(currentTime); 381 382 // We allow HBM if there is more than the minimum required time available 383 // or if brightness is already in the high range, if there is any time left at all. 384 final boolean isAllowedWithoutRestrictions = remainingTime >= mHbmData.timeMinMillis; 385 final boolean isOnlyAllowedToStayOn = !isAllowedWithoutRestrictions 386 && remainingTime > 0 && mBrightness > mHbmData.transitionPoint; 387 mIsTimeAvailable = isAllowedWithoutRestrictions || isOnlyAllowedToStayOn; 388 389 // Calculate the time at which we want to recalculate mIsTimeAvailable in case a lux or 390 // brightness change doesn't happen before then. 391 long nextTimeout = -1; 392 if (mBrightness > mHbmData.transitionPoint) { 393 // if we're in high-lux now, timeout when we run out of allowed time. 394 nextTimeout = currentTime + remainingTime; 395 } else if (!mIsTimeAvailable && mEvents.size() > 0) { 396 // If we are not allowed...timeout when the oldest event moved outside of the timing 397 // window by at least minTime. Basically, we're calculating the soonest time we can 398 // get {@code timeMinMillis} back to us. 399 final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis; 400 final HbmEvent lastEvent = mEvents.getLast(); 401 final long startTimePlusMinMillis = 402 Math.max(windowstartTimeMillis, lastEvent.startTimeMillis) 403 + mHbmData.timeMinMillis; 404 final long timeWhenMinIsGainedBack = 405 currentTime + (startTimePlusMinMillis - windowstartTimeMillis) - remainingTime; 406 nextTimeout = timeWhenMinIsGainedBack; 407 } 408 409 if (DEBUG) { 410 Slog.d(TAG, "HBM recalculated. IsAllowedWithoutRestrictions: " 411 + isAllowedWithoutRestrictions 412 + ", isOnlyAllowedToStayOn: " + isOnlyAllowedToStayOn 413 + ", remainingAllowedTime: " + remainingTime 414 + ", isLuxHigh: " + mIsInAllowedAmbientRange 415 + ", isHBMCurrentlyAllowed: " + isCurrentlyAllowed() 416 + ", isHdrLayerPresent: " + mIsHdrLayerPresent 417 + ", isAutoBrightnessEnabled: " + mIsAutoBrightnessEnabled 418 + ", mIsTimeAvailable: " + mIsTimeAvailable 419 + ", mIsInAllowedAmbientRange: " + mIsInAllowedAmbientRange 420 + ", mIsThermalStatusWithinLimit: " + mIsThermalStatusWithinLimit 421 + ", mIsBlockedByLowPowerMode: " + mIsBlockedByLowPowerMode 422 + ", mBrightness: " + mBrightness 423 + ", RunningStartTimeMillis: " + mRunningStartTimeMillis 424 + ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1) 425 + ", events: " + mEvents); 426 } 427 428 if (nextTimeout != -1) { 429 mHandler.removeCallbacks(mRecalcRunnable); 430 mHandler.postAtTime(mRecalcRunnable, nextTimeout + 1); 431 } 432 // Update the state of the world 433 updateHbmMode(); 434 } 435 updateHbmMode()436 private void updateHbmMode() { 437 int newHbmMode = calculateHighBrightnessMode(); 438 if (mHbmMode != newHbmMode) { 439 mHbmMode = newHbmMode; 440 mHbmChangeCallback.run(); 441 } 442 } 443 calculateHighBrightnessMode()444 private int calculateHighBrightnessMode() { 445 if (!deviceSupportsHbm()) { 446 return BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; 447 } else if (mIsHdrLayerPresent) { 448 return BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR; 449 } else if (isCurrentlyAllowed()) { 450 return BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT; 451 } 452 453 return BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; 454 } 455 registerHdrListener(IBinder displayToken)456 private void registerHdrListener(IBinder displayToken) { 457 if (mRegisteredDisplayToken == displayToken) { 458 return; 459 } 460 461 unregisterHdrListener(); 462 mRegisteredDisplayToken = displayToken; 463 if (mRegisteredDisplayToken != null) { 464 mHdrListener.register(mRegisteredDisplayToken); 465 } 466 } 467 unregisterHdrListener()468 private void unregisterHdrListener() { 469 if (mRegisteredDisplayToken != null) { 470 mHdrListener.unregister(mRegisteredDisplayToken); 471 mIsHdrLayerPresent = false; 472 } 473 } 474 475 /** 476 * Represents an event in which High Brightness Mode was enabled. 477 */ 478 private static class HbmEvent { 479 public long startTimeMillis; 480 public long endTimeMillis; 481 HbmEvent(long startTimeMillis, long endTimeMillis)482 HbmEvent(long startTimeMillis, long endTimeMillis) { 483 this.startTimeMillis = startTimeMillis; 484 this.endTimeMillis = endTimeMillis; 485 } 486 487 @Override toString()488 public String toString() { 489 return "[Event: {" + startTimeMillis + ", " + endTimeMillis + "}, total: " 490 + ((endTimeMillis - startTimeMillis) / 1000) + "]"; 491 } 492 } 493 494 @VisibleForTesting 495 class HdrListener extends SurfaceControlHdrLayerInfoListener { 496 @Override onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers, int maxW, int maxH, int flags)497 public void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers, 498 int maxW, int maxH, int flags) { 499 mHandler.post(() -> { 500 mIsHdrLayerPresent = numberOfHdrLayers > 0 501 && (float) (maxW * maxH) 502 >= ((float) (mWidth * mHeight) * HDR_PERCENT_OF_SCREEN_REQUIRED); 503 // Calling the brightness update so that we can recalculate 504 // brightness with HDR in mind. 505 onBrightnessChanged(mBrightness); 506 }); 507 } 508 } 509 510 private final class SkinThermalStatusObserver extends IThermalEventListener.Stub { 511 private final Injector mInjector; 512 private final Handler mHandler; 513 514 private IThermalService mThermalService; 515 private boolean mStarted; 516 SkinThermalStatusObserver(Injector injector, Handler handler)517 SkinThermalStatusObserver(Injector injector, Handler handler) { 518 mInjector = injector; 519 mHandler = handler; 520 } 521 522 @Override notifyThrottling(Temperature temp)523 public void notifyThrottling(Temperature temp) { 524 if (DEBUG) { 525 Slog.d(TAG, "New thermal throttling status " 526 + ", current thermal status = " + temp.getStatus() 527 + ", threshold = " + mHbmData.thermalStatusLimit); 528 } 529 mHandler.post(() -> { 530 mIsThermalStatusWithinLimit = temp.getStatus() <= mHbmData.thermalStatusLimit; 531 // This recalculates HbmMode and runs mHbmChangeCallback if the mode has changed 532 updateHbmMode(); 533 }); 534 } 535 startObserving()536 void startObserving() { 537 if (mStarted) { 538 if (DEBUG) { 539 Slog.d(TAG, "Thermal status observer already started"); 540 } 541 return; 542 } 543 mThermalService = mInjector.getThermalService(); 544 if (mThermalService == null) { 545 Slog.w(TAG, "Could not observe thermal status. Service not available"); 546 return; 547 } 548 try { 549 // We get a callback immediately upon registering so there's no need to query 550 // for the current value. 551 mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN); 552 mStarted = true; 553 } catch (RemoteException e) { 554 Slog.e(TAG, "Failed to register thermal status listener", e); 555 } 556 } 557 stopObserving()558 void stopObserving() { 559 mIsThermalStatusWithinLimit = true; 560 if (!mStarted) { 561 if (DEBUG) { 562 Slog.d(TAG, "Stop skipped because thermal status observer not started"); 563 } 564 return; 565 } 566 try { 567 mThermalService.unregisterThermalEventListener(this); 568 mStarted = false; 569 } catch (RemoteException e) { 570 Slog.e(TAG, "Failed to unregister thermal status listener", e); 571 } 572 mThermalService = null; 573 } 574 dump(PrintWriter writer)575 void dump(PrintWriter writer) { 576 writer.println(" SkinThermalStatusObserver:"); 577 writer.println(" mStarted: " + mStarted); 578 if (mThermalService != null) { 579 writer.println(" ThermalService available"); 580 } else { 581 writer.println(" ThermalService not available"); 582 } 583 } 584 } 585 586 private final class SettingsObserver extends ContentObserver { 587 private final Uri mLowPowerModeSetting = Settings.Global.getUriFor( 588 Settings.Global.LOW_POWER_MODE); 589 private boolean mStarted; 590 SettingsObserver(Handler handler)591 SettingsObserver(Handler handler) { 592 super(handler); 593 } 594 595 @Override onChange(boolean selfChange, Uri uri)596 public void onChange(boolean selfChange, Uri uri) { 597 updateLowPower(); 598 } 599 startObserving()600 void startObserving() { 601 if (!mStarted) { 602 mContext.getContentResolver().registerContentObserver(mLowPowerModeSetting, 603 false /*notifyForDescendants*/, this, UserHandle.USER_ALL); 604 mStarted = true; 605 updateLowPower(); 606 } 607 } 608 stopObserving()609 void stopObserving() { 610 mIsBlockedByLowPowerMode = false; 611 if (mStarted) { 612 mContext.getContentResolver().unregisterContentObserver(this); 613 mStarted = false; 614 } 615 } 616 updateLowPower()617 private void updateLowPower() { 618 final boolean isLowPowerMode = isLowPowerMode(); 619 if (isLowPowerMode == mIsBlockedByLowPowerMode) { 620 return; 621 } 622 if (DEBUG) { 623 Slog.d(TAG, "Settings.Global.LOW_POWER_MODE enabled: " + isLowPowerMode); 624 } 625 mIsBlockedByLowPowerMode = isLowPowerMode; 626 // this recalculates HbmMode and runs mHbmChangeCallback if the mode has changed 627 updateHbmMode(); 628 } 629 isLowPowerMode()630 private boolean isLowPowerMode() { 631 return Settings.Global.getInt( 632 mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE, 0) != 0; 633 } 634 } 635 636 public static class Injector { getClock()637 public Clock getClock() { 638 return SystemClock::uptimeMillis; 639 } 640 getThermalService()641 public IThermalService getThermalService() { 642 return IThermalService.Stub.asInterface( 643 ServiceManager.getService(Context.THERMAL_SERVICE)); 644 } 645 } 646 } 647