本文主要跟踪分析通过按松power键来唤醒,熄灭屏幕的逻辑。下面是一些相关类的介绍

PowerManagerService.java:简称PMS,负责Andorid系统中电源管理方面的工作。作为系统核心服务之一,主要处理系统中与power相关的计算,然后决策系统该如何反应。同时PowerManagerService与其他服务及HAL层等都有交互关系,协调power如何与系统其他模块的交互,比如没有用户活动时屏幕变暗等。

DisplayPowerController.java:简称DPC管理Display设备的电源状态。仅在PowerManagerService中实例化了一个对象,它算是PowerManagerService的一部分,只不过是独立出来了而已。主要处理和距离传感器,

灯光传感器,以及包括关屏在内的一些动画,通过异步回调的方式来通知PowerManagerService某些事情发生了变化。

DisplayPowerState.java:简称DPS,在本质上类似于View,只不过用来描述一个display的属性,当这些属性发生变化时,可以通过一个序列化的命令,让这些和display电源状态的属性一起产生变化。

这个类的对象只能被DispalyPowerController的Looper持有。而这个Looper应该就是PowerManagerService中新建的一个HandlerThread中的Looper。和PowerManager相关的,

包括DisplayPowerState和DisplayPowerController相关的消息处理应该都可以通过这个HandlerThread进行运转的

Notifier.java:将power状态的重要变化,通过广播发送出去,并且参与了与AMS,WMS,IMP的交互。

ColorFade.java:负责屏幕由关到开,由开到关的一些GL动画,由DPC进行控制。

AutomaticBrightnessController.java:主要处理光传感器,将底层上传的参数进行处理计算,将计算的新的亮度值传给DPC。

RampAnimator.java:处理屏幕亮度渐变动画。

  1. 代码流程分析

    1. Power事件上报跟踪

正常我们按下power键后,出触发Kernel关于该事件的中断。然后inputReader通过EventHub获取该事件,并做出事件处理,之后由inputdispatch进行事件分发。

因为power事件是key事件,会调用interceptKeyBeforeQueueing进行处理,通过如果流程

InputDispatcher.cpp

->com_android_server_input_InputManagerService.cpp

->InputManagerService.java

->InputMonitor.java

->WindowManagerPolicy.java

->PhoneWindowManager.java

  1. PhoneWindowManager.java

最终由PhoneWindowManager.java的interceptKeyBeforeQueueing方法对该次事件进行处理,对power键以及屏幕状态进行判断,来决定亮屏还是灭屏等操作。当需要亮屏时,会调用PowerMangerService中的wakeup函数进行处理。

主要代码如下:

首先定义以下几个布尔值,主要是按键属性和屏幕状态

//是否亮屏状态,代码是否可以与用户交互

final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;

//按键事件是否为down事件

final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;

//事件是否被取消

final boolean canceled = event.isCanceled();

//按键的keycode,不同按键 keycode不同,一般power 116

final int keyCode = event.getKeyCode();

//是否是输入事件

final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;

//锁屏界面状态

final boolean keyguardActive = (mKeyguardDelegate == null ? false :(interactive ?isKeyguardShowingAndNotOccluded() :mKeyguardDelegate.isShowing()));

//flags有wake标记,或者按键(isWakeKey函数)为KEYCODE_BACK, KEYCODE_MENU, KEYCODE_WAKEUP, KEYCODE_PAIRING, KEYCODE_STEM_1, KEYCODE_STEM_2, KEYCODE_STEM_3设置isWakeKey为true

boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0 || event.isWakeKey();

if (isValidGlobalKey(keyCode)//有效的全局按键 (KEYCODE_POWER:KEYCODE_WAKEUP:KeyEvent.KEYCODE_SLEEP:),

&& mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) {//在com.android.internal.R.xml.global_keys 有定义

if (isWakeKey) {

wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");唤醒屏幕

}

return result;

}

接下来是对特殊按键的处理,这里只关注power键

switch (keyCode) {

case KeyEvent.KEYCODE_POWER: {

result &= ~ACTION_PASS_TO_USER;

isWakeKey = false; // wake-up will be handled separately

if (down) {

interceptPowerKeyDown(event, interactive);//处理power按下

} else {

interceptPowerKeyUp(event, interactive, canceled);//处理power松开

}

break;

}

power down 键的处理

private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {

// 获取wakelock,报错CPU唤醒状态

if (!mPowerKeyWakeLock.isHeld()) {

mPowerKeyWakeLock.acquire();

}

// 取消多次按下超时监测

if (mPowerKeyPressCounter != 0) {

mHandler.removeMessages(MSG_POWER_DELAYED_PRESS);

}

// Detect user pressing the power button in panic when an application has

// taken over the whole screen.

boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive,

SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags),

isNavBarEmpty(mLastSystemUiFlags));

if (panic) {

mHandler.post(mHiddenNavPanic);

}

// Latch power key state to detect screenshot chord.

if (interactive && !mScreenshotChordPowerKeyTriggered

&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {

mScreenshotChordPowerKeyTriggered = true;

mScreenshotChordPowerKeyTime = event.getDownTime();

interceptScreenshotChord();//屏幕截屏

interceptDisableTouchModeChord();  //添加口袋模式

}

// 当power键按下,停止电话响铃或者结束通话

TelecomManager telecomManager = getTelecommService();

boolean hungUp = false;

if (telecomManager != null) {

if (telecomManager.isRinging()) {

停止响铃

telecomManager.silenceRinger();

} else if ((mIncallPowerBehavior

& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0

&& telecomManager.isInCall() && interactive) {

Setting数据库读取设置,如果开了挂断电话

hungUp = telecomManager.endCall();

}

}

SprdGestureLauncherService gestureService = LocalServices.getService(

SprdGestureLauncherService.class);

boolean gesturedServiceIntercepted = false;

if (gestureService != null) {

gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive,

mTmpBoolean);

if (mTmpBoolean.value && mGoingToSleep) {

mCameraGestureTriggeredDuringGoingToSleep = true;

}

}

// If the power key has still not yet been handled, then detect short

// 如果power键还没有被处理,判断是短按,多按,长按等场景并做出对应处理

mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered

|| mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted;

if (!mPowerKeyHandled) {

if (interactive) {

//如果屏幕是亮的,长按动作处理对应的长按动作

if (hasLongPressOnPowerBehavior()) {

Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);

msg.setAsynchronous(true);

mHandler.sendMessageDelayed(msg,

ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());

}

} else {//屏幕休眠的状态,唤醒屏幕

wakeUpFromPowerKey(event.getDownTime());

if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {

Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);

msg.setAsynchronous(true);

mHandler.sendMessageDelayed(msg,//如果长按动作继续执行长按操作

ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());

mBeganFromNonInteractive = true;

} else {

final int maxCount = getMaxMultiPressPowerCount();

if (maxCount <= 1) {

mPowerKeyHandled = true;

} else {

mBeganFromNonInteractive = true;

......

power up 键的处理

private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {

final boolean handled = canceled || mPowerKeyHandled; //power键是否被处理了

mScreenshotChordPowerKeyTriggered = false;

cancelPendingScreenshotChordAction();

cancelPendingPowerKeyAction();

if (!handled) {

// power press 加1

mPowerKeyPressCounter += 1;

final int maxCount = getMaxMultiPressPowerCount();//多按的次数

final long eventTime = event.getDownTime();//press power 时间

if (mPowerKeyPressCounter < maxCount) {

// 这种情况可能是一个多次按键事件,等待一会做确认处理

Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS,

interactive ? 1 : 0, mPowerKeyPressCounter, eventTime);

msg.setAsynchronous(true);

mHandler.sendMessageDelayed(msg, ViewConfiguration.getDoubleTapTimeout());

return;

}

// 没有其他action,继续处理power按下事件

powerPress(eventTime, interactive, mPowerKeyPressCounter);

}

// Done.  Reset our state.

finishPowerKeyPress();//power按键处理结束

}

private void finishPowerKeyPress() {

mBeganFromNonInteractive = false;

mPowerKeyPressCounter = 0;

if (mPowerKeyWakeLock.isHeld()) {

mPowerKeyWakeLock.release();//释放wakelock

}

}

下面继续跟踪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) {//按两次power事件

powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);

} else if (count == 3) {//按三次power事件

powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);

} else if (interactive && !mBeganFromNonInteractive) {//屏幕如果亮着而且mBeganFromNonInteractive为false会执行下面操作

switch (mShortPressOnPowerBehavior) {//根据配置om.android.internal.R.integer.config_shortPressOnPowerBehavior 决定对应值 此处为1

case SHORT_PRESS_POWER_NOTHING:

break;

case SHORT_PRESS_POWER_GO_TO_SLEEP:

mPowerManager.goToSleep(eventTime,

PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);//执行灭屏操作

break;

......

}

我们继续跟踪亮屏操作,从power down事件处理可知,调用wakeUpFromPowerKey点亮屏幕

private void wakeUpFromPowerKey(long eventTime) {

Slog.d(TAG, "wake Up From Power Key"); // 此处会打印这句log,可作为分析问题是注意点

wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, "android.policy:POWER");//调用wakeup继续执行亮屏操作,注意调用mPowerManager.wakeUp操作

}

  1. PowerManager.wakeUp

下面我们看下PowerManager.wakeUp的相关处理

public void wakeUp(long time) {

try {

mService.wakeUp(time, "wakeUp", mContext.getOpPackageName());//调用到PWM.wakeUpInternal->PWM.wakeUpNoUpdateLocked

} catch (RemoteException e) {

throw e.rethrowFromSystemServer();

}

}

当power接收到亮灭屏调用后,会先进行设置手机wakefullness状态. 之后发送亮灭屏广播通知其他应用手机处于亮屏还是灭屏状态。

private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid,

String opPackageName, int opUid) {

//mWakefulness标识系统当前状态共有四种定义:

WAKEFULNESS_ASLEEP:表示系统当前处于休眠状态,只能被wakeUp()调用唤醒。

WAKEFULNESS_AWAKE:表示系统目前处于正常运行状态。

WAKEFULNESS_DREAMING:表示系统当前正处于互动屏保的状态。

WAKEFULNESS_DOZING:表示系统正处于“doze”状态

if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE

|| !mBootCompleted || !mSystemReady) {//按键时间在睡下去之前,屏幕是正常状态,没有完成开机流程,系统没有准备完成,这四种情况不继续执行唤醒流程

return false;

}

try {//系统不是点亮状态,打出相关log

switch (mWakefulness) {

case WAKEFULNESS_ASLEEP:

case WAKEFULNESS_DREAMING:

case WAKEFULNESS_DOZING:

Slog.i(TAG, "Waking up from dozing due to"+opPackageName+" "+reason+" (uid " + reasonUid +")...");

break;

}

mLastWakeTime = eventTime;//更新wake时间

setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);//更新mWakefulness,并调用Notifier.onWakefulnessChangeStarted发送亮屏广播

mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid);//调用Notifier通知battery处理

userActivityNoUpdateLocked(eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);//更新最后一次用户事件的时间

} finally {

}

return true;

}

  1. Notifier.java

然后主要就是在Notifier.java中与AMS,window,input进行交互,通知各模块手机状态发生了改变,根据屏幕状态各自进行处理, 最后发送亮灭屏广播, 通知相关的模块.

private void setWakefulnessLocked(int wakefulness, int reason) {

if (mWakefulness != wakefulness) {

mWakefulness = wakefulness;//更新mWakefulness变量

mWakefulnessChanging = true;

mDirty |= DIRTY_WAKEFULNESS;

mNotifier.onWakefulnessChangeStarted(wakefulness, reason);//调用Notify的函数onWakefulnessChangeStarted 发广播通知

}

}

public void onWakefulnessChangeStarted(final int wakefulness, int reason) {

//获取交互模式变量,亮屏为true, 灭屏为false

final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);

通知AMS wakefulness状态的变化

mHandler.post(new Runnable() {

@Override

public void run() {

mActivityManagerInternal.onWakefulnessChanged(wakefulness);

}

});

//interactive 变化时才会执行里面的逻辑

if (mInteractive != interactive) {

// Finish up late behaviors if needed.

if (mInteractiveChanging) {

handleLateInteractiveChange();

}

//在input中记录现在的屏幕状态

mInputManagerInternal.setInteractive(interactive);

mInputMethodManagerInternal.setInteractive(interactive);

//唤醒battery状态

try {

mBatteryStats.noteInteractive(interactive);

} catch (RemoteException ex) { }

// Handle early behaviors.

mInteractive = interactive;

mInteractiveChangeReason = reason;

mInteractiveChanging = true;

handleEarlyInteractiveChange();//处理交互模式改变

}

}

private void handleEarlyInteractiveChange() {

synchronized (mLock) {

if (mInteractive) {//mInteractive 为true代表要执行亮屏逻辑

// Waking up...

mHandler.post(new Runnable() {

@Override

public void run() {

// Note a SCREEN tron event is logged in PowerManagerService.

mPolicy.startedWakingUp();//调用PhoneWindowManager 方法开始亮屏流程

}

});

// 更新亮屏广播

mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;

mPendingWakeUpBroadcast = true;

updatePendingBroadcastLocked();//mSuspendBlocker.acquire();持有partial 锁

} else {//mInteractive 为false代表要执行灭屏逻辑

final int why = translateOffReason(mInteractiveChangeReason);

mHandler.post(new Runnable() {

@Override

public void run() {

mPolicy.startedGoingToSleep(why);//调用PhoneWindowManager 方法开始灭屏流程

}

});

}

}

}

  1. 广播更新的流程

调用updatePendingBroadcastLocked函数发出handle msg 消息

Message msg = mHandler.obtainMessage(MSG_BROADCAST);

msg.setAsynchronous(true);

mHandler.sendMessage(msg);

然后handle收到后执行sendNextBroadcast函数

case MSG_BROADCAST:

sendNextBroadcast();

break;

private void sendNextBroadcast() {

final int powerState;

//mBroadcastedInteractiveState 这个变量记录当前的系统亮灭屏情况

synchronized (mLock) {

if (mBroadcastedInteractiveState == INTERACTIVE_STATE_UNKNOWN) {

// 当前状态位置更新为唤醒状态

mPendingWakeUpBroadcast = false;

mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;

} else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) {

// 当前是唤醒状态,更新为灭屏状态

if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast

|| mPendingInteractiveState == INTERACTIVE_STATE_ASLEEP) {

mPendingGoToSleepBroadcast = false;

mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP;

} else {

finishPendingBroadcastLocked();//释放partial 锁

return;

}

} else {

// 当前是灭屏状态,更新为唤醒状态

if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast

|| mPendingInteractiveState == INTERACTIVE_STATE_AWAKE) {

mPendingWakeUpBroadcast = false;

mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;

} else {

finishPendingBroadcastLocked();

return;

}

}

//记录下当前系统时间为广播开始时间

mBroadcastStartTime = SystemClock.uptimeMillis();

powerState = mBroadcastedInteractiveState;

}

EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);//打出event log

if (powerState == INTERACTIVE_STATE_AWAKE) {//根据powerState的值决定发亮屏还是灭屏广播

sendWakeUpBroadcast();

} else {

sendGoToSleepBroadcast();

}

}

广播都是前台广播,然后其他模块收到广播后作出对应处理

mScreenOnIntent.addFlags(

Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND

| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);

mScreenOffIntent.addFlags(

Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND

| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);

Notify 收到这个广播后会,继续调用sendNextBroadcast,因为mPendingWakeUpBroadcast值变化,会调用finishPendingBroadcastLocked();释放partial 锁

private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() {

@Override

public void onReceive(Context context, Intent intent) {

EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1,

SystemClock.uptimeMillis() - mBroadcastStartTime, 1);

sendNextBroadcast();

}

};

总结:当power接收到亮灭屏调用后,会先进行设置手机wakefullness状态,之后发送亮灭屏广播通知其他模块手机处于亮屏还是灭屏状态。并且在发送广播过程中power也与AMS,window,input进行交互,通知各模块手机状态发生了改变,根据屏幕状态各自进行处理。其中发送亮灭屏广播的主要实现在Notifier.java中。

当wakefulness状态发生改变,AMS收到通知。如果亮屏操作,AMS就会通过函数comeOutOfSleepIfNeedLocked调用到ActivityStackSupervisor中,将sleep超时消息移除,最后将在栈顶的activity显示出来。

power是通过WindowManagerPolicy与PhoneWindowManager进行交互,当屏幕在唤醒时需要通知window进行更新手势监听,更新方向监听,更新锁屏超时时间。最后通知keyguard屏幕点亮了,进行刷新锁屏界面时间,之后通过回调接口,回调回PhoneWindowManager的onShown函数,通知window keyguard准备完成。

当亮屏时通过InputManagerService将当前屏幕状态传入JNI中进行记录,当再次发生power键事件可以方便确认该事件是需要亮屏还是灭屏。

  1. Power状态的更新和处理

当广播处理完毕后就会调用PMS的内部函数updatePowerStateLocked来更新全局电源状态。其实在PMS中很多函数都只是对一些必须的属性进行赋值,大部分最终都会调用到updatePowerStateLocked函数中进行功能执行,其中更新电源状态主要依据就是变量mDirty。mDirty就是用来记录power state的变化的标记位,这样的状态变化在系统中一共定义了12个,每一个状态对应一个固定的数字,都是2的倍数。这样,若有多个状态一块变化,进行按位取或这样结果既是唯一的,又能准确标记出各个状态的变化。在updatePowerStateLocked中主要做了如下事情:

  • 首先判断手机是否处于充电状态,如果标记位DIRTY_BATTERY_STATE发生改变时就证明电池状态发生了改变,然后通过对比判断(通过电池前后变化与充电状态的变化),确认手机是否处于充电状态。如果手机处于充电状态会将表示充电的标记位记入mDirty中。当有USB插拔时我们也可以通过配置信息来决定是否需要点亮屏幕。并且是否在充电或者充电方式发生改变,系统都会认为发生了一次用户事件进行更新最后用户操作时间,以此来重新计算屏幕超时时间。

private void updateIsPoweredLocked(int dirty) {

if ((dirty & DIRTY_BATTERY_STATE) != 0) {

final boolean wasPowered = mIsPowered;//是否在充电

final int oldPlugType = mPlugType;//充电类型

final boolean oldLevelLow = mBatteryLevelLow;//低电模式

mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);

mPlugType = mBatteryManagerInternal.getPlugType();

mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();

mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();

if (wasPowered != mIsPowered || oldPlugType != mPlugType) {

mDirty |= DIRTY_IS_POWERED;//如果充电中设置mDirty

/* < Bug#696466 optimization for sceenoff */

if (wasPowered != mIsPowered)

SystemProperties.set("sys.sprd.power.ispowered", (mIsPowered? "1": "0"));//设置属性sys.sprd.power.ispowered

/* Bug#696466 optimization for sceenoff > */

//无线充电

final boolean dockedOnWirelessCharger = mWirelessChargerDetector.update(

mIsPowered, mPlugType, mBatteryLevel);

final long now = SystemClock.uptimeMillis();

if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,

dockedOnWirelessCharger)) {//插拔USB是否要点亮屏幕

wakeUpNoUpdateLocked(now, "android.server.power:POWER", Process.SYSTEM_UID,

mContext.getOpPackageName(), Process.SYSTEM_UID);

}

userActivityNoUpdateLocked(//插拔USB算一次用户事件,重新设置最后一次用户事件的时间点

now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);

// Tell the notifier whether wireless charging has started so that

// it can provide feedback to the user.

if (dockedOnWirelessCharger) {

mNotifier.onWirelessChargingStarted();

}

}

if (wasPowered != mIsPowered || oldLevelLow != mBatteryLevelLow) {//低电模式

if (oldLevelLow != mBatteryLevelLow && !mBatteryLevelLow) {

mAutoLowPowerModeSnoozing = false;

}

updateLowPowerModeLocked();//更新低电模式

}

}

}

  • 更新wakefulness

for (;;) {

int dirtyPhase1 = mDirty;

dirtyPhase2 |= dirtyPhase1;

mDirty = 0;

updateWakeLockSummaryLocked(dirtyPhase1);

updateUserActivitySummaryLocked(now, dirtyPhase1);

if (!updateWakefulnessLocked(dirtyPhase1)) {

break;

}

}

updateWakeLockSummaryLocked函数将wakeLock的类型用mWakeLockSummary进行记录,最后与Wakefulness状态结合重新算出新的mWakeLockSummary值,再判断是否需要睡眠时会使用。

之后updateUserActivitySummaryLocked就会更新屏幕超时时间,根据最后一次的用户事件与屏幕超时时间与dim持续时间来计算屏幕超时的时间,然后与现在的时间进行对比,来决定屏幕要继续高亮,还是要变为dim状态。

PowerManagerHandler接收屏幕超时的消息, 并且调用handleUserActivityTimeout进行处理, 该函数之后就在Handler线程中运行

private final class PowerManagerHandler extends Handler {

public PowerManagerHandler(Looper looper) {

super(looper, null, true /*async*/);

}

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

case MSG_USER_ACTIVITY_TIMEOUT:

handleUserActivityTimeout();    //处理用户超时事件

break;

case MSG_SANDMAN:

handleSandman();

break;

case MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT:

handleScreenBrightnessBoostTimeout();

break;

}

}

}

private void handleUserActivityTimeout() { // runs on handler thread

synchronized (mLock) {

if (DEBUG_SPEW) {

Slog.d(TAG, "handleUserActivityTimeout");

}

mDirty |= DIRTY_USER_ACTIVITY;  //设置有用户活动的mDirty值

updatePowerStateLocked();   //更新电源状态, 最后去判断是否要睡眠

}

}

根据前面流程图可以看出更新wakefulness过程是通过一个死循环来执行的,只有调用函数updateWakefulnessLocked返回false时才会跳出循环。在循环中对wakeLockSummary进行更新,并且更新自动灭屏时间后,进行判断系统是否该睡眠了,是否可以跳出循环。

在updateWakefulnessLocked中主要根据是否存在wakeLock,用户活动进行判断设备是否需要进入睡眠状态。从函数isBeingKeptAwakeLocked可以看出当device拿着一个wake lock,有用户事件,有距离传感器等都不会灭屏进行睡眠状态。如果需要睡眠就会往下面调用,最后跳出循环。

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) {

if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {   //mWakefulness为AWAKE, 并且到了睡觉时间, 就去睡觉

if (DEBUG_SPEW) {

Slog.d(TAG, "updateWakefulnessLocked: Bed time...");

}

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;

}

[java] view plain copy

private boolean isItBedTimeYetLocked() {// 对所有该唤醒的情况取反, 就是该休眠了

return mBootCompleted && !isBeingKeptAwakeLocked();

}

private boolean isBeingKeptAwakeLocked() {

return mStayOn  //设置了stay on

|| mProximityPositive  //距离传感器返回一个positive结果,保持唤醒

|| (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0  //当有wake lock时保持唤醒

|| (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT

| USER_ACTIVITY_SCREEN_DIM)) != 0   //有user activity时保持唤醒

|| mScreenBrightnessBoostInProgress;

}

[java] view plain copy

mDreamsActivateOnSleepSetting = (Settings.Secure.getIntForUser(resolver,

Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,

mDreamsActivatedOnSleepByDefaultConfig ? 1 : 0,

UserHandle.USER_CURRENT) != 0);     //从settings数据库获取对应值

mDreamsActivateOnDockSetting = (Settings.Secure.getIntForUser(resolver,

Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,

mDreamsActivatedOnDockByDefaultConfig ? 1 : 0,

UserHandle.USER_CURRENT) != 0);

private boolean shouldNapAtBedTimeLocked() {  //当返回true, 设备自动nap

return mDreamsActivateOnSleepSetting

|| (mDreamsActivateOnDockSetting

&& mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED);

}

[java] view plain copy

private boolean napNoUpdateLocked(long eventTime, int uid) {

if (DEBUG_SPEW) {

Slog.d(TAG, "napNoUpdateLocked: eventTime=" + eventTime + ", uid=" + uid);

}

if (eventTime < mLastWakeTime || mWakefulness != WAKEFULNESS_AWAKE

|| !mBootCompleted || !mSystemReady) {

return false;

}

Trace.traceBegin(Trace.TRACE_TAG_POWER, "nap");

try {

Slog.i(TAG, "Nap time (uid " + uid +")...");

mSandmanSummoned = true;

setWakefulnessLocked(WAKEFULNESS_DREAMING, 0);  //设置WAKEFULNESS_DREAMING

} finally {

Trace.traceEnd(Trace.TRACE_TAG_POWER);

}

return true;

}

调用goToSleepNoUpdateLocked进行睡眠, 当按power键灭屏是也会调用该函数.

[java] view plain copy

private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {

if (DEBUG_SPEW) {

Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime

+ ", reason=" + reason + ", flags=" + flags + ", uid=" + uid);

}

if (eventTime < mLastWakeTime

|| mWakefulness == WAKEFULNESS_ASLEEP

|| mWakefulness == WAKEFULNESS_DOZING

|| !mBootCompleted || !mSystemReady) {

return false;   //判断设备是否应该睡眠

}

Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep");

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;

default:

Slog.i(TAG, "Going to sleep by application request (uid " + uid +")...");

reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION;

break;

}

mLastSleepTime = eventTime;

mSandmanSummoned = true;

setWakefulnessLocked(WAKEFULNESS_DOZING, reason);

// Report the number of wake locks that will be cleared by going to sleep.

int numWakeLocksCleared = 0;

final int numWakeLocks = mWakeLocks.size();

for (int i = 0; i < numWakeLocks; i++) {  //遍历所有的wakeLocks, 将FULL, BRIGHT, DIM Locks,计入numWakeLocksCleared中

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;

}

}

EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared);

// Skip dozing if requested.

if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {

reallyGoToSleepNoUpdateLocked(eventTime, uid); //如果没有doze流程,直接设置WAKEFULNESS_ASLEEP

}

} finally {

Trace.traceEnd(Trace.TRACE_TAG_POWER);

}

return true;   //返回true

}

当第一个for循环中将所有的状态都设置好了, 并且此时也没有重要的mDirty发生变化, 在下一次循环中mDirty的值为0, updateWakefulnessLocked返回false,就会跳出循环.

当跳出循环之后在函数updateDisplayPowerStateLocked中进行获取需要请求的设备电源状态是亮还是灭或者dim,判断是否开启了自动调节亮度开关,是否使用了距离传感器,并经过一系列的计算获取亮度值等,最终都记录到DisplayPowerRequest中,经过DMS传入DPC中,进行处理。

在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerRequest中传入DisplayPowerController中进一步处理。在亮屏过程中DisplayPowerController会根据传过来的数值来设置新的电源状态为亮屏,然后调用DisplayPowerState来对状态进行设置。由于此时ColorFade level(就是在手机屏幕的一层surface,当level为0是为一层黑帧,level为1.0时为透明)的值为0,表示屏幕还没有绘制好,所以此时需要block screen直到window界面绘制完成。当需要亮屏时调用PhoneWindowManager的screenTurningOn函数,通知window屏幕就要点亮了。然后调用WMS中函数waitForAllWindowsDrawn函数等待将所有需要绘制的window绘制完成后回调回来,超时时间为1000ms。在WMS中获取需要绘制的window将其加入mWaitingForDrawn中等待绘制,通过检查mWaitingForDrawn是否为空来判断,window是否绘制完成。此时screenTurningOn函数就执行完了,剩下的就是等待windows绘制完成。

Android 8.0 手机亮灭屏相关推荐

  1. Android 系统(40)--Android7.0 PowerManagerService亮灭屏分析(一)

    Android7.0 PowerManagerService亮灭屏分析(一) 可以导致手机亮灭屏的因素有多种,而在本文中主要讲解按power键亮灭屏过程以及来电亮屏.在亮灭屏过程power中主要的实现 ...

  2. Android 系统(42)---Android7.0 PowerManagerService亮灭屏分析(三)

    Android7.0 PowerManagerService亮灭屏分析(三) 在前面两部分已经对绘制windows与设置设备状态进行了详细讲解. 之后接着就该对亮度值进行设置, 实现亮屏动作了. 在D ...

  3. Android 系统(41)---Android7.0 PowerManagerService亮灭屏分析(二)

    Android7.0 PowerManagerService亮灭屏分析(二) 3029 在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerReque ...

  4. Android7.0 PowerManagerService亮灭屏分析(一)

    绪论 可以导致手机亮灭屏的因素有多种,而在本文中主要讲解按power键亮灭屏过程以及来电亮屏.在亮灭屏过程power中主要的实现类与功能如下所述: PowerManagerService.java:以 ...

  5. Android7.0 PowerManagerService亮灭屏分析(三)

    在前面两部分已经对绘制windows与设置设备状态进行了详细讲解. 之后接着就该对亮度值进行设置, 实现亮屏动作了. 在DisplayPowerController中的animateScreenBri ...

  6. Android7.0 PowerManagerService亮灭屏分析(二)

    在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerRequest中传入DisplayPowerController中进一步处理.在亮屏过程中Disp ...

  7. Android 知识点 109 —— Android7.0 PowerManagerService 之亮灭屏

    原文地址: https://www.cnblogs.com/dyufei/p/8017604.html 写的太好了,粘过来! 本篇从按下power按键后,按键事件从InputManagerServic ...

  8. Android7.0 PowerManagerService 之亮灭屏(二) PMS 电源状态管理updatePowerStateLocked()...

    本篇注意接着上篇[Android7.0 PowerManagerService 之亮灭屏(一)]继续分析量灭屏的流程,这篇主要分析PMS的状态计算和更新流程,也是PMS中最为重要和复杂的一部分电源状态 ...

  9. 结合源码探讨Android距离传感器亮灭屏机制

    结合源码探讨Android距离传感器亮灭屏机制 本文的分析是基于Android 5.0.0 Google原生代码的. Android中的距离传感器,也就是P-sensors(Proximity Sen ...

最新文章

  1. oracle 创建用户、授权、表空间
  2. jsp上传文件名乱码
  3. 五行代码快速使用python的turtle库绘画
  4. 如何实现Oracle数据库之间的数据同步?
  5. HTML+CSS+JS实现 ❤️简单的翻纸牌记忆游戏❤️
  6. java如何获得集合的长度_Java集合长度
  7. android id 重名_android - 解决“应用自定义权限重名”
  8. PLSQL Developer中test window的使用
  9. matlab查找指定文件夹下文件(附汉字和标点符号读取方法)
  10. 桶排序,冒泡排序,快速排序三者比较(例子说名)
  11. Android Studio3.5 JAVA调用C++源码方法总结
  12. FortiClient cannot establish caused by TLS version
  13. 【Duet display】Mac分屏神器
  14. 金蝶KIS应用虚拟化集成解决方案
  15. 大型电商数据库设计与分析
  16. 战略管理与资本运作案例剖析
  17. 通过关键词获取微博内容
  18. python中set集合的使用
  19. 深度学习——keras教程系列基础知识
  20. 使用计算机的快捷键有哪些,电脑操作的快捷键有哪些,都是什么作用?

热门文章

  1. antd mobile在微信公众号开发中使用笔记
  2. 部门平均工资mysql_按部门统计各工资级别的人数、平均工资。
  3. numpy 5 study task
  4. Javaweb中上传图片,获取相对路径,绝对路径
  5. UDP是全双工通信的吗
  6. 【DockerCE】RHEL 7.9完整安装DockerCE 20.10.5的包集合
  7. 连接redis集群报错: no reachable node in cluster
  8. XSSFWorkbook 设置单元格样式_CVA高校精英计划第二弹:执行最佳操作,做好设置准备...
  9. jboot 增加llog4j日志
  10. HTML怎么设置图片和文字间距离,div字间距-div内文字之间间距设置方法