美文网首页
android 按键事件响应流程(二)InputReader

android 按键事件响应流程(二)InputReader

作者: Ed_Lannister | 来源:发表于2019-12-04 14:58 被阅读0次

    虚拟机启动SystemServer后,SystemServer在StartOtherService中新建了InputManager对象。记下来看看InputManagerService中做了什么事情。
    先看看InputManagerService的构造函数

    /frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
    public InputManagerService(Context context) {
            this.mContext = context;
            //获得能和UI主进程交互的Handler
            this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
            mUseDevInputEventForAudioJack =
                    context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
            //调用了这个native层的初始化函数
            mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
            LocalServices.addService(InputManagerInternal.class, new LocalService());
        }
    

    这里看看这个nativeInit干了什么事情

    private static native long nativeInit(InputManagerService service,Context context, MessageQueue messageQueue);
    ------JNI函数
    /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
    { "nativeInit","(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/MessageQueue;)J",(void*) nativeInit },
    ------JNI方法
    static jlong nativeInit(JNIEnv* env, jclass clazz,
            jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
        //获得一个native MessageQueue对象
        sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
        if (messageQueue == NULL) {
            jniThrowRuntimeException(env, "MessageQueue is not initialized.");
            return 0;
        }
        //new一个native InputManager对象,把刚才获得的native的message queue 对象传递给这个IM
        NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
                messageQueue->getLooper());
        im->incStrong(0);
        return reinterpret_cast<jlong>(im);
    }
    
    

    接下来看看这个native IM干了些什么事情。

    /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
    //这个函数有个内置函数,直接返回一个InputManager的强指针
    inline sp<InputManager> getInputManager() const { return mInputManager; }
    //
    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;
        }
        //新建了一个EventHub,然后根据这个EventHub新建了一个CPP层的InputManager对象,
        sp<EventHub> eventHub = new EventHub();
        mInputManager = new InputManager(eventHub, this, this);
    }
    

    EventHub的构建函数

    EventHub::EventHub(void) :
            mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
            mOpeningDevices(0), mClosingDevices(0),
            mNeedToSendFinishedDeviceScan(false),
            mNeedToReopenDevices(false), mNeedToScanDevices(true),
            mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
        //创建一个epoll对象
        mEpollFd = epoll_create(EPOLL_SIZE_HINT);
        //初始化一个INotifyFd对象,为应用监听文件系统提供帮助
        mINotifyFd = inotify_init();
        //将设备节点的路径dev/input/设置到INotifyFd对象中去
        int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
        //通过epoll_ctl,epoll事件注册函数,将INotifyFd对象注册到mEpollFd中去,这样当设备节点有读写变化的时候就会通过mINotifyFd向上进行通知。
        struct epoll_event eventItem;
        memset(&eventItem, 0, sizeof(eventItem));
        eventItem.events = EPOLLIN;
        eventItem.data.u32 = EPOLL_ID_INOTIFY;
        result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
        //管道文件描述符,描述一个管道的读端文件描述符和写端文件描述符.当一个线程的消息队列没有消息需要处理时,它就会在这个管道的读端文件描述符上进行睡眠等待.直到其他线程通过这个管道的写端文件描述符来唤醒它为止.                                                                                                                                                                          
        int wakeFds[2];
        result = pipe(wakeFds);
        mWakeReadPipeFd = wakeFds[0];
        mWakeWritePipeFd = wakeFds[1];
        //设置管道文件描述符的新旗标
        result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
        result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    
        eventItem.data.u32 = EPOLL_ID_WAKE;
        result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    
        int major, minor;
        getLinuxRelease(&major, &minor);
        mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
    }
    

    epoll是一种I/O事件通知机制,是linux 内核同时监听多个输入输出源,在其中一个或多个输入输出源可用的时候返回,然后对其的进行读写操作的一个实现。


    7368936-c868a5ee3b4cf9ff.png

    NativeInputManager还新建了一个InputManager对象

    /frameworks/native/services/inputflinger/InputManager.cpp
    InputManager::InputManager(
            //eventhub是NativeInputManager新建的,两个policy是NativeInputManager自身。
            const sp<EventHubInterface>& eventHub,
            const sp<InputReaderPolicyInterface>& readerPolicy,
            const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {  
        //这里新建了一个Dispatcher对象去分发事件,新建了一个Reader对象去读取事件
        mDispatcher = new InputDispatcher(dispatcherPolicy);
        mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
        initialize();
    }
    
    

    InputReader利用EventHub获取数据后,生成EventEntry事件,加入到InputDispatcher的mInboundQueue队列,再唤醒InputDispatcher线程。先看看initialize()函数干了什么事情,很简单的新建了mDispatcherThread和mReaderThread

    void InputManager::initialize() {
        mReaderThread = new InputReaderThread(mReader);
        mDispatcherThread = new InputDispatcherThread(mDispatcher);
    }
    

    然后前面讲到了systemserver在创建inputmanagerservice的时候就已经调用了start函数,这里的start就是run了新建的这两个读取分发线程。

    status_t InputManager::start() {
        status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
        if (result) {
            return result;
        }
    
        result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
        if (result) {
            mDispatcherThread->requestExit();
            return result;
        }
        return OK;
    }
    

    InputReaderThread的ThreadLoop就是调用mReader的loopOnce。

    bool InputReaderThread::threadLoop() {
        mReader->loopOnce();
        return true;
    }
    

    mReader是一个InputReaderInterface内部接口对象,InputReaderInterface是处理input event数据并将处理好的数据发送给input的listerner,接口里面可以做一些input设备信息、状态信息的查询,判断物理按键是否匹配framework按键列表key值;最重要的方法就是上面的loopOnce,这个方法只能在InputReaderThread里面调用,用来处理读取和处理从eventhub过来的信息。

    sp<InputReaderInterface> mReader;
    //无穷无尽的从eventhub里面读取数据并处理
    class InputReaderThread : public Thread {
    public:
        InputReaderThread(const sp<InputReaderInterface>& reader);
        virtual ~InputReaderThread();
    private:
        sp<InputReaderInterface> mReader;
        virtual bool threadLoop();
    };
    

    loopOnce查看InputReader配置是否修改,如界面大小、方向、键盘布局重新加载、指针速度改变等、从EventHub读取事件,其中EVENT_BUFFER_SIZE = 256、处理事件、将事件传到InputDispatcher,getEvent是阻塞式的,只有有事件或者被唤醒的时候才会执行。


    20170609101625473.png
    void InputReader::loopOnce() {
    ···
    //从EventHub里面通过getEvent获取事件,
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    ···
    //如果count大于0就是代表有数据,就processEventsLocked处理
    //原始输入事件、设备加载事件、设备卸载事件及FINISHED_DEVICE_SCAN事件
            if (count) {
                processEventsLocked(mEventBuffer, count);
            }
    ···
    //回调回java层通知input设备状态发生改变
    if (inputDevicesChanged) {
            mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    ···
    //将数据送到listener,这里的listener就是diapatcher,为了防止死锁,flush操作放在了loopOnce的锁外面,因为dispatcher很有可能回调InputReader,如果也请求相同的锁对象就会死锁
    mQueuedListener->flush();
    }
    

    processEventsLocked里会根据rawEvent的type进行事件的预处理,包含上面提到的四种操作,其中processEventsForDeviceLocked(deviceId, rawEvent, batchSize);会根据device id,先判断device会不会被忽略,然后不会被忽略就交给相应的设备进行处理,inputDevice会调用inputMapper进行处理

                for (size_t i = 0; i < numMappers; i++) {
                    InputMapper* mapper = mMappers[i];
                    mapper->process(rawEvent);
                }
    ···
    //一共有这么多mapper
    SwitchInputMapper 
    VibratorInputMapper
    KeyboardInputMapper
    CursorInputMapper
    TouchInputMapper
    SingleTouchInputMapper
    MultiTouchInputMapper
    JoystickInputMapper 
    ···
    
    5982616-ab16c91640ccbfa3.png

    这里我们仅仅看看按键的mapper,调用getListener()->notifyKey(&args)调用的是QueuedInputListener的notifyKey函数,它里边有许多notifyXXX函数,做的事情都是将NotifyXXXArgs放入它的mArgsQueue队列中存储,等待处理。

    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;
                if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {
                    keyCode = AKEYCODE_UNKNOWN;
                    flags = 0;
                }
                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;
            }
        }
        }
    }
    

    processKey函数,当keydown,keyup的时候都会去找对应的keycode,scancode,还有down/up time,keyup的时候还要专门在map表里面清除key down的信息。

    void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
            int32_t scanCode, uint32_t policyFlags) {
            ···
            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);
            ···
    }
    

    这里就通过getListener到InputDispacter里面notifyKey函数,mPolicy->interceptKeyBeforeQueueing(&event, /byref/ policyFlags);该函数最后会调用到java层的PhoneWindowManagerService函数,之后构造一个KeyEntry的对象,通过enqueueInboundEventLocked将按键事件入队列,入队成功,通过mLooper唤醒等待的InputDispatcher,进行事件分发。

        KeyEvent event;
        event.initialize(args->deviceId, args->source, args->action,
                flags, keyCode, args->scanCode, metaState, 0,
                args->downTime, args->eventTime);
    
        mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    bool needWake;
        { // acquire lock
            mLock.lock();
    
            if (shouldSendKeyToInputFilterLocked(args)) {
                mLock.unlock();
    
                policyFlags |= POLICY_FLAG_FILTERED;
                if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                    return; // event was consumed by the filter
                }
                mLock.lock();
            }
    
            int32_t repeatCount = 0;
            KeyEntry* newEntry = new KeyEntry(args->eventTime,
                    args->deviceId, args->source, policyFlags,
                    args->action, flags, keyCode, args->scanCode,
                    metaState, repeatCount, args->downTime);
    
            needWake = enqueueInboundEventLocked(newEntry);
            mLock.unlock();
        } // release lock
    
        if (needWake) {
            mLooper->wake();
        }
    

    现在看看mQueuedListener.flush是个什么操作,把刚才args队列里面的事件全部通知给InputDispatcher,去到dispatcher里面继续执行。

    ···
    sp<QueuedInputListener> mQueuedListener;
    ···
    //InputListenerInterface 是input用来通知InputListener输入事件的接口
    class InputListenerInterface : public virtual RefBase {
    protected:
        InputListenerInterface() { }
        virtual ~InputListenerInterface() { }
    
    public:
        virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) = 0;
        virtual void notifyKey(const NotifyKeyArgs* args) = 0;
        virtual void notifyMotion(const NotifyMotionArgs* args) = 0;
        virtual void notifySwitch(const NotifySwitchArgs* args) = 0;
        virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) = 0;
    };
    ···
    //QueuedInputListener的构造函数就是新建一个这样的内部listener
    QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
            mInnerListener(innerListener) {
    }
    ···
    void QueuedInputListener::flush() {
        size_t count = mArgsQueue.size();
        for (size_t i = 0; i < count; i++) {
            //NotifyArgs是所有input事件对象的父类
            NotifyArgs* args = mArgsQueue[i];
            args->notify(mInnerListener);
            delete args;
        }
        mArgsQueue.clear();
    }
    
    图片.png

    相关文章

      网友评论

          本文标题:android 按键事件响应流程(二)InputReader

          本文链接:https://www.haomeiwen.com/subject/isyfgctx.html