文章内容主要从 https://segmentfault.com/a/1190000011826846 而来
1,InputManagerService
Android Framework 层的 service,大部分都是在 SystemServer 进程中创建,InputManagerService 即是如此,创建完 Service 后,便把它加入到 ServiceManager 中,并同时设置了 InputMonitor,如下
inputManager = new InputManagerService(context);
WindowManagerService wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
mActivityManagerService.setWindowManager(wm);
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
看一下InputManagerservice 的构造函数
public InputManagerService(Context context) {
this.mContext = context;
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
String doubleTouchGestureEnablePath = context.getResources().getString(
R.string.config_doubleTouchGestureEnableFile);
mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
new File(doubleTouchGestureEnablePath);
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
nativeInit 的实现如下
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast(im);
}
首先是获取 MessageQueue, 接着创建 NativeInputManager ,NativeInputManager 的实现如下
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp& looper) :
mLooper(looper), mInteractive(true) {
JNIEnv* env = jniEnv();
...
mInteractive = true;
sp eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
NativeInputManager 中创建了 EventHub,并利用 EventHub 创建了InputManger, InputManager 实现如下
InputManager::InputManager(
const sp& eventHub,
const sp& readerPolicy,
const sp& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
代码中创建了 InputDispatcher,接着创建了 InputReader,接着是 initialize 方法
void InputManager::initialize()
{
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
分别将 reader 和 dispatcher 封装到相应的 Thread 中,此时对 SystemServer 来说,InputManager 基本创建完成,接着便是 start Service ,对应的 native 方法便是 nativeStart(mPtr)
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast(ptr);
status_t result = im->getInputManager()->start();
}
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
}
2,小结
整体流程如下
InputManger 通过 InputReaderThread 读和处理未加工的输入事件然后分发到 DispatcherThread 队列中, InputDispatcherThread 将接收的队列发送给相应的应用程序
3,epoll
这里首先了解一下 epoll,之前我们处理输入流的时候,我们会对每一个流进行遍历,然后检测到有修改的数据,将其取出来,这其中存在大量的资源消耗,尤其是在流比较多的时候,epoll 便在这里优化,当无数据的时候会阻塞队列,当有数据的时候,只将其中有变化的进行分发。
epoll更详细的分析
4,EventHub
在 NativeInputManager 中,曾经创建的EventHub,并作为参数传递给了 InputManger。EventHub是将不同来源的消息转化为统一类型并交给上层处理。先查看其构造函数
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);
//创建一个epoll句柄
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
mINotifyFd = inotify_init();
//监视dev/input目录的变化删除和创建变化
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = EPOLL_ID_INOTIFY;
//把inotify的句柄加入到epoll监测
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
//创建匿名管道
int wakeFds[2];
result = pipe(wakeFds);
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;
//将管道的读端加入到epoll监测
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
int major, minor;
getLinuxRelease(&major, &minor);
// EPOLLWAKEUP was introduced in kerel 3.5
mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
}
其中创建了 epoll 句柄、inotify 句柄、匿名管道(非阻塞),inotify负责监控目录和文件的变化,这里监控的是/dev/input 目录。
EventHub 负责监控,相关事件的处理由 ReaderThread 处理。ReaderThread 是继承Android 的 Thread
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
这个方法返回 true , 表示 threadLoop会被循环调用,也就是 loopOnce 会被循环调用,下面是 loopOnce
void InputReader::loopOnce() {
.....
//从EventHub中获取事件
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
//如果读到数据,处理事件数据
if (count) {
processEventsLocked(mEventBuffer, count);
}
if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTS
ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
mNextTimeout = LLONG_MAX;
timeoutExpiredLocked(now);
}
}
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
getInputDevicesLocked(inputDevices);
}
} // release lock
//将排队的事件队列发送给监听者,实际上这个监听者就是Input dispatcher
mQueuedListener->flush();
}
我们要分析的是其中的两个方法 getEvents 和 processEventsLocked
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
RawEvent* event = buffer;
size_t capacity = bufferSize;
for(;;) {
....
while (mPendingEventIndex < mPendingEventCount) {
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
.....
ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
if (eventItem.events & EPOLLIN) {
int32_t readSize = read(device->fd, readBuffer,
sizeof(struct input_event) * capacity);
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
// 设备被移除,关闭设备
deviceChanged = true;
closeDeviceLocked(device);
} else if (readSize < 0) {
//无法获得事件
if (errno != EAGAIN && errno != EINTR) {
ALOGW("could not get event (errno=%d)", errno);
}
} else if ((readSize % sizeof(struct input_event)) != 0) {
//获得事件的大小非事件类型整数倍
ALOGE("could not get event (wrong size: %d)", readSize);
} else {
int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
//计算读入了多少事件
size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
if (iev.type == EV_MSC) {
if (iev.code == MSC_ANDROID_TIME_SEC) {
device->timestampOverrideSec = iev.value;
continue;
} else if (iev.code == MSC_ANDROID_TIME_USEC) {
device->timestampOverrideUsec = iev.value;
continue;
}
}
//事件时间相关计算,时间的错误可能会导致ANR和一些bug。这里采取一系列的防范
.........
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
event += 1;
capacity -= 1;
}
if (capacity == 0) {
//每到我们计算完一个事件,capacity就会减1,如果为0。则表示 结果缓冲区已经满了,
//需要重置开始读取时间的索引值,来读取下一个事件迭代
mPendingEventIndex -= 1;
break;
}
}
//表明读到事件了,跳出循环
if (event != buffer || awoken) {
break;
}
mPendingEventIndex = 0;
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
if (pollResult == 0) {
mPendingEventCount = 0;
break;
}
//判断是否有事件发生
if (pollResult < 0) {
mPendingEventCount = 0;
} else {
//产生的事件的数目
mPendingEventCount = size_t(pollResult);
}
}
//产生的事件数目
return event - buffer;
}
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type;
size_t batchSize = 1;
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
int32_t deviceId = rawEvent->deviceId;
while (batchSize < count) {
if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
|| rawEvent[batchSize].deviceId != deviceId) {
break;
}
batchSize += 1;
}
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED:
removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::FINISHED_DEVICE_SCAN:
handleConfigurationChangedLocked(rawEvent->when);
break;
default:
ALOG_ASSERT(false); // can't happen
break;
}
}
count -= batchSize;
rawEvent += batchSize;
}
}
getEvents 方法会进行一些新增设备和移除设备的更新操作。至于点击事件是通过指针参数 RawEvent, 其作为起始地址记录事件,在循环体中,处理获取时间、检测相关设备类型、读取事件,如果检测到事件,则跳出循环。更新 mPendingEventCount 和 mPendingEventIndex 来控制事件的读取,epoll_wait 来得到事件的来源。
在 looperOnce 获取到事件后,就会调用 processEventsLocked。其负责事件添加、设备移除等,事件相关还是 processEventsForDeviceLocked 方法,根据事件获取相应的设备类型,并交给相应的设备处理,即 InputMapper 。
在这里,事件会被 mapper->process 处理
void TouchInputMapper::process(const RawEvent* rawEvent) {
mCursorButtonAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
mTouchButtonAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
sync(rawEvent->when);
}
}
void TouchInputMapper::sync(nsecs_t when) {
.....
processRawTouches(false /*timeout*/);
}
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
....
dispatchMotion();
....
}
void TouchInputMapper::dispatchMotion() {
....
NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
action, actionButton, flags, metaState, buttonState, edgeFlags,
mViewport.displayId, pointerCount, pointerProperties, pointerCoords,
xPrecision, yPrecision, downTime);
getListener()->notifyMotion(&args);
}
InputListenerInterface* InputReader::ContextImpl::getListener() {
return mReader->mQueuedListener.get();
}
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
mArgsQueue.push(new NotifyMotionArgs(*args));
}
此时数据被加入到参数队列中,此时回到 loopOnce 中,调用 QueuedInputListener 的 flush 方法
void QueuedInputListener::flush() {
size_t count = mArgsQueue.size();
for (size_t i = 0; i < count; i++) {
NotifyArgs* args = mArgsQueue[i];
args->notify(mInnerListener);
delete args;
}
mArgsQueue.clear();
}
void NotifyMotionArgs::notify(const sp& listener) const {
listener->notifyMotion(this);
}
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
.....
MotionEvent event;
event.initialize(args->deviceId, args->source, args->action, args->actionButton,
args->flags, args->edgeFlags, args->metaState, args->buttonState,
0, 0, args->xPrecision, args->yPrecision,
args->downTime, args->eventTime,
args->pointerCount, args->pointerProperties, args->pointerCoords);
....
MotionEntry* newEntry = new MotionEntry(args->eventTime,
args->deviceId, args->source, policyFlags,
args->action, args->actionButton, args->flags,
args->metaState, args->buttonState,
args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
args->displayId,
args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);
needWake = enqueueInboundEventLocked(newEntry);
....
if (needWake) {
mLooper->wake();
}
}
在 notifyMotion 中将参数包装成 MotionEntry,加入到 enqueueInboundEventLocked 中,然后唤醒 looper。
Dispatcher 下的 dispatchOnce
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
void InputDispatcher::dispatchOnce() {
...
dispatchOnceInnerLocked(&nextWakeupTime);
...
}
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
....
mPendingEvent = mInboundQueue.dequeueAtHead();
....
switch (mPendingEvent->type) {
case EventEntry::TYPE_MOTION: {
MotionEntry* typedEntry = static_cast(mPendingEvent);
if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
dropReason = DROP_REASON_APP_SWITCH;
}
if (dropReason == DROP_REASON_NOT_DROPPED
&& isStaleEventLocked(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DROP_REASON_BLOCKED;
}
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
break;
}
....
}
}
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
EventEntry* eventEntry, const Vector& inputTargets) {
....
pokeUserActivityLocked(eventEntry);
.....
for (size_t i = 0; i < inputTargets.size(); i++) {
const InputTarget& inputTarget = inputTargets.itemAt(i);
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp connection = mConnectionsByFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
}
}
}
prepareDispatchCycleLocked 调用 enqueueDispatchEntriesLocked 调用 startDispatchCycleLocked
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp& connection) {
EventEntry* eventEntry = dispatchEntry->eventEntry;
....
switch (eventEntry->type) {
....
case EventEntry::TYPE_MOTION: {
status = connection->inputPublisher.publishMotionEvent( ....);
break;
}
....
}
...
}
status_t InputPublisher::publishMotionEvent(...) {
....
InputMessage msg;
msg.header.type = InputMessage::TYPE_MOTION;
msg.body.motion.seq = seq;
msg.body.motion.deviceId = deviceId;
msg.body.motion.source = source;
msg.body.motion.action = action;
msg.body.motion.actionButton = actionButton;
msg.body.motion.flags = flags;
msg.body.motion.edgeFlags = edgeFlags;
msg.body.motion.metaState = metaState;
msg.body.motion.buttonState = buttonState;
msg.body.motion.xOffset = xOffset;
msg.body.motion.yOffset = yOffset;
msg.body.motion.xPrecision = xPrecision;
msg.body.motion.yPrecision = yPrecision;
msg.body.motion.downTime = downTime;
msg.body.motion.eventTime = eventTime;
msg.body.motion.pointerCount = pointerCount;
for (uint32_t i = 0; i < pointerCount; i++) {
msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
}
return mChannel->sendMessage(&msg);
}
ReaderThread 开启后会从EventHub轮训获取时间,获取事件后,经过一系列的封装,通过 InputChannel 发送出去
Touch 事件原理分析 (下)