Android输入事件从读取到分发一:是谁在读取输入事件

零.写在最前

第一次尝试阅读android输入系统的代码,免不了理解错误,如有错误,欢迎指正。

一.提出问题

android是基于linux kernel的,linux的事件获取需要读/dev/input下的设备文件节点。对android系统而言,是谁在读这些设备文件节点?读到以后又是怎么把它发送给view的?

二.猜测与验证

事件是一种看不到的东西,在android下,看不见的东西一般交给service来处理,系统service在系统启动的时候注册。android的输入事件的管理,应该是在系统启动的时候,注册成为系统的服务的。系统服务的注册在framworks/base/services/java/com/android/server/SystemServer.java中,使用addService来注册,在这个文件中搜索Input,很容易就发现有个InputManagerService类,从类名上看应该是输入事件管理服务类的意思,和我猜测的差不多。进去这个类看看:

  1. /* 
  2.  * Wraps the C++ InputManager and provides its callbacks. 
  3.  */  
  4. public class InputManagerService extends IInputManager.Stub  
  5.         implements Watchdog.Monitor {  
/*
 * Wraps the C++ InputManager and provides its callbacks.
 */
public class InputManagerService extends IInputManager.Stub
        implements Watchdog.Monitor {
    看一个类先看它的注释,注释中说:包装C++的inputManager 并且提供它的回调。再看这个类是继承于 IInputManager.Stub的,就大概知道它实现来远程系统调用的接口,其实所有的服务类都会继承于 IInputManager.Stub,因为stub继承自binder类,而binder是android用于进程间通信的。stub类的声明如下:

  1. /** Local-side IPC implementation stub class. */  
  2. public static abstract class Stub extends android.os.Binder implements android.hardware.input.IInputManager  
  3. {  
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.hardware.input.IInputManager
{
    回过头来,既然输入系统以服务的形式进行管理,那先看看把InputManagerService注册位服务的代码,就在SystemServer.java中往下索 InputManagerService,就会发现:

  1. Slog.i(TAG, "Input Manager");  
  2. inputManager = new InputManagerService(context);  
  3.   
  4. Slog.i(TAG, "Window Manager");  
  5. wm = WindowManagerService.main(context, inputManager,  
  6.         mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,  
  7.         !mFirstBoot, mOnlyCore);  
  8. ServiceManager.addService(Context.WINDOW_SERVICE, wm);  
  9. ServiceManager.addService(Context.INPUT_SERVICE, inputManager);  
  10.   
  11. mActivityManagerService.setWindowManager(wm);  
  12.   
  13. inputManager.setWindowManagerCallbacks(wm.getInputMonitor());  
  14. inputManager.start();  
            Slog.i(TAG, "Input Manager");
            inputManager = new InputManagerService(context);

            Slog.i(TAG, "Window Manager");
            wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

            mActivityManagerService.setWindowManager(wm);

            inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
            inputManager.start();

这里首先创建了一个InputManagerService的实例,然后把它传给了一个main函数,这里是个疑问,不知道是干什么的,暂时不管,然后就是重点了,可以看到调用了

ServiceManager的addService方法注册一个service.这里注册的当然是InputManagerService。注册后,给inputManager设置了一些回调函数,然后,就调用了start函数。

那当然显示从InputManagerSercie的构造函数入手了,毕竟构造函数最先杯调用。start方法暂时放置,这个方法应该很重要,从名字可以猜测它是启动事件输入框架的。

三.InputManagerService的构造函数


  1. <span style="font-family: Arial, Helvetica, sans-serif;">  
  2. </span>  
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
  1. <span style="font-family: Arial, Helvetica, sans-serif;"public InputManagerService(Context context) {</span>  
<span style="font-family: Arial, Helvetica, sans-serif;"> public InputManagerService(Context context) {</span>
  1.     this.mContext = context;  
  2.     this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());  
  3.   
  4.     mUseDevInputEventForAudioJack =  
  5.             context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);  
  6.     Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="  
  7.             + mUseDevInputEventForAudioJack);  
  8.     mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());  
  9.   
  10.     LocalServices.addService(InputManagerInternal.classnew LocalService());  
  11. }  
        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());

        LocalServices.addService(InputManagerInternal.class, new LocalService());
    }

它只有一个构造函数,看起来也不是很难。首先创建了一个Handler,其次去读mUseDevInputEventForAudioJack变量的值,这个值应该实在某个地方配置好的,暂且

不管它,然后就注册了一个handler,那看下这个handler是做什么的:

 
 
  1. </pre><pre code_snippet_id="1720818" snippet_file_name="blog_20160618_11_1111284" name="code" class="java">        public void handleMessage(Message msg) {  
  2.             switch (msg.what) {  
  3.                 case MSG_DELIVER_INPUT_DEVICES_CHANGED:  
  4.                     deliverInputDevicesChanged((InputDevice[])msg.obj);  
  5.                     break;  
  6.                 case MSG_SWITCH_KEYBOARD_LAYOUT:  
  7.                     handleSwitchKeyboardLayout(msg.arg1, msg.arg2);  
  8.                     break;  
  9.                 case MSG_RELOAD_KEYBOARD_LAYOUTS:  
  10.                     reloadKeyboardLayouts();  
  11.                     break;  
  12.                 case MSG_UPDATE_KEYBOARD_LAYOUTS:  
  13.                     updateKeyboardLayouts();  
  14.                     break;  
  15.                 case MSG_RELOAD_DEVICE_ALIASES:  
  16.                     reloadDeviceAliases();  
  17.                     break;  
  18.             }  
</pre><pre code_snippet_id="1720818" snippet_file_name="blog_20160618_11_1111284" name="code" class="java">        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DELIVER_INPUT_DEVICES_CHANGED:
                    deliverInputDevicesChanged((InputDevice[])msg.obj);
                    break;
                case MSG_SWITCH_KEYBOARD_LAYOUT:
                    handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
                    break;
                case MSG_RELOAD_KEYBOARD_LAYOUTS:
                    reloadKeyboardLayouts();
                    break;
                case MSG_UPDATE_KEYBOARD_LAYOUTS:
                    updateKeyboardLayouts();
                    break;
                case MSG_RELOAD_DEVICE_ALIASES:
                    reloadDeviceAliases();
                    break;
            }


 
 具体看不懂,表面上看好像是处理输入时间变动之类的东西,暂且不管,先看它的nativeInit方法,这份方法应该比较重要吧,要不然下一如看什么地方呀?先做好思想准备,nativeInit这个函数应该比较重要,可是这个方法在哪里呢?应该在一个向InputManagerService中注册方法的本地.cpp文件中,在android的framwork下搜索: 
 

find -name *InputManagerService* 发现

base/services/core/jni/com_android_server_input_InputManagerService.cpp

进去搜nativeInit果然存在:

  1. static jlong nativeInit(JNIEnv* env, jclass clazz,  
  2.         jobject serviceObj, jobject contextObj, jobject messageQueueObj) {  
  3.     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);  
  4.     if (messageQueue == NULL) {  
  5.         jniThrowRuntimeException(env, "MessageQueue is not initialized.");  
  6.         return 0;  
  7.     }  
  8.   
  9.     NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,  
  10.             messageQueue->getLooper());  
  11.     im->incStrong(0);  
  12.     return reinterpret_cast<jlong>(im);  
  13. }  
static jlong 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* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

    这里面创建了一个 NativeInputManager类的实例,并且调用了它的incStrong方法。NativeInputManager应该是InputManager的C++部分,Android系统的代码是很规范的,从名字可以猜出个一二。那先看看 NativeInputManager的构造函数吧。

三.NativeInputManager的构造函数与incStrong方法。

NativeInputManager同样定义在base/services/core/jni/com_android_server_input_InputManagerService.cpp中。

  1. NativeInputManager::NativeInputManager(jobject contextObj,  
  2.         jobject serviceObj, const sp<Looper>& looper) :  
  3.         mLooper(looper), mInteractive(true) {  
  4.     JNIEnv* env = jniEnv();  
  5.   
  6.     mContextObj = env->NewGlobalRef(contextObj);  
  7.     mServiceObj = env->NewGlobalRef(serviceObj);  
  8.   
  9.     {  
  10.         AutoMutex _l(mLock);  
  11.         mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;  
  12.         mLocked.pointerSpeed = 0;  
  13.         mLocked.pointerGesturesEnabled = true;  
  14.         mLocked.showTouches = false;  
  15.     }  
  16.   
  17.     sp<EventHub> eventHub = new EventHub();  
  18.     mInputManager = new InputManager(eventHub, thisthis);  
  19. }  
NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    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;
    }

    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

它的构造函数有个代码块,大概是做一些初始化的工作,然后创建了两个看着很重要的东西,eventHub和mInputManager,可以看到eventHub作为参数传给了

InputManager的构造函数,所以,这里着重看一下InputManager的构造函数。

四.InputManager的构造函数

  1. InputManager::InputManager(  
  2.         const sp<EventHubInterface>& eventHub,  
  3.         const sp<InputReaderPolicyInterface>& readerPolicy,  
  4.         const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {  
  5.     mDispatcher = new InputDispatcher(dispatcherPolicy);  
  6.     mReader = new InputReader(eventHub, readerPolicy, mDispatcher);  
  7.     initialize();  
  8. }  
InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}
这里又创建了 InputReader,   InputDispatcher两个类的实例,然后调用 initialize();用来初始化。代码到这里就越来越复杂了,头绪也越来越多,但是不能忘记我们的目标,也就是我们搞懂一开始提出的两个疑问,我们先想想第一个问题:“是谁在读/dev/input/下的文件节点?”,说到读,那这的InputReader是不是就是读的呢?从名字上 来看好像是的。所以,这里,我们还是要牢牢的抓住主干,先搞明白是谁在读/dev/input/下的文件节点,然后再思考其他的部分。那么,这里暂时对InputDisPatcher和initialize置之不理,着重看下InputReader到底做了什么?

五.InputReader的构造函数。


  1. InputReader::InputReader(const sp<EventHubInterface>& eventHub,  
  2.         const sp<InputReaderPolicyInterface>& policy,  
  3.         const sp<InputListenerInterface>& listener) :  
  4.         mContext(this), mEventHub(eventHub), mPolicy(policy),  
  5.         mGlobalMetaState(0), mGeneration(1),  
  6.         mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),  
  7.         mConfigurationChangesToRefresh(0) {  
  8.     mQueuedListener = new QueuedInputListener(listener);  
  9.   
  10.     { // acquire lock  
  11.         AutoMutex _l(mLock);  
  12.   
  13.         refreshConfigurationLocked(0);  
  14.         updateGlobalMetaStateLocked();  
  15.     } // release lock  
  16. }  
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
}
这里面就调用了两个函数,首先看第一个函数: refreshConfigurationLocked( 0 );

  1. void InputReader::refreshConfigurationLocked(uint32_t changes) {  
  2.     mPolicy->getReaderConfiguration(&mConfig);  
  3.     mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);  
  4.   
  5.     if (changes) {  
  6.         ALOGI("Reconfiguring input devices.  changes=0x%08x", changes);  
  7.         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);  
  8.   
  9.         if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {  
  10.             mEventHub->requestReopenDevices();  
  11.         } else {  
  12.             for (size_t i = 0; i < mDevices.size(); i++) {  
  13.                 InputDevice* device = mDevices.valueAt(i);  
  14.                 device->configure(now, &mConfig, changes);  
  15.             }  
  16.         }  
  17.     }  
  18. }  
void InputReader::refreshConfigurationLocked(uint32_t changes) {
    mPolicy->getReaderConfiguration(&mConfig);
    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);
            }
        }
    }
}
这个函数也是看的一脸懵逼,可以看到出现了InputDevice 这个类,从名字上看这不就是输入设备吗?然后调用了这个类的configure函数,这不就是配置一些参数吗?看来好像没错,这个类就是对/dev/input/下设备文件类进行读写的类。再看第二个函数:

  1. void InputReader::updateGlobalMetaStateLocked() {  
  2.     mGlobalMetaState = 0;  
  3.   
  4.     for (size_t i = 0; i < mDevices.size(); i++) {  
  5.         InputDevice* device = mDevices.valueAt(i);  
  6.         mGlobalMetaState |= device->getMetaState();  
  7.     }  
  8. }  
void InputReader::updateGlobalMetaStateLocked() {
    mGlobalMetaState = 0;

    for (size_t i = 0; i < mDevices.size(); i++) {
        InputDevice* device = mDevices.valueAt(i);
        mGlobalMetaState |= device->getMetaState();
    }
}

可以看到也是操作这个InputDevice。这个类到底是何方圣神,它到底是不是代表/dev/input/那些设备文件节点?

六.InputDevice

InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
        int32_t controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes) :
        mContext(context), mId(id), mGeneration(generation), mControllerNumber(controllerNumber),
        mIdentifier(identifier), mClasses(classes),
        mSources(0), mIsExternal(false), mDropUntilNextSync(false) {
}
InputDevice的构造函数是空的,那看看它有什么方法:

1.int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc)--获得状态?

2.boolInputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,

        const int32_t* keyCodes, uint8_t* outFlags)                         --支持的按键码?
3void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo)            --获得设备的信息?

4. bool isKeyPressed(int32_t code) {
    return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN; 
}                                                                           --判断按键是否按下吧?
。。。

可以看到它确实与/dev/input/下的设备文件节点密切相关。但并没有直接操作,而是借助getEventHub返回的EventHubInterface进行操作的。

七.EventHubInterface和EventHub

EventHubInterface是个虚函数,它的实现类是EventHub.
那我们想一下,要怎么读/dev/input/设备文件节点,怎么读呢?一般是多路复用吧?select,poll,epoll等,那我们在EventHub中搜搜看。
搜索的结果是select没找到,poll没出现,但是出现了epoll.搜索epoll_ctl等试试:
  
  
  1. if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {  
  2. mEpollFd = epoll_create(EPOLL_SIZE_HINT);  
  3. int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);  
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
重要的三个函数都出现了,所以这里应该就是对/dev/input读写的地方了。
具体的代码就不继续分析了,确实看的很累,这里是不是就找到了第一个问题的答案了呢?别忘了第一个问题:“是谁在读这些设备文件节点?”。
是的,就是这个EventHub类。
仔细回想一下,现在只是根据猜测加阅读,找到了读写/dev/input/设备文件节点的位置。可以想象一下,如果是自己写这部分代码,应该要一直监听这些输入设备吧,
应该有一个线程,一直循环监听这些输入设备,有事件的时候就去处理,没有事件的时候就睡眠,等待事件的到来。那么,这部分的代码是怎么样的呢?这次的分析知识找到了
一开始猜想的第一个答案,第二个答案还没有找到。要找到第二个答案,就需要更加细致的分析前面的流程,找到更多的内容,尤其要找到一个循环监听输入设备的线程的地方,
我相信它肯定存在,至于它是在什么地方创建,怎么调用的,下一节继续分析。











  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值