参考地址:https://www.jianshu.com/p/9241f3a91095

本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:

1.前言
2.Power键灭屏
3.超时灭屏
4.PSensor灭屏

PowerManagerService 之前系列文章请参考如下
1. PowerManagerService分析(一)之PMS启动
2. PowerManagerService分析(二)之updatePowerStateLocked()核心
3. PowerManagerService分析(三)之WakeLock机制
4. Android手机亮屏流程分析

前言

在之前的PMS文章分析中知道,PMS中定义了四种屏幕状态:

  • Awake状态:表示唤醒状态
  • Dream状态:表示处于屏保状态
  • Doze状态:表示处于Doze状态
  • Asleep状态:表示处于休眠状态

Power键灭屏

power键灭屏时,会在PhoneWindowManager中处理按键事件后,调用到PMSgotoSleep()进行灭屏处理,下面直接看看PhoneWindowManger中对Power键灭屏的处理以及和PMS的交互。

在按power后,PWS中如下:

case KeyEvent.KEYCODE_POWER: {.......if (down) {//按下时//处理按下事件interceptPowerKeyDown(event, interactive);} else //抬起时//处理抬起事件interceptPowerKeyUp(event, interactive, canceled);}break;
}

在处理PowerinterceptPowerKeyUp抬起事件时,开始了灭屏流程:

    private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
    .......if (!handled) {// No other actions.  Handle it immediately.开始灭屏流程powerPress(eventTime, interactive, mPowerKeyPressCounter);}// Done.  Reset our state.finishPowerKeyPress();}

powerPress灭屏流程

private void powerPress(long eventTime, boolean interactive, int count) {if (mScreenOnEarly && !mScreenOnFully) {Slog.i(TAG, "Suppressed redundant power key press while "+ "already in the process of turning the screen on.");return;}if (count == 2) {......} else if (interactive && !mBeganFromNonInteractive) {switch (mShortPressOnPowerBehavior) {//灭屏case SHORT_PRESS_POWER_GO_TO_SLEEP:goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);break;//灭屏,直接跳过Doze状态case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);break;} else {shortPressPowerGoHome();}break;}}}
}

在这里调用了goToSleep()方法,该方法如下:

private void goToSleep(long eventTime, int reason, int flags) {mRequestedOrGoingToSleep = true;mPowerManager.goToSleep(eventTime, reason, flags);
}

最终,PhoneWindowManager中调用了PowerManagergoToSleep()方法来灭屏。

现在我们进入到PowerManager.goToSleep()方法:

public void goToSleep(long time, int reason, int flags) {try {mService.goToSleep(time, reason, flags);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}

可以看到,在PowerManger中开始向下调用到了PoweManagerService(以下简称PMS)中的goToSleep()中。
我们进入PMS中,就需要详细分析其中的方法了,先来看看goToSleep()方法:

/*** @param eventTime 时间* @param reason 原因,Power键灭屏则是PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON* @param flags 目前只有两个值:0和1(GO_TO_SLEEP_FLAG_NO_DOZE)*/
@Override // Binder call
public void goToSleep(long eventTime, int reason, int flags) {if (eventTime > SystemClock.uptimeMillis()) {throw new IllegalArgumentException("event time must not be in the future");}//检查权限mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);final int uid = Binder.getCallingUid();final long ident = Binder.clearCallingIdentity();try {//调用gotToSleepInternalgoToSleepInternal(eventTime, reason, flags, uid);} finally {Binder.restoreCallingIdentity(ident);}
}

这个方法的参数和PowerManager,PhoneWindowManager中的同名方法对应,需要注意的是第二个参数和第三个参数;
第二个参数:表示灭屏原因,在PowerManager中定义了一些常量值来表示;
第三个参数:是一个标识,用来表示是否直接进入灭屏,一般的灭屏流程,都会先进入Doze状态,然后才会进入Sleep状态,如果将flag设置为1,则将会直接进入Sleep状态,这部分会在下文中逐渐分析到。

goToSleep()方法中,检查权限之后,开始调用了goToSleepInternal()方法,该方法如下:

private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {synchronized (mLock) {if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {updatePowerStateLocked();}}
}

这个方法逻辑很简单,首先是调用了goToSleepNoUpdateLocked()方法,并根据该方法返回值来决定是否调用updatePowerStateLocked()方法。

一般来说,goToSleepNoUpdateLocked()都会返回true,现在看看该方法:

@SuppressWarnings("deprecation")
private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {
if (eventTime < mLastWakeTime|| mWakefulness == WAKEFULNESS_ASLEEP|| mWakefulness == WAKEFULNESS_DOZING|| !mBootCompleted || !mSystemReady) {return false;}try {switch (reason) {case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:Slog.i(TAG, "Going to sleep due to device administration policy "+ "(uid " + uid +")...");break;case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")...");break;case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH:Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")...");break;case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")...");break;case PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON:Slog.i(TAG, "Going to sleep due to sleep button (uid " + uid +")...");break;case PowerManager.GO_TO_SLEEP_REASON_HDMI:Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")...");break;case PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY:Slog.i(TAG, "Going to sleep by an accessibility service request (uid "+ uid +")...");break;default:Slog.i(TAG, "Going to sleep by application request (uid " + uid +")...");reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION;break;}//标记最后一次灭屏时间mLastSleepTime = eventTime;//用于判定是否进入屏保mSandmanSummoned = true;//设置wakefulness值为WAKEFULNESS_DOZING,因此先进Doze状态setWakefulnessLocked(WAKEFULNESS_DOZING, reason);// Report the number of wake locks that will be cleared by going to sleep.//灭屏时,将清除以下三种使得屏幕保持亮屏的wakelock锁,numWakeLocksCleared统计下个数int numWakeLocksCleared = 0;final int numWakeLocks = mWakeLocks.size();for (int i = 0; i < numWakeLocks; i++) {final WakeLock wakeLock = mWakeLocks.get(i);switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {case PowerManager.FULL_WAKE_LOCK:case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:case PowerManager.SCREEN_DIM_WAKE_LOCK:numWakeLocksCleared += 1;break;}}// Skip dozing if requested.//如果带有PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE的flag,则直接进入Sleep状态,不再进入Doze状态if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {//该方法才会真正地进入睡眠reallyGoToSleepNoUpdateLocked(eventTime, uid);}} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}return true;
}

在这个方法中:
首先,是判断调用该方法的原因并打印log,该log在日常分析问题时非常有用;
然后,通过setWakefulnessLocked()将当前wakefulness设置为Doze状态;
最后,通过flag判断,如果flag为1,则调用reallyGoToSleepNoUpdateLocked()方法直接进入Sleep状态。
因此,系统其他模块在调用PM.goToSleep()灭屏时,在除指定flag为PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE的情况外,都会首先进入Doze,再由Doze进入Sleep。

setWakefulnessLocked()方法用来设置wakefulness值,同时将会调用Notifierwakefulness相关的逻辑,这部分在之前的流程分析中也分析过,这里再来看下:

@VisibleForTesting
void setWakefulnessLocked(int wakefulness, int reason) {if (mWakefulness != wakefulness) {//设置mWakefulnessmWakefulness = wakefulness;mWakefulnessChanging = true;mDirty |= DIRTY_WAKEFULNESS;if (mNotifier != null) {//调用Notifier中的方法,做wakefulness改变开始时的工作mNotifier.onWakefulnessChangeStarted(wakefulness, reason);}}
}

我们跟着执行流程来进行分析,NotifierPMS模块中用于进行“通知”的一个组件类,比如发送亮灭屏广播就是它来负责,具体详细的分析请点击这里 查看。这里针对于灭屏场景,再来看下其中的逻辑:

public void onWakefulnessChangeStarted(final int wakefulness, int reason) {//由于wakefulness为Doze,故interactive为falsefinal boolean interactive = PowerManagerInternal.isInteractive(wakefulness);// ............................................// Handle any early interactive state changes.// Finish pending incomplete ones from a previous cycle.if (mInteractive != interactive) {// Finish up late behaviors if needed.if (mInteractiveChanging) {handleLateInteractiveChange();}// Handle early behaviors.mInteractive = interactive;mInteractiveChangeReason = reason;mInteractiveChanging = true;//处理早期工作handleEarlyInteractiveChange();}
}

在这个方法中,首先根据wakefulness值判断了系统当前的交互状态,如果是处于Awake状态和Dream状态,则表示可交互;如果处于DozeAsleep状态,则表示不可交互;
由于在setWakefulnessLocked()中已经设置了wakefulness为DOZE状态,因此此时处于不可交互状态,接下来开始执行handleEarlyInteractiveChange()方法:

private void handleEarlyInteractiveChange() {synchronized (mLock) {//此时为falseif (mInteractive) {// Waking up...mHandler.post(new Runnable() {@Overridepublic void run() {// Note a SCREEN tron event is logged in PowerManagerService.mPolicy.startedWakingUp();}});// Send interactive broadcast.mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;mPendingWakeUpBroadcast = true;updatePendingBroadcastLocked();} else {final int why = translateOffReason(mInteractiveChangeReason);mHandler.post(new Runnable() {@Overridepublic void run() {//通过PhoneWindowManager设置锁屏mPolicy.startedGoingToSleep(why);}});}}
}

在这个方法中,将调用mPolicy.startedGoingToSleep(why)进行锁屏流程(Keyguard的绘制)。

回到PMS中,在处理完setWakefulnessLocked()方法后,由于没有PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE,所以不会立即执行reallyGoToSleepNoUpdateLocked()方法,此时goToSleepNoUpdateLocked()方法完毕并返回true。

之后开始执行updatePowerStateLocked()方法了,这个方法对于熟悉PMS模块的人来说再熟悉不过了,它是整个PMS的核心,详细的分析请点击这里 , 在这里我们只看其灭屏时的一些处理。

updatePowerStateLocked()方法中,和灭屏直接相关的有如下部分:

// 更新屏幕状态
boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
//更新屏保信息
updateDreamLocked(dirtyPhase2, displayBecameReady);
// 收尾工作
finishWakefulnessChangeIfNeededLocked();
//释放锁
updateSuspendBlockerLocked();
  • updateDisplayPowerStateLocked()将会向DisplayPowerController请求新的屏幕状态,完成屏幕的更新;

  • updateDreamLocked()方法用来更新屏保信息,除此之外还有一个任务
    调用reallyGoToSleep()方法进入休眠,即由DOZE状态进入Sleep状态。

  • finishWakefulnessChangeIfNeededLocked()方法用来做最后的收尾工作,当然,在这里会调用到Notifier中进行收尾。

  • updateSuspendBlockerLocked()方法将用来更新SuspendBlocker锁,会根据当前的WakeLock类型以及屏幕状态来决定是否需要申请SuspendBlocker锁。

updateDreamLocked()中更新屏保状态时,如果此时处于Doze状态且没有进行屏保,则将调用reallyGoToSleepNoUpdateLocked()方法,将wakefulness值设置为了Sleep,部分代码如下:

else if (wakefulness == WAKEFULNESS_DOZING) {if (isDreaming) {return; // continue dozing}// Doze has ended or will be stopped.  Update the power state.reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);updatePowerStateLocked();}

再来看看该方法:

private boolean reallyGoToSleepNoUpdateLocked(long eventTime, int uid) {if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP|| !mBootCompleted || !mSystemReady) {return false;}try {//设置为ASLEEP状态setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}return true;}
private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {if (eventTime < mLastSleepTime || eventTime < mLastWakeTime|| !mBootCompleted || !mSystemReady) {return false;}mNotifier.onUserActivity(event, uid);           if (mUserInactiveOverrideFromWindowManager) {mUserInactiveOverrideFromWindowManager = false;mOverriddenTimeout = -1;}//如果wakefulness为Asleep或Doze,不再计算超时时间,直接返回if (mWakefulness == WAKEFULNESS_ASLEEP|| mWakefulness == WAKEFULNESS_DOZING|| (flags & PowerManager.USER_ACTIVITY_FLAG_INDIRECT) != 0) {return false;}//如果带有该flag,则会小亮一会儿再灭屏if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {if (eventTime > mLastUserActivityTimeNoChangeLights&& eventTime > mLastUserActivityTime) {//将当前时间赋值给mLastUserActivityTimeNoChangeLightsmLastUserActivityTimeNoChangeLights = eventTime;mDirty |= DIRTY_USER_ACTIVITY;if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {mDirty |= DIRTY_QUIESCENT;}
                return true;}} else {if (eventTime > mLastUserActivityTime) {//将当前时间赋值给mLastUserActivityTimemLastUserActivityTime = eventTime;mDirty |= DIRTY_USER_ACTIVITY;if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {mDirty |= DIRTY_QUIESCENT;}return true;}}} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}return false;
}

以上就是整个Power键灭屏PMS部分的流程,其时序图如下:

超时灭屏

经过上面的分析,我们知道了Power键灭屏PhoneWindowManager发起了goToSleep,现在来看看超时灭屏是如何实现的。

超时灭屏主要有两个影响因素休眠时间用户活动。休眠时间在Settings中进行设置,用户活动是指当手机处于亮屏状态,都会调用userActivityNoUpdateLocked()方法去更新用户活动时间。接下来我们就从userActivityNoUpdateLocked()方法开始分析其超时灭屏的流程。

首先来看该方法:

其中updateWakeLockSummaryLocked()用来统计WakeLock,这里就不分析该方法了,详细的分析请点击这里,现在从updateUserActivitySummaryLocked()方法开始分析,该方法如下:

 private void updateUserActivitySummaryLocked(long now, int dirty) {// Update the status of the user activity timeout timer.if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY| DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);long nextTimeout = 0;if (mWakefulness == WAKEFULNESS_AWAKE|| mWakefulness == WAKEFULNESS_DREAMING private void updateUserActivitySummaryLocked(long now, int dirty) {// Update the status of the user activity timeout timer.if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY| DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);

private void updateUserActivitySummaryLocked(long now, int dirty) {
// Update the status of the user activity timeout timer.
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY
| DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {
mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);

                || mWakefulness == WAKEFULNESS_DOZING) {//获取睡眠时长,为Settings.Secure.SLEEP_TIMEOUT的值和最小休眠时间的最大值,Settings.Secure.SLEEP_TIMEOUT一般为-1,//表示禁用,因此该值默认为-1final int sleepTimeout = getSleepTimeoutLocked();//获取休眠时长,在Settings中设置的值final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);//获取Dim时长,由休眠时长剩Dim百分比得到final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);//用户活动是否由Window覆盖final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;//该值用来统计用户活动状态,每次进入该方法,置为0mUserActivitySummary = 0;//上次用户活动时间>=上次唤醒时间if (mLastUserActivityTime >= mLastWakeTime) {//下次超时时间为上次用户活动时间+休眠时间-Dim时间,到达这个时间后,将进入Dim状态nextTimeout = mLastUserActivityTime+ screenOffTimeout - screenDimDuration;//如果当前时间<nextTimeout,则此时处于亮屏状态,标记mUserActivitySummary为USER_ACTIVITY_SCREEN_BRIGHTif (now < nextTimeout) {mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;} else {//如果当前时间>nextTimeout,此时有两种情况,要么进入Dim要么进入Sleep//将上次用户活动时间+灭屏时间赋值给nextTimeout,如果该值大于当前时间,则说明此时应该处于Dim状态//因此将标记mUserActivitySummary为USER_ACTIVITY_SCREEN_DIMnextTimeout = mLastUserActivityTime + screenOffTimeout;if (now < nextTimeout) {mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;}}}//判断和USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS标记相关,如果带有此标记,才会进入该ifif (mUserActivitySummary == 0&& mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {//下次超时时间=上次用户活动时间+灭屏时间nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;//根据当前时间和nextTimeout设置mUserActivitySummaryif (now < nextTimeout) {if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT|| mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) {mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;} else if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;}}}//不满足以上条件时,此时mUserActivitySummary为0,这种情况应该为当mUserActivitySummary经历了USER_ACTIVITY_SCREEN_BRIGHT//和USER_ACTIVITY_SCREEN_DIM之后才会执行到这里if (mUserActivitySummary == 0) {if (sleepTimeout >= 0) {//获取上次用户活动时间的最后一次时间final long anyUserActivity = Math.max(mLastUserActivityTime,mLastUserActivityTimeNoChangeLights);if (anyUserActivity >= mLastWakeTime) {nextTimeout = anyUserActivity + sleepTimeout;//将mUserActivitySummary值置为USER_ACTIVITY_SCREEN_DREAM,表示屏保if (now < nextTimeout) {mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;}}} else {//将mUserActivitySummary值置为USER_ACTIVITY_SCREEN_DREAM,表示屏保mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;nextTimeout = -1;}}if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) {if ((mUserActivitySummary &(USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) {// Device is being kept awake by recent user activityif (nextTimeout >= now && mOverriddenTimeout == -1) {// Save when the next timeout would have occurredmOverriddenTimeout = nextTimeout;}}mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;nextTimeout = -1;}if (mUserActivitySummary != 0 && nextTimeout >= 0) {//发送一个异步Handler定时消息Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, nextTimeout);}} else {//当wakefulness=Sleep的时候,直接将mUserActivitySummary置为0mUserActivitySummary = 0;}}
}

该方法用来更新用户活动状态,其中细节在代码中都进行了注释,该方法中来看,通过Handler多次再此进入updatePowerStateLocked()从而调用updateUserActivitySummaryLocked()方法,直到nextTime=-1mUserActivitySummary=0时将不再发送Handler,从而完成了mUserActivitySummary的更新。根据流程来看,当设备从亮屏到休眠时间到达灭屏,mUserActivitySummary的值的变化应为:
USER_ACTIVITY_SCREEN_BRIGHT—>USER_ACTIVITY_SCREEN_DIM—>USER_ACTIVITY_SCREEN_DREAM—>0.
Handler的调用处理逻辑如下:

        @Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_USER_ACTIVITY_TIMEOUT:handleUserActivityTimeout();break;}}
private void handleUserActivityTimeout() { // runs on handler threadsynchronized (mLock) {mDirty |= DIRTY_USER_ACTIVITY;updatePowerStateLocked();}
}

当执行到这个方法后,现在就统计得到了mWakeLockSummary和mUserActivitySummary的值,现在我们看下一个方法——updateWakefulnessLocked(),在for循环中,会根据该方法返回值来决定是否进行循环,为何会如此设计呢?在分析完该方法后,就会有答案了,如下:

    private boolean updateWakefulnessLocked(int dirty) {boolean changed = false;if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED| DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE| DIRTY_DOCK_STATE)) != 0) {//isItBedTimeYetLocked()判断是否需要"睡觉"了if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {final long time = SystemClock.uptimeMillis();if (shouldNapAtBedTimeLocked()) {//进入屏保changed = napNoUpdateLocked(time, Process.SYSTEM_UID);} else {//开始休眠changed = goToSleepNoUpdateLocked(time,PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);}}}return changed;}

这个方法中可以看到,首先根据isItBedTimeYetLocked()和mWakefulness来决定是否执行,然后根据shouldNapAtBedTimeLocked()决定进入屏保还是休眠。
该方法如果返回值为true,则说明此时屏幕状态发生改变(在goToSleepNoUpdateLocked()napNoUpdateLocked()中会分别设置mWakefulness为DREAM和ASLEEP),因此将不会跳出for循环,再次进行一次循环。这就是为何会设置一个死循环的目的,同时也说明只有超时灭屏才会循环两次,其他情况下都会只执行一次for循环就退出。

回到该方法中,我们继续看看isItBedTimeYetLocked():

    private boolean isItBedTimeYetLocked() {return mBootCompleted && !isBeingKeptAwakeLocked();}
private boolean isBeingKeptAwakeLocked() {return mStayOn//是否需要保持常亮|| mProximityPositive//PSensor是否靠近|| (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0//当前是否有Wakelock类型为屏幕相关的锁|| (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT| USER_ACTIVITY_SCREEN_DIM)) != 0//当前用户活动状态是否为Draem或者0|| mScreenBrightnessBoostInProgress;//是否处于亮度增强过程中
}

以上代码可以看出,如果有任意一个条件为true,那么就不能进入休眠或者屏保状态,因此只有全部为false时,才会返回false,从而说明需要“睡觉”了。

仔细看这个方法,这里正是mWakeLockSummarymUserActivitySummary的作用体现之一。

在平时分析问题时,如果存在无法超时灭屏问题,就需要查看mWakeLockSummarymUserActivitySummary的值了。前者查看是否存在亮屏锁,后者查看用户活动是否已经处于0了。
现在继续分析updateWakfulnessLocked()方法中的下一个逻辑,当进入if语句后,就开始判断是要进入屏保呢?还是要直接休眠呢?

如果shouldNapAtBedTimeLocked()返回true,则开始屏保,否则直接休眠,这里对于屏保相关就不再分析了,以后的时间中如果有机会,会单独进行分析。

当开始休眠时,直接调用了goToSleepNoUpdateLocked()方法中了,于是开始走休眠流程,之后的逻辑和Power键灭屏一样了。

整个超时灭屏的流程分析就到这里了,从以上流程中可以看到,mWakeLockSummarymUserActivitySummayr的作用相当重要,我之前在android4.4手机上遇到过一个问题就是到达休眠时间后不会灭屏,分析后发现有一个应用申请了一个PowerManager.SCREEN_BRIGHT_WAKE_LOCK锁,该锁导致mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0,从而没有灭屏。
整个超时灭屏流程的时序图如下:

PSensor灭屏

什么是PSensor灭屏呢?
Proximity Sensor,即距离传感器,当通话或微信时,如果脸部靠近屏幕,将会灭屏,这就是通过PSensor灭屏的。
为何会有PSensor灭屏呢?
为了防止脸部误触,有更好的用户体验。

在原生的Android系统中,PSensor灭屏不同于Power键灭屏超时灭屏,前者仅仅是设置屏幕的状态和关闭背光,而后两者在设置屏幕的状态和关闭背光后,让CPU也进入了休眠状态(如果不持有PowerManger.PARTIAL_WAKE_LOCK)。

PSensor灭屏涉及到更多的是DisplayPowerController中的内容,因此,将会在之后的文章中进行分析。

Android 手机灭屏流程分析详解相关推荐

  1. Android手机亮屏流程分析

    极力推荐Android 开发大总结文章:欢迎收藏程序员Android 力荐 ,Android 开发者需要的必备技能 注:文章转于网络,点击查看原文 PowerManagerService 之前系列文章 ...

  2. 常见的亮灭屏流程分析

    一 . 亮屏主要关键log 驱动按键时间 ??-?? ??:??:??.??? <3>[14082.058160]  (0)[70:pmic_thread]kpd: Power Key g ...

  3. Android Keyguard 亮灭屏流程分析

    文章目录 概述 Keyguard 关键文件 Power 灭屏 超时灭屏 Power 亮屏 FAQ 亮屏慢 概述 Keyguard 锁屏流程: Keyguard 加载时机:一个是开机的时候:另一个是灭屏 ...

  4. Android 手机设备信息获取使用详解

    Android 手机是我们常用的工具之一,买手机之前,手机厂商会提供一些手机参数给我们,那么问题来了,我们该如何获取手机上的参数信息呢? 通过本文你讲了解到获取手机常用信息的基本方法. 获取手机基本信 ...

  5. Android 手机按键客制化详解

    在Android 中会有以下5个按键(Back.Home.Menu.Power.Volume)与用户进行交互,Framework 层中实现按键功能,因此,从手机系统定制的角度,可以满足客户的客制化要求 ...

  6. android 亮灭屏流程

    1. frameworks\base\core\java\android\hardware\display\DisplayManagerInternal.java 内部服务 frameworks\ba ...

  7. 荣耀畅玩7C设置语言步骤,荣耀畅玩7C怎么截图?华为荣耀畅玩7C手机截屏方法图文详解...

    3月12日下午,荣耀正式发布了旗下第二款全面屏手机---荣耀畅玩7C,该机主打全面屏和双摄,售价899元起,具有不错的性价比表现,该机已在线上和线下全面发售了,相信大家在使用过程当中难免会进行各种截屏 ...

  8. 详解如何将 Android 手机投屏在 Ubuntu 上

    你知道如何将Android手机投屏到Linux系统吗?本文就以 Scrcpy 软件为例,来讲解一下如何将Android手机投屏到Ubuntu系统. 1 Scrcpy 介绍 首先,我们来认识一下Scrc ...

  9. Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(下)

       本文原创, 转载请注明出处:http://blog.csdn.net/qinjuning 上篇文章<<Android中measure过程.WRAP_CONTENT详解以及xml布局文 ...

最新文章

  1. ECMAScript 2015~2020 语法全解析
  2. 二维数组名作为实参或者形参
  3. 关于php正则表达式得选择题,经典PHP笔试题
  4. pytorch卷积神经网络_使用Pytorch和Matplotlib可视化卷积神经网络的特征
  5. docx文档怎么排列图片_PDF怎么转Word?这几款软件满足你的要求
  6. IE11 0x2ee4 bug 以及类似问题解决方法
  7. r语言8c多字节字符串有错,R 学习笔记《十》 R语言初学者指南--图形工具
  8. 201671010406 词频统计软件项目报告
  9. android自定义加载旋转框
  10. linux中磁盘阵列说明,常用磁盘阵列说明
  11. Unity3D Shader 新手教程(1/6)
  12. SharePoint 2010 Webpart 部署 报错的解决方法
  13. 使用html2canvas实现批量生成条形码
  14. 推荐系统常用数据集介绍
  15. 一文让你了解数据采集
  16. Java引用包的方法
  17. 2021-08-10
  18. 公司裁员不想给补偿,竟然耍这些不要脸的裁员手段..
  19. PDF文件怎么转Word?分享两种转换小技巧
  20. 平板win10 android哪个耗电,Win10充分利用设置提升平板/笔记本电池续航方法

热门文章

  1. 根据HSV颜色空间识别魔方是否还原
  2. 【英语】 英语的重音怎么读
  3. 独家思维导图!让你秒懂李宏毅2020机器学习(二)—— Classification分类
  4. 计算机故障四类,计算机内存出现问题的四大症状
  5. java excel中重复数据 事务处理_Java导出excel时合并同一列中相同内容的行思路详解...
  6. 科学计算机解三角函数方程,用科学计算器解方程 急!!!
  7. NTFS -usnjournal监控
  8. ffmpeg的各种黑科技
  9. 简单MP3的软件架构深度解读
  10. 分别使用正则表达式的子模式编号和子模式命名两种方法匹配ABAC、AABB、ABAB式成语