前言:
在android2.3版本的时候,详细的对照代码了解了遍按键事件从驱动到framework的流程。并在高仿机上实现新增按键从驱动到系统的功能。随着时间的不断推进,工作的重点越来越向驱动集中,对HAL和系统的了解停留在之前的理解上。近来看一个windowstate的提交,实现发现对按键流程还有不太明白的地方。现在回过头来详细的再对按键梳理一遍,首先来着个大体的序列图:
在这里按照序列图的标号,对照着代码大体的理下思路:
1.InputManager.cpp这个类是inputflinger的入口类,在JNI里面会通过new生成一个实体对象,然后start启动inputflinger的功能。构造函数如图所示
上面的图涉及到4个非常重要的类,就是这四个类实现事件的读取和派发
1.InputReader :这个类主要是从input目录下,读取所有设备产生的所有按键事件。并根据设备的不同(如键盘,单点触摸,多点触摸,轨迹球…)对数据进行处理
2.InputDispatcher :这个类主要的功能就是派发事件,会涉及到两次按键拦截
3.InputReaderThread : 创建一个线程不停的调用inputReader读取事件
3.InputDispatcherThread :创建一个线程不停的轮询事件队列和命令队列是否有数据并处理
start()接口调用后,上面两个线程就run起来了。这里首先从读按键线程开始看
2-3:read线程的启动
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
以上可以看出looponce,线程循环主要作用有2:
1.通过eventhub获取按键事件,并将事件保存在Buffer里面。并返回事件个数
2.如果事件数不为0,则进行对事件的处理
4.事件获取getEvents接口解读
getEvents这个接口在不同的android版本变化比较大,在最开始的2.3及后继一段时间内的版本实现,都是不停的主动循询/dev/input 目录下的所有设备并从中读取可能的事件。在android8.0也就是我目前使用的版本上,实现方式优化了些,但原理不变。
首先:来看下EventHub的构造函数
这里通过inotify的方式监控dev/input/目录
然后把inofity添加到epoll上,通过epoll_wait的方式等待数据的到来,比不停的轮询效率要高。下面来看看getEvent的比较核心的代码:
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
//监听并等待事件到来
上面代码截图就是核心的对device设备读取按键事件,这里需要注意的是。原生代码里面最大只支持8个input设备,按键事件缓存最大只有256个按键时键。
5.如果有读取到按键事件,则对数据进行原始处理。有些数据需要经过处理才能被上层更好的使用。如多点触摸等等,来看processEventsLocked实现
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex < 0) {
ALOGW("Discarding event for unknown deviceId %d.", deviceId);
return;
}
InputDevice* device = mDevices.valueAt(deviceIndex);
if (device->isIgnored()) {
//ALOGD("Discarding event for ignored deviceId %d.", deviceId);
return;
}
device->process(rawEvents, count);
//这里就是对不同设备的事件进行不同的处理了,在不同的android版本上这里的实现也不太一样
}
说到device设备类型,这里需要说明下代码里面支持多少种设备。我们在代码里面搜索下addMapper这个函数调用,每个不同的设备都需要调用它把自己注册上去
device->addMapper(new **KeyboardInputMapper**(device, keyboardSource, keyboardType));
//键盘也就是按键事件
if (classes & INPUT_DEVICE_CLASS_SWITCH) {
device->addMapper(new **SwitchInputMapper**(device));
}
// Scroll wheel-like devices.
if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
device->addMapper(new **RotaryEncoderInputMapper**(device));
}
// Vibrator-like devices.
if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
device->addMapper(new **VibratorInputMapper**(device));
}
// Cursor-like devices.
if (classes & INPUT_DEVICE_CLASS_CURSOR) {
device->addMapper(new **CursorInputMapper**(device));
}
// Touchscreens and touchpad devices.
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
device->addMapper(new **MultiTouchInputMapper**(device));
//多点触摸
} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
device->addMapper(new **SingleTouchInputMapper**(device));
//单点触摸
}
// Joystick-like devices.
if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
device->addMapper(new **JoystickInputMapper**(device));
}
// External stylus-like devices.
if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
device->addMapper(new **ExternalStylusInputMapper**(device));
}
按键设备会调用KeyboardInputMapper的process函数进行处理
触摸处理会调用SingleTouchInputMapper或者MultiTouchInputMapper的process函数来进行处理,处理完成后就是对事件的派发过程了。这里为了篇幅不要太大只跟踪key事件处理和派发,touch事件的可以对照序列图自行跟踪代码。
7&10.Key事件的process处理:
如果事件类型为EV_KEY,则调用processKey函数来进行处理,来看实现
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
getListener()->notifyKey(&args);
然后通过nofityKey接口将事件,发送到消息队列。notifyKey这个getListener 不同的版本实现不一样,但最终都是指向inputDispatch类的notifyKey接口,当然touch事件会指向notifyMotion接口。
12.inputDispatch类的notifyKey接口实现。
上面图有主要有两个重点:
1.interceptKeyBeforeQueueing第一次按键事件拦截,这个最终的实现是在PhoneWindowManager.java文件里面。
2.将按键事件添加到mInboundQueue这个队列的尾部,会有线程来从这个队列读取数据进行处理。
开头就说过inputManger会创建两个线程,read线程上面已经介绍完了。下面来介绍dispatch线程,也就是处理事件队列的线程
13-15 线程创建和事件派发
我们来看dispatchOnceInnerLocked这个函数对key事件的处理的核心代码
dispatchKeyLocked对TYPE_KEY事件处理的核心代码:
doInterceptKeyBeforeDispatchingLockedInterruptible这个command处理就是第二次事件拦截的地方。
dispatchEventLocked(currentTime, entry, inputTargets);
//经过两次事件拦截后,这里是最终的事件处理地方。无论key事件还是touch事件。虽然touch事件也会有两次拦截,但是基本没什么用。intercept处理的java代码基本都是key的
16事件向android派发
这里的代码调用流程就不细说了,大体是这样的
dispatchEventLocked-〉prepareDispatchCycleLocked->enqueueDispatchEntriesLocked->startDispatchCycleLocked->inputPublisher.publishKeyEvent
17.sendMessage.在inputTransport.cpp文件里面,有publickKeyevent和sendmessage的实现
status_t InputPublisher::publishKeyEvent(
uint32_t seq,
int32_t deviceId,
int32_t source,
int32_t action,
int32_t flags,
int32_t keyCode,
int32_t scanCode,
int32_t metaState,
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
"action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
"downTime=%lld, eventTime=%lld",
mChannel->getName().string(), seq,
deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
downTime, eventTime);
#endif
if (!seq) {
ALOGE("Attempted to publish a key event with sequence number 0.");
return BAD_VALUE;
}
InputMessage msg;
msg.header.type = InputMessage::TYPE_KEY;
msg.body.key.seq = seq;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
msg.body.key.scanCode = scanCode;
msg.body.key.metaState = metaState;
msg.body.key.repeatCount = repeatCount;
msg.body.key.downTime = downTime;
msg.body.key.eventTime = eventTime;
return mChannel->**sendMessage**(&msg);
}
status_t InputChannel::sendMessage(const InputMessage* msg) {
size_t msgLength = msg->size();
ssize_t nWrite;
do {
nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
if (nWrite < 0) {
int error = errno;
#if DEBUG_CHANNEL_MESSAGES
ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(),
msg->header.type, error);
#endif
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
}
if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) {
return DEAD_OBJECT;
}
return -error;
}
if (size_t(nWrite) != msgLength) {
#if DEBUG_CHANNEL_MESSAGES
ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
mName.string(), msg->header.type);
#endif
return DEAD_OBJECT;
}
#if DEBUG_CHANNEL_MESSAGES
ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type);
#endif
return OK;
}
sendMessage最终的实现原理就是通过socketpair函数生成两个相互关联的socket,
服务端发,client端收.JNI为服务端, framework为client。通过socket最终把事件送到了framework层,在下篇文章里面会着重介绍channel的创建和,上层消息的派出
本篇到这里就结束了,如果有疑问和错误敬请指出,谢谢!