零.写在最前

第一次尝试阅读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. 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等试试:
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/设备文件节点的位置。可以想象一下,如果是自己写这部分代码,应该要一直监听这些输入设备吧,
应该有一个线程,一直循环监听这些输入设备,有事件的时候就去处理,没有事件的时候就睡眠,等待事件的到来。那么,这部分的代码是怎么样的呢?这次的分析知识找到了
一开始猜想的第一个答案,第二个答案还没有找到。要找到第二个答案,就需要更加细致的分析前面的流程,找到更多的内容,尤其要找到一个循环监听输入设备的线程的地方,
我相信它肯定存在,至于它是在什么地方创建,怎么调用的,下一节继续分析。










更多相关文章

  1. Android应用程序线程消息循环模型分析
  2. Android应用程序线程消息循环模型分析
  3. 范例解析:学习Android的IPC主板模式
  4. Android(安卓)JNI 技术简介
  5. Cocos2d-x从C++端调用Android端的非静态函数接口
  6. android NDK的第一个实验
  7. android webkit JavaScript 不能处理onkeydown的上下左右键,引发
  8. Android应用程序线程消息循环模型分析
  9. 箭头函数的基础使用

随机推荐

  1. android fragment的显示隐藏生命周期简述
  2. 第八节(Activity布局初步一)
  3. Android(安卓)刷脸神器,颜值价更高
  4. 自定义两行可左右滑动的GridView
  5. Android(安卓)读取工程中的txt文件
  6. ninepatch 分析
  7. Zygote
  8. Android Gradle和Gradle插件区别
  9. Android使用cocos2d做简单的平移动画
  10. Android train——ListView绑定ArrayAdap