直接从inputflinger
开始分析Android是获取到键盘输入和屏幕触控事件的流程。代码路径如下:
frameworks/native/services/inputflinger
frameworks/native/include/input
frameworks/native/libs/input
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
InputManager
InputManager
是Android 输入事件处理的核心,其创建了两个线程:
-
InputReaderThread
该线程负责从获取linux 输入事件,并根据配置文件中的设置对linux输入事件进行预处理,最后将其放入到一个事件队列中,等待另一个
InputDispatcherThread
处理。 -
InputDispatcherThread
从事件队列中获取事件并将其异步的分发到应用程序。
对InputManager
的分析主要分为三步部分:
InputManager
的创建InputReaderThread
InputDispatcherThread
开干!!!
InputManager
是如何创建的
首先介绍一下其成员变量。
sp<InputReaderInterface> mReader;
sp<InputReaderThread> mReaderThread; //读线程
sp<InputDispatcherInterface> mDispatcher;
sp<InputDispatcherThread> mDispatcherThread; // 事件分发线程
-
InputReaderInterface
其实际类型就是
InputReader
。其负责从EventHub
中获取linux raw event,并进行相应的预处理。 -
InputDispatcherInterface
实际类型就是
InputDispatcher
。用于通知Android系统有事件输入。
再来看一下其构造函数的实现。
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
// 实例化
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
//创建前面提到的两个线程
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
额,有三个参数,关于EventHub
,点这里。
另外两个?
//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
// ...
// EventHub在这里初始化
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
-
InputReaderPolicyInterface
实际类型是
NativeInputManager
,其接口的实际实现都是在InputManagerService
中,NativeInputManager
只是通过jni调用其java端的实现。InputReader
构造时用到了其作为参数,后面分析InputReader
时,再来分析起作用。类注释:
Input reader policy interface.
The input reader policy is used by the input reader to interact with the Window Manager and other system components.
-
InputDispatcherPolicyInterface
实际类型是
NativeInputManager
,其接口的实际实现都是在InputManagerService
中,NativeInputManager
只是通过jni调用其java端的实现。TODO: 待补充
最后,在InputManagerService
中,会通过JNI调用InputMananger::start
,这时,我们的两个线程就要开始工作了。。。
输入事件读取线程(InputReaderThread
)
创建InputReader
InputReader
构造函数:
//mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy),
mGlobalMetaState(0), mGeneration(1),
mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
{ // acquire lock
AutoMutex _l(mLock);
refreshConfigurationLocked(0);
updateGlobalMetaStateLocked();
} // release lock
}
构建步骤:
-
创建
QueuedInputListener
实例。class QueuedInputListener : public InputListenerInterface { protected: virtual ~QueuedInputListener(); public: explicit QueuedInputListener(const sp<InputListenerInterface>& innerListener); virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args); virtual void notifyKey(const NotifyKeyArgs* args); virtual void notifyMotion(const NotifyMotionArgs* args); virtual void notifySwitch(const NotifySwitchArgs* args); virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args); void flush(); private: sp<InputListenerInterface> mInnerListener; Vector<NotifyArgs*> mArgsQueue; };
QueuedInputListener::mInnerListener
实际上就是InputDispatcher
的实例。当InputReader
处理完事件后将RawEvent
转换成NotifyArgs
对象,并调用QueuedInputListener
的成员函数notify*(...)
(继承至InputListenserInterface
)将其放入到QueuedInputListener::mArgsQueue
中。然后再调用QueueInputListener::flush
将队列中的NotifyArgs
交由InputDispatcher
处理。void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) { mArgsQueue.push(new NotifyKeyArgs(*args)); } 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(); }
以
NotifyKeyArgs
为例:void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const { listener->notifyKey(this); }
最终,还是调用了
InputDispatcher::notifyKey
。到这里,事件就从
InputReader
转交给InputDispatcher
处理。但是,如下函数还是在
InputReaderThread
线程中执行,InputDispatcherThread
主要干甚???virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args); virtual void notifyKey(const NotifyKeyArgs* args); virtual void notifyMotion(const NotifyMotionArgs* args); virtual void notifySwitch(const NotifySwitchArgs* args); virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
-
refreshConfigurationLocked(int changes)
根据
changes
更新所有输入设备的配置信息。void InputReader::refreshConfigurationLocked(uint32_t changes) { mPolicy->getReaderConfiguration(&mConfig); // /system/etc/excluded-input-devices.xml mEventHub->setExcludedDevices(mConfig.excludedDeviceNames); if (changes) { ALOGI("Reconfiguring input devices. changes=0x%08x", changes); nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) { mEventHub->requestReopenDevices(); } else { for (size_t i = 0; i < mDevices.size(); i++) { InputDevice* device = mDevices.valueAt(i); device->configure(now, &mConfig, changes); } } } }
-
mPolicy->getReaderConfiguration(&mConfig);
这个实际实现在NativeInputMnanager
中,通过jni回调java函数,获取一些配置信息和一些常量(比如双击之间的最小时间和最大时间间隔)并将其保存到InputReaderConfiguration
中。 -
mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
告诉EventHub
有些设备我们是不要处理的。当
EventHub
打开输入设备后,会通过ioctl获取到设备名称,然后在根据其是否在禁用设备列表中来决定是否使用该输入设备。 -
后面就是根据
changes
的值来选择性的执行一些操作。InputDevice
...后面会提到的。。。
-
-
updateGlobalMetaStateLocked
void InputReader::updateGlobalMetaStateLocked() { mGlobalMetaState = 0; //所以,我接入两个键盘,一个按下 shift 另一个按下 a, 也会变成 A??? for (size_t i = 0; i < mDevices.size(); i++) { InputDevice* device = mDevices.valueAt(i); mGlobalMetaState |= device->getMetaState(); } }
似乎和组合按键的处理有关。
InputReadThread的主循环
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
额,没毛病。重点就是InputReadr::loopOnce
,主要功能就是通过EventHub::getEvents
获取事件,然后将处理相应的事件(通过调用与事件关联的InputDevice
的process
方法处理事件),最后就是将处理后的事件(都在QueuedInputListener
中存着)都交友InputDispatcher
处理(通过``QueuedInputListener::flush
函数)。
void InputReader::loopOnce() {
...
// 从 EventHub 中获取事件。具体包括 输入设备的接入、移除,还有就是按键或触摸等
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
processEventsLocked(mEventBuffer, count);
...
mQueuedListener->flush();
}
processEventsLocked
从EventHub
中获取到的事件有两种,一种是输入设备的产生的事件信息比如鼠标移动,按下键盘,触摸屏幕等,还有一种就是输入设备的热插拔信息,比如蓝牙输入设备的连入和断开。
在EventHub
中,使用Device
来描述输入设备;而在InputReader
中,使用InputDevice
来表示输入设备。前者主要是保存输入设备的硬件信息(从内核获取到的);后者主要是描述这个输入设备的功能。
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;
}
}
InputDevice
先来看一下InputDevice
提供了那些功能,后面分析事件处理的时候就很容易了。
只分析InputDevice
的创建。。
先来看看其成员变量
InputReaderContext* mContext;
int32_t mId;// 通过这个Id就能获取到EventHub中的`Device`,进而获取到与其关联的配置信息
int32_t mGeneration;
int32_t mControllerNumber;
InputDeviceIdentifier mIdentifier; // 同EvnetHub::Device::InputDeviceIdentifier
String8 mAlias;
uint32_t mClasses;//同EventHub::Device::mClasses,输入设备接入时,EventHub通过从驱动获取信息以及解析配置文件,来判断该输入设备的类型(同一个设备可能属于多种输入类型)。
Vector<InputMapper*> mMappers; // 每一种输入类型,对应一个 InputMapper
uint32_t mSources; // 感觉和 classes有冲突啊,
bool mIsExternal;
bool mHasMic;
bool mDropUntilNextSync;
typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
PropertyMap mConfiguration; // 对应IDC文件
创建过程:
// 根据 class的取值添加 InputMapper
InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
// 和前面的InputReader::refreshConfigurationLocked类似,
device->configure(when, &mConfig, 0);
device->reset(when); //忽略
这里介绍一下configure
void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) {
mSources = 0;
if (!isIgnored()) {
if (!changes) { // first time only
mContext->getEventHub()->getConfiguration(mId, &mConfiguration);
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
// 从InputManangerService中获取键盘布局,该键盘布局是从设置中加载的。
// 不同国家的键盘布局可能不一样,需要动态设置
sp<KeyCharacterMap> keyboardLayout =
mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
// 将应用设置的布局 和 输入设备关联的布局文件合并。 overlay具备最高有限级
if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) {
bumpGeneration();
}
}
}
// 所以,为什么要这样???直接在创建 InputMapper时加上不就好了?
size_t numMappers = mMappers.size();
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->configure(when, config, changes);
mSources |= mapper->getSources();
}
}
}
以键盘为例,其使用的布局通常是在generic.kl
中定义的,这个布局文件是按照qwerty形式的键盘定义的,有些键盘的布局不是这样的,如果我们要正常使用这种键盘,就需要在设置中将键盘布局更改为何我们键盘对应的布局。
frameworks/base/packages/InputDevices/res/raw
目录下定义了很多国家的键盘 kcm 文件。在kcm文件中,通过
map key
来重新创建 linux key code和android key code的映射关系,这个优先级大于kl
文件中定义的映射关系。
处理事件
实际工作由InputDevice::process
完成。
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
mDropUntilNextSync = false;
} else {
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
mDropUntilNextSync = true;
reset(rawEvent->when);
} else {
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent);
}
}
}
}
之前提到过,创建InputDevice
时,会根据输入设备的classes
来添加InputMapper
,我们以键盘为例,其对应的InputMapper
是KeyboardInputMapper
。
KeyBoardInputMapper
通过getevent -l
我们能看到操作键盘时InputReader
需要处理的事件。
#按下
/dev/input/event2: EV_MSC MSC_SCAN 0007002c
/dev/input/event2: EV_KEY KEY_SPACE DOWN
/dev/input/event2: EV_SYN SYN_REPORT 00000000
#松开
/dev/input/event2: EV_MSC MSC_SCAN 0007002c
/dev/input/event2: EV_KEY KEY_SPACE UP
/dev/input/event2: EV_SYN SYN_REPORT 00000000
一次操作对应的事件顺序都是固定的:EV_MSC
、EV_KEY
和EV_SYN
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)) {
// 处理键值
processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
}
break;
}
case EV_MSC: {
if (rawEvent->code == MSC_SCAN) {
mCurrentHidUsage = rawEvent->value;
}
break;
}
case EV_SYN: {
if (rawEvent->code == SYN_REPORT) {
mCurrentHidUsage = 0;
}
}
}
}
解释一下 usageCode
在 kl文件中, 有如下语法
key usage 0xXXXXXXXX A
0xXXXXXXXX 就是usage code ,对应的就是 EV_MSC MSC_SCAN 0007002c 中的0007002c 。
usageCode 的优先级高于 scanCode
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
int32_t usageCode) {
int32_t keyCode;
int32_t keyMetaState;
uint32_t policyFlags;
// scanCode 和 usageCode是linux key Code
// 这里将其按照 kl 文件 或 kcm 文件中的`map key`将其转换成 Android Key Code
// 然后按照 KCM 中定义的规则,将其转换成相应的 Android Key Code
// 并且获取 keyMetaState(组合按键) 和 policyFlags (kl文件中定义, 默认为0, VIRTUAL GESTURE 和 FUNCTION)
if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
&keyCode, &keyMetaState, &policyFlags)) {
keyCode = AKEYCODE_UNKNOWN;
keyMetaState = mMetaState;
policyFlags = 0;
}
...
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
//上报事件,QueuedInputListener::notifyKey.
// 本质上就是 将 NotifyKeyArgs 放入了其 持有的 队列中.....
getListener()->notifyKey(&args);
}
得,在这里将事件放入到QueuedInputListener
中的队列,然后在InputReader::loopOnce
中将这些事件逐一交由InputDispatcher
处理。