按键事件在native和jni中的流程

按键事件在activity中的流程一文已经简单介绍了按键事件在activity中的处理流程。本文则着重介绍事件进入activity之前的nativie和jni层的流程。

Native层的流程
native层相关类都在/frameworks/base/services/input目录下,InputManager、InputReader、InputDispatcher、EventHub是几个主要类。InputManager负责初始化的一些工作。在其构造方法里,会创建InputReader、InputDispatcher、EventHub三个对象,并创建相应的reader和dispatcher线程。在InputManager的start()和stop()方法里,会分别启动和停止这2个线程。

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();  // 初始化线程
}

InputReaderThread线程会循环调用InputReader的loopOnec()方法,而该方法会先从EventHub的getEvent()函数获取到当前InputDevice的事件,然后在processEventsLocked()中处理这些事件,最终事件会传递到InputDevice的process()函数中。

void InputReader::loopOnce() {
    // ... 一些本地变量
    { // acquire lock
      // config change和timeout的一些逻辑
    } // release lock

    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); // 读取事件并存入mEventBuffer

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();

        if (count) { // 事件处理
            processEventsLocked(mEventBuffer, count);
        }

        // timeout和device change的一些逻辑
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }

    // Flush queued events out to the listener.
    mQueuedListener->flush();
}

每一个InputDevice对应一种输入设备,而每一种输入设备都有自己的InputMapper列表。InputMapper的process()函数将RawEvent转换成key、touch等事件。按键事件对应的是KeyboardInputMapper。在其process()函数中,会先底层按键根据其key map layout转换为java层定义的按键值,然后将处理过的事件转换为NotifyKeyArgs,并存入一个Vector,通知到InputListener的notifyKey()函数。而InputReader里的InputListener正是在其构造方法里传入的InputDispatcher,即事件从InputReader里传递到了InputDispatcher中。

下面再来看InputDispatcher的notifyKey()函数。

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    // 检查key action是否有效...
    // 设置policy flag和meta state ...
    KeyEvent event;
    // 初始化key event
    event.initialize(args->deviceId, args->source, args->action,
            flags, args->keyCode, args->scanCode, metaState, 0,
            args->downTime, args->eventTime);

    // mPolicy是在InputDispatcher构造方法中传入
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);

    if (policyFlags & POLICY_FLAG_WOKE_HERE) {
        flags |= AKEY_EVENT_FLAG_WOKE_HERE;
    }

    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, args->keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);

        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}

代码中可以看到调用了mPolicy的interceptKeyBeforeQueueing()和filterInputEvent()函数,并将KeyEvent转换为了KeyEntry,存入事件队列中。在InputDispatcherThread中,InputDispatcher的dispatchOnce()函数会被循环调用。而在dispatchOnce()函数中,会先将事件队列中的事件一个个取出(第一个事件会先与PowerManagerService交互),找到当前焦点window作为target,使用command模式,生成command并放入command队列中,并在随后的循环中,依次执行这些command。中间会调用到mPolicy的interceptKeyBeforeDispatching(),和InputDispatcher的dispatchEventLocked()函数。而事件将最终转化为DispatchEntry存入当前focus window对应的Connection的outboundQueue中,并在InputDispatcher的startDispatchCycleLocked()函数中依次被取出并通过inputPublisher的publishKeyEvent()函数分发出去。这个inputPublisher是在/frameworks/base/libs/androidfw/InputTransport.h中定义,而实现则在/frameworks/base/libs/androidfw/InputTransport.cpp中。在InputTransport.h中分别定义了InputChannel、InputPublisher、InputConsumer三个接口。InputPublisher作为生产者,被InputDispatcher主动调用,将事件(key/touch)转发给上层应用;InputConsumer作为消费者,被上层应用主动调用,将事件(channel切换等)传给InputDispatcher(抱歉,底层c++代码看的并不是太明白,有错误请指出);而InputChannel使用管道做进程间通讯,提供上层和底层交互的机制。

这一段代码逻辑太复杂,跨度太大,就不一一贴出来了,只说明主要的流程。

疑问:
InputDispatcher由InputManager初始化,而InputManager又是由谁初始化呢?InputChannel最终又将事件传递到了哪里呢?

jni层的流程
jni层的代码在/frameworks/base/services/jni中,主要是几个以com_android_server_input_ 开头的cpp文件。我们首先看com_android_server_input_InputManagerService.cpp。看名字就可以知道,它是InputManagerService.java类(输入相关的系统服务)对应的jni接口。其核心类是NativeInputManager,而这个类实现了InputReaderPolicyInterface和InputDispatcherPolicyInterface接口。在NativeInputManager的构造方法中,就以自己为参数直接创建了InputManager对象,并最终将自己做为policy的实现传递给了InputReader和InputDispatcher。即上面最后一段代码片段中看到的mPolicy就是这个NativeInputManager。NativeInputManager的interceptKeyBeforeQueueing()和filterInputEvent()都是简单将native事件转换为java层的对应的事件对象,然后通过jni接口直接调用到了InputManagerService.java的同名函数中。另外在interceptKeyBeforeQueueing()函数中,还和PowerManagerService有一些简单交互。

在上层的WindowManagerService中的addWindow()方法中,会调用InputChannel的openInputChannelPair()函数,最终这个InputChannel会保存在WindownState中。而java层的InputChannel和native的InputChannel是对应的。

public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, InputChannel outInputChannel) {
            // ....

            if (outInputChannel != null && (attrs.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                String name = win.makeInputChannelName();
                InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
                win.setInputChannel(inputChannels[0]);
                inputChannels[1].transferTo(outInputChannel);

                mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
            }
        // ...
}

注意上边的InputManagerService的registerInputChannel()函数。该方法会调用native层的com_android_server_input_InputManagerService.cpp中的同名方法,并最终调用到InputDispatcher的同名方法。而InputDispatcher在这里会将InputChannel保存在Connection里。在InputDispatcher的startDispatchCycleLocked()方法中的事件最终都会通过这个InputChannel传到上层(抱歉,上层的InputEventReceiver如何和这个InputChannel对接并未弄明白)。最终在上层的InputEventReceiver的onInputEvent()函数中接收到这些事件。而InputEventReceiver的实现是在ViewRootImpl.java中,onInputEvent()回调中会调用ViewRootImpl的enqueueInputEvent()函数,最终进入PhoneWindow的内部类的dispatchKeyEvent()函数。如果事件未被处理,ViewRootImpl中会对方向键做查找焦点。如果不想使用默认的InputEventReceiver实现,还有一种方式是调用Window的takeInputQueue(InputQueue.Callback)函数,通过callback可以拿到所有的事件,自己做处理。细节逻辑可以查看ViewRootImpl的setView()函数。

疑问:
InputDispatcher的事件如何通过InputChannel传给InputEventReceiver???

其它的细节
上边已经提到了jni层的com_android_server_input_InputManagerService.cpp会调用java层InputManagerService的interceptKeyBeforeQueueing()、interceptKeyBeforeDispatching()、filterInputEvent()三个函数,下面我们再看看这3个函数的逻辑处理。其实在InputManagerService中还有很多其它jni函数,这些函数也都和native层InputManager的函数一一对应。
下面是这3个函数的代码,很简单的都传给了回调。

    // Native callback.
    private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
        return mWindowManagerCallbacks.interceptKeyBeforeQueueing(
                event, policyFlags, isScreenOn);
    }

    // Native callback.
    private long interceptKeyBeforeDispatching(InputWindowHandle focus,
            KeyEvent event, int policyFlags) {
        return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
    }

    // Native callback.
    final boolean filterInputEvent(InputEvent event, int policyFlags) {
        synchronized (mInputFilterLock) {
            if (mInputFilter != null) {
                try {
                    mInputFilter.filterInputEvent(event, policyFlags);
                } catch (RemoteException e) {
                    /* ignore */
                }
                return false;
            }
        }
        event.recycle();
        return true;
    }

前2个函数中的mWindowManagerCallbacks是在SystemServer将WindowManagerService的InputMonitor传递给了InputManagerService,而这个InputMonitor又将这些事件传给了WindowManagerService中的WindowManagerPolicy,而这个WindowManagerPolicy的最终实现在/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java中。在PhoneWindowManager中,interceptKeyBeforeQueueing()函数主要处理power、call事件(这些事件优先级最高,直接关系到screen on/off和电话)相关一些逻辑,如果要实现一些自定义按键开关screen的操作,可以放在这里处理;而interceptKeyBeforeDispatching()则主要处理了home、search、tab事件,这些事件都会直接启动相应的一些界面,如果要实现一些自定义按键启动应用的操作,可以放在这里处理。

第3个函数中的InputFilter则是在AccessbilityManagerService中通过WindowManagerService设置到InputManagerService中的,是一个AccessbilityInputFilter实例。这些事件会由系统中的AccessbilityService做处理,之后再有InputFilterHost送回InputManagerService,再由InputManagerService通过jni接口nativeInjectInputEvent()送回给native层的InputDispatcher,并最终在InputDispatcher的enqueueInboundEventLocked()函数中进入事件队列。接下来对这些filted的事件和其它事件就是一样的处理流程了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值