作者:FightFightFight
来源:CSDN
原文:https://blog.csdn.net/FightFightFight/article/details/81460803
概述
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亮屏?
暂无