content: /*
   * Copyright (C) 2012 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.server.display;
  
  import android.animation.Animator;
  import android.animation.ObjectAnimator;
  import android.annotation.Nullable;
  import android.annotation.UserIdInt;
  import android.app.ActivityManager;
  import android.content.Context;
  import android.content.pm.ParceledListSlice;
  import android.content.res.Resources;
  import android.database.ContentObserver;
  import android.hardware.Sensor;
  import android.hardware.SensorEvent;
  import android.hardware.SensorEventListener;
  import android.hardware.SensorManager;
  import android.hardware.display.AmbientBrightnessDayStats;
  import android.hardware.display.BrightnessChangeEvent;
  import android.hardware.display.BrightnessConfiguration;
  import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
  import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
  import android.metrics.LogMaker;
  import android.net.Uri;
  import android.os.Handler;
  import android.os.Looper;
  import android.os.Message;
  import android.os.PowerManager;
  import android.os.RemoteException;
  import android.os.SystemClock;
  import android.os.Trace;
  import android.os.UserHandle;
  import android.provider.Settings;
  import android.text.TextUtils;
  import android.util.MathUtils;
  import android.util.Slog;
  import android.util.TimeUtils;
  import android.view.Display;
  
  import com.android.internal.app.IBatteryStats;
  import com.android.internal.logging.MetricsLogger;
  import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
  import com.android.server.LocalServices;
  import com.android.server.am.BatteryStatsService;
  import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
  import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
  import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings;
  import com.android.server.policy.WindowManagerPolicy;
  
  import java.io.PrintWriter;
  import java.util.List;
  
  /**
   * Controls the power state of the display.
   *
   * Handles the proximity sensor, light sensor, and animations between states
   * including the screen off animation.
   *
   * This component acts independently of the rest of the power manager service.
   * In particular, it does not share any state and it only communicates
   * via asynchronous callbacks to inform the power manager that something has
   * changed.
   *
   * Everything this class does internally is serialized on its handler although
   * it may be accessed by other threads from the outside.
   *
   * Note that the power manager service guarantees that it will hold a suspend
   * blocker as long as the display is not ready.  So most of the work done here
   * does not need to worry about holding a suspend blocker unless it happens
   * independently of the display ready signal.
     *
   * For debugging, you can make the color fade and brightness animations run
   * slower by changing the "animator duration scale" option in Development Settings.
   */
  final class DisplayPowerController implements AutomaticBrightnessController.Callbacks,
          DisplayWhiteBalanceController.Callbacks {
      private static final String TAG = "DisplayPowerController";
      private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
      private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
  
      private static final boolean DEBUG = false;
      private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
  
      // If true, uses the color fade on animation.
      // We might want to turn this off if we cannot get a guarantee that the screen
      // actually turns on and starts showing new content after the call to set the
      // screen state returns.  Playing the animation can also be somewhat slow.
      private static final boolean USE_COLOR_FADE_ON_ANIMATION = false;
  
      // The minimum reduction in brightness when dimmed.
      private static final int SCREEN_DIM_MINIMUM_REDUCTION = 10;
  
      private static final int COLOR_FADE_ON_ANIMATION_DURATION_MILLIS = 250;
      private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 400;
  
      private static final int MSG_UPDATE_POWER_STATE = 1;
      private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
      private static final int MSG_SCREEN_ON_UNBLOCKED = 3;
      private static final int MSG_SCREEN_OFF_UNBLOCKED = 4;
      private static final int MSG_CONFIGURE_BRIGHTNESS = 5;
      private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 6;
      private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7;
  
      private static final int PROXIMITY_UNKNOWN = -1;
      private static final int PROXIMITY_NEGATIVE = 0;
      private static final int PROXIMITY_POSITIVE = 1;
  
      // Proximity sensor debounce delay in milliseconds for positive or negative transitions.
      private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
      private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250;
  
      // Trigger proximity if distance is less than 5 cm.
      private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
  
      // State machine constants for tracking initial brightness ramp skipping when enabled.
      private static final int RAMP_STATE_SKIP_NONE = 0;
      private static final int RAMP_STATE_SKIP_INITIAL = 1;
      private static final int RAMP_STATE_SKIP_AUTOBRIGHT = 2;
  
      private static final int REPORTED_TO_POLICY_SCREEN_OFF = 0;
      private static final int REPORTED_TO_POLICY_SCREEN_TURNING_ON = 1;
      private static final int REPORTED_TO_POLICY_SCREEN_ON = 2;
      private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3;
  
      private final Object mLock = new Object();
  
      private final Context mContext;
  
      // Our handler.
      private final DisplayControllerHandler mHandler;
  
      // Asynchronous callbacks into the power manager service.
      // Only invoked from the handler thread while no locks are held.
      private final DisplayPowerCallbacks mCallbacks;
  
      // Battery stats.
      private final IBatteryStats mBatteryStats;
  
      // The sensor manager.
      private final SensorManager mSensorManager;
  
      // The window manager policy.
      private final WindowManagerPolicy mWindowManagerPolicy;
  
      // The display blanker.
      private final DisplayBlanker mBlanker;
  
      // Tracker for brightness changes.
      private final BrightnessTracker mBrightnessTracker;
  
      // Tracker for brightness settings changes.
      private final SettingsObserver mSettingsObserver;
  
      // The proximity sensor, or null if not available or needed.
      private Sensor mProximitySensor;
  
      // The doze screen brightness.
      private final int mScreenBrightnessDozeConfig;
  
      // The dim screen brightness.
      private final int mScreenBrightnessDimConfig;
  
      // The minimum allowed brightness.
      private final int mScreenBrightnessRangeMinimum;
  
      // The maximum allowed brightness.
      private final int mScreenBrightnessRangeMaximum;
  
      // The default screen brightness.
      private final int mScreenBrightnessDefault;
  
      // The minimum allowed brightness while in VR.
      private final int mScreenBrightnessForVrRangeMinimum;
  
      // The maximum allowed brightness while in VR.
      private final int mScreenBrightnessForVrRangeMaximum;
  
      // The default screen brightness for VR.
      private final int mScreenBrightnessForVrDefault;
  
      // True if auto-brightness should be used.
      private boolean mUseSoftwareAutoBrightnessConfig;
  
      // True if should use light sensor to automatically determine doze screen brightness.
      private final boolean mAllowAutoBrightnessWhileDozingConfig;
  
      // Whether or not the color fade on screen on / off is enabled.
      private final boolean mColorFadeEnabled;
  
      // True if we should fade the screen while turning it off, false if we should play
      // a stylish color fade animation instead.
      private boolean mColorFadeFadesConfig;
  
      // True if we need to fake a transition to off when coming out of a doze state.
      // Some display hardware will blank itself when coming out of doze in order to hide
      // artifacts. For these displays we fake a transition into OFF so that policy can appropriately
      // blank itself and begin an appropriate power on animation.
      private boolean mDisplayBlanksAfterDozeConfig;
  
      // True if there are only buckets of brightness values when the display is in the doze state,
      // rather than a full range of values. If this is true, then we'll avoid animating the screen
      // brightness since it'd likely be multiple jarring brightness transitions instead of just one
      // to reach the final state.
      private boolean mBrightnessBucketsInDozeConfig;
  
      // The pending power request.
      // Initially null until the first call to requestPowerState.
      // Guarded by mLock.
      private DisplayPowerRequest mPendingRequestLocked;
  
      // True if a request has been made to wait for the proximity sensor to go negative.
      // Guarded by mLock.
      private boolean mPendingWaitForNegativeProximityLocked;
  
      // True if the pending power request or wait for negative proximity flag
      // has been changed since the last update occurred.
      // Guarded by mLock.
      private boolean mPendingRequestChangedLocked;
  
      // Set to true when the important parts of the pending power request have been applied.
      // The important parts are mainly the screen state.  Brightness changes may occur
      // concurrently.
      // Guarded by mLock.
      private boolean mDisplayReadyLocked;
  
      // Set to true if a power state update is required.
      // Guarded by mLock.
      private boolean mPendingUpdatePowerStateLocked;
  
      /* The following state must only be accessed by the handler thread. */
  
      // The currently requested power state.
      // The power controller will progressively update its internal state to match
      // the requested power state.  Initially null until the first update.
      private DisplayPowerRequest mPowerRequest;
  
      // The current power state.
      // Must only be accessed on the handler thread.
      private DisplayPowerState mPowerState;
  
      // True if the device should wait for negative proximity sensor before
      // waking up the screen.  This is set to false as soon as a negative
      // proximity sensor measurement is observed or when the device is forced to
      // go to sleep by the user.  While true, the screen remains off.
      private boolean mWaitingForNegativeProximity;
  
      // The actual proximity sensor threshold value.
      private float mProximityThreshold;
  
      // Set to true if the proximity sensor listener has been registered
      // with the sensor manager.
      private boolean mProximitySensorEnabled;
  
      // The debounced proximity sensor state.
      private int mProximity = PROXIMITY_UNKNOWN;
  
      // The raw non-debounced proximity sensor state.
      private int mPendingProximity = PROXIMITY_UNKNOWN;
      private long mPendingProximityDebounceTime = -1; // -1 if fully debounced
  
      // True if the screen was turned off because of the proximity sensor.
      // When the screen turns on again, we report user activity to the power manager.
      private boolean mScreenOffBecauseOfProximity;
  
      // The currently active screen on unblocker.  This field is non-null whenever
      // we are waiting for a callback to release it and unblock the screen.
      private ScreenOnUnblocker mPendingScreenOnUnblocker;
      private ScreenOffUnblocker mPendingScreenOffUnblocker;
  
      // True if we were in the process of turning off the screen.
      // This allows us to recover more gracefully from situations where we abort
      // turning off the screen.
      private boolean mPendingScreenOff;
  
      // True if we have unfinished business and are holding a suspend blocker.
      private boolean mUnfinishedBusiness;
  
      // The elapsed real time when the screen on was blocked.
      private long mScreenOnBlockStartRealTime;
      private long mScreenOffBlockStartRealTime;
  
      // Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_SCREEN_* fields.
      private int mReportedScreenStateToPolicy;
  
      // If the last recorded screen state was dozing or not.
      private boolean mDozing;
  
      // Remembers whether certain kinds of brightness adjustments
      // were recently applied so that we can decide how to transition.
      private boolean mAppliedAutoBrightness;
      private boolean mAppliedDimming;
      private boolean mAppliedLowPower;
      private boolean mAppliedScreenBrightnessOverride;
      private boolean mAppliedTemporaryBrightness;
      private boolean mAppliedTemporaryAutoBrightnessAdjustment;
      private boolean mAppliedBrightnessBoost;
  
      // Reason for which the brightness was last changed. See {@link BrightnessReason} for more
      // information.
      // At the time of this writing, this value is changed within updatePowerState() only, which is
      // limited to the thread used by DisplayControllerHandler.
      private BrightnessReason mBrightnessReason = new BrightnessReason();
      private BrightnessReason mBrightnessReasonTemp = new BrightnessReason();
  
      // Brightness animation ramp rates in brightness units per second
      private final int mBrightnessRampRateFast;
      private final int mBrightnessRampRateSlow;
  
      // Whether or not to skip the initial brightness ramps into STATE_ON.
      private final boolean mSkipScreenOnBrightnessRamp;
  
      // Display white balance components.
      @Nullable
      private final DisplayWhiteBalanceSettings mDisplayWhiteBalanceSettings;
      @Nullable
      private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
  
      // A record of state for skipping brightness ramps.
      private int mSkipRampState = RAMP_STATE_SKIP_NONE;
  
      // The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL.
      private int mInitialAutoBrightness;
  
      // The controller for the automatic brightness level.
      private AutomaticBrightnessController mAutomaticBrightnessController;
  
      // The mapper between ambient lux, display backlight values, and display brightness.
      @Nullable
      private BrightnessMappingStrategy mBrightnessMapper;
  
      // The current brightness configuration.
      @Nullable
      private BrightnessConfiguration mBrightnessConfiguration;
  
      // The last brightness that was set by the user and not temporary. Set to -1 when a brightness
      // has yet to be recorded.
      private int mLastUserSetScreenBrightness;
  
      // The screen brightenss setting has changed but not taken effect yet. If this is different
      // from the current screen brightness setting then this is coming from something other than us
      // and should be considered a user interaction.
      private int mPendingScreenBrightnessSetting;
  
      // The last observed screen brightness setting, either set by us or by the settings app on
      // behalf of the user.
      private int mCurrentScreenBrightnessSetting;
  
      // The temporary screen brightness. Typically set when a user is interacting with the
      // brightness slider but hasn't settled on a choice yet. Set to -1 when there's no temporary
      // brightness set.
      private int mTemporaryScreenBrightness;
  
      // The current screen brightness while in VR mode.
      private int mScreenBrightnessForVr;
  
      // The last auto brightness adjustment that was set by the user and not temporary. Set to
      // Float.NaN when an auto-brightness adjustment hasn't been recorded yet.
      private float mAutoBrightnessAdjustment;
  
      // The pending auto brightness adjustment that will take effect on the next power state update.
      private float mPendingAutoBrightnessAdjustment;
  
      // The temporary auto brightness adjustment. Typically set when a user is interacting with the
      // adjustment slider but hasn't settled on a choice yet. Set to Float.NaN when there's no
      // temporary adjustment set.
      private float mTemporaryAutoBrightnessAdjustment;
  
      // Animators.
      private ObjectAnimator mColorFadeOnAnimator;
      private ObjectAnimator mColorFadeOffAnimator;
      private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
  
      /**
       * Creates the display power controller.
       */
      public DisplayPowerController(Context context,
              DisplayPowerCallbacks callbacks, Handler handler,
              SensorManager sensorManager, DisplayBlanker blanker) {
          mHandler = new DisplayControllerHandler(handler.getLooper());
          mBrightnessTracker = new BrightnessTracker(context, null);
          mSettingsObserver = new SettingsObserver(mHandler);
          mCallbacks = callbacks;
  
          mBatteryStats = BatteryStatsService.getService();
          mSensorManager = sensorManager;
          mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
          mBlanker = blanker;
          mContext = context;
  
          final Resources resources = context.getResources();
          final int screenBrightnessSettingMinimum = clampAbsoluteBrightness(resources.getInteger(
                  com.android.internal.R.integer.config_screenBrightnessSettingMinimum));
  
          mScreenBrightnessDozeConfig = clampAbsoluteBrightness(resources.getInteger(
                  com.android.internal.R.integer.config_screenBrightnessDoze));
  
          mScreenBrightnessDimConfig = clampAbsoluteBrightness(resources.getInteger(
                  com.android.internal.R.integer.config_screenBrightnessDim));
  
          mScreenBrightnessRangeMinimum =
                  Math.min(screenBrightnessSettingMinimum, mScreenBrightnessDimConfig);
  
          mScreenBrightnessRangeMaximum = clampAbsoluteBrightness(resources.getInteger(
                      com.android.internal.R.integer.config_screenBrightnessSettingMaximum));
          mScreenBrightnessDefault = clampAbsoluteBrightness(resources.getInteger(
                      com.android.internal.R.integer.config_screenBrightnessSettingDefault));
  
          mScreenBrightnessForVrRangeMinimum = clampAbsoluteBrightness(resources.getInteger(
                      com.android.internal.R.integer.config_screenBrightnessForVrSettingMinimum));
          mScreenBrightnessForVrRangeMaximum = clampAbsoluteBrightness(resources.getInteger(
                      com.android.internal.R.integer.config_screenBrightnessForVrSettingMaximum));
          mScreenBrightnessForVrDefault = clampAbsoluteBrightness(resources.getInteger(
                      com.android.internal.R.integer.config_screenBrightnessForVrSettingDefault));
  
          mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
                  com.android.internal.R.bool.config_automatic_brightness_available);
  
          mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
                  com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing);
  
          mBrightnessRampRateFast = resources.getInteger(
                  com.android.internal.R.integer.config_brightness_ramp_rate_fast);
          mBrightnessRampRateSlow = resources.getInteger(
                  com.android.internal.R.integer.config_brightness_ramp_rate_slow);
          mSkipScreenOnBrightnessRamp = resources.getBoolean(
                  com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
  
          if (mUseSoftwareAutoBrightnessConfig) {
              final float dozeScaleFactor = resources.getFraction(
                      com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
                      1, 1);
  
              int[] ambientBrighteningThresholds = resources.getIntArray(
                      com.android.internal.R.array.config_ambientBrighteningThresholds);
              int[] ambientDarkeningThresholds = resources.getIntArray(
                      com.android.internal.R.array.config_ambientDarkeningThresholds);
              int[] ambientThresholdLevels = resources.getIntArray(
                      com.android.internal.R.array.config_ambientThresholdLevels);
              HysteresisLevels ambientBrightnessThresholds = new HysteresisLevels(
                      ambientBrighteningThresholds, ambientDarkeningThresholds,
                      ambientThresholdLevels);
  
              int[] screenBrighteningThresholds = resources.getIntArray(
                      com.android.internal.R.array.config_screenBrighteningThresholds);
              int[] screenDarkeningThresholds = resources.getIntArray(
                      com.android.internal.R.array.config_screenDarkeningThresholds);
              int[] screenThresholdLevels = resources.getIntArray(
                      com.android.internal.R.array.config_screenThresholdLevels);
              HysteresisLevels screenBrightnessThresholds = new HysteresisLevels(
                      screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels);
  
              long brighteningLightDebounce = resources.getInteger(
                      com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce);
              long darkeningLightDebounce = resources.getInteger(
                      com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce);
              boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
                      com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
  
              int lightSensorWarmUpTimeConfig = resources.getInteger(
                      com.android.internal.R.integer.config_lightSensorWarmupTime);
              int lightSensorRate = resources.getInteger(
                      com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
              int initialLightSensorRate = resources.getInteger(
                      com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate);
              if (initialLightSensorRate == -1) {
                  initialLightSensorRate = lightSensorRate;
              } else if (initialLightSensorRate > lightSensorRate) {
                  Slog.w(TAG, "Expected config_autoBrightnessInitialLightSensorRate ("
                          + initialLightSensorRate + ") to be less than or equal to "
                          + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
              }
              int shortTermModelTimeout = resources.getInteger(
                      com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout);
  
              String lightSensorType = resources.getString(
                      com.android.internal.R.string.config_displayLightSensorType);
              Sensor lightSensor = findDisplayLightSensor(lightSensorType);
  
              mBrightnessMapper = BrightnessMappingStrategy.create(resources);
              if (mBrightnessMapper != null) {
                  mAutomaticBrightnessController = new AutomaticBrightnessController(this,
                          handler.getLooper(), sensorManager, lightSensor, mBrightnessMapper,
                          lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum,
                          mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
                          initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
                          autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
                          screenBrightnessThresholds, shortTermModelTimeout,
                          context.getPackageManager());
              } else {
                  mUseSoftwareAutoBrightnessConfig = false;
              }
          }
  
          mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic();
          mColorFadeFadesConfig = resources.getBoolean(
                  com.android.internal.R.bool.config_animateScreenLights);
  
          mDisplayBlanksAfterDozeConfig = resources.getBoolean(
                  com.android.internal.R.bool.config_displayBlanksAfterDoze);
  
          mBrightnessBucketsInDozeConfig = resources.getBoolean(
                  com.android.internal.R.bool.config_displayBrightnessBucketsInDoze);
  
          if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
              mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
              if (mProximitySensor != null) {
                  mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
                          TYPICAL_PROXIMITY_THRESHOLD);
              }
          }
  
          mCurrentScreenBrightnessSetting = getScreenBrightnessSetting();
          mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
          mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
          mTemporaryScreenBrightness = -1;
          mPendingScreenBrightnessSetting = -1;
          mTemporaryAutoBrightnessAdjustment = Float.NaN;
          mPendingAutoBrightnessAdjustment = Float.NaN;
  
          DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null;
          DisplayWhiteBalanceController displayWhiteBalanceController = null;
          try {
              displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler);
              displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler,
                      mSensorManager, resources);
              displayWhiteBalanceSettings.setCallbacks(this);
              displayWhiteBalanceController.setCallbacks(this);
          } catch (Exception e) {
              Slog.e(TAG, "failed to set up display white-balance: " + e);
          }
          mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
          mDisplayWhiteBalanceController = displayWhiteBalanceController;
      }
  
      private Sensor findDisplayLightSensor(String sensorType) {
          if (!TextUtils.isEmpty(sensorType)) {
              List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
              for (int i = 0; i < sensors.size(); i++) {
                  Sensor sensor = sensors.get(i);
                  if (sensorType.equals(sensor.getStringType())) {
                      return sensor;
                  }
              }
          }
          return mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
      }
  
      /**
       * Returns true if the proximity sensor screen-off function is available.
       */
      public boolean isProximitySensorAvailable() {
          return mProximitySensor != null;
      }
  
      /**
       * Get the {@link BrightnessChangeEvent}s for the specified user.
       * @param userId userId to fetch data for
       * @param includePackage if false will null out the package name in events
       */
      public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(
              @UserIdInt int userId, boolean includePackage) {
          return mBrightnessTracker.getEvents(userId, includePackage);
      }
  
      public void onSwitchUser(@UserIdInt int newUserId) {
          handleSettingsChange(true /* userSwitch */);
          mBrightnessTracker.onSwitchUser(newUserId);
      }
  
      public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
              @UserIdInt int userId) {
          return mBrightnessTracker.getAmbientBrightnessStats(userId);
      }
  
      /**
       * Persist the brightness slider events and ambient brightness stats to disk.
       */
      public void persistBrightnessTrackerState() {
          mBrightnessTracker.persistBrightnessTrackerState();
      }
  
      /**
       * Requests a new power state.
       * The controller makes a copy of the provided object and then
       * begins adjusting the power state to match what was requested.
       *
       * @param request The requested power state.
       * @param waitForNegativeProximity If true, issues a request to wait for
       * negative proximity before turning the screen back on, assuming the screen
       * was turned off by the proximity sensor.
       * @return True if display is ready, false if there are important changes that must
       * be made asynchronously (such as turning the screen on), in which case the caller
       * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
       * then try the request again later until the state converges.
       */
      public boolean requestPowerState(DisplayPowerRequest request,
              boolean waitForNegativeProximity) {
          if (DEBUG) {
              Slog.d(TAG, "requestPowerState: "
                      + request + ", waitForNegativeProximity=" + waitForNegativeProximity);
          }
  
          synchronized (mLock) {
              boolean changed = false;
  
              if (waitForNegativeProximity
                      && !mPendingWaitForNegativeProximityLocked) {
                  mPendingWaitForNegativeProximityLocked = true;
                  changed = true;
              }
  
              if (mPendingRequestLocked == null) {
                  mPendingRequestLocked = new DisplayPowerRequest(request);
                  changed = true;
              } else if (!mPendingRequestLocked.equals(request)) {
                  mPendingRequestLocked.copyFrom(request);
                  changed = true;
              }
  
              if (changed) {
                  mDisplayReadyLocked = false;
              }
  
              if (changed && !mPendingRequestChangedLocked) {
                  mPendingRequestChangedLocked = true;
                  sendUpdatePowerStateLocked();
              }
  
              return mDisplayReadyLocked;
          }
      }
  
      public BrightnessConfiguration getDefaultBrightnessConfiguration() {
          if (mAutomaticBrightnessController == null) {
              return null;
          }
          return mAutomaticBrightnessController.getDefaultConfig();
      }
  
      private void sendUpdatePowerState() {
          synchronized (mLock) {
              sendUpdatePowerStateLocked();
          }
      }
  
      private void sendUpdatePowerStateLocked() {
          if (!mPendingUpdatePowerStateLocked) {
              mPendingUpdatePowerStateLocked = true;
              Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
              mHandler.sendMessage(msg);
          }
      }
  
      private void initialize() {
          // Initialize the power state object for the default display.
          // In the future, we might manage multiple displays independently.
          mPowerState = new DisplayPowerState(mBlanker,
                  mColorFadeEnabled ? new ColorFade(Display.DEFAULT_DISPLAY) : null);
  
          if (mColorFadeEnabled) {
              mColorFadeOnAnimator = ObjectAnimator.ofFloat(
                      mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 0.0f, 1.0f);
              mColorFadeOnAnimator.setDuration(COLOR_FADE_ON_ANIMATION_DURATION_MILLIS);
              mColorFadeOnAnimator.addListener(mAnimatorListener);
  
              mColorFadeOffAnimator = ObjectAnimator.ofFloat(
                      mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 1.0f, 0.0f);
              mColorFadeOffAnimator.setDuration(COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS);
              mColorFadeOffAnimator.addListener(mAnimatorListener);
          }
  
          mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
                  mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS);
          mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener);
  
          // Initialize screen state for battery stats.
          try {
              mBatteryStats.noteScreenState(mPowerState.getScreenState());
              mBatteryStats.noteScreenBrightness(mPowerState.getScreenBrightness());
          } catch (RemoteException ex) {
              // same process
          }
  
          // Initialize all of the brightness tracking state
          final float brightness = convertToNits(mPowerState.getScreenBrightness());
          if (brightness >= 0.0f) {
              mBrightnessTracker.start(brightness);
          }
  
          mContext.getContentResolver().registerContentObserver(
                  Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS),
                  false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
          mContext.getContentResolver().registerContentObserver(
                  Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR),
                  false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
          mContext.getContentResolver().registerContentObserver(
                  Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ),
                  false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
      }
  
      private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
          @Override
          public void onAnimationStart(Animator animation) {
          }
          @Override
          public void onAnimationEnd(Animator animation) {
              sendUpdatePowerState();
          }
          @Override
          public void onAnimationRepeat(Animator animation) {
          }
          @Override
          public void onAnimationCancel(Animator animation) {
          }
      };
  
      private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() {
          @Override
          public void onAnimationEnd() {
              sendUpdatePowerState();
          }
      };
  
      private void updatePowerState() {
          // Update the power state request.
          final boolean mustNotify;
          final int previousPolicy;
          boolean mustInitialize = false;
          int brightnessAdjustmentFlags = 0;
          mBrightnessReasonTemp.set(null);
  
          synchronized (mLock) {
              mPendingUpdatePowerStateLocked = false;
              if (mPendingRequestLocked == null) {
                  return; // wait until first actual power request
              }
  
              if (mPowerRequest == null) {
                  mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);
                  mWaitingForNegativeProximity = mPendingWaitForNegativeProximityLocked;
                  mPendingWaitForNegativeProximityLocked = false;
                  mPendingRequestChangedLocked = false;
                  mustInitialize = true;
                  // Assume we're on and bright until told otherwise, since that's the state we turn
                  // on in.
                  previousPolicy = DisplayPowerRequest.POLICY_BRIGHT;
              } else if (mPendingRequestChangedLocked) {
                  previousPolicy = mPowerRequest.policy;
                  mPowerRequest.copyFrom(mPendingRequestLocked);
                  mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
                  mPendingWaitForNegativeProximityLocked = false;
                  mPendingRequestChangedLocked = false;
                  mDisplayReadyLocked = false;
              } else {
                  previousPolicy = mPowerRequest.policy;
              }
  
              mustNotify = !mDisplayReadyLocked;
          }
  
          // Initialize things the first time the power state is changed.
          if (mustInitialize) {
              initialize();
          }
  
          // Compute the basic display state using the policy.
          // We might override this below based on other factors.
          int state;
          int brightness = PowerManager.BRIGHTNESS_DEFAULT;
          boolean performScreenOffTransition = false;
          switch (mPowerRequest.policy) {
              case DisplayPowerRequest.POLICY_OFF:
                  state = Display.STATE_OFF;
                  performScreenOffTransition = true;
                  break;
              case DisplayPowerRequest.POLICY_DOZE:
                  if (mPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) {
                      state = mPowerRequest.dozeScreenState;
                  } else {
                      state = Display.STATE_DOZE;
                  }
                  if (!mAllowAutoBrightnessWhileDozingConfig) {
                      brightness = mPowerRequest.dozeScreenBrightness;
                      mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE);
                  }
                  break;
              case DisplayPowerRequest.POLICY_VR:
                  state = Display.STATE_VR;
                  break;
              case DisplayPowerRequest.POLICY_DIM:
              case DisplayPowerRequest.POLICY_BRIGHT:
              default:
                  state = Display.STATE_ON;
                  break;
          }
          assert(state != Display.STATE_UNKNOWN);
  
          // Apply the proximity sensor.
          if (mProximitySensor != null) {
              if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
                  setProximitySensorEnabled(true);
                  if (!mScreenOffBecauseOfProximity
                          && mProximity == PROXIMITY_POSITIVE) {
                      mScreenOffBecauseOfProximity = true;
                      sendOnProximityPositiveWithWakelock();
                  }
              } else if (mWaitingForNegativeProximity
                      && mScreenOffBecauseOfProximity
                      && mProximity == PROXIMITY_POSITIVE
                      && state != Display.STATE_OFF) {
                  setProximitySensorEnabled(true);
              } else {
                  setProximitySensorEnabled(false);
                  mWaitingForNegativeProximity = false;
              }
              if (mScreenOffBecauseOfProximity
                      && mProximity != PROXIMITY_POSITIVE) {
                  mScreenOffBecauseOfProximity = false;
                  sendOnProximityNegativeWithWakelock();
              }
          } else {
              mWaitingForNegativeProximity = false;
          }
          if (mScreenOffBecauseOfProximity) {
              state = Display.STATE_OFF;
          }
  
          // Animate the screen state change unless already animating.
          // The transition may be deferred, so after this point we will use the
          // actual state instead of the desired one.
          final int oldState = mPowerState.getScreenState();
          animateScreenStateChange(state, performScreenOffTransition);
          state = mPowerState.getScreenState();
  
          // Use zero brightness when screen is off.
          if (state == Display.STATE_OFF) {
              brightness = PowerManager.BRIGHTNESS_OFF;
              mBrightnessReasonTemp.setReason(BrightnessReason.REASON_SCREEN_OFF);
          }
  
          // Always use the VR brightness when in the VR state.
          if (state == Display.STATE_VR) {
              brightness = mScreenBrightnessForVr;
              mBrightnessReasonTemp.setReason(BrightnessReason.REASON_VR);
          }
  
          if (brightness < 0 && mPowerRequest.screenBrightnessOverride > 0) {
              brightness = mPowerRequest.screenBrightnessOverride;
              mBrightnessReasonTemp.setReason(BrightnessReason.REASON_OVERRIDE);
              mAppliedScreenBrightnessOverride = true;
          } else {
              mAppliedScreenBrightnessOverride = false;
          }
  
          final boolean autoBrightnessEnabledInDoze =
                  mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
          final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness
                      && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
                      && brightness < 0
                      && mAutomaticBrightnessController != null;
  
          final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
  
          // Use the temporary screen brightness if there isn't an override, either from
          // WindowManager or based on the display state.
          if (mTemporaryScreenBrightness > 0) {
              brightness = mTemporaryScreenBrightness;
              mAppliedTemporaryBrightness = true;
              mBrightnessReasonTemp.setReason(BrightnessReason.REASON_TEMPORARY);
          } else {
              mAppliedTemporaryBrightness = false;
          }
  
          final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
          if (autoBrightnessAdjustmentChanged) {
              mTemporaryAutoBrightnessAdjustment = Float.NaN;
          }
  
          // Use the autobrightness adjustment override if set.
          final float autoBrightnessAdjustment;
          if (!Float.isNaN(mTemporaryAutoBrightnessAdjustment)) {
              autoBrightnessAdjustment = mTemporaryAutoBrightnessAdjustment;
              brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO_TEMP;
              mAppliedTemporaryAutoBrightnessAdjustment = true;
          } else {
              autoBrightnessAdjustment = mAutoBrightnessAdjustment;
              brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO;
              mAppliedTemporaryAutoBrightnessAdjustment = false;
          }
  
          // Apply brightness boost.
          // We do this here after deciding whether auto-brightness is enabled so that we don't
          // disable the light sensor during this temporary state.  That way when boost ends we will
          // be able to resume normal auto-brightness behavior without any delay.
          if (mPowerRequest.boostScreenBrightness
                  && brightness != PowerManager.BRIGHTNESS_OFF) {
              brightness = PowerManager.BRIGHTNESS_ON;
              mBrightnessReasonTemp.setReason(BrightnessReason.REASON_BOOST);
              mAppliedBrightnessBoost = true;
          } else {
              mAppliedBrightnessBoost = false;
          }
  
          // If the brightness is already set then it's been overridden by something other than the
          // user, or is a temporary adjustment.
          boolean userInitiatedChange = brightness < 0
                  && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged);
  
          boolean hadUserBrightnessPoint = false;
          // Configure auto-brightness.
          if (mAutomaticBrightnessController != null) {
              hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
              mAutomaticBrightnessController.configure(autoBrightnessEnabled,
                      mBrightnessConfiguration,
                      mLastUserSetScreenBrightness / (float) PowerManager.BRIGHTNESS_ON,
                      userSetBrightnessChanged, autoBrightnessAdjustment,
                      autoBrightnessAdjustmentChanged, mPowerRequest.policy);
          }
  
          // Apply auto-brightness.
          boolean slowChange = false;
          if (brightness < 0) {
              float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
              if (autoBrightnessEnabled) {
                  brightness = mAutomaticBrightnessController.getAutomaticScreenBrightness();
                  newAutoBrightnessAdjustment =
                          mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment();
              }
  
              if (brightness >= 0) {
                  // Use current auto-brightness value and slowly adjust to changes.
                  brightness = clampScreenBrightness(brightness);
                  if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
                      slowChange = true; // slowly adapt to auto-brightness
                  }
                  // Tell the rest of the system about the new brightness. Note that we do this
                  // before applying the low power or dim transformations so that the slider
                  // accurately represents the full possible range, even if they range changes what
                  // it means in absolute terms.
                  putScreenBrightnessSetting(brightness);
                  mAppliedAutoBrightness = true;
                  mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
              } else {
                  mAppliedAutoBrightness = false;
              }
              if (autoBrightnessAdjustment != newAutoBrightnessAdjustment) {
                  // If the autobrightness controller has decided to change the adjustment value
                  // used, make sure that's reflected in settings.
                  putAutoBrightnessAdjustmentSetting(newAutoBrightnessAdjustment);
              } else {
                  // Adjustment values resulted in no change
                  brightnessAdjustmentFlags = 0;
              }
          } else {
              mAppliedAutoBrightness = false;
              brightnessAdjustmentFlags = 0;
          }
  
          // Use default brightness when dozing unless overridden.
          if (brightness < 0 && Display.isDozeState(state)) {
              brightness = mScreenBrightnessDozeConfig;
              mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
          }
  
          // Apply manual brightness.
          if (brightness < 0) {
              brightness = clampScreenBrightness(mCurrentScreenBrightnessSetting);
              mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
          }
  
          // Apply dimming by at least some minimum amount when user activity
          // timeout is about to expire.
          if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
              if (brightness > mScreenBrightnessRangeMinimum) {
                  brightness = Math.max(Math.min(brightness - SCREEN_DIM_MINIMUM_REDUCTION,
                          mScreenBrightnessDimConfig), mScreenBrightnessRangeMinimum);
                  mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED);
              }
              if (!mAppliedDimming) {
                  slowChange = false;
              }
              mAppliedDimming = true;
          } else if (mAppliedDimming) {
              slowChange = false;
              mAppliedDimming = false;
          }
  
          // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor
          // as long as it is above the minimum threshold.
          if (mPowerRequest.lowPowerMode) {
              if (brightness > mScreenBrightnessRangeMinimum) {
                  final float brightnessFactor =
                          Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1);
                  final int lowPowerBrightness = (int) (brightness * brightnessFactor);
                  brightness = Math.max(lowPowerBrightness, mScreenBrightnessRangeMinimum);
                  mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER);
              }
              if (!mAppliedLowPower) {
                  slowChange = false;
              }
              mAppliedLowPower = true;
          } else if (mAppliedLowPower) {
              slowChange = false;
              mAppliedLowPower = false;
          }
  
          // Animate the screen brightness when the screen is on or dozing.
          // Skip the animation when the screen is off or suspended or transition to/from VR.
          if (!mPendingScreenOff) {
              if (mSkipScreenOnBrightnessRamp) {
                  if (state == Display.STATE_ON) {
                      if (mSkipRampState == RAMP_STATE_SKIP_NONE && mDozing) {
                          mInitialAutoBrightness = brightness;
                          mSkipRampState = RAMP_STATE_SKIP_INITIAL;
                      } else if (mSkipRampState == RAMP_STATE_SKIP_INITIAL
                              && mUseSoftwareAutoBrightnessConfig
                              && brightness != mInitialAutoBrightness) {
                          mSkipRampState = RAMP_STATE_SKIP_AUTOBRIGHT;
                      } else if (mSkipRampState == RAMP_STATE_SKIP_AUTOBRIGHT) {
                          mSkipRampState = RAMP_STATE_SKIP_NONE;
                      }
                  } else {
                      mSkipRampState = RAMP_STATE_SKIP_NONE;
                  }
              }
  
              final boolean wasOrWillBeInVr =
                      (state == Display.STATE_VR || oldState == Display.STATE_VR);
              final boolean initialRampSkip =
                      state == Display.STATE_ON && mSkipRampState != RAMP_STATE_SKIP_NONE;
              // While dozing, sometimes the brightness is split into buckets. Rather than animating
              // through the buckets, which is unlikely to be smooth in the first place, just jump
              // right to the suggested brightness.
              final boolean hasBrightnessBuckets =
                      Display.isDozeState(state) && mBrightnessBucketsInDozeConfig;
              // If the color fade is totally covering the screen then we can change the backlight
              // level without it being a noticeable jump since any actual content isn't yet visible.
              final boolean isDisplayContentVisible =
                      mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0f;
              final boolean brightnessIsTemporary =
                      mAppliedTemporaryBrightness || mAppliedTemporaryAutoBrightnessAdjustment;
              if (initialRampSkip || hasBrightnessBuckets
                      || wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) {
                  animateScreenBrightness(brightness, 0);
              } else {
                  animateScreenBrightness(brightness,
                          slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
              }
  
              if (!brightnessIsTemporary) {
                  if (userInitiatedChange && (mAutomaticBrightnessController == null
                          || !mAutomaticBrightnessController.hasValidAmbientLux())) {
                      // If we don't have a valid lux reading we can't report a valid
                      // slider event so notify as if the system changed the brightness.
                      userInitiatedChange = false;
                  }
                  notifyBrightnessChanged(brightness, userInitiatedChange, hadUserBrightnessPoint);
              }
  
          }
  
          // Log any changes to what is currently driving the brightness setting.
          if (!mBrightnessReasonTemp.equals(mBrightnessReason) || brightnessAdjustmentFlags != 0) {
              Slog.v(TAG, "Brightness [" + brightness + "] reason changing to: '"
                      + mBrightnessReasonTemp.toString(brightnessAdjustmentFlags)
                      + "', previous reason: '" + mBrightnessReason + "'.");
              mBrightnessReason.set(mBrightnessReasonTemp);
          }
  
          // Update display white-balance.
          if (mDisplayWhiteBalanceController != null) {
              if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
                  mDisplayWhiteBalanceController.setEnabled(true);
                  mDisplayWhiteBalanceController.updateDisplayColorTemperature();
              } else {
                  mDisplayWhiteBalanceController.setEnabled(false);
              }
          }
  
          // Determine whether the display is ready for use in the newly requested state.
          // Note that we do not wait for the brightness ramp animation to complete before
          // reporting the display is ready because we only need to ensure the screen is in the
          // right power state even as it continues to converge on the desired brightness.
          final boolean ready = mPendingScreenOnUnblocker == null &&
                  (!mColorFadeEnabled ||
                          (!mColorFadeOnAnimator.isStarted() && !mColorFadeOffAnimator.isStarted()))
                  && mPowerState.waitUntilClean(mCleanListener);
          final boolean finished = ready
                  && !mScreenBrightnessRampAnimator.isAnimating();
  
          // Notify policy about screen turned on.
          if (ready && state != Display.STATE_OFF
                  && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_ON) {
              setReportedScreenState(REPORTED_TO_POLICY_SCREEN_ON);
              mWindowManagerPolicy.screenTurnedOn();
          }
  
          // Grab a wake lock if we have unfinished business.
          if (!finished && !mUnfinishedBusiness) {
              if (DEBUG) {
                  Slog.d(TAG, "Unfinished business...");
              }
              mCallbacks.acquireSuspendBlocker();
              mUnfinishedBusiness = true;
          }
  
          // Notify the power manager when ready.
          if (ready && mustNotify) {
              // Send state change.
              synchronized (mLock) {
                  if (!mPendingRequestChangedLocked) {
                      mDisplayReadyLocked = true;
  
                      if (DEBUG) {
                          Slog.d(TAG, "Display ready!");
                      }
                  }
              }
              sendOnStateChangedWithWakelock();
          }
  
          // Release the wake lock when we have no unfinished business.
          if (finished && mUnfinishedBusiness) {
              if (DEBUG) {
                  Slog.d(TAG, "Finished business...");
              }
              mUnfinishedBusiness = false;
              mCallbacks.releaseSuspendBlocker();
          }
  
          // Record if dozing for future comparison.
          mDozing = state != Display.STATE_ON;
  
          if (previousPolicy != mPowerRequest.policy) {
              logDisplayPolicyChanged(mPowerRequest.policy);
          }
      }
  
      @Override
      public void updateBrightness() {
          sendUpdatePowerState();
      }
  
      public void setBrightnessConfiguration(BrightnessConfiguration c) {
          Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, c);
          msg.sendToTarget();
      }
  
      public void setTemporaryBrightness(int brightness) {
          Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS,
                  brightness, 0 /*unused*/);
          msg.sendToTarget();
      }
  
      public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
          Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT,
                  Float.floatToIntBits(adjustment), 0 /*unused*/);
          msg.sendToTarget();
      }
  
      private void blockScreenOn() {
          if (mPendingScreenOnUnblocker == null) {
              Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
              mPendingScreenOnUnblocker = new ScreenOnUnblocker();
              mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();
              Slog.i(TAG, "Blocking screen on until initial contents have been drawn.");
          }
      }
  
      private void unblockScreenOn() {
          if (mPendingScreenOnUnblocker != null) {
              mPendingScreenOnUnblocker = null;
              long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;
              Slog.i(TAG, "Unblocked screen on after " + delay + " ms");
              Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
          }
      }
  
      private void blockScreenOff() {
          if (mPendingScreenOffUnblocker == null) {
              Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
              mPendingScreenOffUnblocker = new ScreenOffUnblocker();
              mScreenOffBlockStartRealTime = SystemClock.elapsedRealtime();
              Slog.i(TAG, "Blocking screen off");
          }
      }
  
      private void unblockScreenOff() {
          if (mPendingScreenOffUnblocker != null) {
              mPendingScreenOffUnblocker = null;
              long delay = SystemClock.elapsedRealtime() - mScreenOffBlockStartRealTime;
              Slog.i(TAG, "Unblocked screen off after " + delay + " ms");
              Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
          }
      }
  
      private boolean setScreenState(int state) {
          return setScreenState(state, false /*reportOnly*/);
      }
  
      private boolean setScreenState(int state, boolean reportOnly) {
          final boolean isOff = (state == Display.STATE_OFF);
          if (mPowerState.getScreenState() != state) {
  
              // If we are trying to turn screen off, give policy a chance to do something before we
              // actually turn the screen off.
              if (isOff && !mScreenOffBecauseOfProximity) {
                  if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON) {
                      setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);
                      blockScreenOff();
                      mWindowManagerPolicy.screenTurningOff(mPendingScreenOffUnblocker);
                      unblockScreenOff();
                  } else if (mPendingScreenOffUnblocker != null) {
                      // Abort doing the state change until screen off is unblocked.
                      return false;
                  }
              }
  
              if (!reportOnly) {
                  Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
                  mPowerState.setScreenState(state);
                  // Tell battery stats about the transition.
                  try {
                      mBatteryStats.noteScreenState(state);
                  } catch (RemoteException ex) {
                      // same process
                  }
              }
          }
  
          // Tell the window manager policy when the screen is turned off or on unless it's due
          // to the proximity sensor.  We temporarily block turning the screen on until the
          // window manager is ready by leaving a black surface covering the screen.
          // This surface is essentially the final state of the color fade animation and
          // it is only removed once the window manager tells us that the activity has
          // finished drawing underneath.
          if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF
                  && !mScreenOffBecauseOfProximity) {
              setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
              unblockScreenOn();
              mWindowManagerPolicy.screenTurnedOff();
          } else if (!isOff
                  && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_OFF) {
  
              // We told policy already that screen was turning off, but now we changed our minds.
              // Complete the full state transition on -> turningOff -> off.
              unblockScreenOff();
              mWindowManagerPolicy.screenTurnedOff();
              setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
          }
          if (!isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF) {
              setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON);
              if (mPowerState.getColorFadeLevel() == 0.0f) {
                  blockScreenOn();
              } else {
                  unblockScreenOn();
              }
              mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker);
          }
  
          // Return true if the screen isn't blocked.
          return mPendingScreenOnUnblocker == null;
      }
  
      private void setReportedScreenState(int state) {
          Trace.traceCounter(Trace.TRACE_TAG_POWER, "ReportedScreenStateToPolicy", state);
          mReportedScreenStateToPolicy = state;
      }
  
      private int clampScreenBrightnessForVr(int value) {
          return MathUtils.constrain(
                  value, mScreenBrightnessForVrRangeMinimum, mScreenBrightnessForVrRangeMaximum);
      }
  
      private int clampScreenBrightness(int value) {
          return MathUtils.constrain(
                  value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
      }
  
      private void animateScreenBrightness(int target, int rate) {
          if (DEBUG) {
              Slog.d(TAG, "Animating brightness: target=" + target +", rate=" + rate);
          }
          if (mScreenBrightnessRampAnimator.animateTo(target, rate)) {
              Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", target);
              try {
                  mBatteryStats.noteScreenBrightness(target);
              } catch (RemoteException ex) {
                  // same process
              }
          }
      }
  
      private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
          // If there is already an animation in progress, don't interfere with it.
          if (mColorFadeEnabled &&
                  (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) {
              if (target != Display.STATE_ON) {
                  return;
              }
              // If display state changed to on, proceed and stop the color fade and turn screen on.
              mPendingScreenOff = false;
          }
  
          if (mDisplayBlanksAfterDozeConfig
                  && Display.isDozeState(mPowerState.getScreenState())
                  && !Display.isDozeState(target)) {
              // Skip the screen off animation and add a black surface to hide the
              // contents of the screen.
              mPowerState.prepareColorFade(mContext,
                      mColorFadeFadesConfig ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP);
              if (mColorFadeOffAnimator != null) {
                  mColorFadeOffAnimator.end();
              }
              // Some display hardware will blank itself on the transition between doze and non-doze
              // but still on display states. In this case we want to report to policy that the
              // display has turned off so it can prepare the appropriate power on animation, but we
              // don't want to actually transition to the fully off state since that takes
              // significantly longer to transition from.
              setScreenState(Display.STATE_OFF, target != Display.STATE_OFF /*reportOnly*/);
          }
  
          // If we were in the process of turning off the screen but didn't quite
          // finish.  Then finish up now to prevent a jarring transition back
          // to screen on if we skipped blocking screen on as usual.
          if (mPendingScreenOff && target != Display.STATE_OFF) {
              setScreenState(Display.STATE_OFF);
              mPendingScreenOff = false;
              mPowerState.dismissColorFadeResources();
          }
  
          if (target == Display.STATE_ON) {
              // Want screen on.  The contents of the screen may not yet
              // be visible if the color fade has not been dismissed because
              // its last frame of animation is solid black.
              if (!setScreenState(Display.STATE_ON)) {
                  return; // screen on blocked
              }
              if (USE_COLOR_FADE_ON_ANIMATION && mColorFadeEnabled && mPowerRequest.isBrightOrDim()) {
                  // Perform screen on animation.
                  if (mPowerState.getColorFadeLevel() == 1.0f) {
                      mPowerState.dismissColorFade();
                  } else if (mPowerState.prepareColorFade(mContext,
                          mColorFadeFadesConfig ?
                                  ColorFade.MODE_FADE :
                                          ColorFade.MODE_WARM_UP)) {
                      mColorFadeOnAnimator.start();
                  } else {
                      mColorFadeOnAnimator.end();
                  }
              } else {
                  // Skip screen on animation.
                  mPowerState.setColorFadeLevel(1.0f);
                  mPowerState.dismissColorFade();
              }
          } else if (target == Display.STATE_VR) {
              // Wait for brightness animation to complete beforehand when entering VR
              // from screen on to prevent a perceptible jump because brightness may operate
              // differently when the display is configured for dozing.
              if (mScreenBrightnessRampAnimator.isAnimating()
                      && mPowerState.getScreenState() == Display.STATE_ON) {
                  return;
              }
  
              // Set screen state.
              if (!setScreenState(Display.STATE_VR)) {
                  return; // screen on blocked
              }
  
              // Dismiss the black surface without fanfare.
              mPowerState.setColorFadeLevel(1.0f);
              mPowerState.dismissColorFade();
          } else if (target == Display.STATE_DOZE) {
              // Want screen dozing.
              // Wait for brightness animation to complete beforehand when entering doze
              // from screen on to prevent a perceptible jump because brightness may operate
              // differently when the display is configured for dozing.
              if (mScreenBrightnessRampAnimator.isAnimating()
                      && mPowerState.getScreenState() == Display.STATE_ON) {
                  return;
              }
  
              // Set screen state.
              if (!setScreenState(Display.STATE_DOZE)) {
                  return; // screen on blocked
              }
  
              // Dismiss the black surface without fanfare.
              mPowerState.setColorFadeLevel(1.0f);
              mPowerState.dismissColorFade();
          } else if (target == Display.STATE_DOZE_SUSPEND) {
              // Want screen dozing and suspended.
              // Wait for brightness animation to complete beforehand unless already
              // suspended because we may not be able to change it after suspension.
              if (mScreenBrightnessRampAnimator.isAnimating()
                      && mPowerState.getScreenState() != Display.STATE_DOZE_SUSPEND) {
                  return;
              }
  
              // If not already suspending, temporarily set the state to doze until the
              // screen on is unblocked, then suspend.
              if (mPowerState.getScreenState() != Display.STATE_DOZE_SUSPEND) {
                  if (!setScreenState(Display.STATE_DOZE)) {
                      return; // screen on blocked
                  }
                  setScreenState(Display.STATE_DOZE_SUSPEND); // already on so can't block
              }
  
              // Dismiss the black surface without fanfare.
              mPowerState.setColorFadeLevel(1.0f);
              mPowerState.dismissColorFade();
          } else if (target == Display.STATE_ON_SUSPEND) {
              // Want screen full-power and suspended.
              // Wait for brightness animation to complete beforehand unless already
              // suspended because we may not be able to change it after suspension.
              if (mScreenBrightnessRampAnimator.isAnimating()
                      && mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) {
                  return;
              }
  
              // If not already suspending, temporarily set the state to on until the
              // screen on is unblocked, then suspend.
              if (mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) {
                  if (!setScreenState(Display.STATE_ON)) {
                      return;
                  }
                  setScreenState(Display.STATE_ON_SUSPEND);
              }
  
              // Dismiss the black surface without fanfare.
              mPowerState.setColorFadeLevel(1.0f);
              mPowerState.dismissColorFade();
          } else {
              // Want screen off.
              mPendingScreenOff = true;
              if (!mColorFadeEnabled) {
                  mPowerState.setColorFadeLevel(0.0f);
              }
  
              if (mPowerState.getColorFadeLevel() == 0.0f) {
                  // Turn the screen off.
                  // A black surface is already hiding the contents of the screen.
                  setScreenState(Display.STATE_OFF);
                  mPendingScreenOff = false;
                  mPowerState.dismissColorFadeResources();
              } else if (performScreenOffTransition
                      && mPowerState.prepareColorFade(mContext,
                              mColorFadeFadesConfig ?
                                      ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
                      && mPowerState.getScreenState() != Display.STATE_OFF) {
                  // Perform the screen off animation.
                  mColorFadeOffAnimator.start();
              } else {
                  // Skip the screen off animation and add a black surface to hide the
                  // contents of the screen.
                  mColorFadeOffAnimator.end();
              }
          }
      }
  
      private final Runnable mCleanListener = new Runnable() {
          @Override
          public void run() {
              sendUpdatePowerState();
          }
      };
  
      private void setProximitySensorEnabled(boolean enable) {
          if (enable) {
              if (!mProximitySensorEnabled) {
                  // Register the listener.
                  // Proximity sensor state already cleared initially.
                  mProximitySensorEnabled = true;
                  mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
                          SensorManager.SENSOR_DELAY_NORMAL, mHandler);
              }
          } else {
              if (mProximitySensorEnabled) {
                  // Unregister the listener.
                  // Clear the proximity sensor state for next time.
                  mProximitySensorEnabled = false;
                  mProximity = PROXIMITY_UNKNOWN;
                  mPendingProximity = PROXIMITY_UNKNOWN;
                  mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
                  mSensorManager.unregisterListener(mProximitySensorListener);
                  clearPendingProximityDebounceTime(); // release wake lock (must be last)
              }
          }
      }
  
      private void handleProximitySensorEvent(long time, boolean positive) {
          if (mProximitySensorEnabled) {
              if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {
                  return; // no change
              }
              if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
                  return; // no change
              }
  
              // Only accept a proximity sensor reading if it remains
              // stable for the entire debounce delay.  We hold a wake lock while
              // debouncing the sensor.
              mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
              if (positive) {
                  mPendingProximity = PROXIMITY_POSITIVE;
                  setPendingProximityDebounceTime(
                          time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY); // acquire wake lock
              } else {
                  mPendingProximity = PROXIMITY_NEGATIVE;
                  setPendingProximityDebounceTime(
                          time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY); // acquire wake lock
              }
  
              // Debounce the new sensor reading.
              debounceProximitySensor();
          }
      }
  
      private void debounceProximitySensor() {
          if (mProximitySensorEnabled
                  && mPendingProximity != PROXIMITY_UNKNOWN
                  && mPendingProximityDebounceTime >= 0) {
              final long now = SystemClock.uptimeMillis();
              if (mPendingProximityDebounceTime <= now) {
                  // Sensor reading accepted.  Apply the change then release the wake lock.
                  mProximity = mPendingProximity;
                  updatePowerState();
                  clearPendingProximityDebounceTime(); // release wake lock (must be last)
              } else {
                  // Need to wait a little longer.
                  // Debounce again later.  We continue holding a wake lock while waiting.
                  Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
                  mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
              }
          }
      }
  
      private void clearPendingProximityDebounceTime() {
          if (mPendingProximityDebounceTime >= 0) {
              mPendingProximityDebounceTime = -1;
              mCallbacks.releaseSuspendBlocker(); // release wake lock
          }
      }
  
      private void setPendingProximityDebounceTime(long debounceTime) {
          if (mPendingProximityDebounceTime < 0) {
              mCallbacks.acquireSuspendBlocker(); // acquire wake lock
          }
          mPendingProximityDebounceTime = debounceTime;
      }
  
      private void sendOnStateChangedWithWakelock() {
          mCallbacks.acquireSuspendBlocker();
          mHandler.post(mOnStateChangedRunnable);
      }
  
      private void logDisplayPolicyChanged(int newPolicy) {
          LogMaker log = new LogMaker(MetricsEvent.DISPLAY_POLICY);
          log.setType(MetricsEvent.TYPE_UPDATE);
          log.setSubtype(newPolicy);
          MetricsLogger.action(log);
      }
  
      private void handleSettingsChange(boolean userSwitch) {
          mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
          if (userSwitch) {
              // Don't treat user switches as user initiated change.
              mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
              if (mAutomaticBrightnessController != null) {
                  mAutomaticBrightnessController.resetShortTermModel();
              }
          }
          mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
          // We don't bother with a pending variable for VR screen brightness since we just
          // immediately adapt to it.
          mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
          sendUpdatePowerState();
      }
  
      private float getAutoBrightnessAdjustmentSetting() {
          final float adj = Settings.System.getFloatForUser(mContext.getContentResolver(),
                  Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f, UserHandle.USER_CURRENT);
          return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
      }
  
      private int getScreenBrightnessSetting() {
          final int brightness = Settings.System.getIntForUser(mContext.getContentResolver(),
                  Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessDefault,
                  UserHandle.USER_CURRENT);
          return clampAbsoluteBrightness(brightness);
      }
  
      private int getScreenBrightnessForVrSetting() {
          final int brightness = Settings.System.getIntForUser(mContext.getContentResolver(),
                  Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mScreenBrightnessForVrDefault,
                  UserHandle.USER_CURRENT);
          return clampScreenBrightnessForVr(brightness);
      }
  
      private void putScreenBrightnessSetting(int brightness) {
          mCurrentScreenBrightnessSetting = brightness;
          Settings.System.putIntForUser(mContext.getContentResolver(),
                  Settings.System.SCREEN_BRIGHTNESS, brightness, UserHandle.USER_CURRENT);
      }
  
      private void putAutoBrightnessAdjustmentSetting(float adjustment) {
          mAutoBrightnessAdjustment = adjustment;
          Settings.System.putFloatForUser(mContext.getContentResolver(),
                  Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adjustment, UserHandle.USER_CURRENT);
      }
  
      private boolean updateAutoBrightnessAdjustment() {
          if (Float.isNaN(mPendingAutoBrightnessAdjustment)) {
              return false;
          }
          if (mAutoBrightnessAdjustment == mPendingAutoBrightnessAdjustment) {
              mPendingAutoBrightnessAdjustment = Float.NaN;
              return false;
          }
          mAutoBrightnessAdjustment = mPendingAutoBrightnessAdjustment;
          mPendingAutoBrightnessAdjustment = Float.NaN;
          return true;
      }
  
      private boolean updateUserSetScreenBrightness() {
          if (mPendingScreenBrightnessSetting < 0) {
              return false;
          }
          if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
              mPendingScreenBrightnessSetting = -1;
              mTemporaryScreenBrightness = -1;
              return false;
          }
          mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
          mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
          mPendingScreenBrightnessSetting = -1;
          mTemporaryScreenBrightness = -1;
          return true;
      }
  
      private void notifyBrightnessChanged(int brightness, boolean userInitiated,
              boolean hadUserDataPoint) {
          final float brightnessInNits = convertToNits(brightness);
          if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f
                  && mAutomaticBrightnessController != null) {
              // We only want to track changes on devices that can actually map the display backlight
              // values into a physical brightness unit since the value provided by the API is in
              // nits and not using the arbitrary backlight units.
              final float powerFactor = mPowerRequest.lowPowerMode
                      ? mPowerRequest.screenLowPowerBrightnessFactor
                      : 1.0f;
              mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
                      powerFactor, hadUserDataPoint,
                      mAutomaticBrightnessController.isDefaultConfig());
          }
      }
  
      private float convertToNits(int backlight) {
          if (mBrightnessMapper != null) {
              return mBrightnessMapper.convertToNits(backlight);
          } else {
              return -1.0f;
          }
      }
  
      private final Runnable mOnStateChangedRunnable = new Runnable() {
          @Override
          public void run() {
              mCallbacks.onStateChanged();
              mCallbacks.releaseSuspendBlocker();
          }
      };
  
      private void sendOnProximityPositiveWithWakelock() {
          mCallbacks.acquireSuspendBlocker();
          mHandler.post(mOnProximityPositiveRunnable);
      }
  
      private final Runnable mOnProximityPositiveRunnable = new Runnable() {
          @Override
          public void run() {
              mCallbacks.onProximityPositive();
              mCallbacks.releaseSuspendBlocker();
          }
      };
  
      private void sendOnProximityNegativeWithWakelock() {
          mCallbacks.acquireSuspendBlocker();
          mHandler.post(mOnProximityNegativeRunnable);
      }
  
      private final Runnable mOnProximityNegativeRunnable = new Runnable() {
          @Override
          public void run() {
              mCallbacks.onProximityNegative();
              mCallbacks.releaseSuspendBlocker();
          }
      };
  
      public void dump(final PrintWriter pw) {
          synchronized (mLock) {
              pw.println();
              pw.println("Display Power Controller Locked State:");
              pw.println("  mDisplayReadyLocked=" + mDisplayReadyLocked);
              pw.println("  mPendingRequestLocked=" + mPendingRequestLocked);
              pw.println("  mPendingRequestChangedLocked=" + mPendingRequestChangedLocked);
              pw.println("  mPendingWaitForNegativeProximityLocked="
                      + mPendingWaitForNegativeProximityLocked);
              pw.println("  mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked);
          }
  
          pw.println();
          pw.println("Display Power Controller Configuration:");
          pw.println("  mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig);
          pw.println("  mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
          pw.println("  mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
          pw.println("  mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
          pw.println("  mScreenBrightnessDefault=" + mScreenBrightnessDefault);
          pw.println("  mScreenBrightnessForVrRangeMinimum=" + mScreenBrightnessForVrRangeMinimum);
          pw.println("  mScreenBrightnessForVrRangeMaximum=" + mScreenBrightnessForVrRangeMaximum);
          pw.println("  mScreenBrightnessForVrDefault=" + mScreenBrightnessForVrDefault);
          pw.println("  mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
          pw.println("  mAllowAutoBrightnessWhileDozingConfig=" +
                  mAllowAutoBrightnessWhileDozingConfig);
          pw.println("  mBrightnessRampRateFast=" + mBrightnessRampRateFast);
          pw.println("  mBrightnessRampRateSlow=" + mBrightnessRampRateSlow);
          pw.println("  mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
          pw.println("  mColorFadeFadesConfig=" + mColorFadeFadesConfig);
          pw.println("  mColorFadeEnabled=" + mColorFadeEnabled);
          pw.println("  mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
          pw.println("  mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
  
          mHandler.runWithScissors(new Runnable() {
              @Override
              public void run() {
                  dumpLocal(pw);
              }
          }, 1000);
      }
  
      private void dumpLocal(PrintWriter pw) {
          pw.println();
          pw.println("Display Power Controller Thread State:");
          pw.println("  mPowerRequest=" + mPowerRequest);
          pw.println("  mUnfinishedBusiness=" + mUnfinishedBusiness);
          pw.println("  mWaitingForNegativeProximity=" + mWaitingForNegativeProximity);
          pw.println("  mProximitySensor=" + mProximitySensor);
          pw.println("  mProximitySensorEnabled=" + mProximitySensorEnabled);
          pw.println("  mProximityThreshold=" + mProximityThreshold);
          pw.println("  mProximity=" + proximityToString(mProximity));
          pw.println("  mPendingProximity=" + proximityToString(mPendingProximity));
          pw.println("  mPendingProximityDebounceTime="
                  + TimeUtils.formatUptime(mPendingProximityDebounceTime));
          pw.println("  mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
          pw.println("  mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness);
          pw.println("  mCurrentScreenBrightnessSetting=" + mCurrentScreenBrightnessSetting);
          pw.println("  mPendingScreenBrightnessSetting=" + mPendingScreenBrightnessSetting);
          pw.println("  mTemporaryScreenBrightness=" + mTemporaryScreenBrightness);
          pw.println("  mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
          pw.println("  mBrightnessReason=" + mBrightnessReason);
          pw.println("  mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment);
          pw.println("  mPendingAutoBrightnessAdjustment=" + mPendingAutoBrightnessAdjustment);
          pw.println("  mScreenBrightnessForVr=" + mScreenBrightnessForVr);
          pw.println("  mAppliedAutoBrightness=" + mAppliedAutoBrightness);
          pw.println("  mAppliedDimming=" + mAppliedDimming);
          pw.println("  mAppliedLowPower=" + mAppliedLowPower);
          pw.println("  mAppliedScreenBrightnessOverride=" + mAppliedScreenBrightnessOverride);
          pw.println("  mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness);
          pw.println("  mDozing=" + mDozing);
          pw.println("  mSkipRampState=" + skipRampStateToString(mSkipRampState));
          pw.println("  mInitialAutoBrightness=" + mInitialAutoBrightness);
          pw.println("  mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime);
          pw.println("  mScreenOffBlockStartRealTime=" + mScreenOffBlockStartRealTime);
          pw.println("  mPendingScreenOnUnblocker=" + mPendingScreenOnUnblocker);
          pw.println("  mPendingScreenOffUnblocker=" + mPendingScreenOffUnblocker);
          pw.println("  mPendingScreenOff=" + mPendingScreenOff);
          pw.println("  mReportedToPolicy=" +
                  reportedToPolicyToString(mReportedScreenStateToPolicy));
  
          if (mScreenBrightnessRampAnimator != null) {
              pw.println("  mScreenBrightnessRampAnimator.isAnimating()=" +
                      mScreenBrightnessRampAnimator.isAnimating());
          }
  
          if (mColorFadeOnAnimator != null) {
              pw.println("  mColorFadeOnAnimator.isStarted()=" +
                      mColorFadeOnAnimator.isStarted());
          }
          if (mColorFadeOffAnimator != null) {
              pw.println("  mColorFadeOffAnimator.isStarted()=" +
                      mColorFadeOffAnimator.isStarted());
          }
  
          if (mPowerState != null) {
              mPowerState.dump(pw);
          }
  
          if (mAutomaticBrightnessController != null) {
              mAutomaticBrightnessController.dump(pw);
          }
  
          if (mBrightnessTracker != null) {
              pw.println();
              mBrightnessTracker.dump(pw);
          }
  
          pw.println();
          if (mDisplayWhiteBalanceController != null) {
              mDisplayWhiteBalanceController.dump(pw);
              mDisplayWhiteBalanceSettings.dump(pw);
          }
      }
  
      private static String proximityToString(int state) {
          switch (state) {
              case PROXIMITY_UNKNOWN:
                  return "Unknown";
              case PROXIMITY_NEGATIVE:
                  return "Negative";
              case PROXIMITY_POSITIVE:
                  return "Positive";
              default:
                  return Integer.toString(state);
          }
      }
  
      private static String reportedToPolicyToString(int state) {
          switch (state) {
              case REPORTED_TO_POLICY_SCREEN_OFF:
                  return "REPORTED_TO_POLICY_SCREEN_OFF";
              case REPORTED_TO_POLICY_SCREEN_TURNING_ON:
                  return "REPORTED_TO_POLICY_SCREEN_TURNING_ON";
              case REPORTED_TO_POLICY_SCREEN_ON:
                  return "REPORTED_TO_POLICY_SCREEN_ON";
              default:
                  return Integer.toString(state);
          }
      }
  
      private static String skipRampStateToString(int state) {
          switch (state) {
              case RAMP_STATE_SKIP_NONE:
                  return "RAMP_STATE_SKIP_NONE";
              case RAMP_STATE_SKIP_INITIAL:
                  return "RAMP_STATE_SKIP_INITIAL";
              case RAMP_STATE_SKIP_AUTOBRIGHT:
                  return "RAMP_STATE_SKIP_AUTOBRIGHT";
              default:
                  return Integer.toString(state);
          }
      }
  
      private static int clampAbsoluteBrightness(int value) {
          return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
      }
  
      private static float clampAutoBrightnessAdjustment(float value) {
          return MathUtils.constrain(value, -1.0f, 1.0f);
      }
  
      private final class DisplayControllerHandler extends Handler {
          public DisplayControllerHandler(Looper looper) {
              super(looper, null, true /*async*/);
          }
  
          @Override
          public void handleMessage(Message msg) {
              switch (msg.what) {
                  case MSG_UPDATE_POWER_STATE:
                      updatePowerState();
                      break;
  
                  case MSG_PROXIMITY_SENSOR_DEBOUNCED:
                      debounceProximitySensor();
                      break;
  
                  case MSG_SCREEN_ON_UNBLOCKED:
                      if (mPendingScreenOnUnblocker == msg.obj) {
                          unblockScreenOn();
                          updatePowerState();
                      }
                      break;
                  case MSG_SCREEN_OFF_UNBLOCKED:
                      if (mPendingScreenOffUnblocker == msg.obj) {
                          unblockScreenOff();
                          updatePowerState();
                      }
                      break;
                  case MSG_CONFIGURE_BRIGHTNESS:
                      mBrightnessConfiguration = (BrightnessConfiguration)msg.obj;
                      updatePowerState();
                      break;
  
                  case MSG_SET_TEMPORARY_BRIGHTNESS:
                      // TODO: Should we have a a timeout for the temporary brightness?
                      mTemporaryScreenBrightness = msg.arg1;
                      updatePowerState();
                      break;
  
                  case MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT:
                      mTemporaryAutoBrightnessAdjustment = Float.intBitsToFloat(msg.arg1);
                      updatePowerState();
                      break;
              }
          }
      }
  
      private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
          @Override
          public void onSensorChanged(SensorEvent event) {
              if (mProximitySensorEnabled) {
                  final long time = SystemClock.uptimeMillis();
                  final float distance = event.values[0];
                  boolean positive = distance >= 0.0f && distance < mProximityThreshold;
                  handleProximitySensorEvent(time, positive);
              }
          }
  
          @Override
          public void onAccuracyChanged(Sensor sensor, int accuracy) {
              // Not used.
          }
      };
  
  
      private final class SettingsObserver extends ContentObserver {
          public SettingsObserver(Handler handler) {
              super(handler);
          }
  
          @Override
          public void onChange(boolean selfChange, Uri uri) {
              handleSettingsChange(false /* userSwitch */);
          }
      }
  
      private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {
          @Override
          public void onScreenOn() {
              Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this);
              mHandler.sendMessage(msg);
          }
      }
  
      private final class ScreenOffUnblocker implements WindowManagerPolicy.ScreenOffListener {
          @Override
          public void onScreenOff() {
              Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this);
              mHandler.sendMessage(msg);
          }
      }
  
      void setAutoBrightnessLoggingEnabled(boolean enabled) {
          if (mAutomaticBrightnessController != null) {
              mAutomaticBrightnessController.setLoggingEnabled(enabled);
          }
      }
  
      @Override // DisplayWhiteBalanceController.Callbacks
      public void updateWhiteBalance() {
          sendUpdatePowerState();
      }
  
      void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
          if (mDisplayWhiteBalanceController != null) {
              mDisplayWhiteBalanceController.setLoggingEnabled(enabled);
              mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled);
          }
      }
  
      void setAmbientColorTemperatureOverride(float cct) {
          if (mDisplayWhiteBalanceController != null) {
              mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct);
              // The ambient color temperature override is only applied when the ambient color
              // temperature changes or is updated, so it doesn't necessarily change the screen color
              // temperature immediately. So, let's make it!
              sendUpdatePowerState();
          }
      }
  
      /**
       * Stores data about why the brightness was changed.  Made up of one main
       * {@code BrightnessReason.REASON_*} reason and various {@code BrightnessReason.MODIFIER_*}
       * modifiers.
       */
      private final class BrightnessReason {
          static final int REASON_UNKNOWN = 0;
          static final int REASON_MANUAL = 1;
          static final int REASON_DOZE = 2;
          static final int REASON_DOZE_DEFAULT = 3;
          static final int REASON_AUTOMATIC = 4;
          static final int REASON_SCREEN_OFF = 5;
          static final int REASON_VR = 6;
          static final int REASON_OVERRIDE = 7;
          static final int REASON_TEMPORARY = 8;
          static final int REASON_BOOST = 9;
          static final int REASON_MAX = REASON_BOOST;
  
          static final int MODIFIER_DIMMED = 0x1;
          static final int MODIFIER_LOW_POWER = 0x2;
          static final int MODIFIER_MASK = 0x3;
  
          // ADJUSTMENT_*
          // These things can happen at any point, even if the main brightness reason doesn't
          // fundamentally change, so they're not stored.
  
          // Auto-brightness adjustment factor changed
          static final int ADJUSTMENT_AUTO_TEMP = 0x1;
          // Temporary adjustment to the auto-brightness adjustment factor.
          static final int ADJUSTMENT_AUTO = 0x2;
  
          // One of REASON_*
          public int reason;
          // Any number of MODIFIER_*
          public int modifier;
  
          public void set(BrightnessReason other) {
              setReason(other == null ? REASON_UNKNOWN : other.reason);
              setModifier(other == null ? 0 : other.modifier);
          }
  
          public void setReason(int reason) {
              if (reason < REASON_UNKNOWN || reason > REASON_MAX) {
                  Slog.w(TAG, "brightness reason out of bounds: " + reason);
              } else {
                  this.reason = reason;
              }
          }
  
          public void setModifier(int modifier) {
              if ((modifier & ~MODIFIER_MASK) != 0) {
                  Slog.w(TAG, "brightness modifier out of bounds: 0x"
                          + Integer.toHexString(modifier));
              } else {
                  this.modifier = modifier;
              }
          }
  
          public void addModifier(int modifier) {
              setModifier(modifier | this.modifier);
          }
  
          @Override
          public boolean equals(Object obj) {
              if (obj == null || !(obj instanceof BrightnessReason)) {
                  return false;
              }
              BrightnessReason other = (BrightnessReason) obj;
              return other.reason == reason && other.modifier == modifier;
          }
  
          @Override
          public String toString() {
              return toString(0);
          }
  
          public String toString(int adjustments) {
              final StringBuilder sb = new StringBuilder();
              sb.append(reasonToString(reason));
              sb.append(" [");
              if ((adjustments & ADJUSTMENT_AUTO_TEMP) != 0) {
                  sb.append(" temp_adj");
              }
              if ((adjustments & ADJUSTMENT_AUTO) != 0) {
                  sb.append(" auto_adj");
              }
              if ((modifier & MODIFIER_LOW_POWER) != 0) {
                  sb.append(" low_pwr");
              }
              if ((modifier & MODIFIER_DIMMED) != 0) {
                  sb.append(" dim");
              }
              int strlen = sb.length();
              if (sb.charAt(strlen - 1) == '[') {
                  sb.setLength(strlen - 2);
              } else {
                  sb.append(" ]");
              }
              return sb.toString();
          }
  
          private String reasonToString(int reason) {
              switch (reason) {
                  case REASON_MANUAL: return "manual";
                  case REASON_DOZE: return "doze";
                  case REASON_DOZE_DEFAULT: return "doze_default";
                  case REASON_AUTOMATIC: return "automatic";
                  case REASON_SCREEN_OFF: return "screen_off";
                  case REASON_VR: return "vr";
                  case REASON_OVERRIDE: return "override";
                  case REASON_TEMPORARY: return "temporary";
                  case REASON_BOOST: return "boost";
                  default: return Integer.toString(reason);
              }
          }
      }
  }
  