Android按键消息传播流程(WindowManagerService.java)
主要涉及的文件有:
WindowManagerService.java frameworks\base\services\java\com\android\server\
PhoneWindow.java frameworks\policies\base\phone\com\android\internal\policy\impl
KeyInputQueue.java frameworks\base\services\java\com\android\server
com_android_server_KeyInputQueue.cpp frameworks\base\services\jni
EventHub.cpp frameworks\base\libs\ui
WindowManagerService.java主要有两个线程,一个负责分发按键的InputDisapath Thread,另一个负责从底层读取按键消息InputDeviceRender Thread。
WindowManagerService.java的成员类KeyQ(),负责获取各种按键设备的状态,它继承于KeyInputQueue类。通过线程InputDeviceRender Thread的readEvent对按键消息不停读取,然后调用KeyQ实例化后的processEvent函数告诉该按键是否应该传给上层。接着WindowManagerService通过InputDisPatch Thread在按键消息队列里取出,并进行分发。
由此可知,InputDisapath线程负责分发,InputDeviceRender线程通过jni方式调用android_server_KeyInputQueue_readEvent(),在这里负责转化C++的按键消息为java的格式,android_server_KeyInputQueue_readEvent在EventHub.cpp中获取按键消息。
具体一些细节代码如下:
WindowManagerService中的KeyQ()类,preporcessEvent函数负责对按键进行预处理,主要的事件类型包括EV_KEY(按键事件)、EV_REL(相对值,如鼠标移动,报告相对于最后一次位置的偏移)和EV_ABS(绝对值,如触摸屏)。
[java] view plain copy print ?- @Override
- booleanpreprocessEvent(InputDevicedevice,RawInputEventevent){
- if(mPolicy.preprocessInputEventTq(event)){
- returntrue;
- }
- switch(event.type){
- caseRawInputEvent.EV_KEY:{
- //XXXbeginhack
- if(DEBUG){
- if(event.keycode==KeyEvent.KEYCODE_G){
- if(event.value!=0){
- //Gdown
- mPolicy.screenTurnedOff(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
- }
- returnfalse;
- }
- if(event.keycode==KeyEvent.KEYCODE_D){
- if(event.value!=0){
- //dump();
- }
- returnfalse;
- }
- }
- //XXXendhack
- booleanscreenIsOff=!mPowerManager.isScreenOn();
- booleanscreenIsDim=!mPowerManager.isScreenBright();
- intactions=mPolicy.interceptKeyTq(event,!screenIsOff);/**********按键预处理********//
- if((actions&WindowManagerPolicy.ACTION_GO_TO_SLEEP)!=0){
- mPowerManager.goToSleep(event.when);
- }
- if(screenIsOff){
- event.flags|=WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if(screenIsDim){
- event.flags|=WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- if((actions&WindowManagerPolicy.ACTION_POKE_USER_ACTIVITY)!=0){
- mPowerManager.userActivity(event.when,false,
- LocalPowerManager.BUTTON_EVENT,false);
- }
- if((actions&WindowManagerPolicy.ACTION_PASS_TO_USER)!=0){
- if(event.value!=0&&mPolicy.isAppSwitchKeyTqTiLwLi(event.keycode)){
- filterQueue(this);
- mKeyWaiter.appSwitchComing();
- }
- returntrue;
- }else{
- returnfalse;
- }
- }
- caseRawInputEvent.EV_REL:{
- booleanscreenIsOff=!mPowerManager.isScreenOn();
- booleanscreenIsDim=!mPowerManager.isScreenBright();
- if(screenIsOff){
- if(!mPolicy.isWakeRelMovementTq(event.deviceId,
- device.classes,event)){
- //Slog.i(TAG,"droppingbecausescreenIsOffand!isWakeKey");
- returnfalse;
- }
- event.flags|=WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if(screenIsDim){
- event.flags|=WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- returntrue;
- }
- caseRawInputEvent.EV_ABS:{
- booleanscreenIsOff=!mPowerManager.isScreenOn();
- booleanscreenIsDim=!mPowerManager.isScreenBright();
- if(screenIsOff){
- if(!mPolicy.isWakeAbsMovementTq(event.deviceId,
- device.classes,event)){
- //Slog.i(TAG,"droppingbecausescreenIsOffand!isWakeKey");
- returnfalse;
- }
- event.flags|=WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if(screenIsDim){
- event.flags|=WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- returntrue;
- }
- default:
- returntrue;
- }
- }
preporcessEvent调用了InterceptKeyTQ
PhoneWindowManager.java中的InterceptKeyTQ判断该按键是否应该送给上层,还是在此层进行截取,如待机休眠唤醒则在此层进行截取。
[java] view plain copy print ?- /**{@inheritDoc}*/
- //2.3中名为interceptKeyBeforeQueueing
- publicintinterceptKeyTq(RawInputEventevent,booleanscreenIsOn){
- intresult=ACTION_PASS_TO_USER;
- finalbooleanisWakeKey=isWakeKeyTq(event);
- //Ifscreenisoffthenwetreatthecasewherethekeyguardisopenbuthidden
- //thesameasifitwereopenandinfront.
- //Thiswillpreventanykeysotherthanthepowerbuttonfromwakingthescreen
- //whenthekeyguardishiddenbyanotheractivity.
- finalbooleankeyguardActive=(screenIsOn?
- mKeyguardMediator.isShowingAndNotHidden():
- mKeyguardMediator.isShowing());
- if(false){
- Log.d(TAG,"interceptKeyTqevent="+event+"keycode="+event.keycode
- +"screenIsOn="+screenIsOn+"keyguardActive="+keyguardActive);
- }
- if(keyguardActive){
- if(screenIsOn){
- //whenthescreenison,alwaysgivetheeventtothekeyguard
- result|=ACTION_PASS_TO_USER;
- }else{
- //otherwise,don'tpassittotheuser
- result&=~ACTION_PASS_TO_USER;
- finalbooleanisKeyDown=
- (event.type==RawInputEvent.EV_KEY)&&(event.value!=0);
- if(isWakeKey&&isKeyDown){
- //tellthemediatoraboutawakekey,itmaydecideto
- //turnonthescreendependingonwhetherthekeyis
- //appropriate.
- if(!mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(event.keycode)
- &&(event.keycode==KeyEvent.KEYCODE_VOLUME_DOWN
- ||event.keycode==KeyEvent.KEYCODE_VOLUME_UP)){
- //whenkeyguardisshowingandscreenoff,weneed
- //tohandlethevolumekeyforcallsandmusichere
- if(isInCall()){
- handleVolumeKey(AudioManager.STREAM_VOICE_CALL,event.keycode);
- }elseif(isMusicActive()){
- handleVolumeKey(AudioManager.STREAM_MUSIC,event.keycode);
- }
- }
- }
- }
- }elseif(!screenIsOn){
- //Ifwearein-callwithscreenoffandkeyguardisnotshowing,
- //thenhandlethevolumekeyourselves.
- //Thisisnecessarybecausethephoneappwilldisablethekeyguard
- //whentheproximitysensorisinuse.
- if(isInCall()&&event.type==RawInputEvent.EV_KEY&&
- (event.keycode==KeyEvent.KEYCODE_VOLUME_DOWN
- ||event.keycode==KeyEvent.KEYCODE_VOLUME_UP)){
- result&=~ACTION_PASS_TO_USER;
- handleVolumeKey(AudioManager.STREAM_VOICE_CALL,event.keycode);
- }
- if(isWakeKey){
- //awakekeyhasasolepurposeofwakingthedevice;don'tpass
- //ittotheuser
- result|=ACTION_POKE_USER_ACTIVITY;
- result&=~ACTION_PASS_TO_USER;
- }
- }
- inttype=event.type;
- intcode=event.keycode;
- booleandown=event.value!=0;
- if(type==RawInputEvent.EV_KEY){
- if(code==KeyEvent.KEYCODE_ENDCALL
- ||code==KeyEvent.KEYCODE_POWER){
- if(down){
- booleanhandled=false;
- booleanhungUp=false;
- //keyrepeatsaregeneratedbythewindowmanager,andwedon'tseethem
- //here,sounlessthedriverisdoingsomethingitshouldn'tbe,weknow
- //thisistherealpressevent.
- ITelephonyphoneServ=getPhoneInterface();
- if(phoneServ!=null){
- try{
- if(code==KeyEvent.KEYCODE_ENDCALL){
- handled=hungUp=phoneServ.endCall();
- }elseif(code==KeyEvent.KEYCODE_POWER){
- if(phoneServ.isRinging()){
- //PressingPowerwhilethere'saringingincoming
- //callshouldsilencetheringer.
- phoneServ.silenceRinger();
- handled=true;
- }elseif(phoneServ.isOffhook()&&
- ((mIncallPowerBehavior
- &Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP)
- !=0)){
- //Otherwise,if"Powerbuttonendscall"isenabled,
- //thePowerbuttonwillhangupanycurrentactivecall.
- handled=hungUp=phoneServ.endCall();
- }
- }
- }catch(RemoteExceptionex){
- Log.w(TAG,"ITelephonythrewRemoteException"+ex);
- }
- }else{
- Log.w(TAG,"!!!UnabletofindITelephonyinterface!!!");
- }
- if(!screenIsOn
- ||(handled&&code!=KeyEvent.KEYCODE_POWER)
- ||(handled&&hungUp&&code==KeyEvent.KEYCODE_POWER)){
- mShouldTurnOffOnKeyUp=false;
- }else{
- //onlytrytoturnoffthescreenifwedidn'talreadyhangup
- mShouldTurnOffOnKeyUp=true;
- mHandler.postDelayed(mPowerLongPress,
- ViewConfiguration.getGlobalActionKeyTimeout());
- result&=~ACTION_PASS_TO_USER;
- }
- }else{
- mHandler.removeCallbacks(mPowerLongPress);
- if(mShouldTurnOffOnKeyUp){
- mShouldTurnOffOnKeyUp=false;
- booleangohome,sleeps;
- if(code==KeyEvent.KEYCODE_ENDCALL){
- gohome=(mEndcallBehavior
- &Settings.System.END_BUTTON_BEHAVIOR_HOME)!=0;
- sleeps=(mEndcallBehavior
- &Settings.System.END_BUTTON_BEHAVIOR_SLEEP)!=0;
- }else{
- gohome=false;
- sleeps=true;
- }
- if(keyguardActive
- ||(sleeps&&!gohome)
- ||(gohome&&!goHome()&&sleeps)){
- //theymustalreadybeonthekeyguadorhomescreen,
- //gotosleepinstead
- Log.d(TAG,"I'mtiredmEndcallBehavior=0x"
- +Integer.toHexString(mEndcallBehavior));
- result&=~ACTION_POKE_USER_ACTIVITY;
- result|=ACTION_GO_TO_SLEEP;
- }
- result&=~ACTION_PASS_TO_USER;
- }
- }
- }elseif(isMediaKey(code)){
- //Thiskeyneedstobehandledevenifthescreenisoff.
- //Ifothersneedtobehandledwhileit'soff,thisisareasonable
- //patterntofollow.
- if((result&ACTION_PASS_TO_USER)==0){
- //Onlydothisifwewouldotherwisenotpassittotheuser.Inthat
- //case,thePhoneWindowclasswilldothesamething,exceptitwill
- //onlydoitiftheshowingappdoesn'tprocessthekeyonitsown.
- KeyEventkeyEvent=newKeyEvent(event.when,event.when,
- down?KeyEvent.ACTION_DOWN:KeyEvent.ACTION_UP,
- code,0);
- mBroadcastWakeLock.acquire();
- mHandler.post(newPassHeadsetKey(keyEvent));
- }
- }elseif(code==KeyEvent.KEYCODE_CALL){
- //Ifanincomingcallisringing,answerit!
- //(Wehandlethiskeyhere,ratherthanintheInCallScreen,tomake
- //surewe'llrespondtothekeyeveniftheInCallScreenhasn'tcometo
- //theforegroundyet.)
- //WeanswerthecallontheDOWNevent,toagreewith
- //the"fallback"behaviorintheInCallScreen.
- if(down){
- try{
- ITelephonyphoneServ=getPhoneInterface();
- if(phoneServ!=null){
- if(phoneServ.isRinging()){
- Log.i(TAG,"interceptKeyTq:"
- +"CALLkey-downwhileringing:Answerthecall!");
- phoneServ.answerRingingCall();
- //And*don't*passthiskeythrutothecurrentactivity
- //(whichispresumablytheInCallScreen.)
- result&=~ACTION_PASS_TO_USER;
- }
- }else{
- Log.w(TAG,"CALLbutton:UnabletofindITelephonyinterface");
- }
- }catch(RemoteExceptionex){
- Log.w(TAG,"CALLbutton:RemoteExceptionfromgetPhoneInterface()",ex);
- }
- }
- }elseif((code==KeyEvent.KEYCODE_VOLUME_UP)
- ||(code==KeyEvent.KEYCODE_VOLUME_DOWN)){
- //Ifanincomingcallisringing,eitherVOLUMEkeymeans
- //"silenceringer".Wehandlethesekeyshere,ratherthan
- //intheInCallScreen,tomakesurewe'llrespondtothem
- //eveniftheInCallScreenhasn'tcometotheforegroundyet.
- //LookfortheDOWNeventhere,toagreewiththe"fallback"
- //behaviorintheInCallScreen.
- if(down){
- try{
- ITelephonyphoneServ=getPhoneInterface();
- if(phoneServ!=null){
- if(phoneServ.isRinging()){
- Log.i(TAG,"interceptKeyTq:"
- +"VOLUMEkey-downwhileringing:Silenceringer!");
- //Silencetheringer.(It'ssafetocallthis
- //eveniftheringerhasalreadybeensilenced.)
- phoneServ.silenceRinger();
- //And*don't*passthiskeythrutothecurrentactivity
- //(whichisprobablytheInCallScreen.)
- result&=~ACTION_PASS_TO_USER;
- }
- }else{
- Log.w(TAG,"VOLUMEbutton:UnabletofindITelephonyinterface");
- }
- }catch(RemoteExceptionex){
- Log.w(TAG,"VOLUMEbutton:RemoteExceptionfromgetPhoneInterface()",ex);
- }
- }
- }
- }
- returnresult;
- }
- privatevoidprocess(){
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
- //Thelastkeyeventwesaw
- KeyEventlastKey=null;
- //Lastkeydowntimeforauto-repeatingkeys
- longlastKeyTime=SystemClock.uptimeMillis();
- longnextKeyTime=lastKeyTime+LONG_WAIT;
- longdownTime=0;
- //Howmanysuccessiverepeatswegenerated
- intkeyRepeatCount=0;
- //Needtoreportthatconfigurationhaschanged?
- booleanconfigChanged=false;
- while(true){
- longcurTime=SystemClock.uptimeMillis();
- if(DEBUG_INPUT)Slog.v(
- TAG,"Waitingfornextkey:now="+curTime
- +",repeat@"+nextKeyTime);
- //Retrievenextevent,waitingonlyaslongasthenext
- //repeattimeout.Iftheconfigurationhaschanged,then
- //don'twaitatall--we'llreportthechangeassoonas
- //wehaveprocessedallevents.
- QueuedEventev=mQueue.getEvent(//*****获取队列中的消息***//
- (int)((!configChanged&&curTime<nextKeyTime)
- ?(nextKeyTime-curTime):0));
- if(DEBUG_INPUT&&ev!=null)Slog.v(
- TAG,"Event:type="+ev.classType+"data="+ev.event);
- if(MEASURE_LATENCY){
- lt.sample("2gotevent",System.nanoTime()-ev.whenNano);
- }
- if(lastKey!=null&&!mPolicy.allowKeyRepeat()){
- //cancelkeyrepeatattherequestofthepolicy.
- lastKey=null;
- downTime=0;
- lastKeyTime=curTime;
- nextKeyTime=curTime+LONG_WAIT;
- }
- try{
- if(ev!=null){
- curTime=SystemClock.uptimeMillis();
- inteventType;
- if(ev.classType==RawInputEvent.CLASS_TOUCHSCREEN){
- eventType=eventType((MotionEvent)ev.event);
- }elseif(ev.classType==RawInputEvent.CLASS_KEYBOARD||
- ev.classType==RawInputEvent.CLASS_TRACKBALL){
- eventType=LocalPowerManager.BUTTON_EVENT;
- }else{
- eventType=LocalPowerManager.OTHER_EVENT;
- }
- try{
- if((curTime-mLastBatteryStatsCallTime)
- >=MIN_TIME_BETWEEN_USERACTIVITIES){
- mLastBatteryStatsCallTime=curTime;
- mBatteryStats.noteInputEvent();
- }
- }catch(RemoteExceptione){
- //Ignore
- }
- if(ev.classType==RawInputEvent.CLASS_CONFIGURATION_CHANGED){
- //donotwakescreeninthiscase
- }elseif(eventType!=TOUCH_EVENT
- &&eventType!=LONG_TOUCH_EVENT
- &&eventType!=CHEEK_EVENT){
- mPowerManager.userActivity(curTime,false,
- eventType,false);
- }elseif(mLastTouchEventType!=eventType
- ||(curTime-mLastUserActivityCallTime)
- >=MIN_TIME_BETWEEN_USERACTIVITIES){
- mLastUserActivityCallTime=curTime;
- mLastTouchEventType=eventType;
- mPowerManager.userActivity(curTime,false,
- eventType,false);
- }
- switch(ev.classType){
- caseRawInputEvent.CLASS_KEYBOARD:
- KeyEventke=(KeyEvent)ev.event;
- if(ke.isDown()){
- lastKey=ke;
- downTime=curTime;
- keyRepeatCount=0;
- lastKeyTime=curTime;
- nextKeyTime=lastKeyTime
- +ViewConfiguration.getLongPressTimeout();
- if(DEBUG_INPUT)Slog.v(
- TAG,"Receivedkeydown:firstrepeat@"
- +nextKeyTime);
- }else{
- lastKey=null;
- downTime=0;
- //Arbitrarylongtimeout.
- lastKeyTime=curTime;
- nextKeyTime=curTime+LONG_WAIT;
- if(DEBUG_INPUT)Slog.v(
- TAG,"Receivedkeyup:ignorerepeat@"
- +nextKeyTime);
- }
- dispatchKey((KeyEvent)ev.event,0,0);
- mQueue.recycleEvent(ev);
- break;
- caseRawInputEvent.CLASS_TOUCHSCREEN:
- //Slog.i(TAG,"Readnextevent"+ev);
- dispatchPointer(ev,(MotionEvent)ev.event,0,0);
- break;
- caseRawInputEvent.CLASS_TRACKBALL:
- dispatchTrackball(ev,(MotionEvent)ev.event,0,0);
- break;
- caseRawInputEvent.CLASS_CONFIGURATION_CHANGED:
- configChanged=true;
- break;
- default:
- mQueue.recycleEvent(ev);
- break;
- }
- }elseif(configChanged){
- configChanged=false;
- sendNewConfiguration();
- }elseif(lastKey!=null){
- curTime=SystemClock.uptimeMillis();
- //Timeoutoccurredwhilekeywasdown.Ifitisator
- //pastthekeyrepeattime,dispatchtherepeat.
- if(DEBUG_INPUT)Slog.v(
- TAG,"Keytimeout:repeat="+nextKeyTime
- +",now="+curTime);
- if(curTime<nextKeyTime){
- continue;
- }
- lastKeyTime=nextKeyTime;
- nextKeyTime=nextKeyTime+KEY_REPEAT_DELAY;
- keyRepeatCount++;
- if(DEBUG_INPUT)Slog.v(
- TAG,"Keyrepeat:count="+keyRepeatCount
- +",next@"+nextKeyTime);
- KeyEventnewEvent;
- if(downTime!=0&&(downTime
- +ViewConfiguration.getLongPressTimeout())
- <=curTime){
- newEvent=KeyEvent.changeTimeRepeat(lastKey,
- curTime,keyRepeatCount,
- lastKey.getFlags()|KeyEvent.FLAG_LONG_PRESS);
- downTime=0;
- }else{
- newEvent=KeyEvent.changeTimeRepeat(lastKey,
- curTime,keyRepeatCount);
- }
- dispatchKey(newEvent,0,0);
- }else{
- curTime=SystemClock.uptimeMillis();
- lastKeyTime=curTime;
- nextKeyTime=curTime+LONG_WAIT;
- }
- }catch(Exceptione){
- Slog.e(TAG,
- "Inputthreadreceiveduncaughtexception:"+e,e);
- }
- }
- }
- }
个人水平有限,有错误欢迎指出,谢谢。
补充:
1、生成
存在这样一个线程,它不断地从driver读取Event,并把它放到RawEvent队列中。这个队列中的RawEvent既有按键,也有触摸、轨迹球等事件。
RawEvent队列中的每个RawEvent最后都会通过一系列转化,最终变为KeyEvent被发送给另外一个线程,即输入线程,也就是一个Activity的主线程。
2、传递
KeyEvent传递过程主要可以划分为三步:过滤器、View树、Activity
过滤器部分主要对应着PhoneWindowManager.java中的interceptKeyTq和interceptKeyTi这两个方法。它们的代码可以在frameworks/base/policy/base/phone/com/Android/internal/policy/impl/PhoneWindowManager.java中看到。
这两个过滤器最大的不同就是interceptKeyTq用于RawEvent,而interceptKeyTi用于KeyEvent。
在一个没有实体键盘的机器上,Power键会被interceptKeyTq这个过滤器吃掉用来调用关机对话框或者使机器休眠。而Home键会被interceptKeyTi这个过滤器吃掉,用来把当前Activity切换到后台并把桌面程序切换到前台。所以,应用程序在View和Activity的onKeyDown/Up中是监听不到这两个按键的。除了这两个键以外的按键,都有机会继续前进。接下来,KeyEvent会先经过interceptKeyTi过滤器,如果这个过滤器不吃掉的话,就会继续前进,进入View树,如果没有被哪个View吃掉的话,最后进入到Activity的onKeyDown/Up方法中。
当一个KeyEvent经过上面的过程还没有被吃掉的话,系统就会利用它做一些定制的功能。比如音量键被系统用来调整声音,多媒体按键用来控制媒体播放,搜索键用来快速打开搜索功能,返回键用来退出当前Activity等。
更多相关文章
- android异步图片加载三之handler+线程池+消息队列模式
- Android进程与线程基本知识一
- 有关Android线程的学习
- Android单线程模型相关概念详解
- Android 多线程-----AsyncTask详解
- 浅析Android线程模型
- android > 调用拨打电话 并子线程监控然后返回跳转
- android按键模拟测试