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的工作流程解释),对此,本章就不再介绍了,希望读者能通过直接阅读源码加深理解。