上篇文章我们大概讲解了一下EventHub的工作原理,有提到EventHub将原始input数据粗加工成RawEvent给InputReader。
这篇文章会讲解InputReader如何来处理这些RawEvent。。。。。
为了使文章保持连贯,先大概解释一下InputReader是如何被创建和工作起来的。。。。
Java层管理输入系统的是InputManagerService,它属于核心的系统service,当然会在SystemServer中创建。
ServerThread.java---->initAndLoop() 顺便提一下,我使用的是MTK8127 4.4的代码做分析,mtk做了一些定制可能和Google原生代码有区别,但是本质是一样的。
InputManagerService inputManager = null;
……………………
//注意构造InputManagerService的参数wmHandler,为了保证输入事件相应的实时性,InputManagerService会有很多任务在wmHandler中处理
inputManager = new InputManagerService(context, wmHandler);
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, power, display, inputManager,
wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
!firstBoot, onlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
ActivityManagerService.self().setWindowManager(wm);
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
//启动InputManagerService
inputManager.start();
display.setWindowManager(wm);
display.setInputManager(inputManager);
我们看看InputManagerService的构造函数:
public InputManagerService(Context context, Handler handler) {
this.mContext = context;
this.mHandler = new InputManagerHandler(handler.getLooper());
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
}
初始化工作都在nativeInit,看看:
com_android_server_input_InputManagerService.cpp---->nativeInit()
static jint nativeInit(JNIEnv* env, jclass clazz,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
//创建NativeInputManager,它将用来和Java层的InputManagerService通信
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jint>(im);
}
再进一步走入NativeInputManager构造函数:
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper) {
JNIEnv* env = jniEnv();
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;
}
//大名鼎鼎的EventHub被创建出来了,但愿大家没有忘记上篇文章讲过,EventHub的构造函数通过INotify+Epoll使"/dev/input"目录被监听
sp<EventHub> eventHub = new EventHub();
//构造native层的InputManager
mInputManager = new InputManager(eventHub, this, this);
}
继续看InputManager构造函数:
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
// InputDispatcher被创建
mDispatcher = new InputDispatcher(dispatcherPolicy);
// InputReader被创建
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
到这里后,InputDispatcher和InputReader都被创建出来了。
继续看initialize();
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
很简单,创建了两个thread InputReaderThread&InputDispatcherThread。
OK,通过一步一步构造,Java和native层都已经万事俱备,只欠东风了。。。。
东风在哪里?
东风已经在上面出现了,,,inputManager.start();
这个过程和前面类似,不一步步分析了,它最终会调用InputManager.cpp的start()函数。。。。。。。写到这里才发现,这里涉及到的文件比较多,分布也比较散,我比较懒,不想一个个列出来,大家把整个framework加到source insight就非常方便了,跟踪代码神器!
我们看看InputManager.cpp的start()函数:
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
if (result) {
ALOGE("Could not start InputDispatcher thread due to error %d.", result);
return result;
}
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
if (result) {
ALOGE("Could not start InputReader thread due to error %d.", result);
mDispatcherThread->requestExit();
return result;
}
return OK;
}
这里mDispatcherThread&mReaderThread两个线程都开始工作了,关于这两个线程工作原理我简单讲一下,启动线程时会不断的调用线程的threadLoop()函数,直到其返回false则停止。实际上,Android系统mDispatcherThread&mReaderThread的threadLoop()函数都是反馈true,所以这两个线程一旦开机便不会停止。
前面已经过了,开机线程实际上就是不断调用threadLoop()函数,这章我们讲的是InputReader,所以我们来看看他的threadLoop():
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
看看loopOnce()
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
Vector<InputDeviceInfo> inputDevices;
{ // acquire lock
AutoMutex _l(mLock);
oldGeneration = mGeneration;
timeoutMillis = -1;
uint32_t changes = mConfigurationChangesToRefresh;
if (changes) {
mConfigurationChangesToRefresh = 0;
timeoutMillis = 0;
refreshConfigurationLocked(changes);
} else if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
}
} // release lock
//上篇文章讲过,getEvents会将输入事件转化为RawEvent带给InputReader
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
// Send out a message that the describes the changed input devices.
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
// on another thread similarly waiting to acquire the InputReader lock thereby
// resulting in a deadlock. This situation is actually quite plausible because the
// listener is actually the input dispatcher, which calls into the window manager,
// which occasionally calls into the input reader.
mQueuedListener->flush();
}
我们只关注processEventsLocked(mEventBuffer, count);
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;
}
#if DEBUG_RAW_EVENTS
ALOGD("BatchSize: %d Count: %d", batchSize, count);
#endif
//处理一般的输入事件
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {
//处理增加input device add事件
case EventHubInterface::DEVICE_ADDED:
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
//处理增加input device remove事件
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;
}
}
我们只关注processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
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);
}
这里出现了一个mDevices变量,存储的是InputDevice类型,其实大家可以认为和前面EventHub中的Device是差不多的东西,都是代表一个输入设备。
最终处理还是调用对应InputDevice的process()函数来处理。
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
// Process all of the events in order for each mapper.
// We cannot simply ask each mapper to process them in bulk because mappers may
// have side-effects that must be interleaved. For example, joystick movement events and
// gamepad button presses are handled by different mappers but they should be dispatched
// in the order received.
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
#if DEBUG_RAW_EVENTS
ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld",
rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
rawEvent->when);
#endif
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
mDropUntilNextSync = false;
#if DEBUG_RAW_EVENTS
ALOGD("Recovered from input event buffer overrun.");
#endif
} else {
#if DEBUG_RAW_EVENTS
ALOGD("Dropped input event while waiting for next input sync.");
#endif
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
ALOGI("Detected input event buffer overrun for device %s.", getName().string());
mDropUntilNextSync = true;
reset(rawEvent->when);
} else {
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent);
}
}
}
}
又出现了个InputMapper,并且调用了其process()处理,那么InputMapper又是个啥?哪来的?
要回答这个问题,我们需要退回去,前面的processEventsLocked()函数,我们只分析processEventsForDeviceLocked(),大家可以自己去看看
case EventHubInterface::DEVICE_ADDED:
EventHubInterface::DEVICE_REMOVED:
情况下的处理流程就明白了,当有设备添加时,会向mDevices中添加对应的InputDevice,创建InputDevice的过程就包含了添加InputMapper过程,限于篇幅,这些就不细说了。
InputMapper有很多子类,我们只讨论KeyboardInputMapper,看看其process()函数:
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_KEY: {
int32_t scanCode = rawEvent->code;
int32_t usageCode = mCurrentHidUsage;
mCurrentHidUsage = 0;
if (isKeyboardOrGamepadKey(scanCode)) {
int32_t keyCode;
uint32_t flags;
//这里会通过kl文件对键值进行映射以及添加flag,这样做可以大大降低代码的耦合度,不同的硬件平台,key不同的硬件实现方式,对于上层都不需要做任何调整,只需要修改kl文件即可,关于kl文件中各个字段意思,大家可以自行百度
if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {
keyCode = AKEYCODE_UNKNOWN;
flags = 0;
}
//处理key
processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
}
break;
}
case EV_MSC: {
if (rawEvent->code == MSC_SCAN) {
mCurrentHidUsage = rawEvent->value;
}
break;
}
case EV_SYN: {
if (rawEvent->code == SYN_REPORT) {
mCurrentHidUsage = 0;
}
}
}
}
对于上面出现的EV_MSC,EV_SYN等等是什么东东,我从网上copy了一份表格:
EV_SYN | 用于标识独立的事件,这些独立的事件时在时间或者空间上是可以分离的,比如在多点触摸中 |
EV_KEY | 用于标识按键,按钮或者类似按键的设备状态的变化 |
EV_REL | 用于描述 对于轴线相对变化量,如鼠标向左移动5个单位 |
EV_ABS | 用于描述 对于轴线的绝对变化量, 比如在触摸屏上的触摸点的坐标 |
EV_SW | 标识二进制的开关状态 |
EV_LED | 表示设备上的LED是开or关 |
EV_SND | 用于标识发送声音到设备 |
EV_REP | 表示自动重复的设备 |
V_FF | 用于标识发送强制要回馈的命令到设备 |
EV_PWR | 对于Power键的一个特殊状态或者切换输入 |
EV_FF_STATUS | 用于收到需要强制回馈的设备状态 |
EV_MSC | 如果不是这些已存在的状态,那么就用这个标识 |
多点触屏 | 大多是EV_ABS, EV_KEY, EV_SYN,有的还设置了EV_MSC |
键盘 | EV_KEY, EV_SW |
鼠标 | EV_REL, EV_KEY, EV_ABS |
我们接下来看看processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
int32_t scanCode, uint32_t policyFlags) {
nsecs_t now;
if (down) {
// Rotate key codes according to orientation if needed.
if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
keyCode = rotateKeyCode(keyCode, mOrientation);
}
// Add key down.
ssize_t keyDownIndex = findKeyDown(scanCode);
if (keyDownIndex >= 0) {
// key repeat, be sure to use same keycode as before in case of rotation
keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
} else {
// key down
if ((policyFlags & POLICY_FLAG_VIRTUAL)
&& mContext->shouldDropVirtualKey(when,
getDevice(), keyCode, scanCode)) {
return;
}
mKeyDowns.push();
KeyDown& keyDown = mKeyDowns.editTop();
keyDown.keyCode = keyCode;
keyDown.scanCode = scanCode;
}
mDownTime = when;
} else {
// Remove key down.
ssize_t keyDownIndex = findKeyDown(scanCode);
if (keyDownIndex >= 0) {
// key up, be sure to use same keycode as before in case of rotation
keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
mKeyDowns.removeAt(size_t(keyDownIndex));
} else {
// key was not actually down
ALOGI("Dropping key up from device %s because the key was not down. "
"keyCode=%d, scanCode=%d",
getDeviceName().string(), keyCode, scanCode);
return;
}
}
int32_t oldMetaState = mMetaState;
int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState);
bool metaStateChanged = oldMetaState != newMetaState;
if (metaStateChanged) {
mMetaState = newMetaState;
updateLedState(false);
}
nsecs_t downTime = mDownTime;
// Key down on external an keyboard should wake the device.
// We don't do this for internal keyboards to prevent them from waking up in your pocket.
// For internal keyboards, the key layout file should specify the policy flags for
// each wake key individually.
// TODO: Use the input device configuration to control this behavior more finely.
if (down && getDevice()->isExternal()
&& !(policyFlags & (POLICY_FLAG_WAKE | POLICY_FLAG_WAKE_DROPPED))) {
policyFlags |= POLICY_FLAG_WAKE_DROPPED;
}
if (metaStateChanged) {
getContext()->updateGlobalMetaState();
}
if (down && !isMetaKey(keyCode)) {
getContext()->fadePointer();
}
now = systemTime(SYSTEM_TIME_MONOTONIC);
ALOGD_READER("Latency info debug, processKey now(ms): %lld",now/1000000);
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
getListener()->notifyKey(&args);
}
这里最终将一个key event封装成一个NotifyKeyArgs调用getListener()->notifyKey(&args);处理了,getListener()得到的其实是mQueuedListener,我们看看mQueuedListener的notifyKey()
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
mArgsQueue.push(new NotifyKeyArgs(*args));
}
将NotifyKeyArgs压入到了队列中。然后就没有然后了。。。
其实还有有然后的,让我们回到loopOnce(),最后面有一句:mQueuedListener->flush();
很明显,是要队列中的数据送出去,送给谁呢?
我们看看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();
}
注意到args->notify(mInnerListener);
这里的mInnerListener其实就是InputDispatcher,当然下面的内容是下面章节要分析的了。。。。
好了,InputReader的内容就讲到这了,InputReader的基本任务就是从EventHub中取得数据,然后细加工成NotifyArgs传给InputDispatcher,InputDispatcher进一步处理后发送给准备好接受数据的窗口。
顺带提一下,虽然InputReader看上去一直在threadLoop()中死循环,但是大家不要忘了,threadLoop()中会通过getEvent()从EventHub中取数据,而当没有数据时getEvent()会阻塞在epoll_wait中,所以,大部分时间,InputReader还是在睡大觉的。