Android Framework window & display (2)-事件

400 阅读6分钟

Android系统的事件输入基于linux的Input子系统。触控屏事件和按键事件,从哪里输入又往哪里输出,虽然这个流程大部分对于Android系统来说是透明的,但是在多display,多窗口,多屏幕的情况下是值得研究的。

input对应各种输入事件,通过IMS,WMS,给到对应的window去处理事件,附一张最简化的事件传递流程:

img

1、Android Input子系统简介:

事件的输入

事件的输入,是从不同类获的设备得到不同的事件类型,事件类型也都是定义在/include/linux/input.h文件中。

adb shell getevent -l -c 16
​
add device 1: /dev/input/event0        
  name:     "ACCDET"
add device 2: /dev/input/event3
  name:     "goodix_ts"
add device 3: /dev/input/event2
  name:     "aw8697_haptic"
add device 4: /dev/input/event1
  name:     "mtk-kpd"
  
//按键
adb shell getevent -l /dev/input/event1
EV_KEY       KEY_VOLUMEDOWN       DOWN
EV_SYN       SYN_REPORT           00000000
EV_KEY       KEY_VOLUMEDOWN       UP
EV_SYN       SYN_REPORT           00000000
EV_KEY       KEY_VOLUMEUP         DOWN
EV_SYN       SYN_REPORT           00000000
EV_KEY       KEY_VOLUMEUP         UP
EV_SYN       SYN_REPORT           00000000//触摸
adb shell getevent -l /dev/input/event3
EV_ABS       ABS_MT_TRACKING_ID   0000036c
EV_ABS       ABS_MT_POSITION_X    000001fe
EV_ABS       ABS_MT_POSITION_Y    00000717
EV_KEY       BTN_TOUCH            DOWN
EV_SYN       SYN_REPORT           00000000
EV_ABS       ABS_MT_POSITION_X    00000207
EV_SYN       SYN_REPORT           00000000
EV_ABS       ABS_MT_POSITION_X    00000219
EV_ABS       ABS_MT_POSITION_Y    00000716

Android设备可以同时连接多个输入设备,比如说触摸屏,键盘,鼠标等等。用户在任何一个设备上的输入就会产生一个中断,经由Linux内核的中断处理以及设备驱动转换成一个Event,并专递给用户空间的应用程序进行处理。每个输入设备都有自己的驱动程序,数据接口也不尽相同,如何在一个线程里(上面说过只有一个nputReader Thread)把所有的用户输入都给捕捉到:这首先要归功于Linux 内核的输入子系统 (Input Subsystem),它在各种各样的设备驱动程序上加了一个抽象层,只要底层的设备驱动程席按照这层抽象接口来实现,上层应用就可以通过统的接口来访问所有的输入设备。

事件在IMS中的流程

输入子系统对input设备和event处理分发的枢纽是EventHub,以下构造函数,主要是通过mEpollFd和mINotifyFd监听input事件和设备增删事件:

EventHub::EventHub(void)
      : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
        mNextDeviceId(1),
        mControllerNumbers(),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false),
        mNeedToScanDevices(true),
        mPendingEventCount(0),
        mPendingEventIndex(0),
        mPendingINotify(false) {
    ensureProcessCanBlockSuspend();
​
    mEpollFd = epoll_create1(EPOLL_CLOEXEC);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
​
    mINotifyFd = inotify_init();
    mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,
                        strerror(errno));
    if (isV4lScanningEnabled()) {
        mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);
        LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",
                            VIDEO_DEVICE_PATH, strerror(errno));
    } else {
        mVideoWd = -1;
        ALOGI("Video device scanning disabled");
    }
​
    struct epoll_event eventItem = {};
    eventItem.events = EPOLLIN | EPOLLWAKEUP;
    eventItem.data.fd = mINotifyFd;
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
​
    int wakeFds[2];
    result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
​
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
​
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
                        errno);
​
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
                        errno);
​
    eventItem.data.fd = mWakeReadPipeFd;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
                        errno);
}

这个方法的调用在InputReader里面启了一个线程,循环调用去读事件mEventHub->getEvents和处理 processEventsLocked(mEventBuffer, count);

status_t InputReader::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    mThread = std::make_unique<InputThread>(
            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
    return OK;
}
​
...
​
void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> inputDevices;
    { // acquire lock
        std::scoped_lock _l(mLock);
​
        oldGeneration = mGeneration;
        timeoutMillis = -1;
​
        uint32_t changes = mConfigurationChangesToRefresh;
        if (changes) {
            mConfigurationChangesToRefresh = 0;
            timeoutMillis = 0;
            refreshConfigurationLocked(changes);
        } else if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
        }
    } // release lock
​
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
​
        if (count) {
            processEventsLocked(mEventBuffer, count);
        }
​
...
​
// mEventHub->getEvents
​
        mLock.unlock(); // release lock before poll
​
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
​
        mLock.lock(); // reacquire lock after poll
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||
                    rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
#if DEBUG_RAW_EVENTS
            ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
#endif
        //1处理输入事件    processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            switch (rawEvent->type) {
                case EventHubInterface::DEVICE_ADDED:
        //2添加输入设备    addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::DEVICE_REMOVED:
        //3删除输入设备    removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::FINISHED_DEVICE_SCAN:
        //4完成设备扫描    handleConfigurationChangedLocked(rawEvent->when);
                    break;
                default:
                    ALOG_ASSERT(false); // can't happen
                    break;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

输入事件处理,从这里找到指定的设备来处理。

void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
                                               size_t count) {
    auto deviceIt = mDevices.find(eventHubId);
    if (deviceIt == mDevices.end()) {
        ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
        return;
    }
​
    std::shared_ptr<InputDevice>& device = deviceIt->second;
    if (device->isIgnored()) {
        // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }
​
    device->process(rawEvents, count);
}

再之后经过一系统复杂的调用,到InputDispacher来进行分发,具体分析流程可以见博文,讲得非常详细www.jianshu.com/p/d10cfe854…,同时在这些调用中,可以发现安卓事件分发响应为啥这么耗时,因为这里面流程复杂,且有相当多的耗时函数。

这里的注释意思是所有事件都要保存按顺序执行。

    // Process all of the events in order for each mapper.
    // We cannot simply ask each mapper to process them in bulk because mappers may
    // have side-effects that must be interleaved.  For example, joystick movement events and
    // gamepad button presses are handled by different mappers but they should be dispatched
    // in the order received.

分发过程中,要确定targetdisplayid,inputTargets,就是拿到channels。

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // Preprocessing.
...
​
    // Add monitor channels from event's or focused display.
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
​
    // Dispatch the key.
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

InputPublisher通过 InputChannel::sendMessage将DispatchEntry发送给焦点窗口,而InputChannel的创建也就到了WMS给每个应用进程创建window的过程。

再附一张最复杂的事件处理流程:

img

2、Input与WMS的连接InputChannel:

IMS里面InputChannel::sendMessage发送消息

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
                                          std::shared_ptr<EventEntry> eventEntry,
                                          const std::vector<InputTarget>& inputTargets) {
    ATRACE_CALL();
#if DEBUG_DISPATCH_CYCLE
    ALOGD("dispatchEventToCurrentInputTargets");
#endif
​
    updateInteractionTokensLocked(*eventEntry, inputTargets);
​
    ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
​
    pokeUserActivityLocked(*eventEntry);
​
    for (const InputTarget& inputTarget : inputTargets) {
        sp<Connection> connection =
                getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
        if (connection != nullptr) {
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
        } else {
            if (DEBUG_FOCUS) {
                ALOGD("Dropping event delivery to target with channel '%s' because it "
                      "is no longer registered with the input dispatcher.",
                      inputTarget.inputChannel->getName().c_str());
            }
        }
    }

最终会调到enqueueDispatchEntryLocked里的 connection->outboundQueue.push_back(dispatchEntry.release());

void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
                                                 std::shared_ptr<EventEntry> eventEntry,
                                                 const InputTarget& inputTarget,
                                                 int32_t dispatchMode) {
  ...
​
    // Enqueue the dispatch entry.
    connection->outboundQueue.push_back(dispatchEntry.release());
    traceOutboundQueueLength(*connection);
}

发送消息和选取window是事件需要处理的两个问题,当确定哪个inputchannel的时候就已经有这两个答案了。

InputChannel其实就是linux unix socket的一种封装, unixsocket是linux的一种跨进程通信方式。系统创建InputChannel对即unix socket对,系统server端和程序client各只有其中一个,这样通过unix socket就可以给对方发送消息,而这里的事件就是通过这种方式从系统进程传递到程序进程的。系统InputChannel的整个处理逻辑如下:

这里写图片描述

在上篇中,viewroot创建时会根据需要创建一个inputchannel,传递给wms,window创建完之后,WMS addwindow的过程中,创建channel对,一个保存在window里,另一个传到程序端。

        /**
         * Does not construct an input channel for this window.  The channel will therefore
         * be incapable of receiving input.
         *
         * @hide
         */
        public static final int INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002;           
           
            
          if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
    void openInputChannel(InputChannel outInputChannel) {
        if (mInputChannel != null) {
            throw new IllegalStateException("Window already has an input channel.");
        }
        String name = getName();
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        mInputChannel = inputChannels[0];
        mClientChannel = inputChannels[1];
        mInputWindowHandle.token = mClient.asBinder();
        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            // If the window died visible, we setup a dummy input channel, so that taps
            // can still detected by input monitor channel, and we can relaunch the app.
            // Create dummy event receiver that simply reports all events as handled.
            mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
        }
        mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
    }
​

InputChannel.openInputChannelPair(name)在实现在native中,

frameworks/base/core/jni/android_view_inputChannel.cpp,frameworks/native/libs/input/InputTransport.cpp

通过registerInputChannel注册到native层。

客户端ViewRootImpl.setView的时候会把创建的inputchannel,注册并加到线程的looper里面。

    public InputEventReceiver(InputChannel inputChannel, Looper looper) {
        if (inputChannel == null) {
            throw new IllegalArgumentException("inputChannel must not be null");
        }
        if (looper == null) {
            throw new IllegalArgumentException("looper must not be null");
        }
​
        mInputChannel = inputChannel;
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);
​
        mCloseGuard.open("dispose");
    }

frameworks/base/core/jni/android_view_inputEventReceiver.cpp

status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}
​
void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}

至此InputChannel在ViewRootImpl中创建,在wms中创建连接socket,保存到IMS的连接池的过程就完成了。Input子系统的事件可以传到应用进程了。当接收到事件的时候,就会在应用层按如下流程处理,基本都是标准流程,就不展开说明了。

img

Anrdoid事件流程流程大体如文中几个图所示,需要特别注意的是:对系统来说,Inputdispatcher.cpp是一个非常重要的类,event的监听,设备的监听,与client端channel的连接,window的分发都是由他来处理的。

参考:

[Android Input(五)-InputChannel通信]  www.jianshu.com/p/b09afd403…

[Android输入子系统之InputDispatcher分发键盘消息过程分析​]  blog.csdn.net/chenweiaiya…