输入事件系统框架
Android事件输入系统主要负责键盘、触屏、鼠标等输入设备的事件输入及向焦点窗口和焦点视图的事件派发,插入,过滤,拦截等功能。Android支持的输入设备主要有:键盘、鼠标、触摸屏、轨迹球、游戏摇杆/手柄、绘图板等。
Android系统中输入系统主要包括如下几部分:View,InputDispatcher,InputReader,EventHub ,kernel几部分,他们之间的关系如下:
与其他模块的交互关系如下:
Input系统启动流程
Input在SystemServer中的startOtherServices方法中启动,在启动过程中主要过程如下:
主要代码如下:
startOtherServices(){
inputManager = new InputManagerService(context);
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
......
}
创建InputMangerService对象
public InputManagerService(Context context) {
this.mContext = context;
//创建一个负责处理DisplayThread线程中的Message的Handler
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
//用mPtr保存native层的InputManagerService
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
String doubleTouchGestureEnablePath = context.getResources().getString(
R.string.config_doubleTouchGestureEnableFile);
mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
new File(doubleTouchGestureEnablePath);
//初始化完成后将Service添加到LocalServices,通过Map以键值对的形式存储
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
初始化native层的InputManagerService
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
JNIEnv* env = jniEnv();
//将Java层的Context和InputManagerService转换为native层的Context和InputManagerService存储在mContextObj和mServiceObj中
mContextObj = env->NewGlobalRef(contextObj);
mServiceObj = env->NewGlobalRef(serviceObj);
//初始化变量
{
AutoMutex _l(mLock);
mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
mLocked.pointerCapture = false;
}
mInteractive = true;
//创建EventHub
sp<EventHub> eventHub = new EventHub();
//创建InputManager
mInputManager = new InputManager(eventHub, this, this);
}
EventHub的创建(见2.1)
InputManager的创建
//创建一个InputDispatcher对象用于分发事件,一个InputReader对象用于读事件并把事件交给InputDispatcher分发,然后调用initialize()初始化,其实也就是创建了InputReaderThread和InputDispatcherThread
InputManager::InputManager(
const sp<InputReaderInterface>& reader,
const sp<InputDispatcherInterface>& dispatcher) :
mReader(reader),
mDispatcher(dispatcher) {
initialize();
}
//InputDispatcher和InputReader的创建都相对简单。InputDispatcher会创建自己线程的Looper,以及设置根据传入的dispatchPolicy设置分发规则。InputReader则会将传入的InputDispatcher封装为监听对象存起来,做一些数据初始化就结束了
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
InputManagerService的start()
public void start() {
Slog.i(TAG, "Starting input manager");
//调用nativeStart方法,其实就是调用InputManager的start()方法
nativeStart(mPtr);
// 将InputManagerService交给WatchDog监控
Watchdog.getInstance().addMonitor(this);
//注册触控点速度、显示触控的观察者,并注册广播监控它们
registerPointerSpeedSettingObserver();
registerShowTouchesSettingObserver();
registerAccessibilityLargePointerSettingObserver();
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
//主动调用updateXXX方法更新(初始化)
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
Input涉及主要线程
从上述结构图中看到Input系统涉及到的主要线程为:
InputReader线程:通过EventHub从/dev/input节点获取事件,转换成EventEntry事件加入到InputDispatcher的mInboundQueue。当IMS.start启动时启动
InputDispatcher线程:从mInboundQueue队列取出事件,转换成DispatchEntry事件加入到connection的outboundQueue队列。再然后开始处理分发事件,取出outbound队列,放入waitQueue.
UI线程:(DisplayThread)创建socket pair,分别位于InputDispatcher线程和focused窗口所在进程的UI主线程,可相互通信。
a:UI主线程:通setFdEvents() 监听socket客户端,收到消息后回调NativeInputEventReceiver();
b:InputDispatcher线程: 通过IMS.registerInputChannel(),监听socket服务端,收到消息后回调handleReceiveCallback
InputReader
InputReader同样是system_server的子线程,负责输入设备的管理和输入事件的处理,主要的作用如下:
- 从EventHub中获得底层事件
- 处理输入设备添加、删除事件,管理设备的InputMapper
- 处理输入事件,由设备对应的各个InputMapper处理之后交付InputDispatcher进行分发
从上述结构图看到:
InputReaderThread线程是和EventHub关联是通过InputReaderThread线程启动后,会循环执行mReader->looperonce,loopOnce()中会调用mEventHub->getEvents读取事件,读取事件后读到了事件就会调用processEventsLocked处理事件处理完成后调用getInputDevicesLocked获取输入设备信息调用mPolicy->notifyInputDevicesChanged函数利用InputManagerService的代理通过Handler发送MSG_DELIVER_INPUT_DEVICES_CHANGED消息,通知输入设备发生了变化最后调用mQueuedListener->flush(),将事件队列中的所有事件交给在InputReader中注册过的InputDispatcher
EventHub
EventHub为输入系统的最底层模块,输入事件的源头,此类主要的作用为:
►监控输入设备添加、删除事件
►获取新增设备的信息,如设备的类别、支持哪些按键、键盘映射表等。
►获取input_event,封装成RawEvent传递给InputReader
►支持查询输入设备当前的状态。
●组合键状态
●是否有LED灯或支持振动
new EventHub
EventHub的创建过程中做了以下事情:
创建mEpollFd用于监听是否有数据(有无事件)可读
创建mINotifyFd将它注册到DEVICE_PATH(这里路径就是/dev/input)节点,并将它交给内核用于监听该设备节点的增删数据事件。那么只要有数据增删的事件到来,epoll_wait()就会返回,使得EventHub能收到来自系统的通知,并获取事件的详细信息
调用epoll_ctl函数将mEpollFd和mINotifyFd注册到epoll中
定义int wakeFd[2]作为事件传输管道的读写两端,并将读端注册到epoll中让mEpollFd监听
相关代码如下:
EventHub::EventHub(void) :
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
mOpeningDevices(0), mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false), mNeedToScanDevices(true),
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
//创建mEpollFd用于监听是否有数据(有无事件)可读,EPOLL_SIZE_HINT:8
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
//创建mINotifyFd将它注册到DEVICE_PATH(这里路径就是/dev/input)节点,并将它交给内核用于监听该设备节点的增删数据事件。那么只要有数据增删的事件到来,epoll_wait()就会返回,使得EventHub能收到来自系统的通知,并获取事件的详细信息
mINotifyFd = inotify_init();
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d",
DEVICE_PATH, errno);
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));//epoll_event这个结构体清空
eventItem.events = EPOLLIN;//监测的事件类型为EPOLLIN
eventItem.data.u32 = EPOLL_ID_INOTIFY;
//调用epoll_ctl函数将mEpollFd和mINotifyFd注册到epoll中,监测的fd为:mINotifyFd
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno);
//作为事件传输管道的读写两端,并将读端注册到epoll中让mEpollFd监听
int wakeFds[2];
result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
mWakeReadPipeFd = wakeFds[0];//建立读取事件的通道
mWakeWritePipeFd = wakeFds[1];//建立写入事件的通道
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
eventItem.data.u32 = EPOLL_ID_WAKE;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
int major, minor;
getLinuxRelease(&major, &minor);
// EPOLLWAKEUP was introduced in kernel 3.5
mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
}
那么这里抛出一个问题:为什么要把管道的读端注册到epoll中?假如EventHub因为getEvents读不到事件而阻塞在epoll_wait()里,而我们没有绑定读端的话,我们要怎么唤醒EventHub?如果绑定了管道的读端,我们就可以通过向管道的写端写数据从而让EventHub因为得到管道写端的数据而被唤醒。
EventHub如何监听设备的插拔和读取底层的事件,主要通过inotify和epoll