SoFunction
Updated on 2025-03-11

How to achieve automatic brightness adjustment on Android

There is a brightness progress bar in the pull-down status bar. If the brightness automatic adjustment switch is turned on, it will change with the surrounding light, and the progress bar will also change. Next, let’s see how this function is implemented.

Source code version

Based on Android 9.0 analysis.

BrightnessDialog, located at:
frameworks/base/packages/SystemUI/src/com/android/systemui/settings/

ToggleSliderView, located at:
frameworks/base/packages/SystemUI/src/com/android/systemui/settings/

DisplayPowerController, located at:
frameworks/base/services/core/java/com/android/server/display/

AutomaticBrightnessController, located at:
frameworks/base/services/core/java/com/android/server/display/

BrightnessMappingStrategy,

Overview

The brightness page in the status bar is BrightnessDialog, where the progress bar setting is ToggleSliderView, and the brightness automatic adjustment is mainly two classes: DisplayPowerController and AutomaticBrightnessController. When the brightness changes, if it is associated with the ToggleSliderView, it uses ContentObserver and Uri is .SCREEN_AUTO_BRIGHTNESS_ADJ.

Source code sorting

1、BrightnessDialog#onCreate:

@Override
protected void onCreate(Bundle savedInstanceState) {
  (savedInstanceState);
  //Omit some code  mBrightnessController = new BrightnessController(this, icon, slider);
}

2. The BrightnessController is initialized here, let’s take a look:

public BrightnessController(Context context, ImageView icon, ToggleSlider control) {
  //Omit some code  mBrightnessObserver = new BrightnessObserver(mHandler);
  //Omit some code}

BrightnessObserver initialization is carried out again:

/** ContentObserver to watch brightness **/
private class BrightnessObserver extends ContentObserver {
  //Omit some code  private final Uri BRIGHTNESS_FOR_VR_URI =
      (.SCREEN_BRIGHTNESS_FOR_VR);
  //Add By WuXiaolong for AutomaticBrightness
  private final Uri BRIGHTNESS_ADJ_URI =
      (.SCREEN_AUTO_BRIGHTNESS_ADJ);
  public BrightnessObserver(Handler handler) {
    super(handler);
  }
  @Override
  public void onChange(boolean selfChange) {
    onChange(selfChange, null);
  }
  @Override
  public void onChange(boolean selfChange, Uri uri) {
    if (selfChange) return;
    if (BRIGHTNESS_MODE_URI.equals(uri)) {
      (mUpdateModeRunnable);
      (mUpdateSliderRunnable);
    } 
    //Omit some code    //Add By WuXiaolong for AutomaticBrightness
    else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) {
      (mUpdateSliderRunnable);
    } else {
      (mUpdateModeRunnable);
      (mUpdateSliderRunnable);
    }
    for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
      ();
    }
  }
  public void startObserving() {
    final ContentResolver cr = ();
    (this);
    //Omit some code    (
        BRIGHTNESS_FOR_VR_URI,
        false, this, UserHandle.USER_ALL);
    //Add By WuXiaolong for AutomaticBrightness
    (
        BRIGHTNESS_ADJ_URI,
        false, this, UserHandle.USER_ALL);
  }
  public void stopObserving() {
    final ContentResolver cr = ();
    (this);
  }
}

In fact, the source code I have downloaded is incomplete. I have added it. Where can I register the ContentObserver for BrightnessObserver?

3. Back to BrightnessDialog#onStart:

@Override
protected void onStart() {
  ();
  ();
  (this, MetricsEvent.BRIGHTNESS_DIALOG);
}

4. Call(); finally go to mStartListeningRunnable:

private final Runnable mStartListeningRunnable = new Runnable() {
  @Override
  public void run() {
    //BrightnessObserver Registration    ();
    ();
    // Update the slider and mode before attaching the listener so we don't
    // receive the onChanged notifications for the initial values.
    ();
    ();
    (MSG_ATTACH_LISTENER);
  }
};

When the brightness changes, it will go BrightnessObserver#onChange, and finally go to:

private final Handler mHandler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
    mExternalChange = true;
    try {
      switch () {
        //Omit some code        case MSG_UPDATE_SLIDER:
          updateSlider(msg.arg1, msg.arg2 != 0);
          break;
        //Omit some code        default:
          (msg);
      }
    } finally {
      mExternalChange = false;
    }
  }
};

Go to the updateSlider method and go to:

private void animateSliderTo(int target) {
  if (!mControlValueInitialized) {
    // Don't animate the first value since it's default state isn't mea
    (target);
    mControlValueInitialized = true;
  }
  //Omit some code}

5. Jump to ToggleSliderView#setValue:

@Override
public void setValue(int value) {
  //This is the modification progress bar  (value);
  if (mMirror != null) {
    (value);
  }
}

Next is to look at the two main categories of automatic brightness adjustment, DisplayPowerController and AutomaticBrightnessController. DisplayPowerController belongs to the Display module. It controls the screen of the device to turn off, backlight, and is closely related to Power. Here we mainly look at the logic of controlling the screen brightness.

6. First, initialize the DisplayPowerController in the DisplayManagerService, as follows:

private final class LocalService extends DisplayManagerInternal {
  @Override
  public void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler,
      SensorManager sensorManager) {
    synchronized (mSyncRoot) {
      //Omit some code      mDisplayPowerController = new DisplayPowerController(
          mContext, callbacks, handler, sensorManager, blanker);
    }
    (MSG_LOAD_BRIGHTNESS_CONFIGURATION);
  }

7. Next, look at the DisplayPowerController construction method, as follows:

public DisplayPowerController(Context context,
    DisplayPowerCallbacks callbacks, Handler handler,
    SensorManager sensorManager, DisplayBlanker blanker) {
  //Omit some code  mUseSoftwareAutoBrightnessConfig = (
      .config_automatic_brightness_available);
  //Omit some code  if (mUseSoftwareAutoBrightnessConfig) {
    //Omit some code    mBrightnessMapper = (resources);
    if (mBrightnessMapper != null) {
      mAutomaticBrightnessController = new AutomaticBrightnessController(this,
          (), sensorManager, mBrightnessMapper,
          lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum,
          mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
          initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
          autoBrightnessResetAmbientLuxAfterWarmUp, hysteresisLevels);
    } else {
      mUseSoftwareAutoBrightnessConfig = false;
    }
  }
  //Omit some code  mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
  mTemporaryAutoBrightnessAdjustment = ;
  //Omit some code}

Since the automatic brightness of the screen will take effect only after the screen is turned on, the process will go to the core function updatePowerState() in DisplayPowerController:

private void updatePowerState() {
  // Update the power state request.
  //Omit some code  
  final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
  if (autoBrightnessAdjustmentChanged) {
    mTemporaryAutoBrightnessAdjustment = ;
  }
  // Use the autobrightness adjustment override if set.
  final float autoBrightnessAdjustment;
  if (!(mTemporaryAutoBrightnessAdjustment)) {
    autoBrightnessAdjustment = mTemporaryAutoBrightnessAdjustment;
    mAppliedTemporaryAutoBrightnessAdjustment = true;
  } else {
    autoBrightnessAdjustment = mAutoBrightnessAdjustment;
    mAppliedTemporaryAutoBrightnessAdjustment = false;
  }
  
  boolean hadUserBrightnessPoint = false;
  // Configure auto-brightness.
  if (mAutomaticBrightnessController != null) {
    hadUserBrightnessPoint = ();
    (autoBrightnessEnabled,
        mBrightnessConfiguration,
        mLastUserSetScreenBrightness / (float) PowerManager.BRIGHTNESS_ON,
        userSetBrightnessChanged, autoBrightnessAdjustment,
        autoBrightnessAdjustmentChanged, );
  }
  
  // Apply auto-brightness.
  boolean slowChange = false;
  if (brightness < 0) {
    float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
    if (autoBrightnessEnabled) {
      brightness = ();
      newAutoBrightnessAdjustment =
          ();
    }
    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;
    } 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 {
    mAppliedAutoBrightness = false;
  }
  //Omit some code}

Next, let’s take a look at how autoBrightnessAdjustment and newAutoBrightnessAdjustment come from?

autoBrightnessAdjustment is assigned from mTemporaryAutoBrightnessAdjustment or mAutoBrightnessAdjustment. mAutoBrightnessAdjustment In step 7 mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting(); there is initialization, see getAutoBrightnessAdjustmentSetting():

private float getAutoBrightnessAdjustmentSetting() {
  final float adj = ((),
      .SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f, UserHandle.USER_CURRENT);
  return (adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
}

Continue to check clampAutoBrightnessAdjustment:

private static float clampAutoBrightnessAdjustment(float value) {
  return (value, -1.0f, 1.0f);
}

Note here that () represents the percentage scaling function, such as (0.5, 0, 255) represents (255-0)*0.5.

This way I understand autoBrightnessAdjustment, and then look at newAutoBrightnessAdjustment.

8. Back to DisplayPowerController#updatePowerState(),

I saw that newAutoBrightnessAdjustment called (), and finally I arrived at BrightnessMapper#getAutoBrightnessAdjustment() where the mAutoBrightnessAdjustment variable is assigned in BrightnessMapper#setAutoBrightnessAdjustment:

@Override
public boolean setAutoBrightnessAdjustment(float adjustment) {
  adjustment = (adjustment, -1, 1);
  if (adjustment == mAutoBrightnessAdjustment) {
    return false;
  }
  if (DEBUG) {
    (TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
        adjustment);
    ("auto-brightness adjustment");
  }
  mAutoBrightnessAdjustment = adjustment;
  computeSpline();
  return true;
}

9、BrightnessMapper#setAutoBrightnessAdjustment

This method call returns to AutomaticBrightnessController#setAutoBrightnessAdjustment:

private boolean setAutoBrightnessAdjustment(float adjustment) {
  return (adjustment);
}

The AutomaticBrightnessController#setAutoBrightnessAdjustment call comes to the AutomaticBrightnessController#configure() method:

public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
    float brightness, boolean userChangedBrightness, float adjustment,
    boolean userChangedAutoBrightnessAdjustment, int displayPolicy) {
  // While dozing, the application processor may be suspended which will prevent us from
  // receiving new information from the light sensor. On some devices, we may be able to
  // switch to a wake-up light sensor instead but for now we will simply disable the sensor
  // and hold onto the last computed screen auto brightness. We save the dozing flag for
  // debugging purposes.
  boolean dozing = (displayPolicy == DisplayPowerRequest.POLICY_DOZE);
  boolean changed = setBrightnessConfiguration(configuration);
  changed |= setDisplayPolicy(displayPolicy);
  if (userChangedAutoBrightnessAdjustment) {
    changed |= setAutoBrightnessAdjustment(adjustment);
  }
  if (userChangedBrightness && enable) {
    // Update the brightness curve with the new user control point. It's critical this
    // happens after we update the autobrightness adjustment since it may reset it.
    changed |= setScreenBrightnessByUser(brightness);
  }
  final boolean userInitiatedChange =
      userChangedBrightness || userChangedAutoBrightnessAdjustment;
  if (userInitiatedChange && enable && !dozing) {
    prepareBrightnessAdjustmentSample();
  }
  changed |= setLightSensorEnabled(enable && !dozing);
  if (changed) {
    updateAutoBrightness(false /*sendUpdate*/);
  }
}

AutomaticBrightnessController#configure() is called to DisplayPowerController #updatePowerState().

This also knows newAutoBrightnessAdjustment, continue to putAutoBrightnessAdjustmentSetting:

private void putAutoBrightnessAdjustmentSetting(float adjustment) {
  mAutoBrightnessAdjustment = adjustment;
  ((),
      .SCREEN_AUTO_BRIGHTNESS_ADJ, adjustment, UserHandle.USER_CURRENT);
}

Then adjust to step 4 BrightnessObserver#onChange, and the progress bar changes accordingly, Over!

The above is the detailed content on how Android can automatically adjust brightness. For more information about Android's automatic brightness adjustment, please follow my other related articles!