按键事件inputflinger大体流程

前言:
  在android2.3版本的时候,详细的对照代码了解了遍按键事件从驱动到framework的流程。并在高仿机上实现新增按键从驱动到系统的功能。随着时间的不断推进,工作的重点越来越向驱动集中,对HAL和系统的了解停留在之前的理解上。近来看一个windowstate的提交,实现发现对按键流程还有不太明白的地方。现在回过头来详细的再对按键梳理一遍,首先来着个大体的序列图:按键事件inputflinger大体流程_第1张图片
在这里按照序列图的标号,对照着代码大体的理下思路:

InputFlinger

1.InputManager.cpp这个类是inputflinger的入口类,在JNI里面会通过new生成一个实体对象,然后start启动inputflinger的功能。构造函数如图所示
按键事件inputflinger大体流程_第2张图片
上面的图涉及到4个非常重要的类,就是这四个类实现事件的读取和派发
1.InputReader :这个类主要是从input目录下,读取所有设备产生的所有按键事件。并根据设备的不同(如键盘,单点触摸,多点触摸,轨迹球…)对数据进行处理
2.InputDispatcher :这个类主要的功能就是派发事件,会涉及到两次按键拦截
3.InputReaderThread  : 创建一个线程不停的调用inputReader读取事件
3.InputDispatcherThread :创建一个线程不停的轮询事件队列和命令队列是否有数据并处理
start()接口调用后,上面两个线程就run起来了。这里首先从读按键线程开始看
2-3:read线程的启动

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

按键事件inputflinger大体流程_第3张图片
以上可以看出looponce,线程循环主要作用有2:
1.通过eventhub获取按键事件,并将事件保存在Buffer里面。并返回事件个数
2.如果事件数不为0,则进行对事件的处理

4.事件获取getEvents接口解读
getEvents这个接口在不同的android版本变化比较大,在最开始的2.3及后继一段时间内的版本实现,都是不停的主动循询/dev/input 目录下的所有设备并从中读取可能的事件。在android8.0也就是我目前使用的版本上,实现方式优化了些,但原理不变。
首先:来看下EventHub的构造函数
按键事件inputflinger大体流程_第4张图片
这里通过inotify的方式监控dev/input/目录
然后把inofity添加到epoll上,通过epoll_wait的方式等待数据的到来,比不停的轮询效率要高。下面来看看getEvent的比较核心的代码:

int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
//监听并等待事件到来

按键事件inputflinger大体流程_第5张图片
上面代码截图就是核心的对device设备读取按键事件,这里需要注意的是。原生代码里面最大只支持8个input设备,按键事件缓存最大只有256个按键时键。

5.如果有读取到按键事件,则对数据进行原始处理。有些数据需要经过处理才能被上层更好的使用。如多点触摸等等,来看processEventsLocked实现按键事件inputflinger大体流程_第6张图片

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex < 0) {
        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
        return;
    }

    InputDevice* device = mDevices.valueAt(deviceIndex);
    if (device->isIgnored()) {
        //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }

    device->process(rawEvents, count);
    //这里就是对不同设备的事件进行不同的处理了,在不同的android版本上这里的实现也不太一样
}

说到device设备类型,这里需要说明下代码里面支持多少种设备。我们在代码里面搜索下addMapper这个函数调用,每个不同的设备都需要调用它把自己注册上去

        device->addMapper(new **KeyboardInputMapper**(device, keyboardSource, keyboardType));
        //键盘也就是按键事件
    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
        device->addMapper(new **SwitchInputMapper**(device));
    }

    // Scroll wheel-like devices.
    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
        device->addMapper(new **RotaryEncoderInputMapper**(device));
    }

    // Vibrator-like devices.
    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
        device->addMapper(new **VibratorInputMapper**(device));
    }

    // Cursor-like devices.
    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
        device->addMapper(new **CursorInputMapper**(device));
    }

    // Touchscreens and touchpad devices.
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        device->addMapper(new **MultiTouchInputMapper**(device));
        //多点触摸
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        device->addMapper(new **SingleTouchInputMapper**(device));
        //单点触摸
    }

    // Joystick-like devices.
    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
        device->addMapper(new **JoystickInputMapper**(device));
    }

    // External stylus-like devices.
    if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
        device->addMapper(new **ExternalStylusInputMapper**(device));
    }

按键设备会调用KeyboardInputMapper的process函数进行处理
触摸处理会调用SingleTouchInputMapper或者MultiTouchInputMapper的process函数来进行处理,处理完成后就是对事件的派发过程了。这里为了篇幅不要太大只跟踪key事件处理和派发,touch事件的可以对照序列图自行跟踪代码。
7&10.Key事件的process处理:
如果事件类型为EV_KEY,则调用processKey函数来进行处理,来看实现
按键事件inputflinger大体流程_第7张图片

    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    getListener()->notifyKey(&args);

然后通过nofityKey接口将事件,发送到消息队列。notifyKey这个getListener 不同的版本实现不一样,但最终都是指向inputDispatch类的notifyKey接口,当然touch事件会指向notifyMotion接口。
12.inputDispatch类的notifyKey接口实现。
按键事件inputflinger大体流程_第8张图片
上面图有主要有两个重点:
1.interceptKeyBeforeQueueing第一次按键事件拦截,这个最终的实现是在PhoneWindowManager.java文件里面。
2.将按键事件添加到mInboundQueue这个队列的尾部,会有线程来从这个队列读取数据进行处理。
开头就说过inputManger会创建两个线程,read线程上面已经介绍完了。下面来介绍dispatch线程,也就是处理事件队列的线程
13-15 线程创建和事件派发
按键事件inputflinger大体流程_第9张图片
我们来看dispatchOnceInnerLocked这个函数对key事件的处理的核心代码
按键事件inputflinger大体流程_第10张图片
dispatchKeyLocked对TYPE_KEY事件处理的核心代码:
按键事件inputflinger大体流程_第11张图片
doInterceptKeyBeforeDispatchingLockedInterruptible这个command处理就是第二次事件拦截的地方。

    dispatchEventLocked(currentTime, entry, inputTargets);
    //经过两次事件拦截后,这里是最终的事件处理地方。无论key事件还是touch事件。虽然touch事件也会有两次拦截,但是基本没什么用。intercept处理的java代码基本都是key的

16事件向android派发
这里的代码调用流程就不细说了,大体是这样的
dispatchEventLocked-〉prepareDispatchCycleLocked->enqueueDispatchEntriesLocked->startDispatchCycleLocked->inputPublisher.publishKeyEvent
按键事件inputflinger大体流程_第12张图片
17.sendMessage.在inputTransport.cpp文件里面,有publickKeyevent和sendmessage的实现

status_t InputPublisher::publishKeyEvent(
        uint32_t seq,
        int32_t deviceId,
        int32_t source,
        int32_t action,
        int32_t flags,
        int32_t keyCode,
        int32_t scanCode,
        int32_t metaState,
        int32_t repeatCount,
        nsecs_t downTime,
        nsecs_t eventTime) {
#if DEBUG_TRANSPORT_ACTIONS
    ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
            "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
            "downTime=%lld, eventTime=%lld",
            mChannel->getName().string(), seq,
            deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
            downTime, eventTime);
#endif

    if (!seq) {
        ALOGE("Attempted to publish a key event with sequence number 0.");
        return BAD_VALUE;
    }

    InputMessage msg;
    msg.header.type = InputMessage::TYPE_KEY;
    msg.body.key.seq = seq;
    msg.body.key.deviceId = deviceId;
    msg.body.key.source = source;
    msg.body.key.action = action;
    msg.body.key.flags = flags;
    msg.body.key.keyCode = keyCode;
    msg.body.key.scanCode = scanCode;
    msg.body.key.metaState = metaState;
    msg.body.key.repeatCount = repeatCount;
    msg.body.key.downTime = downTime;
    msg.body.key.eventTime = eventTime;
    return mChannel->**sendMessage**(&msg);
}
status_t InputChannel::sendMessage(const InputMessage* msg) {
    size_t msgLength = msg->size();
    ssize_t nWrite;
    do {
        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);

    if (nWrite < 0) {
        int error = errno;
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(),
                msg->header.type, error);
#endif
        if (error == EAGAIN || error == EWOULDBLOCK) {
            return WOULD_BLOCK;
        }
        if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) {
            return DEAD_OBJECT;
        }
        return -error;
    }

    if (size_t(nWrite) != msgLength) {
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
                mName.string(), msg->header.type);
#endif
        return DEAD_OBJECT;
    }

#if DEBUG_CHANNEL_MESSAGES
    ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type);
#endif
    return OK;
}

sendMessage最终的实现原理就是通过socketpair函数生成两个相互关联的socket,
服务端发,client端收.JNI为服务端, framework为client。通过socket最终把事件送到了framework层,在下篇文章里面会着重介绍channel的创建和,上层消息的派出
本篇到这里就结束了,如果有疑问和错误敬请指出,谢谢!

你可能感兴趣的:(android)