5.4 userActivity及Power按键处理分析

本节介绍userActivity函数及PMS对Power按键的处理流程。

5.4.1 userActivity分析

前面曾经提到过userActivity的作用,此处举一个例子以加深读者对它的印象。

打开手机,并解锁进入桌面。如果在规定时间内不操作手机,那么屏幕将变暗,最后关闭。在此过程中,如果触动屏幕,屏幕又会重新变亮。这个触动屏幕的操作将导致userActivity函数被调用。

在上述例子中实际上包含了两方面的内容:

不操作手机,屏幕将变暗,最后关闭。在PMS中,这是一个状态切换的过程。

操作手机,将触发userActivity,此后屏幕的状态将重置。

来看以下代码:

[—>PowerManagerService.java:userActivity]


public void userActivity(long time, boolean noChangeLights){

……//检查调用进程是否有设置DEVICE_POWER的权限

userActivity(time,-1,noChangeLights, OTHER_EVENT, false);

}


此处将调用另外一个同名函数。注意第三个参数的值OTHER_EVENT。系统一共定义了3种事件,分别是OTHER_EVENT(除按键、触摸屏外的事件)、BUTTON_EVENT(按键事件)和TOUCH_EVENT(触摸屏事件)。它们主要为BatteryStatsService进行电量统计时使用,例如触摸屏事件的耗电量和按键事件的耗电量等。

[—>PowerManagerService.java:userActivity]


private void userActivity(long time, long timeoutOverride,

boolean noChangeLights, int eventType, boolean force){

if(((mPokey&POKE_LOCK_IGNORE_TOUCH_EVENTS)!=0)&&

(eventType==TOUCH_EVENT)){

//mPokey和输入事件的处理策略有关。如果此处的if判断得到满足,则表示忽略TOUCH_EVENT

return;

}

synchronized(mLocks){

if(isScreenTurningOffLocked()){

return;

}

if(mProximitySensorActive&&mProximityWakeLockCount==0)

mProximitySensorActive=false;//控制接近传感器

if(mLastEventTime<=time||force){

mLastEventTime=time;

if((mUserActivityAllowed&&!mProximitySensorActive)||force){

if(eventType==BUTTON_EVENT&&!mUseSoftwareAutoBrightness){

mUserState=(mKeyboardVisible?ALL_BRIGHT:

SCREEN_BUTTON_BRIGHT);

}else{

mUserState|=SCREEN_BRIGHT;//设置用户事件导致的mUserState

}

……//通知BatteryStatsService进行电量统计

mBatteryStats.noteUserActivity(uid, eventType);

//重新计算WakeLock状态

mWakeLockState=mLocks.reactivateScreenLocksLocked();

setPowerState(mUserState|mWakeLockState, noChangeLights,

WindowManagerPolicy.OFF_BECAUSE_OF_USER);

//重新开始屏幕计时

setTimeoutLocked(time, timeoutOverride, SCREEN_BRIGHT);

}

}

}

//mPolicy指向PhoneWindowManager,用于和WindowManagerService交互

if(mPolicy!=null){

mPolicy.userActivity();

}

}


有了前面分析的基础,相信很多读者都会觉得userActivity函数很简单。在前面的代码中,通过setPowerState点亮了屏幕,那么经过一段时间后发生的屏幕状态切换在哪儿进行呢?来看setTimeoutLocked函数的代码:

[—>PowerManagerService.java:setTimeoutLocked]


private void setTimeoutLocked(long now, final long originalTimeoutOverride,

int nextState){

//在本例中,nextState为SCREEN_BRIGHT, originalTimeoutOverride为-1

long timeoutOverride=originalTimeoutOverride;

if(mBootCompleted){

synchronized(mLocks){

long when=0;

if(timeoutOverride<=0){

switch(nextState)

{

case SCREEN_BRIGHT:

when=now+mKeylightDelay;//得到一个超时时间

break;

case SCREEN_DIM:

if(mDimDelay>=0){

when=now+mDimDelay;

break;

}……

case SCREEN_OFF:

synchronized(mLocks){

when=now+mScreenOffDelay;

}

break;

default:

when=now;

break;

}

}……//处理timeoutOverride大于零的情况,无非就是设置状态和超时时间

mHandler.removeCallbacks(mTimeoutTask);

mTimeoutTask.nextState=nextState;

mTimeoutTask.remainingTimeoutOverride=timeoutOverride>0

?(originalTimeoutOverride-timeoutOverride)

:-1;

//抛送一个mTimeoutTask交给mHandler执行,执行时间为when秒后

mHandler.postAtTime(mTimeoutTask, when);

mNextTimeout=when;//调试用

}

}

}


接下来看mTimeOutTask的代码:

[—>PowerManagerService.java:TimeoutTask]


private class TimeoutTask implements Runnable

{

int nextState;

long remainingTimeoutOverride;

public void run()

{

synchronized(mLocks){

if(nextState==-1)return;

mUserState=this.nextState;

//调用setPowerState去真正改变屏幕状态

setPowerState(this.nextState|mWakeLockState);

long now=SystemClock.uptimeMillis();

switch(this.nextState)

{

case SCREEN_BRIGHT:

if(mDimDelay>=0){//设置下一个状态为SCREEN_DIM

setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_DIM);

break;

}

case SCREEN_DIM://设置下一个状态为SCREEN_OFF

setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_OFF);

break;

}……//省略花括号

}


TimeoutTask就是用来切换屏幕状态的,相信不少读者已经在网络上见过一个和PMS屏幕状态切换相关的图(其实就是TimeoutTask的工作流程解释),对此,本章就不再介绍了,希望读者能通过直接阅读源码加深理解。