1. InputManagerService(IMS)
1.1 IMS初始化
在系统启动的时候,SystemServer会启动窗口管理服务WindowManagerService(WMS),同时启动InputManagerService(IMS) ,并且把IMS实例传入WMS之中,以便后面调用。然后调用start()方法开启工作线程,
WMS在启动的时候就会通过系统输入管理器IMS来总负责监控键盘消息。
这些键盘消息一般都是分发给当前激活的Activity窗口来处理的,因此,当前激活的Activity窗口在创建的时候,会到WindowManagerService中去注册一个接收键盘消息的通道,表明它要处理键盘消息,而当InputManager监控到有键盘消息时,就会分给给它处理。
1.2 IputDispatcherThread分发消息
InputDispatcher线程是负责把键盘消息分发给当前激活的Activity窗口的,它的调用流程是通过Looper.pollOnce方法读取事件,在使用Looper类中,会创建一个管道,当调用Looper类的pollOnce函数时,如果管道中没有内容可读,那么当前线程就会进入到空闲等待状态;当有键盘事件发生时,InputReader就会往这个管道中写入新的内容,这样就会唤醒前面正在等待键盘事件发生的线程。
1.3 InputReaderThread读取消息
InputReaderThread概括起来就是一个独立的循环线程加上一些必要的辅助类,它的工作相对单一,即不断的轮询相关设备节点是否有新的事件发生。
它的核心是InputReader类。InputReader实际上不直接去访问设备节点,而是通过EventHub来完成这一工作。EventHub通过读取/dev/input/下的相关文件来判读是否有新事件,来通知InputReader。
1.4 设置激活窗口
什么是激活窗口,就是当前需要把消息传递给谁处理的界面,在InputManager启动以后,就开始负责监控键盘输入事件了。当InputManager监控到有键盘消息时,就会先找到当前被激活的窗口,然后找到其在InputManager中对应的键盘消息接收通道,通过这个通道在InputManager中的一端来通知在应用程序消息循环中的另一端,就把键盘消息分发给当前激活的Activity窗口了。
1.5 消息通道InputChannel
应用程序会为这个Activity激活窗口和InputManager之间创建一个键盘消息接收通道,这个通道的一端由一个Server端的InputChannel构成,另一端由Client端的InputChannel构成,Server端的InputChannel注册在由InputManager所管理的InputDispatcher中,而Client端的InputChannel注册在由应用程序主线程的消息循环对象Looper中。
2. PhoneWindowManager中按键处理
2.1 Power键处理
以电源键KeyEvent.KEYCODE_POWER为例介绍一下放入队列前的预处理:
按下时:
快速启动:sys.quickboot.enable
如果当前按键电源键并且是灭屏状态,
当按下时获取锁,并延迟0.5秒启动快速启动操作。
但是当弹出按键时,释放锁,如果按键时间没有超过0.5秒,则删除快速启动操作。
手电筒:
当按下电源键并且是灭屏状态,如果此时手电筒打开,则发送关闭手电筒广播。
3. 导航栏隐藏、显示
4.截屏功能:
同时按下电源键和音量下键,延迟2.5*0.5秒执行截图
5. 静音和挂断电话
如果当前有来电,则静音。如果正在通话则挂断电话。
6. 长按操作
如果当前不是灭屏状态,并且没有执行挂断电话的操作,并且同时没有触发音量上下键,则延迟0.5秒执行长按操作,打开长按电源键界面。
松开时:
如果截图操作未执行,则删除此操作。
如果长按操作未执行,则删除此操作。
返回结果:
电源执行完后不会放入队列,所以刚开始进行了如下操作:
result &=~ACTION_PASS_TO_USER;
判断当前是否是灭屏状态,是的话返回ACTION_WAKE_UP,不是返回
ACTION_GO_TO_SLEEP
caseKeyEvent.KEYCODE_POWER: {
//结果不会传递给应用
result &=~ACTION_PASS_TO_USER;
if (down) {
mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn,event.getDownTime(),
isImmersiveMode(mLastSystemUiFlags));
// 屏幕如果亮着,则判断是否是同时按键音量下键,打开截图功能
if (isScreenOn &&!mPowerKeyTriggered
&&(event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mPowerKeyTriggered =true;
mPowerKeyTime =event.getDownTime();
interceptScreenshotChord();
}
//如果当前有来电,则来电铃声静音,如果当前正在通话,则挂断电话
ITelephony telephonyService= getTelephonyService();
boolean hungUp = false;
if (telephonyService !=null) {
try {
if(telephonyService.isRinging()) {
// PressingPower while there's a ringing incoming
// call should silence the ringer.
telephonyService.silenceRinger();
/// M:[ALPS00093981] @{
} else if((isScreenOn
|| mScreenOffReason ==OFF_BECAUSE_OF_PROX_SENSOR)
/// @}
&&(mIncallPowerBehavior
&Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
&& telephonyService.isOffhook()){
// Otherwise,if "Power button ends call" is enabled,
// the Powerbutton will hang up any current active call.
hungUp =telephonyService.endCall();
}
} catch(RemoteException ex) {
Log.w(TAG,"ITelephony threw RemoteException", ex);
}
}
//如果没有进行上面的任何处理,则触发长按电源键操作,打开关机界面
interceptPowerKeyDown(!isScreenOn || hungUp
||mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);
} else {
mPowerKeyTriggered = false;
//松开按键是,如果延迟的截图操作还没执行,则删除
cancelPendingScreenshotChordAction();
//如果延迟的长按电源键还没执行,则删除
if(interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {
//返回唤醒或睡眠结果
result = (result &~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;
}
mPendingPowerKeyUpCanceled= false;
}
break;
}
2.2 Home按键处理
以KeyEvent.KEYCODE_HOME为例介绍一下分发前的预处理:
一:松开时
1 如果打开了任务管理界面,则关闭
2 如果有来电,则不做处理,返回继续分发
3 跳转到HOME界面
二:如果是锁屏界面,则返回不继续分发
三:如果第一次按下home键,而且是双击,则执行双击操作。否则执行预加载最近使用的任务
四:如果是长按home键,则打开任务管理器
if (keyCode == KeyEvent.KEYCODE_HOME) {
//如果应用要处理home键,则应用需要设置“FLAG_HOMEKEY_DISPATCHED”属性,
系统不处理,直接传递给应用处理
/// M: [ALPS00054781]Dispatch the home key to theapplication @{
if (win!= null && win.getAttrs() != null) {
final int flag = win.getAttrs().flags;
if((flag & WindowManager.LayoutParams.FLAG_HOMEKEY_DISPATCHED) != 0) {
// the window wants to handle the home key, so dispatch it to it.
return 0;
}
}
/// @}
// Ifwe have released the home key, and didn't do anything else
//while it was pressed, then it is time to go home!
//松开按键后
if(!down) {
cancelPreloadRecentApps();
mHomePressed = false;
if(mHomeConsumed) {
mHomeConsumed = false;
return -1;
}
if(canceled) {
Log.i(TAG, "Ignoring HOME; event canceled.");
return -1;
}
//If an incoming call is ringing, HOME is totally disabled.
//(The user is already on the InCallScreen at this point,
//and his ONLY options are to answer or reject the call.)
//如果此时有来电,则不处理home键
try{
ITelephony telephonyService = getTelephonyService();
if (telephonyService != null && telephonyService.isRinging()) {
Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
return -1;
}
}catch (RemoteException ex) {
Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
}
//Delay handling home if a double-tap is possible.
//如果运行双击home事件,延迟运行返回桌面操作
if(mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
mHomeDoubleTapPending = true;
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
ViewConfiguration.getDoubleTapTimeout());
return -1;
}
//Go home!
launchHomeFromHotKey();
return -1;
}
// If asystem window has focus, then it doesn't make sense
//right now to interact with applications.
//如果当前处理锁屏界面,则不处理home键
WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
if(attrs != null) {
final int type = attrs.type;
if(type == WindowManager.LayoutParams.TYPE_KEYGUARD
|| type == WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM
|| type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
// the "app" is keyguard, so give it the key
return 0;
}
final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
for(int i=0; i<typeCount; i++) {
if (type ==WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
// don't do anything, but also don't pass it to the app
return -1;
}
}
}
//Remember that home is pressed and handle special actions.
//第一次按下home键时
if(repeatCount == 0) {
mHomePressed = true;
//如果运行双击事件,则执行双击事件打开任务管理器
if(mHomeDoubleTapPending) {
mHomeDoubleTapPending = false;
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
handleDoubleTapOnHome();
}else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI
|| mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
//预加载将要显示的任务管理器中的任务
preloadRecentApps();
}
} elseif ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
//如果是长按home键,则打开任务管理器
if(!keyguardOn) {
handleLongPressOnHome();
}
}
//返回-1,则不在往下传递
return-1;
}
3.2 层点击事件处理流程
1. 当TouchEvent发生时,首先在Activity层处理,传递由Activity的 dispatchTouchEvent方法进行分发,如果dispatchTouchEvent返回true,则交给这个Activity的onTouchEvent处理,如果 dispatchTouchEvent返回 false,则交给这个ViewGroup处理。
2. ViewGroup首先由dispatchTouchEvent方法进行分发,和Activity不同的是如果dispatchTouchEvent返回 false,则传递给interceptTouchEvent方法来决定是否要拦截这个事件,
如果 interceptTouchEvent返回 true,也就是拦截掉了,则交给它的onTouchEvent来处理,
如果 interceptTouchEvent返回 false,那么就传递给子 view,由子 view 的dispatchTouchEvent再来开始这个事件的分发。