In Android R PowerManagerService module (4), the distance sensor’s screen is off. The principle of the screen is different from that of the normal screen. For normal screen extinction, such as pressing the Power button, the system state of PMS and the overall state of DMS will be updated (mWakefulness in PMS will be set to ASLEEP and DOZING; In DMS, the display state is set to STATE_OFF, STATE_DOZING, and STATE_DOZING_SUSPEND. If the PSensor is off, the Display state is set to STATE_OFF and the screen brightness is turned off. The system state in the PMS remains Awake. This article analyzes the process and principle of PSensor on and off screen.

1. Registered PSensor

In DMS, register PSensor by applying PROXIMITY_SCREEN_OFF_WAKE_LOCK WakeLock lock of type PROXIMITY_SCREEN_OFF_WAKE_LOCK:

mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK,"TAG");
mWakeLock.acquire();  / / application
mWakeLock.release(/*PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY*/);  / / release
Copy the code

When apply for this type WakeLock in PMS in the request, to DisplayMS by DisplayPowerRequest. UseProximitySensor attributes will be set to true, says you need to use the PSensor. When the request is received in the DisplayPowerController, the PSensor registration will be processed for this property in the updatePowerState() method:


    private void updatePowerState(a) {...// Apply the proximity sensor.
        if(mProximitySensor ! =null) {
            // If the WakeLock lock of PROXIMITY_SCREEN_OFF_WAKE_LOCK type is held, and the Display status is not OFF
            if(mPowerRequest.useProximitySensor && state ! = Display.STATE_OFF) {/ / register PSensor
                setProximitySensorEnabled(true);
                / / PSensor received close to the event, mScreenOffBecauseOfProximity set to true, said due to PSensor out the screen
                if(! mScreenOffBecauseOfProximity && mProximity == PROXIMITY_POSITIVE) { mScreenOffBecauseOfProximity =true;
                    // The callback notifies the PMS that an approaching event has been received
                    sendOnProximityPositiveWithWakelock();
                }
            / / if mWaitingForNegativeProximity is true, and this time PSensor status to close, continue to maintain the PSensor listening in
            } else if(mWaitingForNegativeProximity && mScreenOffBecauseOfProximity && mProximity == PROXIMITY_POSITIVE && state ! = Display.STATE_OFF) { setProximitySensorEnabled(true);
            // Remove PSensor monitoring
            } else {
                setProximitySensorEnabled(false);
                mWaitingForNegativeProximity = false;
            }
            / / said received from events, reset mScreenOffBecauseOfProximity
            if(mScreenOffBecauseOfProximity && mProximity ! = PROXIMITY_POSITIVE) { mScreenOffBecauseOfProximity =false;
                // The callback notifies the PMS of the receipt of the away eventsendOnProximityNegativeWithWakelock(); }}else {
            mWaitingForNegativeProximity = false;
        }
        // If the PSensor receives an approaching event, set the Display status to OFF to remove the screen
        if(mScreenOffBecauseOfProximity) { state = Display.STATE_OFF; }...// Set the stateanimateScreenStateChange(state, performScreenOffTransition); . }Copy the code

In the above code snippet of the DisplayPowerController#updatePowerState() method, PSensor logic is handled, including registering, canceling, and event handling of PSensor listeners:

  • 1. When requesting objectsmPowerRequest.useProximitySensorIs true, and the current state of the Display is OFF, through setProximitySensorEnabled () method sets the PSensor listening;
  • 2. When PSensor reported near the event will be mScreenOffBecauseOfProximity set to true, and perform sendOnProximityPositiveWithWakelock () method to inform the PMS received near events;
  • 3. When the request object mPowerRequest. UseProximitySensor to false, explain related WakeLock in PMS has been released, if with releaseRELEASE_FLAG_WAIT_FOR_NO_PROXIMITYTag, mWaitingForNegativeProximity variable to true, if this time PSensor listening state is still in a state of near, does not immediately remove PSensor listening;
  • 4. If the release WakeLock, without RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY tag, the remove PSensor to monitor, and will be mScreenOffBecauseOfProximity reset;
  • 5. When PSensor reported from events will mScreenOffBecauseOfProximity reset to false, and perform sendOnProximityNegativeWithWakelock () method to inform the PMS received from events;

Through setProximitySensorEnabled PSensor () method to monitor the registration and remove:

// frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
    
    private void setProximitySensorEnabled(boolean enable) {
        if (enable) {
            / / register
            if(! mProximitySensorEnabled) { mProximitySensorEnabled =true;
                mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
                        SensorManager.SENSOR_DELAY_NORMAL, mHandler);
            }
        / / remove
        } else {
            if (mProximitySensorEnabled) {
                mProximitySensorEnabled = false;
                // Reset the PSensor listening status
                mProximity = PROXIMITY_UNKNOWN;
                mPendingProximity = PROXIMITY_UNKNOWN;
                mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
                mSensorManager.unregisterListener(mProximitySensorListener);
                // clearPendingProximityDebounceTime(); }}}Copy the code

2.PSensor event processing

After successful registration, mProximitySensorListener will be used to receive data reported by PSensor:

// frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java

    private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            if (mProximitySensorEnabled) {
                final long time = SystemClock.uptimeMillis();
                // Get the reported value
                final float distance = event.values[0];
                // Determine if the event is near
                boolean positive = distance >= 0.0 f && distance < mProximityThreshold;
                // Handle reported eventshandleProximitySensorEvent(time, positive); }}};Copy the code

In this method, the event value reported by PSensor can be obtained. If 0.0f <= distance < mProximityThreshold, it indicates that the proximity event has been reported, while distance >= mProximityThreshold indicates that the distance event has been reported. Then call handleProximitySensorEvent () method, according to the report event value to make out the screen and bright screen process:

// frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java

    private void handleProximitySensorEvent(long time, boolean positive) {
        if (mProximitySensorEnabled) {
            // If two reported events are far from the event, no processing is performed
            if(mPendingProximity == PROXIMITY_NEGATIVE && ! positive) {return; // no change
            }
            // If two reported events are close to the event, no processing is performed
            if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
                return; // no change
            }
        
            mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
            // Report proximity incident
            if (positive) {
                // indicates that the next setting is close to the event
                mPendingProximity = PROXIMITY_POSITIVE;
                // Set the anti-jitter duration. PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY is 0
                setPendingProximityDebounceTime(
                        time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY); // acquire wake lock
            // Report away from the event
            } else {
                mPendingProximity = PROXIMITY_NEGATIVE;
                setPendingProximityDebounceTime(
                        time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY); // acquire wake lock
            }
            // Anti-jitter operationdebounceProximitySensor(); }}Copy the code

This method, first set the mPendingProximity variables, and then call setPendingProximityDebounceTime () method, set up shake time PSensor events:

// frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java

private void setPendingProximityDebounceTime(long debounceTime) {
    if (mPendingProximityDebounceTime < 0) {
        // Apply for a SuspendBlocker lock from PMS
        mCallbacks.acquireSuspendBlocker();    
    }
    // Updates the anti-jitter duration
    mPendingProximityDebounceTime = debounceTime;    
}
Copy the code

Finally, the debounceProximitySensor() method is executed to conduct anti-jitter processing. In the original process, the anti-jitter duration of close event and away from time is 0ms and 250ms respectively. That is to say, if the event is close to time, it will be processed immediately; if it is away from time, it will be processed after 250ms. Avoid frequent switching on and off due to frequent close and away operations. The method is as follows:

// frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java

private void debounceProximitySensor(a) {
    if(mProximitySensorEnabled && mPendingProximity ! = PROXIMITY_UNKNOWN && mPendingProximityDebounceTime >=0) {   
        final long now = SystemClock.uptimeMillis();
        // Indicates that the current time has exceeded the jitter time
        if (mPendingProximityDebounceTime <= now) {  
            // Update the Psensor monitoring status
            mProximity = mPendingProximity;   
            // Update the global status
            updatePowerState();    
            // Clear the anti-jitter duration and release the SuspendBlcoker lock
            clearPendingProximityDebounceTime(); 
        // Indicates that the anti-jitter time has been reached
        } else{ Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED); mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime); }}}Copy the code

If the current time does not meet the required shaking duration, a timed operation is set up with Handler. After the time reaches, debounceProximitySensor() is executed to complete the update of the mProximity attribute. Then call updatePowerState() to start the global update.

During this update, the Display state is set to display. STATE_OFF and the brightness is also set to 0 according to the value of mProximity in the monitoring state of PSensor.

3. Work in PMS

When PSensor listening state changes, will be called sendOnProximityPositiveWithWakelock () method and sendOnProximityNegativeWithWakelock () method, the callback to the PMS:

// frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java

private void sendOnProximityPositiveWithWakelock(a) {
    // Apply SuspendBlocker lock
    mCallbacks.acquireSuspendBlocker();   
    mHandler.post(mOnProximityPositiveRunnable);
}

private final Runnable mOnProximityPositiveRunnable = new Runnable() {
    @Override
    public void run(a) {
        / / callback PMS. OnProximityPositive ()
        mCallbacks.onProximityPositive();    
        / / release SuspendBlockermCallbacks.releaseSuspendBlocker(); }};private void sendOnProximityNegativeWithWakelock(a) {
    / / SuspendBlocker application
    mCallbacks.acquireSuspendBlocker();   
    mHandler.post(mOnProximityNegativeRunnable);
}

private final Runnable mOnProximityNegativeRunnable = new Runnable() {
    @Override
    public void run(a) {
        / / callback PMS. OnProximityNegative ()
        mCallbacks.onProximityNegative(); 
        / / release SuspendBlockermCallbacks.releaseSuspendBlocker(); }};Copy the code

The mCallbacks object comes from the PMS, so the following two callback methods are executed in the PMS:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

@Override
public void onProximityPositive(a) {
    synchronized (mLock) {
        // Indicates that an approach event is received
        mProximityPositive = true;    
        mDirty |= DIRTY_PROXIMITY_POSITIVE;
        // Update the global Power statusupdatePowerStateLocked(); }}@Override
public void onProximityNegative(a) {
    synchronized (mLock) {
        // Indicates that the away event was received
        mProximityPositive = false;   
        mDirty |= DIRTY_PROXIMITY_POSITIVE;
        // Update user activity time
        userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
                PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);   
        // Update the global Power statusupdatePowerStateLocked(); }}Copy the code

When the status of the PSensor in the DisplayPowerController changes, the PMS is notified to update the mProximityPositive, and the user activity time is also updated if the event is far away. What does this value do? Found in the following isBeingKeptAwakeLocked() method:

private boolean isBeingKeptAwakeLocked(a) {
    returnmStayOn || mProximityPositive || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) ! =0|| (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) ! =0
            || mScreenBrightnessBoostInProgress;
}
Copy the code

This method is analyzed in Android R PowerManagerService module (4). If any condition is true, the screen is not automatically extinguished. Therefore, after the PSensor receives a screen off event, the PMS will remain in the wake state. Even when the automatic sleep time reaches, the automatic screen off process will not be performed.

4. To summarize

From the whole analysis, compared with the normal on-off screen, there are several characteristics:

  • PSensor on and off screen, just modify the display status and brightness, PMS will keep the system awake.
  • If the PSensor screen is on and off, it does not report the screen status to the PhoneWindowManger.
  • After the PSensor goes off, the PMS does not automatically go off. After receiving a remote event, the PMS updates the user activity time as an event.

The sequence diagram of the above process is as follows: