Input子系统框架之InputReader

1、概述

    根据Input子系统框架之深入理解EventHub分析。输入设备扫描完成,并加入epoll中,监听事件。从前面的getEvents函数分析得知,当按键事件发生后,getEvents函数返回。 这里再贴一下Input 处理时间流程图,然后按步骤详细分析。
    上面的type是linux的输入系统里的事件,具体的值可以查看 查看input.h

kernel/include/uapi/linux/input.h
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define EV_SYN 0x00   同步事件
#define EV_KEY 0x01   按键事件
#define EV_REL 0x02   相对坐标
#define EV_ABS 0x03   绝对坐标
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define EV_MSC 0x04   其它
#define EV_SW  0x05   
#define EV_LED 0x11   LED
#define EV_SND 0x12   声音
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define EV_REP 0x14   Repeat
#define EV_FF  0x15   力反馈
#define EV_PWR 0x16   电源
#define EV_FF_STATUS 0x17 状态

2、 InputReader::loopOnce()

返回到InputReader的loopOnce函数

[->frameworks/native/services/inputflinger/InputReader.cpp]

void InputReader::loopOnce() {
  size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

  { // acquire lock
      AutoMutex _l(mLock);
      mReaderIsAliveCondition.broadcast();
      //当有按键事件发生时,count将不为0
      if (count) {
          processEventsLocked(mEventBuffer, count);
      }
   } // release lock
}

    当有按键事件发生时,count将不为0,之后会调用processEventsLocked来处理RawEvent。

3、InputReader.processEventsLocked()

[->frameworks/native/services/inputflinger/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;
      //依次处理rawEvent
      processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
  }
  count -= batchSize;
  rawEvent += batchSize;
  }
}

    该函数调用processEventsForDeviceLocked依次处理rawEvent

4、InputReader.processEventsForDeviceLocked()

[->frameworks/native/services/inputflinger/InputReader.cpp]

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
      const RawEvent* rawEvents, size_t count) {
  ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
  InputDevice* device = mDevices.valueAt(deviceIndex);
  //调用InputDevice的process函数
  device->process(rawEvents, count);
}

    这里根据deviceId获取到InputDevice,然后调用InputDevice的process函数

5、InputDevice.process()

[->frameworks/native/services/inputflinger/InputReader.cpp]

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
    if (mDropUntilNextSync) {
        if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
    } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
    } else {
        for (size_t i = 0; i < numMappers; i++) {    
            InputMapper* mapper = mMappers[i];  
            // InputMapper是做什么的呢,它是用于解析原始输入事件的。比如back, home等VirtualKey,
            // 传上来时是个Touch事件,这里要根据坐标转化为相应的按键事件。再比如多点触摸时,需要计算
            // 每个触摸点分别属于哪条轨迹,安卓系统中每种输入设备都对应了一种Mapper,比如
            // SwitchInputMapper, VibratorInputMapper,KeyBoardInputMapper
            mapper->process(rawEvent);           
        }
     }
   }
}

    这里的mMappers成员变量保存了一系列输入设备事件处理对象,例如负责处理键盘事件的KeyboardKeyMapper对象以及负责处理触摸屏事件的TouchInputMapper对象, 它们是在InputReader类的成员函数createDeviceLocked中创建的。这里查询每一个InputMapper对象是否要对当前发生的事件进行处理。由于发生的是键盘事件,真正会对该事件进行处理的只有KeyboardKeyMapper对象。

6、KeyboardInputMapper.process()

[->frameworks/native/services/inputflinger/InputReader.cpp]

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;

            // 调用EventHub中的mapKey函数进行转化
            // 传入参数
            // scanCode:驱动程序上报的扫描码;keyCode:转化之后的Android使用的按键值

            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;
        }
    }
    }
}

    函数首先调用isKeyboardOrGamepadKey来判断键盘扫描码是否正确,如果正确则调用processKey来进一步处理

7、KeyboardInputMapper.processKey()

[->frameworks/native/services/inputflinger/InputReader.cpp]

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
        int32_t scanCode, uint32_t policyFlags) {

    // 根据扫描码scanCode、按键码keyCode、newMetaState、downTime按下的时间进行处理    
    // NotifyKeyArgs args(when, getDeviceId(), mSource, 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);
     // 通知Listener处理,Dispatch线程会监听该事件,并处理,下次博文会具体分析
    getListener()->notifyKey(&args);
}

    这个函数首先对按键作一些处理,根据扫描码scanCode、按键码keyCode、newMetaState、downTime按下的时间进行处理,最后函数会调用:

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是InputReader初始化时传入的对象,即QueuedInputListener,则会调用QueuedInputListener的notifyKey函数

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

    InputReader的loopOnce()的结尾会调用QueuedInputListener::flush()统一回调缓冲队列中各元素的notify()接口:

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();
}

进一步调用:

void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
	listener->notifyConfigurationChanged(this);
}

    以按键事件为例,由于InputDispatcher 实现了InputListenerInterface接口的notifyConfigurationChanged()函数,所以最后会调用到InputDispatcher的notifyKey()函数中。

8、 InputDispatcher.notifyKey()

[->frameworks/native/services/inputflinger/InputDispatcher.cpp]

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
	......
	// 构造一个KeyEvent对象
	KeyEvent event;
	event.initialize(args->deviceId, args->source, args->action,
	        flags, keyCode, args->scanCode, metaState, 0,
	        args->downTime, args->eventTime);
	// 调用mPolicy的interceptKeyBeforeQueueing函数,该函数最后会调用到java层的PhoneWindowManagerService函数
	mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
	
	bool needWake;
	{ // acquire lock
	    mLock.lock();
	    ......
	    //构造一个KeyEntry对象,调用enqueueInboundEventLocked函数将按键事件加入队列
	    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();
	}
}

    该函数首先调用validateKeyEvent来判断是否是有效按键事件,实际判断是否是UP/DOWN事件

    然后构造一个KeyEvent对象,调用mPolicy的interceptKeyBeforeQueueing函数,该函数最后会调用到java层的PhoneWindowManagerService函数

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);

    之后会调用构造一个KeyEntry对象,调用enqueueInboundEventLocked函数将按键事件加入队列,如果返回true,则调用mLooper.wake函数唤醒等待的InputDispatcher,进行按键分发。

KeyEntry* newEntry = new KeyEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, flags, keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);

needWake = enqueueInboundEventLocked(newEntry);

9、InputDispatcher.enqueueInboundEventLocked()

[->frameworks/native/services/inputflinger/InputDispatcher.cpp]

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.isEmpty();
    mInboundQueue.enqueueAtTail(entry);
    traceInboundQueueLengthLocked();

    switch (entry->type) {
    case EventEntry::TYPE_KEY: {
        // ......
        KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
        if (isAppSwitchKeyEventLocked(keyEntry)) {
            if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
                mAppSwitchSawKeyDown = true;
            } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
                if (mAppSwitchSawKeyDown) {
                    mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
                    mAppSwitchSawKeyDown = false;
                    needWake = true;
                }
            }
        }
        break;
    }
  }
  return needWake;
}

    将EventEntry加入到mInboundQueue中,该函数两种情况下会返回true,一是当加入该键盘事件到mInboundQueue之前,mInboundQueue为空,这表示InputDispatc herThread线程正在睡眠等待InputReaderThread线程的唤醒,因此,它返回true表示要唤醒InputDispatccherThread线程;二是加入该键盘事件到mInboundQueue之前,mInboundQueue不为空,但是此时用户按下的是Home键等需要切换APP的按键,我们知道,在切换App时,新的App会把它的键盘消息接收通道注册到InputDispatcher中去,并且会等待InputReader的唤醒,因此,在这种情况下,也需要返回true,表示要唤醒InputDispatccherThread线程。如果不是这两种情况,那么就说明InputDispatccherThread线程现在正在处理前面的键盘事件,不需要唤醒它。

至此,InputDispatcherThread被唤醒,开始进行按键分发。

10、总结:

    InputReaderThread不断调用InputReader的pollOnce()->getEvents()函数来得到事件,这些事件可以是输入事件,也可以是由inotify监测到设备增减变更所触发的事件。第一次进入时会扫描/dev/input目录建立设备列表,存在mDevice成员变量中(EventHub中有设备列表KeyedVector mDevices;对应的,InputReader中也有设备列表KeyedVector mDevices。这里先添加到前者,然后会在InputReader::addDeviceLocked()中添加到后者。),同时将增加的fd加到epoll的等待集合中。在接下来的epoll_wait()等待时,如果有事件就会返回,同时返回可读事件数量。在这里,从Input driver读出的事件从原始的input_event结构转为RawEvent结构,放到getEvents()的输出参数buffer中。getEvents()返回后,InputReader调用processEventsLocked()处理事件,对于设备改变,会根据实际情况调用addDeviceLocked(), removeDeviceLocked()和handleConfigurationChangedLocked()。对于其它设备中来的输入事件,会调用processEventsForDeviceLocked()进一步处理。其中会根据当时注册的InputMapper对事件进行处理,然后将事件处理请求放入缓冲队列(QueuedInputListener中的mArgsQueue)。

    回到流程主线上,在InputReader的loopOnce()的结尾会调用QueuedInputListener::flush()统一回调缓冲队列中各元素的notify()接口。

    以按键事件为例,最后会调用到InputDispatcher的notifyKey()函数中。这里先将参数封装成KeyEvent: 然后把它作为参数调用NativeInputManager的interceptKeyBeforeQueueing()函数。顾名思义,就是在放到待处理队列前看看是不是需要系统处理的系统按键,它会通过JNI调回Java世界,最终调到PhoneWindowManager的interceptKeyBeforeQueueing()。然后,基于输入事件信息创建KeyEntry对象,调用enqueueInboundEventLocked()将之放入队列等待InputDiaptcherThread线程拿出处理。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值