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

来源:互联网 发布:淘宝店铺分享到微信 编辑:程序博客网 时间:2024/05/21 17:20

零.写在最前

第一次尝试阅读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类,从类名上看应该是输入事件管理服务类的意思,和我猜测的差不多。进去这个类看看:

/* * 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类的声明如下:

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

            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的构造函数


<span style="font-family: Arial, Helvetica, sans-serif;"></span>
<span style="font-family: Arial, Helvetica, sans-serif;"> public InputManagerService(Context context) {</span>
        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是做什么的:

</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果然存在:

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中。

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的构造函数

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的构造函数。


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);

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/下设备文件类进行读写的类。再看第二个函数:

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.boolisKeyPressed(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等试试:
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 0
原创粉丝点击