Android Input系统3 InputReader线程

一 InputReader启动

在上一篇文章Android Input系统2 输入系统启动中,我们知道 IMS 服务的启动过程中会创建两个 native 线程,分别是 InputReader 和 InputDispatcher。接下来从 InputReader 线程的执行过程的起点 threadLoop 开始分析。

1.1 threadLoop

InputReaderBase.cpp

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

threadLoop 返回值 true 代表的是会不断地循环调用 loopOnce()。另外,如果当返回值为 false 则会退出循环。整个过程是不断循环的地调用 InputReader 的 loopOnce() 方法。

1.2 loopOnce

InputReader.cpp

void InputReader::loopOnce() {
    ......
    {
        AutoMutex _l(mLock);
        uint32_t changes = mConfigurationChangesToRefresh;
        if (changes) {
            timeoutMillis = 0;
            ......
        } else if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
        }
    }

    //从EventHub读取事件,其中EVENT_BUFFER_SIZE = 256
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();
        if (count) { //处理事件
            processEventsLocked(mEventBuffer, count);
        }
        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }
        ......
    } // release lock


    if (inputDevicesChanged) { //输入设备发生改变
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    //发送事件到nputDispatcher
    mQueuedListener->flush();
}

二 EventHub

2.1 getEvents

EventHub.cpp

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    AutoMutex _l(mLock); //加锁

    struct input_event readBuffer[bufferSize];
    RawEvent* event = buffer; //原始事件,每存一个事件,event指针向后移动一个元素
    //capacity存buffer中剩余端元素数量,capacity为0,表示buffer已满
    size_t capacity = bufferSize; //容量大小为256
    bool awoken = false;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        ......
        //mNeedToScanDevices初始值为true,所以第一次getEvents会运行该代码块
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;//赋值为false,避免重复扫描打开设备
            scanDevicesLocked(); //扫描设备,打开/dev/input下所有输入设备
            //mNeedToSendFinishedDeviceScan值为true,用于生成FINISHED_DEVICE_SCAN事件
            mNeedToSendFinishedDeviceScan = true;
        }
        
        while (mOpeningDevices != NULL) {
            Device* device = mOpeningDevices;
            ALOGV("Reporting device opened: id=%d, name=%s\n",
                 device->id, device->path.c_str());
            mOpeningDevices = device->next;
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            event->type = DEVICE_ADDED; //添加设备的事件
            event += 1;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }
        ......
        bool deviceChanged = false;
        while (mPendingEventIndex < mPendingEventCount) {
            //从mPendingEventItems读取事件项
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            ......
            //获取设备ID所对应的device
            Device* device = getDeviceByFdLocked(eventItem.data.fd);
            if (eventItem.events & EPOLLIN) {
                //从设备不断读取事件,放入到readBuffer
                int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);

                if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                    deviceChanged = true;
                    closeDeviceLocked(device);//设备已被移除则执行关闭操作
                } else if (readSize < 0) {
                    ......
                } else if ((readSize % sizeof(struct input_event)) != 0) {
                    ......
                } else {
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
                    size_t count = size_t(readSize) / sizeof(struct input_event);

                    for (size_t i = 0; i < count; i++) {
                        //获取readBuffer的数据
                        struct input_event& iev = readBuffer[i];
                        //将input_event信息, 封装成RawEvent
                        event->when = processEventTimestamp(iev);
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
                    if (capacity == 0) {
                        mPendingEventIndex -= 1;
                        break;
                    }
                }
            }
            ......
        }
        ......
        mLock.unlock(); //poll之前先释放锁
        //等待input事件的到来
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
        ......
        mLock.lock(); //poll之后再次请求锁

        if (pollResult < 0) { //出现错误
            mPendingEventCount = 0;
            if (errno != EINTR) {
                usleep(100000); //系统发生错误则休眠1s
            }
        } else {
            mPendingEventCount = size_t(pollResult);
        }
    }

    return event - buffer; //返回所读取的事件个数
}

EventHub 的主要工作:设备管理(加载、卸载)、输入事件读取。核心代码就是这个 getEvent() 函数,在该函数中完成了这些事情。基本原理就是:EventHub 采用 INotify + epoll 机制实现监听目录 /dev/input 下的设备节点,经过 EventHub 将读取的事件数据从 input_event 结构体 + deviceId 转换成 RawEvent 结构体,然后返回,结构体如下:

2.1.1 RawEvent

input.h

struct input_event {
 struct timeval time; //事件发生的时间点
 unsigned int __usec;
 __u16 type;
 __u16 code;
 __s32 value;
};

InputEventReader.h

struct RawEvent {
    nsecs_t when; //事件发生的时间店
    int32_t deviceId; //产生事件的设备Id
    int32_t type; // 事件类型
    int32_t code;
    int32_t value;
};

此处事件类型:

  • DEVICE_ADDED (添加)
  • DEVICE_REMOVED (删除)
  • FINISHED_DEVICE_SCAN (扫描完成)
  • type<FIRST_SYNTHETIC_EVENT (其他事件)

getEvents() 已完成事件转换工作,接下来看看设备扫描过程。

2.2 设备扫描

2.2.1 scanDevicesLocked

void EventHub::scanDevicesLocked() {
    //此处DEVICE_PATH="/dev/input"
    status_t res = scanDirLocked(DEVICE_PATH);
    ......
}

2.2.2 scanDirLocked

status_t EventHub::scanDirLocked(const char *dirname)
{
    char devname[PATH_MAX];
    char *filename;
    DIR *dir;
    struct dirent *de;
    dir = opendir(dirname);
    if(dir == nullptr)
        return -1;
    strcpy(devname, dirname);
    filename = devname + strlen(devname);
    *filename++ = '/';
    //读取/dev/input/目录下所有的设备节点
    while((de = readdir(dir))) {
        if(de->d_name[0] == '.' &&
           (de->d_name[1] == '\0' ||
            (de->d_name[1] == '.' && de->d_name[2] == '\0')))
            continue;
        strcpy(filename, de->d_name);
        //打开相应的设备节点
        openDeviceLocked(devname);
    }
    closedir(dir);
    return 0;
}

scanDirLocked() 函数遍历 /dev/input 文件夹下的所有设备节点,并分别执行 openDeviceLocked(devname),加载设备。我们来看 openDeviceLocked() 函数。

2.2.3 openDeviceLocked

status_t EventHub::openDeviceLocked(const char *devicePath) {
    char buffer[80];
    //打开设备节点的文件描述符,用于获取设备信息以及读取原始输入事件
    int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
    // 接下来的代码通过ioctl()函数从设备节点中获取输入设备的厂商信息
    InputDeviceIdentifier identifier;
    //获取设备名
    if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1){
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.name.setTo(buffer);
    }
    ......
    //获取设备物理地址
    if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.location.setTo(buffer);
    }

    //获取设备唯一ID
    if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.uniqueId.setTo(buffer);
    }
    //将identifier信息填充到fd
    assignDescriptorLocked(identifier);
    //设置fd为非阻塞方式
    fcntl(fd, F_SETFL, O_NONBLOCK);

    // 分配一个设备Id并创建Device结构体
    int32_t deviceId = mNextDeviceId++;
    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
    loadConfigurationLocked(device);// 为此设备加载配置信息
    //通过ioctl函数获取设备的事件位掩码。**事件位掩码指定了输入设备可以产生何种类型的输入事件
    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
    ......

    // Register with epoll.将设备节点描述符的可读事件添加到Epoll中
    //当此设备的输入事件到来时,Epoll会在getEvents()函数的调用中产生一条epoll事件
    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    if (mUsingEpollWakeup) {
        eventItem.events |= EPOLLWAKEUP;
    }
    eventItem.data.u32 = deviceId;//注意,epoll_event的自定义信息是设备的Id
    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
        delete device; //添加失败则删除该设备
        return -1;
    }
    ......   
    addDeviceLocked(device);//将device添加到mDevices字典中
}

2.2.4 addDeviceLocked

void EventHub::addDeviceLocked(Device* device) {
    mDevices.add(device->id, device); //添加到mDevices队列
    device->next = mOpeningDevices;
    mOpeningDevices = device;
}

以上介绍了 EventHub 从设备节点获取事件的流程,当收到事件后接下里便开始处理事件。

三 InputReader

3.1 processEventsLocked

InputReader.cpp

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; //同一设备的事件打包处理
            }
            //处理真正的输入事件
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            switch (rawEvent->type) {
            case EventHubInterface::DEVICE_ADDED:
                //设备添加
                addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                break;
            case EventHubInterface::DEVICE_REMOVED:
                //设备移除
                removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                break;
            case EventHubInterface::FINISHED_DEVICE_SCAN:
                //设备扫描完成
                handleConfigurationChangedLocked(rawEvent->when);
                break;
            default:
                ALOG_ASSERT(false);//不会发生
                break;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

设备事件处理总共有下几类类型:

  • DEVICE_ADDED (设备增加)
  • DEVICE_REMOVED (设备移除)
  • FINISHED_DEVICE_SCAN (设备扫描完成)
  • 输入事件

先来说说 DEVICE_ADDED 设备增加的处理流程。

3.2 设备增加

3.2.1 addDeviceLocked

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex >= 0) {
        return; //已添加的相同设备则不再添加
    }

    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
    int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
    //创建InputDevice
    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    device->configure(when, &mConfig, 0);
    device->reset(when);
    mDevices.add(deviceId, device); //添加设备到mDevices
    ......
}


//mDevices 和 InputDevice定义(截取部分)
//InputReader.h
class InputReader : public InputReaderInterface {
	KeyedVector<int32_t, InputDevice*> mDevices;
}
class InputDevice {
	int32_t mId;//通过mId从EventHub中找到对应的输入设备
	std::vector<InputMapper*> mMappers;//处理上报的事件
}

这里创建了一个 InputDevice,然后将其加入到 mDevices。mDevices 中保存了设备的 id 以及对应的 InputDevice。EventHub 中也有个 mDevices,保存了设备的 id 和对应的 Device 信息。 如下(截取部分):

//EventHub.h
class EventHub : public EventHubInterface {
    KeyedVector<int32_t, Device*> mDevices;
    
    struct Device {
        Device* next;
        int fd; // may be -1 if device is closed  //设备节点 的文件句柄
        const int32_t id;
        const std::string path;
        const InputDeviceIdentifier identifier; //记录设备信息,设备的名称、供应商、型号等等
        std::unique_ptr<TouchVideoDevice> videoDevice;
        uint32_t classes;
		std::unique_ptr<VirtualKeyMap> virtualKeyMap;
        KeyMap keyMap;
	}
}

接下来看下创建 InputDevice 的过程:createDeviceLocked()

3.2.2 createDeviceLocked

InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    //创建InputDevice对象
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);
    ......
    //获取键盘源类型
    uint32_t keyboardSource = 0;
    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
    }
    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
    }
    if (classes & INPUT_DEVICE_CLASS_DPAD) {
        keyboardSource |= AINPUT_SOURCE_DPAD;
    }
    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
    }

    //添加键盘类设备InputMapper
    if (keyboardSource != 0) {
        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
    }

    //添加鼠标类设备InputMapper
    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
        device->addMapper(new CursorInputMapper(device));
    }

    //添加触摸屏设备InputMapper
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        device->addMapper(new MultiTouchInputMapper(device));
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        device->addMapper(new SingleTouchInputMapper(device));
    }
    ......
    return device;
}

该方法主要功能:

  • 创建 InputDevice 对象,将 InputReader 的 mContext 赋给 InputDevice 对象所对应的变量
  • 根据设备类型来创建并添加相对应的 InputMapper,同时设置 mContext,需要注意的是一个设备可以同时属于多个设备类型

input 设备类型有很多种,以上代码只列举部分常见的设备以及相应的 InputMapper

  • 键盘类设备:KeyboardInputMapper
  • 触摸屏设备:MultiTouchInputMapper或SingleTouchInputMapper
  • 鼠标类设备:CursorInputMapper

介绍完设备增加过程,继续回到 processEventsLocked 函数,可以看到除了设备的增删,更常见的便是输入事件的处理了,那么接下来介绍输入事件的处理过程。

3.3 事件处理

3.3.1 processEventsForDeviceLocked

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    ......
    //最终根据deviceId获得设备对应的InputDevice
    InputDevice* device = mDevices.valueAt(deviceIndex);
    if (device->isIgnored()) {
        return; //可忽略则直接返回
    }
    //事件交由对应InputDevice处理。rawEvents是一组事件,可以注意下来源
    device->process(rawEvents, count);
}

3.3.2 InputDevice.process

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {
        //将InputDvice对象中的mMappers依次取出来,调用process()进行处理
            for (InputMapper* mapper : mMappers) {
                mapper->process(rawEvent);
            }
        }
        --count;
    }
}

createDeviceLocked 创建设备并添加 InputMapper,提到会有多种 InputMapper。 这里以 KeyboardInputMapper (按键事件)为例来展开说明

3.4 按键事件处理

3.4.1 KeyboardInputMapper.process

InputReader.cpp ::KeyboardInputMapper

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)) {
            processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
        }
        break;
    }
    case EV_MSC: ...
    case EV_SYN: ...
    }
}

3.4.2 InputMapper.processKey

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
        int32_t usageCode) {
    int32_t keyCode;
    int32_t keyMetaState;
    uint32_t policyFlags;
    //下面有分析
    if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
                              &keyCode, &keyMetaState, &policyFlags)) {
        keyCode = AKEYCODE_UNKNOWN;
        keyMetaState = mMetaState;
        policyFlags = 0;
    }

    if (down) {
        // Rotate key codes according to orientation if needed.
        if (mParameters.orientationAware) {
            keyCode = rotateKeyCode(keyCode, getOrientation());
        }

        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {
            //mKeyDowns记录着所有按下的键
            keyCode = mKeyDowns[keyDownIndex].keyCode;
        } else {
            ......
            KeyDown keyDown;
            keyDown.keyCode = keyCode;
            keyDown.scanCode = scanCode;
            mKeyDowns.push_back(keyDown);
        }
        mDownTime = when; //记录按下时间点

    } else {
        // Remove key down.
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {
            //键抬起操作,则移除按下事件
            keyCode = mKeyDowns[keyDownIndex].keyCode;
            mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
        } else {
            return;  //键盘没有按下操作,则直接忽略抬起操作
        }
    }
    nsecs_t downTime = mDownTime;
    ......    
    //创建NotifyKeyArgs对象, when记录eventTime, downTime记录按下时间
    NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
            getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    //插入到mArgsQueue队列中
    getListener()->notifyKey(&args);
}

InputListenerInterface* InputReader::ContextImpl::getListener() {
    return mReader->mQueuedListener.get();
}

参数说明:

  • mKeyDowns 记录着所有按下的键
  • mDownTime 记录按下时间点
  • 此处 KeyboardInputMapper 的 mContext 指向 InputReader,getListener() 获取的便是 mQueuedListener。

3.4.3 EventHub::mapKey

status_t EventHub::mapKey(int32_t deviceId,
        int32_t scanCode, int32_t usageCode, int32_t metaState,
        int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
    AutoMutex _l(mLock);
    Device* device = getDeviceLocked(deviceId); //获取设备对象
    status_t status = NAME_NOT_FOUND;

    if (device) {
        sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
        if (kcm != nullptr) {
            //根据scanCode找到keyCode
            if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
                *outFlags = 0;
                status = NO_ERROR;
            }
        }
    }
    ......
    return status;
}



status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const {
    ......
    if (scanCode) {
        ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
        if (index >= 0) {
            //根据scanCode找到keyCode
            *outKeyCode = mKeysByScanCode.valueAt(index);
            return OK;
        }
    }
    *outKeyCode = AKEYCODE_UNKNOWN;
    return NAME_NOT_FOUND;
}

将内核上报的扫描码 (scanCode) 转换成 Android 系统使用的键盘码 (Keycode)

3.4.4 QueuedInputListener.notifyKey

void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push_back(new NotifyKeyArgs(*args));
}

//InputListener.h
class QueuedInputListener : public InputListenerInterface {
protected:
    virtual ~QueuedInputListener();

public:
    explicit QueuedInputListener(const sp<InputListenerInterface>& innerListener);

    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
    virtual void notifyKey(const NotifyKeyArgs* args);
    virtual void notifyMotion(const NotifyMotionArgs* args);
    virtual void notifySwitch(const NotifySwitchArgs* args);
    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);

    void flush();

private:
    sp<InputListenerInterface> mInnerListener;
    std::vector<NotifyArgs*> mArgsQueue;
};

mArgsQueue 的数据类型为 Vector<NotifyArgs*>,将该 key 事件压入该栈顶。 到此,整个事件加工完成,再然后就是将事件发送给 InputDispatcher 线程。

接下来,再回调 InputReader 的 loopOnce 过程,可知当执行完 processEventsLocked() 过程,然后便开始执行 mQueuedListener->flush() 过程,如下文。

四 QueuedListener

4.1 QueuedInputListener.flush

InputListener.cpp

void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

遍历整个 mArgsQueue 数组,在 Input 架构中 NotifyArgs 的实现子类主要有以下几类:

  • NotifyConfigurationChangedArgs
  • NotifyKeyArgs
  • NotifyMotionArgs
  • NotifySwitchArgs
  • NotifyDeviceResetArgs

结合上面的分析,我们知道此处调用的是 NotifyKeyArgs 对象的 notify 方法。从 InputManager 对象初始化的过程可知,mInnerListener 便是 InputDispatcher 对象。

4.2 NotifyKeyArgs.notify

InputListener.cpp

void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyKey(this); // this是指NotifyKeyArgs
}

4.3 InputDispatcher.notifyKey

InputDispatcher.cpp

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {    
    ......
    int32_t keyCode = args->keyCode;
    ......
    KeyEvent event; //初始化KeyEvent对象
    event.initialize(args->deviceId, args->source, args->displayId, args->action,
            flags, keyCode, args->scanCode, metaState, repeatCount,
            args->downTime, args->eventTime);
    //mPolicy是NativeInputManager,最终回调到PhoneWindowManager的同名方法
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    ......
    bool needWake;
    {
        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();
        }
        //创建KeyEntry对象
        KeyEntry* newEntry = new KeyEntry(args->sequenceNum, args->eventTime,
                args->deviceId, args->source, args->displayId, policyFlags,
                args->action, flags, keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);
        //将KeyEntry放入InputDispatcherd的mInboundQueue队列
        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    }

    if (needWake) {
        //唤醒InputDispatcher线程
        mLooper->wake();
    }
}

bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) {
    return mInputFilterEnabled;
}

该方法的主要功能:

1.调用 NativeInputManager.interceptKeyBeforeQueueing,加入队列前执行拦截动作,但并不改变流程,调用链:

  • IMS.interceptKeyBeforeQueueing
  • InputManagerCallback.interceptKeyBeforeQueueing (继承 IMS.WindowManagerCallbacks)
  • PhoneWindowManager.interceptKeyBeforeQueueing (继承 WindowManagerPolicy)

2.当 shouldSendKeyToInputFilterLocked() = true (该值默认为 false,可通过 setInputFilterEnabled 设置),则调用 NativeInputManager.filterInputEvent 过滤输入事件

  • 当返回值为 false 则过滤该事件,不再往下分发

3.生成 KeyEvent,并调用 enqueueInboundEventLocked,将该事件加入到 InputDispatcherd 的成员变量 mInboundQueue。

4.3.1 interceptKeyBeforeQueueing

void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
        uint32_t& policyFlags) {
    ......
    if ((policyFlags & POLICY_FLAG_TRUSTED)) {
        nsecs_t when = keyEvent->getEventTime(); //时间
        JNIEnv* env = jniEnv();
        jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
        jint wmActions;
        if (keyEventObj) {
            // 调用Java层的IMS.interceptKeyBeforeQueueing
            wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags);
            ......
        } else {
            ......
        }
        handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
    } else {
        ...
    }
}

该方法会调用 Java 层的 InputManagerService 的 interceptKeyBeforeQueueing() 方法。

4.3.2 filterInputEvent

bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
    jobject inputEventObj;

    JNIEnv* env = jniEnv();
    switch (inputEvent->getType()) {
    case AINPUT_EVENT_TYPE_KEY:
        inputEventObj = android_view_KeyEvent_fromNative(env,
                static_cast<const KeyEvent*>(inputEvent));
        break;
    case AINPUT_EVENT_TYPE_MOTION:
        inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
                static_cast<const MotionEvent*>(inputEvent));
        break;
    default:
        return true; // 走事件正常的分发流程
    }

    if (!inputEventObj) {
        return true; // 当inputEventObj为空, 则走事件正常的分发流程
    }

    //当inputEventObj不为空,则调用Java层的IMS.filterInputEvent()
    jboolean pass = env->CallBooleanMethod(mServiceObj, gServiceClassInfo.filterInputEvent,
            inputEventObj, policyFlags);
    if (checkAndClearExceptionFromCallback(env, "filterInputEvent")) {
        pass = true; //出现Exception,则走事件正常的分发流程
    }
    env->DeleteLocalRef(inputEventObj);
    return pass;
}

在 notifyMotion() 与 notifyKey() 之前,都会将输入事件通过 mPolicy->filterInputEvent() 将事件交给 DispatcherPolicy 进行输入事件的过滤。如果这个函数返回 false,则事件将不再被注入派发队列中。这个调用被 NativeInputManager 转发给 java 层的 IMS 的同名函数中。

// 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;//只要InputFilter处理了事件,都会返回false
        }
    }
    event.recycle();
    return true;
}

可以看到,实际的事件过滤者是 mInputFilter,它是由 IMS 提供的接口 setInputFilter() 设置的一个实现了 IInputFilter 接口的对象。注意,这个 IInputFilter 和 EditText 控件的 InputFilter 没有任何关系。

使用者可以通过 setInputFilter() 函数将自己的 IInputFilter 对象设置给 IMS,从此便可以开始进行输入事件的监听操作了。

不过需要注意的是,IMS 认为一旦事件被一个 InputFilter 截获过,这个事件便被丢弃了返回 false。

如果 IInputFilter 的使用者不作为,将会导致输入事件无法响应,也不会引发 anr。因此,IInputFilter 的使用者截获事件之后,需要以软件的方式将事件重新注入 InputDispatcher。使用者可以从 IInputFilter 的 install() 回调所提供的 IInputFilterHost 对象完成事件的重新注入。不过,对重新注入的事件内容是什么输入系统则不做要求,因此 InputFilter 机制为使用者提供了篡改输入事件的能力。

4.3.3 enqueueInboundEventLocked

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.isEmpty();
    mInboundQueue.enqueueAtTail(entry);//将该事件放入mInboundQueue队列尾部

    switch (entry->type) {
    case EventEntry::TYPE_KEY: {
        
        KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
        if (isAppSwitchKeyEvent(keyEntry)) {
            if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
                mAppSwitchSawKeyDown = true;//按下事件
            } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
                if (mAppSwitchSawKeyDown) {
                //其中APP_SWITCH_TIMEOUT=500ms
                    mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
                    mAppSwitchSawKeyDown = false;
                    needWake = true;
                }
            }
        }
        break;
    }

    case EventEntry::TYPE_MOTION: {
        // 当前App无响应且用户希望切换到其他应用窗口,则drop该窗口事件,并处理其他窗口事件
        MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
        if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
                && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
                && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
                && mInputTargetWaitApplicationToken != nullptr) {
            int32_t displayId = motionEntry->displayId;
            int32_t x = int32_t(motionEntry->pointerCoords[0].
                    getAxisValue(AMOTION_EVENT_AXIS_X));
            int32_t y = int32_t(motionEntry->pointerCoords[0].
                    getAxisValue(AMOTION_EVENT_AXIS_Y));
            //查询可触摸的窗口
            sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
            if (touchedWindowHandle != nullptr
                    && touchedWindowHandle->getApplicationToken()
                            != mInputTargetWaitApplicationToken) {
                mNextUnblockedEvent = motionEntry;
                needWake = true;
            }
        }
        break;
    }
    }

    return needWake;
}

AppSwitchKeyEvent 是指 keyCode 等于以下值:

  • AKEYCODE_HOME
  • AKEYCODE_ENDCALL
  • AKEYCODE_APP_SWITCH

4.3.4 findTouchedWindowAtLocked

sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
        int32_t x, int32_t y, bool addOutsideTargets, bool addPortalWindows) {
    //从前台到后台来遍历查询可触摸的窗口
    const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
    for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
        const InputWindowInfo* windowInfo = windowHandle->getInfo();
        if (windowInfo->displayId == displayId) {
            int32_t flags = windowInfo->layoutParamsFlags;

            if (windowInfo->visible) {
                if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
                    bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
                            | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
                    if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                        int32_t portalToDisplayId = windowInfo->portalToDisplayId;
                        if (portalToDisplayId != ADISPLAY_ID_NONE
                                && portalToDisplayId != displayId) {
                            if (addPortalWindows) {
                                // For the monitoring channels of the display.
                                mTempTouchState.addPortalWindow(windowHandle);
                            }
                            return findTouchedWindowAtLocked(
                                    portalToDisplayId, x, y, addOutsideTargets, addPortalWindows);
                        }
                        // Found window.
                        return windowHandle;//找到目标窗口
                    }
                }

                if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
                    mTempTouchState.addOrUpdateWindow(
                            windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
                }
            }
        }
    }
    return NULL;
}

我们已经知道 getWindowHandlesLocked 返回的是对应 displayId 中的全部窗口,它的赋值过程是由 Java 层的 InputMonitor.setInputWindows(),经过 JNI 调用后进入 InputDispatcher::setInputWindows() 方法完成。进一步说,就是 WMS 执行 addWindow() 过程或许 UI 改变等场景,都会触发该方法的修改。

InputDispatcher.notifyKey 小结:
它会调用 enqueueInboundEventLocked() 方法来决定是否需要唤醒 InputDispatcher 线程。满足唤醒的条件:

  • 执行 enqueueInboundEventLocked 方法前,mInboundQueue 队列为空,执行完必然不再为空,则需要唤醒分发线程
  • 当事件类型为 key 事件,且发生一对按下和抬起操作,则需要唤醒
  • 当事件类型为 motion 事件,且当前可触摸的窗口属于另一个应用,则需要唤醒

五 总结

5.1 核心工作

InputReader 整个过程涉及多次事件封装转换,其主要工作核心是以下三大步骤:

  • getEvents:通过 EventHub (监听目录 /dev/input ) 读取事件放入 mEventBuffer,而 mEventBuffer 是一个大小为256的数组,再将事件 input_event 转换为 RawEvent
  • processEventsLocked:对事件进行加工,转换 RawEvent -> NotifyKeyArgs(NotifyArgs)
  • QueuedListener->flush:将事件发送到 InputDispatcher 线程,转换 NotifyKeyArgs -> KeyEntry(EventEntry)

InputReader 线程不断循环地执行 InputReader.loopOnce(),每次处理完生成的是 EventEntry (比如 KeyEntry,MotionEntry),接下来的工作就交给 InputDispatcher 线程。

5.2 流程图

在这里插入图片描述
InputReader 的核心工作就是从 EventHub 获取数据后生成 EventEntry 事件,加入到 InputDispatcher 的 mInboundQueue 队列,再唤醒InputDispatcher 线程。
在这里插入图片描述
说明:
1.IMS.filterInputEvent 可以过滤无需上报的事件,当该方法返回值为 false 则代表是需要被过滤掉的事件,不会交给 InputDispatcher 来分发。
2.节点 /dev/input 的 event 事件所对应的输入设备信息位于 /proc/bus/input/devices,也可以通过 getevent 来获取事件。不同的 input 事件所对应的物理 input 节点,常见的情形如下:

  • 屏幕触摸和 MENU,HOME,BACK 三个按键:对应同一个 input 设备节点
  • POWER 和音量(下)键:对应同一个 input 设备节点
  • 音量(上)键:对应同一个 input 设备节点
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值