When analyzing the on-off screen flow in Android R PowerManagerService module (4) and Android R PowerManagerService module (3) on screen flow, focus on the PMS part of the flow. The process involved in THE DMS is not analyzed, only the request to the DMS to set the screen brightness and Display state is omitted.

In the analysis of DMS module, emphasis will be placed on the process related to Display. This article will make a detailed analysis of the process after PMS sends a request to DMS in the process of switching on and off the screen, so as to understand the whole logic of setting brightness and Display state.

In the DMS module, the interaction with the PMS is handled by the DisplayPowerController, such as controlling the screen brightness, Display status, and the off screen of the PSensor. DisplayPowerController is a DMS module, but its core logic runs on the PMS thread, ensuring independence from the PMS. It does not share any state with the PMS. The PMS initiates a request to the DMS, and the DisplayPowerController receives the request and notifies the PMS of the change through a callback when the request is processed.

The DisplayPowerController object is created during PMS startup:


    public void systemReady(IAppOpsService appOps) {...// Initialize display power management.mDisplayManagerInternal.initPowerManagement( mDisplayPowerCallbacks, mHandler, sensorManager); . }Copy the code

Create DMS in Local Service:

// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
        @Override
        public void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler,
                SensorManager sensorManager) {
            synchronized (mSyncRoot) {
                DisplayBlanker blanker = new DisplayBlanker() {
                ......
                // Create the DisplayPowerController object
                mDisplayPowerController = new DisplayPowerController(
                        mContext, callbacks, handler, sensorManager, blanker,
                        mDisplayDevices.get(Display.DEFAULT_DISPLAY));
                mSensorManager = sensorManager;
            }
            mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATION);
        }
Copy the code

DisplayPowerController controls state and brightness Settings through two other classes:

  • DisplayPowerState: Represents the overall state of the current Display. Updates to the state and brightness are handed to it for further processing;
  • LocalDisplayAdapter: An adapter that represents the default screen. DisplayPowerState will give it the Display state and brightness, and it will set the state and brightness to the connected physical screen.

The next step is to continue the on-off screen process.

1. RequestPowerState () initiates the request

PMS is called updateDisplayPowerStateLocked request update the Display () method, this method finally call DisplayPowerController# requesetPowerState () method:

/ * * *@paramRequest Request parameters encapsulated in the PMS *@paramThe action of the PSensor lock after the release of waitForNegativeProximity and Proximity Wakelock *@returnTrue indicates that the request processing is complete. False indicates that the request processing is not complete
public boolean requestPowerState(DisplayPowerRequest request,
        boolean waitForNegativeProximity) {
    synchronized (mLock) {
        boolean changed = false;
        // Whether the PSensor listening is delayed after the PSensor WakeLock lock is released
        if(waitForNegativeProximity && ! mPendingWaitForNegativeProximityLocked) { mPendingWaitForNegativeProximityLocked =true;
            changed = true;
        }
        // Enter for the first time after booting
        if (mPendingRequestLocked == null) {
            mPendingRequestLocked = new DisplayPowerRequest(request);
            changed = true;
        } else if(! mPendingRequestLocked.equals(request)) {// The request has changed
            mPendingRequestLocked.copyFrom(request);
            changed = true;
        }
        Return false to the PMS to indicate that the request is not completed
        if (changed) {
            mDisplayReadyLocked = false;
        }
        // Start processing new requests
        if(changed && ! mPendingRequestChangedLocked) {// Indicates that the request has changed and the global request is about to be updated
            mPendingRequestChangedLocked = true;
            sendUpdatePowerStateLocked();
        }
 
        returnmDisplayReadyLocked; }}Copy the code

In the above methods, whether this request can be processed as a new request is determined by the following three conditions:

  • Whether waitForNegativeProximity is true;
  • If the function is entered for the first time after startup, mPendingRequestLocked is null, and the DisplayPowerRequest object representing the request is instantiated.
  • Check whether this request object is the same as the last request object.

If waitForNegativeProximity is true, it indicates that during the release of PROXIMITY_SCREEN_OFF_WAKE_LOCK WakeLock, If PSenor reports an proximity event and the screen is off, In this case, the MONITORING of the PSensor is removed only after the PSensor reports a remote event and the screen turns on. This value is true when powerManager.release_FLAG_WAIT_FOR_no_proximity is used when releasing wakelock. For details, see The WakeLock mechanism in Android R PowerManagerService module (2).

After applying PROXIMITY_SCREEN_OFF_WAKE_LOCK, release the lock using release() and release(RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY) respectively in the case of screen occlusion. Different results can be seen.

MPendingRequestChangedLocked variable is used to say the last request has been processed. The DisplayPowerRequest object represents a request object that encapsulates a number of request parameters:

public static final class DisplayPowerRequest {
    public int policy;                      // Display status of the request
    public boolean useProximitySensor;      // Whether PROXIMITY_SCREEN_OFF_WAKE_LOCK is held
    public int screenBrightnessOverride;    // Whether to override brightness, especially from WindowMananger
    public boolean useAutoBrightness;       // Whether to use automatic brightness
    public boolean lowPowerMode;            // Whether the battery is in low power mode
    public float screenLowPowerBrightnessFactor;   // Adjust brightness factor in low power mode
    public boolean boostScreenBrightness;   // Whether brightness enhancement is used
    public int dozeScreenBrightness;        // Screen brightness after entering Doze state
    public int dozeScreenState;             // Display state after entering Doze state
Copy the code

After the sendUpdatePowerStateLocked () method, through the Handler into the PMS thread began to process the request:

private void sendUpdatePowerStateLocked(a) {
    if(! mPendingUpdatePowerStateLocked) {// The power state needs to be updated
        mPendingUpdatePowerStateLocked = true; Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE); mHandler.sendMessage(msg); }}Copy the code

Then you go to the updatePowerState() method, which is the core method in the DisplayPowerController that does the parsing and processing of the request.

2. UpdatePowerState () Updates the status

The updatePowerState() method is very large, and handles the on-off screen, brightness adjustments, and Display state updates. The complete code for this method is as follows:

private void updatePowerState(a) {
    // Update the power state request.
    final boolean mustNotify;   // Whether to notify PMS to complete request processing
    final int previousPolicy;
    boolean mustInitialize = false;  // Whether to initialize
    int brightnessAdjustmentFlags = 0;  // Automatic brightness adjustment factor mark
    mBrightnessReasonTemp.set(null);   // Record the cause of brightness change
    // ####################################################Part 1####################################################
    synchronized (mLock) {
        mPendingUpdatePowerStateLocked = false;   // means "update is about to start", which has already started, so reset to true
        if (mPendingRequestLocked == null) {
            return; // wait until first actual power request
        }
 
        if (mPowerRequest == null) {   // The if code block is entered only for the first time after the system is booted
            mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);
            mWaitingForNegativeProximity = mPendingWaitForNegativeProximityLocked;
            mPendingWaitForNegativeProximityLocked = false;
            mPendingRequestChangedLocked = false;
            mustInitialize = true;
            previousPolicy = DisplayPowerRequest.POLICY_BRIGHT;
        } else if (mPendingRequestChangedLocked) {   // Update global DisplayPowerRequest
            previousPolicy = mPowerRequest.policy;  
            mPowerRequest.copyFrom(mPendingRequestLocked);   / / update the mPowerRequest
            mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
            mPendingWaitForNegativeProximityLocked = false;
            mPendingRequestChangedLocked = false;   / / reset
            mDisplayReadyLocked = false;
        } else{ previousPolicy = mPowerRequest.policy; } mustNotify = ! mDisplayReadyLocked; }if (mustInitialize) {
        initialize();  // Initialize the on-off screen animation, the DisplayPowerState object...
    }
    // ####################################################Part 2####################################################
    int state;
    float brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
    boolean performScreenOffTransition = false;    // Whether to perform the screen-off animation
    switch (mPowerRequest.policy) {                // The state of Display to be set will be determined according to policy
        case DisplayPowerRequest.POLICY_OFF:       // If policy is off, the Display state will be set to off
            state = Display.STATE_OFF;
            performScreenOffTransition = true;
            break;
        case DisplayPowerRequest.POLICY_DOZE:    / / the policy to doze, can according to mPowerRequest. DozeScreenState determine the status of the Display, the default state to doze
            if(mPowerRequest.dozeScreenState ! = Display.STATE_UNKNOWN) { state = mPowerRequest.dozeScreenState; }else {
                state = Display.STATE_DOZE;
            }
            if(! mAllowAutoBrightnessWhileDozingConfig) {// Whether automatic brightness is supported in Doze state. Default is false
                brightnessState = mPowerRequest.dozeScreenBrightness;    // Set the brightness to the Doze state brightness specified in PMS
                mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE);   // Record the reason for the brightness change -- the brightness of doze state
            }
            break;
        case DisplayPowerRequest.POLICY_VR:  // If policy is VR, set the Display state to VR
            state = Display.STATE_VR;
            break;
        case DisplayPowerRequest.POLICY_DIM:  // If policy is DIM or BRIGHT, the Display state is set to ON
        case DisplayPowerRequest.POLICY_BRIGHT:
        default:
            state = Display.STATE_ON;
            break;
    }
    assert(state ! = Display.STATE_UNKNOWN);// #################################################### Part 3 ####################################################
    if(mProximitySensor ! =null) {
        // If a Proximity WakeLock lock is held, and the Display status is not set to OFF
        if(mPowerRequest.useProximitySensor && state ! = Display.STATE_OFF) {// Register PSensor listener
            setProximitySensorEnabled(true);   
            // Proximity incident reported
            if(! mScreenOffBecauseOfProximity && mProximity == PROXIMITY_POSITIVE) {// represents "Is the screen off due to PSensor receiving the approach time?"
                mScreenOffBecauseOfProximity = true;    
                // The callback notifies the PMS that the PSensor received a proximity event
                sendOnProximityPositiveWithWakelock();  
            }
        If a Proximity WakeLock is released with a "1" marker, and the PSensor continues to wait and monitor the Proximity WakeLock because the PSensor is off and no remote event has been received
        } else if(mWaitingForNegativeProximity && mScreenOffBecauseOfProximity && mProximity == PROXIMITY_POSITIVE && state ! = Display.STATE_OFF) { setProximitySensorEnabled(true);
        } else {  
            // When the screen is off or a Proximity WakeLock is released, remove the PSensor monitoring
            setProximitySensorEnabled(false);
            mWaitingForNegativeProximity = false;
        }
        / / received from incident, reset mScreenOffBecauseOfProximity
        if(mScreenOffBecauseOfProximity && mProximity ! = PROXIMITY_POSITIVE) { mScreenOffBecauseOfProximity =false;
            // Notify the PMS that the PSensor received the away eventsendOnProximityNegativeWithWakelock(); }}else { 
        // If the PSensor is not available, set the value to false
        mWaitingForNegativeProximity = false;
    }
    // If the PSensor receives a report that the time is approaching, it needs to turn OFF the screen, so set the Display state to OFF
    if (mScreenOffBecauseOfProximity) {   
        state = Display.STATE_OFF;
    }
    // #################################################### Part 4 ####################################################
    final int oldState = mPowerState.getScreenState();
    // Set the Display state
    animateScreenStateChange(state, performScreenOffTransition);   
    state = mPowerState.getScreenState();
    // #################################################### Part 5 ####################################################
    // If the Display status is set to OFF, it indicates that the screen is OFF, and the brightness is set to -1.0F
    if (state == Display.STATE_OFF) {   
        brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT;
        // Record the cause of brightness change
        mBrightnessReasonTemp.setReason(BrightnessReason.REASON_SCREEN_OFF);   
    }
    // If the Display state is set to VR, it is not used frequently
    if (state == Display.STATE_VR) {   
        brightnessState = mScreenBrightnessForVr;
        mBrightnessReasonTemp.setReason(BrightnessReason.REASON_VR);
    }
    // Use brightness overlay in PMS, brightness from WindowManager such as video interface
    if ((Float.isNaN(brightnessState))
                && isValidBrightnessValue(mPowerRequest.screenBrightnessOverride)) {  
        brightnessState = mPowerRequest.screenBrightnessOverride;
        // Record the cause of brightness change
        mBrightnessReasonTemp.setReason(BrightnessReason.REASON_OVERRIDE);  
        // indicates that overlay brightness is used
        mAppliedScreenBrightnessOverride = true;   
    } else {
        mAppliedScreenBrightnessOverride = false;
    }
    // Determine whether to use auto brightness in Doze state, usually false
    final boolean autoBrightnessEnabledInDoze =
            mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
    // Check whether auto Brightness is available: Auto Brightness & Bright screen state is started & the first several types of brightness are not used & Auto Brightness curve mapping is configured
    final booleanautoBrightnessEnabled = mPowerRequest.useAutoBrightness && (state == Display.STATE_ON || autoBrightnessEnabledInDoze) &&  Float.isNaN(brightnessState) && mAutomaticBrightnessController ! =null;
    // Updates the status of the manually set brightness value in the user status bar and Settings. Returns true if the user Settings brightness changes
    final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
    // This brightness represents the brightness when the user drags the brightness bar adjustment and does not let go, so it is a "temporary" brightness and must be used if it exists
    if (isValidBrightnessValue(mTemporaryScreenBrightness)) {
        brightnessState = mTemporaryScreenBrightness;
        // Indicates temporary user brightness is used
        mAppliedTemporaryBrightness = true;   
        mBrightnessReasonTemp.setReason(BrightnessReason.REASON_TEMPORARY);
    } else {
        mAppliedTemporaryBrightness = false;
    }
    // Updates the auto brightness curve adjustment value, returning true if the adjustment value changes
    final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
    // Since the change has been updated to the global variable, there is no need for "temporary" automatic variable adjustment, reset
    if (autoBrightnessAdjustmentChanged) {  
        mTemporaryAutoBrightnessAdjustment = Float.NaN;
    }
 
    final float autoBrightnessAdjustment;
    // If a temporary automatic variable adjustment value exists, use it as the global automatic variable adjustment value, and then its value is updated to the global automatic variable adjustment value and reset, as seen in the first five lines
    if(! Float.isNaN(mTemporaryAutoBrightnessAdjustment)) { autoBrightnessAdjustment = mTemporaryAutoBrightnessAdjustment; brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO_TEMP;// Indicates that a temporary automatic brightness adjustment value is used
        mAppliedTemporaryAutoBrightnessAdjustment = true; 
    // Otherwise use the global auto brightness adjustment value
    } else {   
        autoBrightnessAdjustment = mAutoBrightnessAdjustment;
        brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO;  
        mAppliedTemporaryAutoBrightnessAdjustment = false;
    }
    // If brightness enhancement is used and the Display state is not off, the brightness will be set to the maximum
    if(mPowerRequest.boostScreenBrightness && brightness ! = PowerManager.BRIGHTNESS_OFF) { brightnessState = PowerManager.BRIGHTNESS_ON; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_BOOST);// Indicates that brightness enhancement is used
        mAppliedBrightnessBoost = true;    
    } else {
        mAppliedBrightnessBoost = false;
    }
    / / indicates whether or not the user has launched a brightness change: don't use more than a few class brightness & (automatic brightness adjustment value change | | users to set the brightness)
    // If this value is true, the brightness is changed by the user dragging
    boolean userInitiatedChange = (Float.isNaN(brightnessState))
            && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged);
    // Whether the automatic brightness has been manually adjusted
    boolean hadUserBrightnessPoint = false;   
    if(mAutomaticBrightnessController ! =null) {
        hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
        // Set automatic brightness parameters
    mAutomaticBrightnessController.configure(autoBrightnessEnabled,
            mBrightnessConfiguration,
            mLastUserSetScreenBrightness,
            userSetBrightnessChanged, autoBrightnessAdjustment,
            autoBrightnessAdjustmentChanged, mPowerRequest.policy);    }
 
    // #################################################### Part 6 ####################################################
    boolean slowChange = false;
    if (brightness < 0) {   // If the first several types of brightness are not used, use automatic brightness
        float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
        if (autoBrightnessEnabled) {
            // Get automatic brightness
            brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness(); 
            // Get the new auto brightness adjustment value
            newAutoBrightnessAdjustment =
               mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment(); 
        }
        // Auto brightness is used
        if (isValidBrightnessValue(brightnessState)) {  
            // Limit the range of automatic brightness obtained
            brightnessState = clampScreenBrightness(brightness);  
            // These two conditions are adjusted by automatic brightness
            if(mAppliedAutoBrightness && ! autoBrightnessAdjustmentChanged) {// Use to control the brightness adjustment rate
                slowChange = true;
            }
            // Update the brightness value to Settings
            putScreenBrightnessSetting(brightnessState);
            // Indicates that auto brightness is used
            mAppliedAutoBrightness = true;   
            mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
        } else {
            mAppliedAutoBrightness = false;
        }
        // If the automatic brightness adjustment value changes, update the automatic brightness adjustment value to Settings
        if(autoBrightnessAdjustment ! = newAutoBrightnessAdjustment) { putAutoBrightnessAdjustmentSetting(newAutoBrightnessAdjustment); }else {
            // It is pure auto brightness, and there is no adjustment
            brightnessAdjustmentFlags = 0; }}else {
        mAppliedAutoBrightness = false;
        brightnessAdjustmentFlags = 0;
    }
    // #################################################### Part 7 ####################################################
    // If the PMS does not specify the Doze state brightness, use the default Doze state brightness
    if ((Float.isNaN(brightnessState))
            && Display.isDozeState(state)) {   
        brightnessState = mScreenBrightnessDozeConfig;
        mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
    }
 
    // #################################################### Part 8 ####################################################
    // If there is no brightness here, the user-set brightness will be used
    if (Float.isNaN(brightnessState)) {   
        brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
        mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
    }
    // #################################################### Part 9 ####################################################
    // Set the brightness after entering Dim
    if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {   
        if (brightnessState > mScreenBrightnessRangeMinimum) {
            brightnessState = Math.max(Math.min(brightness - SCREEN_DIM_MINIMUM_REDUCTION,
                    mScreenBrightnessDimConfig), mScreenBrightnessRangeMinimum);
            mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED); 
        }
        if(! mAppliedDimming) { slowChange =false;   // Indicates that the brightness needs to be set quickly
        }
        mAppliedDimming = true;  // Indicates that Dim is used
    } else if (mAppliedDimming) {
        slowChange = false;
        mAppliedDimming = false;
    }
    // #################################################### Part 10 ####################################################
    // In low power mode, lower the brightness to save power consumption
    if (mPowerRequest.lowPowerMode) {   
        if (brightnessState > mScreenBrightnessRangeMinimum) {
            final float brightnessFactor =
                    Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1);
            final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor);
            brightnessState = Math.max(lowPowerBrightness, mScreenBrightnessRangeMinimum);
            mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER);
        }
        if(! mAppliedLowPower) { slowChange =false;
        }
        // Indicates that low power mode brightness is used
        mAppliedLowPower = true;  
    } else if (mAppliedLowPower) {
        slowChange = false;
        mAppliedLowPower = false;
    }
    // #################################################### Part 11 ####################################################
    if(! mPendingScreenOff) {if (mSkipScreenOnBrightnessRamp) {   / / mSkipScreenOnBrightnessRamp says bright screen when not using brightness animation
            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; }}// Whether is or was in VR state
        final boolean wasOrWillBeInVr =
                (state == Display.STATE_VR || oldState == Display.STATE_VR);
        // Indicates whether to use the brightness animation when the screen is on. Use the brightness animation when the screen is on. Determined by the mSkipScreenOnBrightnessRamp
        final booleaninitialRampSkip = state == Display.STATE_ON && mSkipRampState ! = RAMP_STATE_SKIP_NONE;// Whether the display hardware supports the full range of brightness values, if not, do not use brightness animation Settings when setting brightness
        final boolean hasBrightnessBuckets =
                Display.isDozeState(state) && mBrightnessBucketsInDozeConfig;    // Whether the display hardware supports the full range of brightness values when representing the doZE state
        // Whether colorfade has finished covering the display content, if it has, then there is no need to use brightness animation, because the display content is no longer visible
        final boolean isDisplayContentVisible =
                mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0 f;
        // Whether temporary brightness is used
        final boolean brightnessIsTemporary =
                mAppliedTemporaryBrightness || mAppliedTemporaryAutoBrightnessAdjustment;
        
        float animateValue = brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT
                    ? PowerManager.BRIGHTNESS_MIN : brightnessState;
            if (isValidBrightnessValue(animateValue)) {
                // Set brightness directly, no brightness animation (rate=0)
                if(initialRampSkip || hasBrightnessBuckets || wasOrWillBeInVr || ! isDisplayContentVisible || brightnessIsTemporary) { animateScreenBrightness(animateValue, SCREEN_ANIMATION_RATE_MINIMUM); }else {
                    animateScreenBrightness(animateValue,
                            slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
                }
        // #################################################### Part 12 ####################################################
        // Call brightnestempo rary and userInitial Change to record each brightness change
        // common brightnessistemtempo () specifies whether to use temporary brightness. The default value is false and will revert to false when the tempo tempo tempo is changed
        // userInitiatedChange indicates whether the user has adjusted brightness. False during brightness adjustment, true after adjustment, and false again the next time the method is executed
        // Therefore, whenever the brightness changes, it will enter the following statistics, userInitiatedChange indicates whether the change is initiated by the user, Perfect!
        // However, notifyBrightnessChanged() limits the statistical conditions: Only count changes using auto brightness, turn off auto brightness, and use windowOverlay brightness without counting
        if(! brightnessIsTemporary) {if (userInitiatedChange && (mAutomaticBrightnessController == null| |! mAutomaticBrightnessController.hasValidAmbientLux())) { userInitiatedChange =false; } notifyBrightnessChanged(brightness, userInitiatedChange, hadUserBrightnessPoint); }}// After each brightness adjustment, the log output is quite useful for daily handling problems
    if(! mBrightnessReasonTemp.equals(mBrightnessReason) || brightnessAdjustmentFlags ! =0) {
        Slog.v(TAG, "Brightness [" + brightnessState + "] reason changing to: '"
                + mBrightnessReasonTemp.toString(brightnessAdjustmentFlags)
                + "', previous reason: '" + mBrightnessReason + "'.");
        mBrightnessReason.set(mBrightnessReasonTemp);
    }
 
    // #################################################### Part 13 ####################################################
    if(mDisplayWhiteBalanceController ! =null) {    // Adjust the color temperature
        if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
            mDisplayWhiteBalanceController.setEnabled(true);
            mDisplayWhiteBalanceController.updateDisplayColorTemperature();
        } else {
            mDisplayWhiteBalanceController.setEnabled(false); }}// #################################################### Part 14 ####################################################
    / / mPendingScreenOnUnblocker for bright screen process, not empty screen has said is blocked, waiting for the WindowManager module after completion callback will be bright screen operation it is set to null
    // Colorfade is a dark layer animation that fades in and out when the screen is on and off
    final boolean ready = mPendingScreenOnUnblocker == null&& (! mColorFadeEnabled || (! mColorFadeOnAnimator.isStarted() && ! mColorFadeOffAnimator.isStarted()))// Is there a colorfade animation going on
            && mPowerState.waitUntilClean(mCleanListener);   // Check whether the screen state and ColorFade are set, and return true when both are set
    // So ready indicates whether the display state is set
    // Indicates that the display state is set and the brightness is set
    final booleanfinished = ready && ! mScreenBrightnessRampAnimator.isAnimating();// Whether the brightness animation is in progress
    / / mReportedScreenStateToPolicy said report to PhoneWindowManager kill bright screen phase value
    if(ready && state ! = Display.STATE_OFF && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_ON) { setReportedScreenState(REPORTED_TO_POLICY_SCREEN_ON);/ / sets the mReportedScreenStateToPolicy to REPORTED_TO_POLICY_SCREEN_ON, said bright screen completion
        mWindowManagerPolicy.screenTurnedOn();    / / PhoneWindowManager report
    }
    // Indicates that the update display Settings or brightness Settings have not been completed and need to be locked to ensure that they are completed
    if(! finished && ! mUnfinishedBusiness) { mCallbacks.acquireSuspendBlocker(); mUnfinishedBusiness =true;
    }
    // The callback notifies the PMS that the display status has been updated and the PMS will initiate the request again
    if (ready && mustNotify) {
        synchronized (mLock) {
            if(! mPendingRequestChangedLocked) { mDisplayReadyLocked =true;    // Indicates that the update is complete
            }
        }
        sendOnStateChangedWithWakelock();  / / callback PMS
    }
    // Update display and brightness Settings are complete, release the lock
    if (finished && mUnfinishedBusiness) {
        mUnfinishedBusiness = false;
        mCallbacks.releaseSuspendBlocker();
    }
    // Record whether the Display state was set to ON last timemDozing = state ! = Display.STATE_ON; }Copy the code

According to specific functions, it is divided into several parts according to functions:

  • Part 1: Update of mPowerRequest, which represents the request encapsulation object in the current PMS;
  • Part 2: Determine the Display state to be set according to the policy parameter in the mPowerRequest object.
  • Part 3: Processing of Proximity Sensor;
  • Part 4: Set the Display state according to the state specified in Part2 and Part3;
  • Part 5: Determine screen brightness;
  • Part 6: Application of automatic brightness;
  • Part 7: In Doze state, use Doze default brightness;
  • Part 8: Application user manually adjust brightness;
  • Part 9: Setting the brightness of Dim;
  • Part 10: Setting brightness at low power state;
  • Part 11: Determine whether to use brightness animation and brightness Settings;
  • Part 12: Log output and record after brightness changes;
  • Part 13: Update color temperature adjustment state;
  • Part 14: Processing after Display status and brightness Settings are completed.

Parts 1, 2, 4, 11, and 14 are the key processes involved in the on-off screen. Parts 1 and 2 determine the Display state to be set according to the policy in the PMS. This is easy to understand and annotated in the code, so the following steps are directly started from Part4.

3. AnimateScreenStateChange () out the screen animated execution and update the Display state

After determining the Display State to set, use the animateScreenStateChange() method to set it:

private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
    if (mColorFadeEnabled &&
            (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) {   // If the ColorFade animation is already underway, return it directly when it is not display.on
        if(target ! = Display.STATE_ON) {return;
        }
        // Indicates whether the screen has been set to fade (ColorFade, black screen)
        mPendingScreenOff = false;
    }
 
    // Do you need to transition to the OFF state before exiting the Doze state
    if (mDisplayBlanksAfterDozeConfig     // The default is fale&& Display.isDozeState(mPowerState.getScreenState()) && ! Display.isDozeState(target)) { mPowerState.prepareColorFade(mContext, mColorFadeFadesConfig ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP);if(mColorFadeOffAnimator ! =null) {
            mColorFadeOffAnimator.end();
        }
        // Go to the OFF statesetScreenState(Display.STATE_OFF, target ! = Display.STATE_OFF/*reportOnly*/);
    }
 
    // If the Display OFF request is received, the Display state is directly set to OFF without waiting for the completion of the Display OFF animation
    if(mPendingScreenOff && target ! = Display.STATE_OFF) { setScreenState(Display.STATE_OFF); mPendingScreenOff =false;   / / reset
        mPowerState.dismissColorFadeResources();  / / remove ColorFade
    }
    // Follow the request status
    if (target == Display.STATE_ON) {     // Display The target status is on
        // Set Display with DisplayPowerState and notify PhoneWindowManager, WMS callback returns true
        if(! setScreenState(Display.STATE_ON)) {return; 
        }
        // Whether a bright-screen animation is used. The default is false
        if (USE_COLOR_FADE_ON_ANIMATION && mColorFadeEnabled && mPowerRequest.isBrightOrDim()) {  
            if (mPowerState.getColorFadeLevel() == 1.0 f) {
                mPowerState.dismissColorFade();
            } else if (mPowerState.prepareColorFade(mContext,
                    mColorFadeFadesConfig ?
                            ColorFade.MODE_FADE :
                                    ColorFade.MODE_WARM_UP)) {
                mColorFadeOnAnimator.start();   // Start the transition of the screen animation
            } else{ mColorFadeOnAnimator.end(); }}else {     // If no ColorFade animation is used, set the ColorFade level to 1(full transparency).
            mPowerState.setColorFadeLevel(1.0 f); mPowerState.dismissColorFade(); }}else if (target == Display.STATE_VR) {   // Display target state is VR, this state is omitted
        / /...
    } else if (target == Display.STATE_DOZE) {   // Display target state is DOZE
        // If you are performing the brightness setting process while the screen is on, return first
        if (mScreenBrightnessRampAnimator.isAnimating()
                && mPowerState.getScreenState() == Display.STATE_ON) {  
            return;
        }
        // Set the Doze state
        if(! setScreenState(Display.STATE_DOZE)) {return;
        }
 
        mPowerState.setColorFadeLevel(1.0 f);   
        mPowerState.dismissColorFade();
    } else if (target == Display.STATE_DOZE_SUSPEND) {    // Display target state is STATE_DOZE_SUSPEND
        if(mScreenBrightnessRampAnimator.isAnimating() && mPowerState.getScreenState() ! = Display.STATE_DOZE_SUSPEND) {return;
        }
        // State must be STATE_DOZE before Display is set to STATE_DOZE_SUSPEND
        if(mPowerState.getScreenState() ! = Display.STATE_DOZE_SUSPEND) {if(! setScreenState(Display.STATE_DOZE)) {return; 
            }
            setScreenState(Display.STATE_DOZE_SUSPEND); 
        }
 
        mPowerState.setColorFadeLevel(1.0 f); 
        mPowerState.dismissColorFade();
    } else if (target == Display.STATE_ON_SUSPEND) {   // Display the target state is STATE_ON_SUSPEND
        if(mScreenBrightnessRampAnimator.isAnimating() && mPowerState.getScreenState() ! = Display.STATE_ON_SUSPEND) {return;
        }
        // State must be STATE_ON before Display is set to STATE_ON_SUSPEND
        if(mPowerState.getScreenState() ! = Display.STATE_ON_SUSPEND) {if(! setScreenState(Display.STATE_ON)) {return;
            }
            setScreenState(Display.STATE_ON_SUSPEND);
        }
 
        mPowerState.setColorFadeLevel(1.0 f);
        mPowerState.dismissColorFade();
    } else { // If only STATE_OFF is left, the screen is off
        mPendingScreenOff = true;   // Indicates the start of the ColorFade animation
        if(! mColorFadeEnabled) {// If ColorFade is not supported, add it directly
            mPowerState.setColorFadeLevel(0.0 f); 
        }
        // The ColorFade animation has been executed, so start setting the STATE_OFF state
        if (mPowerState.getColorFadeLevel() == 0.0 f) {   
            setScreenState(Display.STATE_OFF);
            mPendingScreenOff = false;   / / reset
            mPowerState.dismissColorFadeResources();
        } else if(performScreenOffTransition && mPowerState.prepareColorFade(mContext, mColorFadeFadesConfig ? ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN) && mPowerState.getScreenState() ! = Display.STATE_OFF) {// If the above conditions are not met, enter here and start executing the animation, the first time will enter the else-if block
            mColorFadeOffAnimator.start();    // Start performing the screen-off animation
        } else {
            mColorFadeOffAnimator.end();    // The screen is off}}}Copy the code

This method is also long, but has only two effects:

  • Call setScreenState() to continue setting the Display state;
  • Perform the on-off animation with ColorFade.

ColorFade is a mask that is completely transparent when its level is set to 1 and black when its level is set to 0. When the screen lights up, gradually set the level to 1 to give the effect of gradually lighting up the screen. When the screen is off, the level is gradually set to 0.

This method can be seen that, when the screen is on, set the Display state first, and then set the ColorFadelevel to 1; When the screen is off, set the ColorFade level to 0 and then set the Display state. This difference results in setting state and brightness priorities during the final on-off screen.

Next, the setScreenState() method is executed:

private boolean setScreenState(int state, boolean reportOnly) {
    // Whether to set the OFF state
    final boolean isOff = (state == Display.STATE_OFF);
    // If the current state is different from the target state
    if(mPowerState.getScreenState() ! = state) {If it is not caused by the PSensor, it reports the status to the PhoneWindowManager
        if(isOff && ! mScreenOffBecauseOfProximity) {if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON) {
                // Record the status
                setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);   
                blockScreenOff();
                // Notify PhoneWindowManager that the screen is off
                mWindowManagerPolicy.screenTurningOff(mPendingScreenOffUnblocker);  
                unblockScreenOff();
            } else if(mPendingScreenOffUnblocker ! =null) {
                return false; }}// reportOnly is ignored, which is generally false
        if(! reportOnly) {// Go to DisplayPowerState and set the Display statemPowerState.setScreenState(state); }}if(isOff && mReportedScreenStateToPolicy ! = REPORTED_TO_POLICY_SCREEN_OFF && ! mScreenOffBecauseOfProximity) {// Record the status
        setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
        // Unblock
        unblockScreenOn();  
        mWindowManagerPolicy.screenTurnedOff();   // Notify PhoneWindowManager that the screen is off
    } else if(! isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_OFF) {// In this case, the user intends to turn on the screen after the screen is half off. In this case, the user should also complete the process of turning off the screen
        unblockScreenOff();
        mWindowManagerPolicy.screenTurnedOff();  // Notify PhoneWindowManager that the screen is off
        setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
    }
    // The state is changed from OFF to another state
    if(! isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF) { setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON);if (mPowerState.getColorFadeLevel() == 0.0 f) {  
            // ColorFade level is always 0, which makes it impossible to set the brightness of the screen and blocks the brightness process until a callback is received in WMS
            blockScreenOn();    
        } else {
            unblockScreenOn();  // Unblock
        }
        mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker);   // Notify PhoneWindowManager that the screen is off
    }
    / / mPendingScreenOnUnblocker in unblockScreenOn () set to null, said unblocked
    return mPendingScreenOnUnblocker == null;
}
Copy the code

In this method, you set the Display state and notify the PhoneWindowManager. MReportedScreenStateToPolicy variable is used to record the current state, according to the different states to PhoneWindowManager notice. When the Display state changes, the value will be set to the corresponding value whenever a certain step is taken. There are four values in total:

// Set the non-off state, or OFF state
private static final int REPORTED_TO_POLICY_SCREEN_OFF = 0;  
// Start setting the non-off state
private static final int REPORTED_TO_POLICY_SCREEN_TURNING_ON = 1;  
// Complete in non-off state
private static final int REPORTED_TO_POLICY_SCREEN_ON = 2;   
// Start setting the non-off state
private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3;
Copy the code

Based on these values, notify the PhoneWindowManger to perform the corresponding action. If, at the beginning of the bright screen will call mWindowManagerPolicy. ScreenTurningOn (mPendingScreenOnUnblocker), WMS module corresponding processing, after receiving the call will be made in the process, Because mPendingScreenOnUnblocker object is not empty, setScreenState () returns false, lead to blocking colorFade set, thus unable to set the screen brightness.

In WMS processing has been completed, and through callback DMS mPendingScreenOnUnblocker parameters, and through unblockScreenOn () method, which will be mPendingScreenOnUnblocker object is null:

private void unblockScreenOn(a) {
    if(mPendingScreenOnUnblocker ! =null) {
        mPendingScreenOnUnblocker = null;
        longdelay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime; }}Copy the code

Again into setScreenState (after), because mPendingScreenOnUnblocker has to null, therefore does not block ColorFade and brightness Settings. Next, go to DisplayPowerState#setScreenState() to further set the state of Display.

4.DisplayPowerState#setScreenState() sets the state

DisplayPowerState represents the overall state of the current Display, including Display state, brightness and ColorFade Level value. All values related to Display will be entered into DisplayPowerState for setting at last. SetScreenState () as follows:

public void setScreenState(int state) {
    if(mScreenState ! = state) { mScreenState = state;// Update the current Display state
        mScreenReady = false;   // Indicates that the update of Display state, brightness and ColorFade level is not completed
        scheduleScreenUpdate(); // Perform the update operation}}Copy the code

The mScreenState variable is updated to false to indicate that the Display state has not been updated, and the scheduleScreenUpdate() method is called. The scheduleScreenUpdate() method analyzes the process in the DisplayController after it has been analyzed.

5. AnimateScreenBrightness update brightness ()

Back in updatePowerState(), after executing Part4, let’s look at the setting of brightness in Part11. To set the brightness value use the animateScreenBrightness() method:

private void animateScreenBrightness(float target, int rate) {
    / / mScreenBrightnessRampAnimator began to set the brightness
    if(mScreenBrightnessRampAnimator.animateTo(target, rate)) { ....... }}Copy the code

The first parameter represents the luminance value to be set, and the second parameter represents the rate at which the luminance value is set. Obtaining the luminance value and the luminance setting rate value, as well as the luminance animation, will be analyzed in detail in the luminance setting section later. This article will focus on the setting process.

Brightness Settings, not as complicated as setting Display state, mScreenBrightnessRampAnimator is a RampAnimator object, class, the class is a custom animation dedicated to update the brightness. After animateTo() of this class, it will finally call the setScreenBrightness() method of DisplayPowerState to set the brightness to DisplayPowerState.

DisplayPowerState#setScreenBrightness() set brightness

public void setScreenBrightness(float brightness) {
    if(mScreenBrightness ! = brightness) {// Update the current brightness value
        mScreenBrightness = brightness;    
        if(mScreenState ! = Display.STATE_OFF) {// Indicates that the update of Display state, brightness and ColorFade level is not completed
            mScreenReady = false;
            // Perform the update operationscheduleScreenUpdate(); }}}Copy the code

The mScreenBrightness variable was updated and scheduleScreenUpdate() was called to start the update operation. The state and brightness of the Display are now passed to DisplayPowerState, which will begin with the scheduleScreenUpdate() method.

ScheduleScreenUpdate () executes the update

private void scheduleScreenUpdate(a) {
    if(! mScreenUpdatePending) {// Indicates that the Display status will be updated
        mScreenUpdatePending = true; postScreenUpdateThreadSafe(); }}private void postScreenUpdateThreadSafe(a) {
    mHandler.removeCallbacks(mScreenUpdateRunnable);
    mHandler.post(mScreenUpdateRunnable);
}
Copy the code

MScreenUpdatePending (true) endgame (screenUpdatepending) endGame (true) endGame (true) endGame (true) endGame (true) endGame (true) endGame (true) endGame (true) endGame (true) endGame


    private final Runnable mScreenUpdateRunnable = new Runnable() {
        @Override
        public void run(a) {
            mScreenUpdatePending = false;
            // Confirm the final brightness. When the Display state is OFF, or when the ColorFade level is 0, it will be set to 0, otherwise it will be set to the brightness value passed in the DPC
            floatbrightnessState = mScreenState ! = Display.STATE_OFF && mColorFadeLevel >0f ? mScreenBrightness : PowerManager.BRIGHTNESS_OFF_FLOAT;
            The PhotonicModulator is a separate thread used to update Display status and brightness
            if (mPhotonicModulator.setState(mScreenState, brightnessState)) {
                // The update is complete
                mScreenReady = true;
                invokeCleanListenerIfNeeded();
            } else{}}};Copy the code

There is another confirmation of the brightness value to be updated, a final brightness value based on the Display state, ColorFade, and then the update starts in the mPhotonicModulator.

PhotonicModulator#setState() updates the child thread

PhotonicModulator is a class that inherits Thread and is used to update Display state and brightness. Therefore, updating screen state and backlight is done asynchronously in a separate Thread. The setState() method is as follows:

public boolean setState(int state, int backlight) {
    synchronized (mLock) {
        // Whether the Display state is changed. MPendingState indicates the state that is to be set, and will not be cleared after the setting
        booleanstateChanged = state ! = mPendingState;// Whether the brightness has changed, mPendingBacklight indicates the brightness to be set, and will not be cleared after setting
        booleanbacklightChanged = backlight ! = mPendingBacklight;// When either the Display status or brightness changes, the update starts
        if (stateChanged || backlightChanged) {  
            mPendingState = state;           // Update the mPendingState value
            mPendingBacklight = backlight;   // Update the mPendingBacklight value
            // Indicates whether the change process is in progress
            boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress;
            mStateChangeInProgress = stateChanged || mStateChangeInProgress;   
            mBacklightChangeInProgress = backlightChanged || mBacklightChangeInProgress;
            // Invoke the PhotonicModulator thread when not in the change process
            if (!changeInProgress) { 
                mLock.notifyAll();
            }
        }
        return! mStateChangeInProgress;// Return true as soon as the State update is complete}}Copy the code

When either screen state or brightness changes, a notifyAll() is used to wake up the PhotonicModulator thread, which then executes its Run () method. This is a production-consumer model that must have waited () somewhere else. Let’s look at its run() method:

public void run(a) {
    for (;;) {
        final int state;
        final boolean stateChanged;
        final int backlight;
        final boolean backlightChanged;
        synchronized (mLock) {
            state = mPendingState;
            // mActualState indicates the actual Display statestateChanged = (state ! = mActualState); backlight = mPendingBacklight;// mActualBacklight indicates the actual brightnessbacklightChanged = (backlight ! = mActualBacklight);if(! stateChanged) {// Initiate the update process again
                postScreenUpdateThreadSafe();   
                mStateChangeInProgress = false;
            }
            if(! backlightChanged) { mBacklightChangeInProgress =false;
            }
            if(! stateChanged && ! backlightChanged) {try {
                    // After state and brightness are set, the thread enters wait state
                    mLock.wait();
                } catch (InterruptedException ex) { }
                continue;
            }
            mActualState = state;
            mActualBacklight = backlight;
        }
        // Call DisplayBlanker to request DisplaymBlanker.requestDisplayState(state, backlight); }}Copy the code

After the Display status and brightness are updated, the thread is WAITTING using object.wait (). In this way, the Display state and brightness are constantly set.

. In the end, the call mBlanker requestDisplayState () to set, the next step to here again after the DMS, the final state of the specific object of DisplayDevice and brightness Settings, this logic is in the middle of the skip.

9.LocalDisplayDevice#requestDisplayStateLocked()

RequestDisplayStateLocked () to a specific physical screen to set the new Display state and brightness values:

        @Override
        public Runnable requestDisplayStateLocked(final int state, final float brightnessState) {
            final booleanstateChanged = (mState ! = state);final booleanbrightnessChanged = (! BrightnessSynchronizer.floatEquals( mBrightnessState, brightnessState)) && mBacklight ! =null;
            if (stateChanged || brightnessChanged) {
                ......
                return new Runnable() {
                    @Override
                    public void run(a) {...// Set brightness
                        if (brightnessChanged || vrModeChange) {
                            setDisplayBrightness(brightnessState);
                        }
                        // Set the state
                        if (state != currentState) {
                            setDisplayState(state);
                        }
                    }

                    private void setDisplayState(int state) {...try {
                            SurfaceControl.setDisplayPowerMode(token, mode);
                        } finally{}}private void setDisplayBrightness(float brightness) {
                        try {
                            if(mBacklight ! =null) { mBacklight.setBrightness(brightness); }}finally{}}}; }return null;
        }
Copy the code

Finally, the brightness is set to The LightsService and the state is set to the SurfaceControl, and the two modules will set the brightness and state respectively to the SurfaceFlinger.

Back in updatePowerState(), in Part14, mDisplayReadyLocked is set to true after the Display status and brightness are set, indicating that the Display status update is complete. And in sendOnStateChangedWithWakelock () method of the callback PMS, the PMS again initiated the request, because mDisplayReadyLocked has become true, so returns true, the PMS will think the DMS to request processing is completed.

The above is the on-off screen process involved in display module. Combined with the previous two on-off screen processes in THE PMS, the on-off screen processes in the Framework layer are analyzed.

Sequence diagram of bright screen:

Screen off sequence diagram: