当前位置: 首页 > 工具软件 > Proximity > 使用案例 >

Android 8.1 DisplayPowerController(二) Proximity Sensor的亮灭屏

墨安阳
2023-12-01

概述

P-Sensor亮屏和按Power键亮屏流程还是有些不同之处,如开始调用位置、PowerManagerService中的流程等,由于在平常遇到过许多PSensor亮屏相关Bug,因此这里独立地进行下分析。

流程

在DisplayPowerController中,实例化了一个SensorEventListener对PSensor事件进行监听:

private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (mProximitySensorEnabled) {
            //获取当前系统时间
            final long time = SystemClock.uptimeMillis();
            //获取distance值,表示距离
            final float distance = event.values[0];
            //如果distance>=0.0f
            boolean positive = distance >= 0.0f && distance < mProximityThreshold;
            //处理PSensor上报事件
            handleProximitySensorEvent(time, positive);
        }
    }

SensorEventListener是一个用于接收Sensor新数据的接口,只有两个方法:

  • onSensorChanged(SensorEvent event):当有新Sensor事件时进行回调;
  • onAccuracyChanged(Sensor, int accuracy):Sensor注册的精度值发生改变时调用;

在DisplayPowerController中,重写了onSensorChanged()方法,因此,在接收到PSensor新事件时,会回调该方法。在这个方法中可以看到,只有mProximitySensorEnabled这个全局变量为true时,才会进入该方法体,这个值表示PSensor是否可用,由屏幕状态和PMS中的请求参数useProximitySensor共同决定,只有满足useProximitySensor=true && state!=Display.STATE_OFF时,该值才会为true,而useProximitySensor=true的条件是当前系统持有PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK类型的锁。

现在进入方法体中,它记录了当前系统时间,并根据SensorEvent判断是否是靠近还是远离后,将time和distance作为参数传给了handleProximitySensorEvent()方法,该方法如下:

private void handleProximitySensorEvent(long time, boolean positive) {
    //PSensor是否可用,由setProximitySensorEnabled()方法根据PMS中请求的参数
//useProximitySensor和STATE_OFF决定
    if (mProximitySensorEnabled) {
        //mPendingProximity表示"将要进行的PSensor状态",初始值为
    // PROXIMITY_UNKNOWN,PROXIMITY_NEGATIVE&&positive=false都表示远离,
    // 所以没有值变化,直接返回,下同
        if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {
            return; // no change
        }
        if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
            return; // no change
        }
        //取消延迟处理Psensor事件的Handler
        mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
        //如果是靠近
        if (positive) {
            //设置mPendingProximity 值,PROXIMITY_POSITIVE表示靠近设备
            mPendingProximity = PROXIMITY_POSITIVE;
            //设置PSensor去抖动时间(或转换延迟时间),会申请一个Display锁
            // 靠近时立即灭屏
            setPendingProximityDebounceTime(
                    time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY); 
        } else {
            mPendingProximity = PROXIMITY_NEGATIVE;
            //设置去抖动时间,远离时会稍等会再亮屏
            setPendingProximityDebounceTime(
                    time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY); 
        }
        // Debounce the new sensor reading.
        //读取新Psensor数据
        debounceProximitySensor();
    }
}

在这个方法中,如果PSensor数据没有改变,则直接返回。如果上报值发生了改变,则首先设置mPendingProximity,表示“将要、打算进行的PSensor事件”,如果是靠近则设置该变量值为PROXIMITY_POSITIVE,远离则设置该变量值为PROXIMITY_NEGATIVE,同时调用setPendingProximityDebounceTime()方法,该方法用来设置PSensor的转换延迟时间,并会通过DisplayPowerCallback向PMS中申请一个Display锁,如果是靠近,延迟时间为0,也即不会进行延迟;如果是远离,默认延迟时间为250。该方法如下:

private void setPendingProximityDebounceTime(long debounceTime) {
    if (mPendingProximityDebounceTime < 0) {
        //申请一个PowerManagerService.Display的SuspendBlocker锁
        mCallbacks.acquireSuspendBlocker(); // acquire wake lock
    }
    //PSensor转换延迟时间
    mPendingProximityDebounceTime = debounceTime;
}

当设置完毕mPendingProximity和mPendingProximityDebounceTime后,会调用debounceProximitySensor()方法,读取新的PSensor数据,该方法如下:

private void debounceProximitySensor() {
    if (mProximitySensorEnabled
            && mPendingProximity != PROXIMITY_UNKNOWN
            && mPendingProximityDebounceTime >= 0) {
        //获取当前系统时间
        final long now = SystemClock.uptimeMillis();
        //如果mPendingProximityDebounceTime <= now,说明是靠近事件或者延迟事件到达,
        //直接进行下一步处理
        if (mPendingProximityDebounceTime <= now) {
            // Sensor reading accepted.  Apply the change then release the wake lock.
            //mProximity表示反应给系统的PSensor事件值,这里进行赋值表示接受Sensor读取值
            mProximity = mPendingProximity;
            //更新状态的核心方法
            updatePowerState();
            //更新完成后,清除延迟时间、释放Display锁
            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.
            //如果mPendingProximityDebounceTime > now,说明有延迟,是远离事件,
            //通过Handler延迟处理事件,到达延迟时间后会重新调用该方法
            Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
        }
    }
}

在这个方法中,首先会通过系统当前时间和mPendingProximityDebounceTime进行比较,这个值在setPendingProximityDebounceTime()中设置,前面已经说过。如果当前时间大于等于后者,说明没有延迟,则直接调用下一步逻辑;反之则说明有延迟,通过Handler异步处理延迟,并在到达延迟后会重新调用该方法,进行下一步逻辑的调用,先看看延迟转换的处理:

@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
        .......
        case MSG_PROXIMITY_SENSOR_DEBOUNCED:
            debounceProximitySensor();
            break;

因此,PSensor事件不管是延迟转换还是不进行延迟,都会进入该方法的if语句中执行相同的逻辑。
在进入if语句后,首先是将mPendingProximity值设置给mProximity,mProximity表示最终系统的PSensor事件状态值,所以可以理解为接受PSensor新的事件状态值。然后调用updatePowerState()方法开始更新屏幕状态,最后当upatePowerState()方法执行完毕后,调用clearPendingProximityDebounceTime()方法,该方法会重置PSensor转换延迟时间、释放Display锁,如下:

private void clearPendingProximityDebounceTime() {
    if (mPendingProximityDebounceTime >= 0) {
        //重置PSensor转换时间
        mPendingProximityDebounceTime = -1;
        //释放Display锁
        mCallbacks.releaseSuspendBlocker(); // release wake lock
    }
}

现在我们开机进入updatePowerState()方法中进行分析,这个方法在第一篇中已经分析了,更详细的分析请见第一篇内容.这里主要来看PSensor相关的部分:

private void updatePowerState() {
    //....................................
    // Apply the proximity sensor.
    if (mProximitySensor != null) {
        //如果mPowerRequest.useProximitySensor=true&&Display状态不等于灭屏状态
        if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
            //设置Psensor可用
            setProximitySensorEnabled(true);
            if (!mScreenOffBecauseOfProximity
                    && mProximity == PROXIMITY_POSITIVE) {
            //该值表示是否由PSensor灭屏
                mScreenOffBecauseOfProximity = true;
            //通过DisplayPowerCallback回调PMS
                sendOnProximityPositiveWithWakelock();
            }
    //如果释放PSensor锁时带有PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY标记,则该值为true
        } else if (mWaitingForNegativeProximity
                && mScreenOffBecauseOfProximity
                && mProximity == PROXIMITY_POSITIVE
                && state != Display.STATE_OFF) {
            setProximitySensorEnabled(true);
        } else {
            //设置PSensor不可用
            setProximitySensorEnabled(false);
            mWaitingForNegativeProximity = false;
        }
    //如果满足说明此时PSensor处理远离事件,重置mScreenOffBecauseOfProximity为false
        if (mScreenOffBecauseOfProximity
                && mProximity != PROXIMITY_POSITIVE) {
            mScreenOffBecauseOfProximity = false;
            sendOnProximityNegativeWithWakelock();
        }
    } else {
        mWaitingForNegativeProximity = false;
    }
//由PSensor灭屏为true,则将state置位DISPLAY.STATE_OFF
    if (mScreenOffBecauseOfProximity) {
        state = Display.STATE_OFF;
    }
    /*-------------PSensor设置 end--------------*/
    //获取屏幕状态,此时还未设置新的屏幕状态,因此是”旧”的
    final int oldState = mPowerState.getScreenState();
    //在这个方法中会进行屏幕状态、亮度的设置和处理亮灭屏动画
    animateScreenStateChange(state, performScreenOffTransition);
    //获取屏幕状态,此时已经设置新的屏幕状态
    state = mPowerState.getScreenState();
    // ........................
}

现在来分析一下updatePowerState()方法的以上逻辑。对于以上逻辑,我们分别对PSensor灭屏和PSensor亮屏逻辑分开进行分析。

1.PSensor灭屏

先看看下PSensor灭屏的逻辑,如果PMS请求时携带的useProximitySensor 值为true,表示此时系统持有PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK锁,同时如果当前屏幕状态不为灭屏状态(Display.STATE_OFF),则:

  • 1.通过setProximitySensorEnabled()方法将PSensor设置为enable状态,即可用状态;
  • 2.将mScreenOffBecauseOfProximity值设置为true,表示由PSensor灭屏,并将请求屏幕状态值设置为Display.STATE_OFF;
  • 3.通过sendOnProximityPositiveWithWakelock()方法回调PMS中,通知PMS在DIC中进行了PSensor灭屏。

setProximitySensorEnabled()方法如下:

private void setProximitySensorEnabled(boolean enable) {
    if (enable) {
        if (!mProximitySensorEnabled) {
            // Register the listener.
            // Proximity sensor state already cleared initially.
            mProximitySensorEnabled = true;//将PSensor设置为可用状态
            //注册PSensor事件接收器
            mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
                    SensorManager.SENSOR_DELAY_FASTEST, mHandler);
        }
    } else {//设置PSensor不可用状态
        if (mProximitySensorEnabled) {
            // Unregister the listener.
            // Clear the proximity sensor state for next time.
            mProximitySensorEnabled = false;//将PSensor设置为不可用状态
            mProximity = PROXIMITY_UNKNOWN;//重置mProximity值为初始值
            mPendingProximity = PROXIMITY_UNKNOWN;//重置mPendingProximity值为初始值
            mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
            //解除PSensor事件接收器
            mSensorManager.unregisterListener(mProximitySensorListener);
            clearPendingProximityDebounceTime(); // 清除去抖动时间,释放Display锁
        }
    }
}

setProximitySensorEnabled()中,根据传入参数决定PSensor是否可用,同时注册或解除PSensor事件接收器,这个接收器就是刚开始分析时监听PSensor上报值的Listener,实际上,这部分逻辑应该在接受PSensor上报值之前,因为只有注册了接受器,才能接收上报值,但便于流程分析,就放在这里分析了。
sendOnProximityPositiveWithWakelock()方法如下:

private void sendOnProximityPositiveWithWakelock() {
    //回调到PMS中,申请Display锁
    mCallbacks.acquireSuspendBlocker();
    mHandler.post(mOnProximityPositiveRunnable);
}
private final Runnable mOnProximityPositiveRunnable = new Runnable() {
    @Override
    public void run() {
        //将PMS中mProximityPositive值置为true
        mCallbacks.onProximityPositive();
        //释放Display锁
        mCallbacks.releaseSuspendBlocker();
    }
};

在以上方法中,mCallbacks对象就是PMS中mDisplayPowerCallbacks对象,因此这里会回调到PMS中去,在PMS中回调的三个方法如下:

@Override
public void acquireSuspendBlocker() {
    mDisplaySuspendBlocker.acquire();
}
@Override
public void releaseSuspendBlocker() {
    mDisplaySuspendBlocker.release();
}
@Override
public void onProximityPositive() {
    synchronized (mLock) {
        mProximityPositive = true;
        mDirty |= DIRTY_PROXIMITY_POSITIVE;
        updatePowerStateLocked();
    }
}

前两个方法用来申请、释放Display锁,onProximityPositive()方法中内容稍后分析。
回到DPC中,当执行完以上流程后,调用animateScreenStateChange()设置屏幕状态、背光值,除一点外:
当执行进入到setScreenState()后:

    private boolean setScreenState(int state, boolean reportOnly) {
        //......
        final boolean isOff = (state == Display.STATE_OFF);
            if (isOff && !mScreenOffBecauseOfProximity) {
                    setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);
            }
        //......
    }

也就是说,PSensor灭屏,不会设置mReportedScreenStateToPolicy值,所以PSensor灭屏后,这个值依旧为亮屏时的REPORTED_TO_POLICY_SCREEN_ON(2),所以不会有blockScreenOff()unblockScreenOff()的调用。

之后的流程就和Power键灭屏流程相同了,这里就不再分析,请参考DisplayPowerController第一篇分析。

PSensor灭屏时PMS中做了什么?

在上面我们分析到了PMS中的回调,现在继续看看回调onProximityPositive()时,更细节的内容。
onProximityPositive()方法中,首先会将mProximityPositive置为true,然后置位mDirty,最后调用PMS核心updatePowerStateLocked()方法。需要注意的是,在调用updatePowerStateLocked()方法时,由于mDirty值不满足条件,这里不会执行其中的几个updatexxx()方法,只会执行最后一个updateSuspendBlockerLocked()方法,我们只看以下部分:

private boolean needDisplaySuspendBlockerLocked() {
//请求Policy为亮屏
    if (mDisplayPowerRequest.isBrightOrDim()) {
    //以下条件任意一个为false,则表示需要Display锁,因此不会进入Suspend状态
        if (!mDisplayPowerRequest.useProximitySensor || !mProximityPositive
                || !mSuspendWhenScreenOffDueToProximityConfig) {
            return true;
        }
    }
    return false;
}

以上方法逻辑很简单,其中三个条件任意一个为false,则该方法将返回true,不会释放Display锁。
mSuspendWhenScreenOffDueToProximityConfig表示当由PSensor灭屏后,是否可以进入Suspend状态,该值从配置文件中获取:

mSuspendWhenScreenOffDueToProximityConfig = resources.getBoolean(
        com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity);

因此如果该值配置为true,则表示当PSensor灭屏后可以进入Suspend状态,实现原因就是这部分逻辑。
再来看看mProximityPositive这个值的用处,除了上面判断是否需要Display锁之外,还有一个地方:

private boolean isBeingKeptAwakeLocked() {
    return mStayOn
            || mProximityPositive
            || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0
            || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
                    | USER_ACTIVITY_SCREEN_DIM)) != 0
            || mScreenBrightnessBoostInProgress;
}

这个方法在分析PMS时分析了,判断是否需要保持唤醒状态,只要任意一个条件为true,则系统就不能进入休眠中状态。
因此,当PSensor灭屏后,不会存在超时进入GoToSleep的逻辑。

2.PSensor亮屏

现在继续回到DPC中,来看看PSensor亮屏时的updatePowerState()方法。在调用到updatePowerState()方法之前,PSensor事件接收器接收到远离事件上报值,将mProximity值置为PROXIMITY_NEGATIVE,然后调用到updatePowerState()后,根据条件判断,则:

  • 1.将mScreenOffBecauseOfProximity值置为false;
  • 2.通过sendOnProximityNegativeWithWakelock()方法回调PMS中,通知PMS在DIC中进行了PSensor亮屏。

此时PMS中请求的屏幕状态依旧为Display.STATE_ON,接下来的步骤和原来的步骤相同,除一点外:
当执行进入到setScreenState()后:

isOff = (state == Display.STATE_OFF) = false;
mReportedScreenStateToPolicy = REPORTED_TO_POLICY_SCREEN_ON;

所以不满足任何一个条件,因此,不会有blockScreenOn()blockScreenOn(),也就是说,PSensor的亮屏不需要WindowManger中的相关回调。

剩下的流程和Power键等亮屏流程完全一样。

这里再来看下sendOnProximityNegativeWithWakelock()方法:

private void sendOnProximityNegativeWithWakelock() {
    mCallbacks.acquireSuspendBlocker();
    mHandler.post(mOnProximityNegativeRunnable);
}
private final Runnable mOnProximityNegativeRunnable = new Runnable() {
    @Override
    public void run() {
        mCallbacks.onProximityNegative();
        mCallbacks.releaseSuspendBlocker();
    }
};

这个方法中还是回调到PMS中的逻辑,其中释放锁和申请锁的已经分析过,再来看看onProximityNegative()方法:

@Override
public void onProximityNegative() {
    synchronized (mLock) {
        //将mProximityPositive 置为false
        mProximityPositive = false;
        mDirty |= DIRTY_PROXIMITY_POSITIVE;
        //更新用户活动时间
        userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
                PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
        updatePowerStateLocked();
    }
}

在这个方法中,将mProximityPositive置为false,同时更新用户活动时间,然后执行updatePowerStateLocked()方法。

总结

分析到这里,整个PSensor亮灭屏的流程就总结完毕,并且从整个分析来看,PSensor亮灭屏,仅仅是修改屏幕状态的背光值,不会像Power键灭屏一样,使系统进入休眠状态。

然而,正是这个原因,在PSensor灭屏后,如果按Power键,是不会亮屏的,这有时候会带来一些用户体验问题,现在市面上好多手机,都会这块有修改,即在PSensor灭屏后,可以通过Power键亮屏,即PSensor灭屏也走休眠流程,这样做的好处显而易见;但是这样一来,好像违反了PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK锁的设计初衷了(该锁表示如果由PSensor导致的灭屏,那么是不会进入休眠状态的, 被视为是持续的用户活动。)

常见问题

Q:PSensor亮灭屏和其他亮灭屏区别?
  • 1.PSensor亮灭屏不存在Wakeup和goToSleep流程;
  • 2.PSensor亮灭屏不需要WindowManager中的回调(unblockScreenOn).
Q:PSensor灭屏后,到达设置休眠时间后会不会进入休眠?

不会,原因如下:

private boolean isBeingKeptAwakeLocked() {
    return mStayOn
            || mProximityPositive //为true,该方法返回true
            || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0
            || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
                    | USER_ACTIVITY_SCREEN_DIM)) != 0
            || mScreenBrightnessBoostInProgress;
}
Q:PSensor灭屏后按Power键,为何不会亮屏?

通过对整个DisplayPowerController的流程分析,在通话过程中,对于PSensor灭屏,只会修改其屏幕状态和背光,因此,如果在PSensor灭屏后,第一次按Power键,此时PMS中向Display请求状态时,mWakefulness值为sleep,即要进行睡眠,系统执行goToSleep流程。由于屏幕状态已经为STATE_OFF,背光值为0,因此只会做setScreenState()之前的工作,如调用WindowManager.setScreenTurnedOff,setScreenTruningOff()等,完成真正意义上的灭屏。
而当goToSleep时,PMS中会忽略掉PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK类型的WakeLock锁:

if (mWakefulness == WAKEFULNESS_ASLEEP) {
                    mWakeLockSummary &= ~WAKE_LOCK_PROXIMITY_SCREEN_OFF;
 }

因此,当进入DisplayPowerController后,mPowerRequest.useProximitySensor将为false,结合updatePowerState()中的判断条件,此时PSensor将会被解除注册,之后将收不到PSensor消息。

接着,我们第二次按Power键,此时系统将执行wakeUp流程,当进入DisplayPowerController后,mPowerRequest.useProximitySensor将为true,结合updatePowerState()中的判断条件,此时PSensor将会被重新注册,由于用户操作未远离设备,故将立即上报PSensor靠近时间,从而由将屏幕设置为灭屏状态,之后如果抬起PSensor,将才会亮屏。

此外,如果由PSensor灭屏后,当释放PSensor的WakeLock时,携带flag值PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY,此时按Power键也不会立即唤醒,这种情况下DisplayPowerController中的参数mWaitingForNegativeProximity为true,之后的执行逻辑和上面分析的差不多。

Q:如何修改使得在PSensor灭屏后,可以被Power键亮屏,在Power键灭屏后,可以被PSensor亮屏?

暂无

 类似资料: