Android输入子系统之InputReader读取键盘消息过程分析

InputReader读取键盘消息过程分析

Android输入子系统之启动过程分析中,InputManagerService启动之后,会调用start函数,之后调用native层的nativeStart函数,然后调用InputManager::start函数,该函数会调用InputReaderThread的threadLoop函数,该函数会调用InputReader的loopOnce函数从EventHub中读取一次键盘事件。
InputReader读取键盘消息

Step 1. 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();

        if (count) {
            processEventsLocked(mEventBuffer, count);
        }    

        if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) {
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }    
        }    

        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }    
    } // release lock
}

loopOnce通过EventHub来读取键盘事件,如果当前有键盘事件发生或者有键盘事件等待处理,则调用getEvents函数就可以得到这个事件,然后调用processEventsLocked函数处理;如果没有键盘事件发生,那么调用getEvents函数就会进入等待状态。

Step 2. EventHub.getEvents

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);

    AutoMutex _l(mLock);

    struct input_event readBuffer[bufferSize];

    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    bool awoken = false;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

        // Reopen input devices if needed.
        //第一次进入该函数,mNeedToReopenDevices为false
        if (mNeedToReopenDevices) {
            mNeedToReopenDevices = false;

            closeAllDevicesLocked();
            mNeedToScanDevices = true;
            break; // return to the caller before we actually rescan
        }

        // Report any devices that had last been added/removed.
        //第一次进入该函数,mClosingDevices为false
        while (mClosingDevices) {
            Device* device = mClosingDevices;
            ALOGV("Reporting device closed: id=%d, name=%s\n",
                 device->id, device->path.string());
            mClosingDevices = device->next;
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
            event->type = DEVICE_REMOVED;
            event += 1;
            delete device;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                            break;
            }
        }
        //第一次进入,mNeedToScanDevices为true
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }
        //第一次进入,mOpeningDevices 为NULL
        while (mOpeningDevices != NULL) {
            Device* device = mOpeningDevices;
            ALOGV("Reporting device opened: id=%d, name=%s\n",
                 device->id, device->path.string());
            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;
            }
        }
        //第一次进入,由于mNeedToScanDevices未true,mNeedToSendFinishedDeviceScan会被置为true
        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan = false;
            event->when = now;
            event->type = FINISHED_DEVICE_SCAN;
            event += 1;
            if (--capacity == 0) {
                break;
            }
        }

        // Grab the next input event.
        //第一次进入mPendingEventIndex == mPendingEventCount,均为0
        bool deviceChanged = false;
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
                if (eventItem.events & EPOLLIN) {
                    mPendingINotify = true;
                }
                continue;
            }

            if (eventItem.data.u32 == EPOLL_ID_WAKE) {
                if (eventItem.events & EPOLLIN) {
                    ALOGV("awoken after wake()");
                    awoken = true;
                    char buffer[16];
                    ssize_t nRead;
                    do {
                        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
                    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
                }
                continue;
            }

            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
            if (deviceIndex < 0) {
                ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
                        eventItem.events, eventItem.data.u32);
                continue;
            }

            Device* device = mDevices.valueAt(deviceIndex);
            if (eventItem.events & EPOLLIN) {
                int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
                if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                    // Device was removed before INotify noticed.
                    ALOGW("could not get event, removed? (fd: %d size: %" PRId32
                            " bufferSize: %zu capacity: %zu errno: %d)\n",
                            device->fd, readSize, bufferSize, capacity, errno);
                    deviceChanged = true;
                    closeDeviceLocked(device);
                } else if (readSize < 0) {
                    if (errno != EAGAIN && errno != EINTR) {
                        ALOGW("could not get event (errno=%d)", errno);
                    }
                } else if ((readSize % sizeof(struct input_event)) != 0) {
                    ALOGE("could not get event (wrong size: %d)", readSize);
                } 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++) {
                        struct input_event& iev = readBuffer[i];

                        event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
                                + nsecs_t(iev.time.tv_usec) * 1000LL;

                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
                    if (capacity == 0) {
                        // The result buffer is full.  Reset the pending event index
                        // so we will try to read the device again on the next iteration.
                        mPendingEventIndex -= 1;
                                                break;
                    }
                }
            } else if (eventItem.events & EPOLLHUP) {
                ALOGI("Removing device %s due to epoll hang-up event.",
                        device->identifier.name.string());
                deviceChanged = true;
                closeDeviceLocked(device);
            } else {
                ALOGW("Received unexpected epoll event 0x%08x for device %s.",
                        eventItem.events, device->identifier.name.string());
            }
        }

        // readNotify() will modify the list of devices so this must be done after
        // processing all other events to ensure that we read all remaining events
        // before closing the devices.
        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            readNotifyLocked();
            deviceChanged = true;
        }

        // Report added or removed devices immediately.
        if (deviceChanged) {
            continue;
        }

        // Return now if we have collected any events or if we were explicitly awoken.
        //如果收集到任何的输入事件,则执行break命令,退出for死循环
        if (event != buffer || awoken) {
            break;
        }
        mPendingEventIndex = 0;

        mLock.unlock(); // release lock before poll, must be before release_wake_lock
        release_wake_lock(WAKE_LOCK_ID);
        //调用epoll_wait等待按键事件,如果没有按键事件,将睡眠
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
        mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock

        if (pollResult == 0) {
            // Timed out.
                        mPendingEventCount = 0;
            break;
        }

        if (pollResult < 0) {
            // An error occurred.
            mPendingEventCount = 0;

            // Sleep after errors to avoid locking up the system.
            // Hopefully the error is transient.
            if (errno != EINTR) {
                ALOGW("poll failed (errno=%d)\n", errno);
                usleep(100000);
            }
        } else {
            // Some events occurred.
            mPendingEventCount = size_t(pollResult);
        }
    }

    // All done, return the number of events we read.
    return event - buffer;
}

该函数第一次进入时,mNeedToReopenDevices和mClosingDevices为false,mNeedToScanDevices为true,所以会调用scanDevicesLocked来打开输入设备节点。之后由于是第一次进入,mPendingEventIndex和mPendingEventCount为0,所以while循环不执行。之后会调用epoll_wait等待按键事件的发生。当有按键事件发生后,会读取到按键事件,然后再走到for死循环中,此时由于mPendingEventCount>0,因此会进入while循环,其中会读出发生的键盘事件,并保存在buffer数组中,然后event != buffer所以退出for死循环,getEvents函数返回。

下面先分析扫描输入设备的过程

Step 3. EventHub::scanDevicesLocked

该函数定义在frameworks/native/services/inputflinger/EventHub.cpp

void EventHub::scanDevicesLocked() {
    status_t res = scanDirLocked(DEVICE_PATH);

    if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
        ALOGE("-- createVirtualKeyboardLocked");
        createVirtualKeyboardLocked();
    }
}

函数主要调用scanDirLocked来扫描/dev/input目录

Step 4. EventHub::scanDirLocked

该函数定义在frameworks/native/services/inputflinger/EventHub.cpp

status_t EventHub::scanDirLocked(const char *dirname)
{
    char devname[PATH_MAX];
    char *filename;
    DIR *dir;
    struct dirent *de;
    dir = opendir(dirname);
    if(dir == NULL)
        return -1;
    strcpy(devname, dirname);
    filename = devname + strlen(devname);
    *filename++ = '/';
    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;
}

函数调用readdir函数扫描/dev/input下的文件,该目录下一般是event0、mice和mouse0三个文件存在,在Mstar的电视芯片上一般还有event1表示遥控器设备。然后调用openDeviceLocked打开该设备文件

Step 5. EventHub::openDeviceLocked

该函数定义在frameworks/native/services/inputflinger/EventHub.cpp

status_t EventHub::openDeviceLocked(const char *devicePath) {
    char buffer[80];

    ALOGV("-- Opening device: %s", devicePath);

    int fd = open(devicePath, O_RDWR | O_CLOEXEC);
    if(fd < 0) {
        ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
        return -1;
    }

    InputDeviceIdentifier identifier;
    ...
    //该段是获取设备的一些信息,初始化identifier的一些属性,以及加载
    ...
    // Load the key map.
    // We need to do this for joysticks too because the key layout may specify axes.
    status_t keyMapStatus = NAME_NOT_FOUND;
    if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
        // Load the keymap for the device.
        //加载kl和kcm文件
        keyMapStatus = loadKeyMapLocked(device);
    }
    ...
    //将前面打开的设备的文件描述符加入到epoll监听中
    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
        ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);
        delete device;
        return -1;
    }

该函数打开扫描到的/dev/input下的设备文件,然后获取该设备文件的name,bus,product,vendor,version,location,uniqueId,descriptor等,以及设备类型,如是否是键盘,是否是虚拟按键等。如果是INPUT_DEVICE_CLASS_KEYBOARD
这个类型,则调用loadKeyMapLocked来加载kl文件

Step 6. EventHub.loadKeyMapLocked

该函数定义在frameworks/native/services/inputflinger/EventHub.cpp

status_t EventHub::loadKeyMapLocked(Device* device) {
    return device->keyMap.load(device->identifier, device->configuration);
}

函数调用Device的KeyMap结构体的load函数

Step 7. KeyMap.load

frameworks/native /libs/input/Keyboard.cpp

status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
    const PropertyMap* deviceConfiguration) {
  // Use the configured key layout if available.
  if (deviceConfiguration) {
      String8 keyLayoutName;
      if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
              keyLayoutName)) {
          status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
      }   

      String8 keyCharacterMapName;
      if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
              keyCharacterMapName)) {
          status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
      }   

      if (isComplete()) {
          return OK; 
      }   
  }   

  // Try searching by device identifier.
  if (probeKeyMap(deviceIdenfifier, String8::empty())) {
      return OK; 
  }   

  // Fall back on the Generic key map.
  // TODO Apply some additional heuristics here to figure out what kind of
  //      generic key map to use (US English, etc.) for typical external keyboards.
  if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
      return OK; 
  }   

  // Try the Virtual key map as a last resort.
  if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) {
      return OK; 
  }   

  // Give up!
  return NAME_NOT_FOUND;
}

如果configuration不为空,即存在idc文件,则根据idc文件中指定的keyboard.layout和keyboard.characterMap来加载指定的kl和kcm文件
如果configuration为空,则根据deviceIdenfifier来加载kl和kcm文件(这个会根据vendor,product,version三个属性来决定加载哪个文件)
如果deviceIdenfifier无法找到配置文件,则根据名字Generic来加载文件,即Generic.kl和Generic.kcm文件
如果Generic.kl和Generic.kcm无法找到,则根据名字Virtual来加载文件,即Virtual.kl和Virtual.kcm文件

至此输入设备扫描完成,并加入epoll中,监听事件。从前面的getEvents函数分析得知,当按键事件发生后,getEvents函数返回,返回值为输入事件的个数,以一次键盘按键为例,得到下面的6个事件

EventHub: /dev/input/event2 got: time=4383.680195, type=4, code=4, value=458792
EventHub: /dev/input/event2 got: time=4383.680195, type=1, code=28, value=1
EventHub: /dev/input/event2 got: time=4383.680195, type=0, code=0, value=0
EventHub: /dev/input/event2 got: time=4383.760186, type=4, code=4, value=458792
EventHub: /dev/input/event2 got: time=4383.760186, type=1, code=28, value=0
EventHub: /dev/input/event2 got: time=4383.760186, type=0, code=0, value=0

上面的type是linux的输入系统里的事件,具体的值可以查看
可以查看input.h,可以事业locate查找,有很多的头文件,找到一个查看一下即可

/* 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 状态

上面6个事件,只有两个type为1的事件,是我们需要处理的按键事件,一个down,一个up

返回到InputReader的loopOnce函数

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

      { // acquire lock
          AutoMutex _l(mLock);
          mReaderIsAliveCondition.broadcast();
          //当有按键事件发生时,count将不为0,以一次按键为例,这里应该是6个事件
          if (count) {
              processEventsLocked(mEventBuffer, count);
          } 
       } // release lock
}

当有按键事件发生时,count将不为0,以一次按键为例,这里应该是6个事件,之后会调用processEventsLocked来处理RawEvent。

Step 8. 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;
          processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
      }
      count -= batchSize;
      rawEvent += batchSize;
  }
}

该函数调用processEventsForDeviceLocked依次处理rawEvent

Step 9. 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);

  device->process(rawEvents, count);
}

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

Step 10. 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];       
                mapper->process(rawEvent);           
            }
        }
    }  
}

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

Step 11. 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;
            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来进一步处理

Step 12. 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) {

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

        // Add key down.
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {
            // key repeat, be sure to use same keycode as before in case of rotation
            keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
        } else {
            // key down
            if ((policyFlags & POLICY_FLAG_VIRTUAL)
                    && mContext->shouldDropVirtualKey(when,
                            getDevice(), keyCode, scanCode)) {
                return;
            }
            if (policyFlags & POLICY_FLAG_GESTURE) {
                mDevice->cancelTouch(when);
            }

            mKeyDowns.push();
            KeyDown& keyDown = mKeyDowns.editTop();
            keyDown.keyCode = keyCode;
            keyDown.scanCode = scanCode;
        }

        mDownTime = when;
    } else {
        // Remove key down.
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {
            // key up, be sure to use same keycode as before in case of rotation
            keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
            mKeyDowns.removeAt(size_t(keyDownIndex));
        } else {
            // key was not actually down
            ALOGI("Dropping key up from device %s because the key was not down.  "
                    "keyCode=%d, scanCode=%d",
                    getDeviceName().string(), keyCode, scanCode);
            return;
        }
    }

    int32_t oldMetaState = mMetaState;
    int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState);
    bool metaStateChanged = oldMetaState != newMetaState;
    if (metaStateChanged) {
        mMetaState = newMetaState;
        updateLedState(false);
    }

    nsecs_t downTime = mDownTime;

    // Key down on external an keyboard should wake the device.
    // We don't do this for internal keyboards to prevent them from waking up in your pocket.
    // For internal keyboards, the key layout file should specify the policy flags for
    // each wake key individually.
    // TODO: Use the input device configuration to control this behavior more finely.
    if (down && getDevice()->isExternal()) {
// MStar Android Patch Begin
#ifdef ENABLE_STR
        char value[PROPERTY_VALUE_MAX];
        property_get("mstar.str.suspending", value, "0");
        if (atoi(value) == 0) {
            policyFlags |= POLICY_FLAG_WAKE;
        }
#else
        policyFlags |= POLICY_FLAG_WAKE;
#endif
// MStar Android Patch End
    }

    if (mParameters.handlesKeyRepeat) {
        policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
    }

    if (metaStateChanged) {
        getContext()->updateGlobalMetaState();
    }

    if (down && !isMetaKey(keyCode)) {
        getContext()->fadePointer();
    }

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

这个函数首先对按键作一些处理,例如,当某一个DPAD键被按下时,根据当时屏幕方向的不同,它所表示的意义也不同,因此,这里需要根据当时屏幕的方向来调整键盘码。
最后函数会调用:

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

Step 13. InputDispatcher.notifyKey

该函数定义在frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    if (!validateKeyEvent(args->action)) {
        return;
    }

    uint32_t policyFlags = args->policyFlags;
    int32_t flags = args->flags;
    int32_t metaState = args->metaState;
    if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
        policyFlags |= POLICY_FLAG_VIRTUAL;
        flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
    }
    if (policyFlags & POLICY_FLAG_FUNCTION) {
        metaState |= AMETA_FUNCTION_ON;
    }

    policyFlags |= POLICY_FLAG_TRUSTED;

    int32_t keyCode = args->keyCode;
    if (metaState & AMETA_META_ON && args->action == AKEY_EVENT_ACTION_DOWN) {
        int32_t newKeyCode = AKEYCODE_UNKNOWN;
        if (keyCode == AKEYCODE_DEL) {
            newKeyCode = AKEYCODE_BACK;
        } else if (keyCode == AKEYCODE_ENTER) {
            newKeyCode = AKEYCODE_HOME;
                    }
        if (newKeyCode != AKEYCODE_UNKNOWN) {
            AutoMutex _l(mLock);
            struct KeyReplacement replacement = {keyCode, args->deviceId};
            mReplacedKeys.add(replacement, newKeyCode);
            keyCode = newKeyCode;
            metaState &= ~AMETA_META_ON;
        }
    } else if (args->action == AKEY_EVENT_ACTION_UP) {
        // In order to maintain a consistent stream of up and down events, check to see if the key
        // going up is one we've replaced in a down event and haven't yet replaced in an up event,
        // even if the modifier was released between the down and the up events.
        AutoMutex _l(mLock);
        struct KeyReplacement replacement = {keyCode, args->deviceId};
        ssize_t index = mReplacedKeys.indexOfKey(replacement);
        if (index >= 0) {
            keyCode = mReplacedKeys.valueAt(index);
            mReplacedKeys.removeItemsAt(index);
            metaState &= ~AMETA_META_ON;
        }
    }

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

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

Step 14. 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: {
        // Optimize app switch latency.
        // If the application takes too long to catch up then we drop all events preceding
        // the app switch 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被唤醒,开始进行按键分发。

参考

http://blog.csdn.net/luoshengyang/article/details/6882903

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值