您的位置:首页 > 移动开发

Android6.0 按键流程 KeyboardInputMapper扫描码转成键盘码 (二)

2017-02-16 14:19 411 查看
我们详细分析了InputReader中读取设备事件,到processEventsLocked函数处理事件(包括设备事件,设备添加、删除等),再到ProcessEventsForDeviceLocked处理设备事件,最后到InputDevice的process函数,去遍历各个InputMapper执行process函数。
今天我们继续从这里开始分析,Input设备有很多种类,其消息格式各不相同,因此就有很多InputMapper对各个不同的RawEvent进行处理。

我们今天主要从按键的KeyboardInputMapper来讲解。

一、KeyboardInputMapper

下面我们主要从按键,比如音量键、power键的这个InputMapper说起:

[cpp]
view plain
copy





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

1.1 EventHub的mapKey函数

我们先来看下EventHub的mapKey函数,这个函数中outKeycode是入参,但是传的是指针,会往里面写值。

[cpp]
view plain
copy





status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,  
        int32_t* outKeycode, uint32_t* outFlags) const {  
    AutoMutex _l(mLock);  
    Device* device = getDeviceLocked(deviceId);  
  
    if (device) {  
        // Check the key character map first.  
        sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();  
        if (kcm != NULL) {  
            if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {  
                    scanCode, *outKeycode);  
                *outFlags = 0;  
                return NO_ERROR;  
            }  
        }  
  
        // Check the key layout next.  
        if (device->keyMap.haveKeyLayout()) {  
            if (!device->keyMap.keyLayoutMap->mapKey(  
                    scanCode, usageCode, outKeycode, outFlags)) {  
                return NO_ERROR;  
            }  
        }  
    }  
  
    *outKeycode = 0;  
    *outFlags = 0;  
    return NAME_NOT_FOUND;  
}  

我们看到还是通过device中的成员变量,由此可知肯定是在EventHub的openDeviceLocked函数中,

[cpp]
view plain
copy





status_t keyMapStatus = NAME_NOT_FOUND;  
if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {  
    // Load the keymap for the device.  
    keyMapStatus = loadKeyMapLocked(device);  
}  

果然在openDeviceLocked中有上面这段代码。

[cpp]
view plain
copy





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

再来看下load函数,先通过deviceConfiguration获取keyLayoutName,deviceConfiguration肯定也是前面openDeviceLocked的时候创建的,这里就不分析了。

[cpp]
view plain
copy





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)) {//通过deviceConfiguration获取keyLayoutName  
            status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);  
            if (status == NAME_NOT_FOUND) {  
                ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "  
                        "it was not found.",  
                        deviceIdenfifier.name.string(), keyLayoutName.string());  
            }  
        }  
  
        String8 keyCharacterMapName;  
        if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),  
                keyCharacterMapName)) {  
            status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);  
            if (status == NAME_NOT_FOUND) {  
                ALOGE("Configuration for keyboard device '%s' requested keyboard character "  
                        "map '%s' but it was not found.",  
                        deviceIdenfifier.name.string(), keyLayoutName.string());  
            }  
        }  
  
        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!  
    ALOGE("Could not determine key map for device '%s' and no default key maps were found!",  
            deviceIdenfifier.name.string());  
    return NAME_NOT_FOUND;  
}  

我们再来看loadKeyLayout函数

[cpp]
view plain
copy





status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,  
        const String8& name) {  
    String8 path(getPath(deviceIdentifier, name,  
            INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));  
    if (path.isEmpty()) {  
        return NAME_NOT_FOUND;  
    }  
  
    status_t status = KeyLayoutMap::load(path, &keyLayoutMap);  
    if (status) {  
        return status;  
    }  
  
    keyLayoutFile.setTo(path);  
    return OK;  
}  

我们先来分析下getPath函数,看它是如何获取path的

[cpp]
view plain
copy





String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,  
        const String8& name, InputDeviceConfigurationFileType type) {  
    return name.isEmpty()  
            ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)  
            : getInputDeviceConfigurationFilePathByName(name, type);  
}  

我们再看getInputDeviceConfigurationFilePathByName函数,到system/usr目录下

[cpp]
view plain
copy





String8 getInputDeviceConfigurationFilePathByName(  
        const String8& name, InputDeviceConfigurationFileType type) {  
    // Search system repository.  
    String8 path;  
    path.setTo(getenv("ANDROID_ROOT"));  
    path.append("/usr/");  
    appendInputDeviceConfigurationFileRelativePath(path, name, type);  
#if DEBUG_PROBE  
    ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());  
#endif  
    if (!access(path.string(), R_OK)) {  
#if DEBUG_PROBE  
        ALOGD("Found");  
#endif  
        return path;  
    }  

再看appendInputDeviceConfigurationFileRelativePath函数

[cpp]
view plain
copy





static void appendInputDeviceConfigurationFileRelativePath(String8& path,  
        const String8& name, InputDeviceConfigurationFileType type) {  
    path.append(CONFIGURATION_FILE_DIR[type]);  
    for (size_t i = 0; i < name.length(); i++) {  
        char ch = name[i];  
        if (!isValidNameChar(ch)) {  
            ch = '_';  
        }  
        path.append(&ch, 1);  
    }  
    path.append(CONFIGURATION_FILE_EXTENSION[type]);  
}  

CONFIGURATION_FILE_DIR是各个 type对应的各个目录

[cpp]
view plain
copy





static const char* CONFIGURATION_FILE_DIR[] = {  
        "idc/",  
        "keylayout/",  
        "keychars/",  
};  

而CONFIGURATION_FILE_EXTENSION是各个type对应的文件后缀

[cpp]
view plain
copy





static const char* CONFIGURATION_FILE_EXTENSION[] = {  
        ".idc",  
        ".kl",  
        ".kcm",  
};  

我们再结合手机里的文件看下:

[html]
view plain
copy





root@lte26007:/system/usr/keylayout # ls  
ls  
AVRCP.kl  
Generic.kl  
Vendor_0079_Product_0011.kl  
Vendor_045e_Product_028e.kl  
Vendor_046d_Product_b501.kl  
Vendor_046d_Product_c216.kl  
Vendor_046d_Product_c219.kl  
Vendor_046d_Product_c21d.kl  
Vendor_046d_Product_c21f.kl  
Vendor_046d_Product_c294.kl  
Vendor_046d_Product_c299.kl  
Vendor_046d_Product_c532.kl  
Vendor_054c_Product_0268.kl  
Vendor_0583_Product_2060.kl  
Vendor_05ac_Product_0239.kl  
Vendor_0b05_Product_4500.kl  
Vendor_1038_Product_1412.kl  
Vendor_12bd_Product_d015.kl  
Vendor_1532_Product_0900.kl  
Vendor_1689_Product_fd00.kl  
Vendor_1689_Product_fd01.kl  
Vendor_1689_Product_fe00.kl  
Vendor_18d1_Product_2c40.kl  
Vendor_1949_Product_0401.kl  
Vendor_1bad_Product_f016.kl  
Vendor_1bad_Product_f023.kl  
Vendor_1bad_Product_f027.kl  
Vendor_1bad_Product_f036.kl  
Vendor_1d79_Product_0009.kl  
Vendor_22b8_Product_093d.kl  
Vendor_2378_Product_1008.kl  
Vendor_2378_Product_100a.kl  
comip-gpio-keys.kl  
comip-keypad.kl  
ft5x06.kl  
h2w_headset.kl  
qwerty.kl  
sensor00fn11.kl  

我们再来看下Generic.kl这个文件,截取部分如下:扫描码对应的键值。

[html]
view plain
copy





.....  
key 108   DPAD_DOWN  
key 109   PAGE_DOWN  
key 110   INSERT  
key 111   FORWARD_DEL  
# key 112 "KEY_MACRO"  
key 113   VOLUME_MUTE  
key 114   VOLUME_DOWN  
key 115   VOLUME_UP  
key 116   POWER  
key 117   NUMPAD_EQUALS  
# key 118 "KEY_KPPLUSMIN  
key 119   BREAK  
# key 120 (undefined)  
key 121   NUMPAD_COMMA  
key 122   KANA  
key 123   EISU  
key 124   YEN  
key 125   META_LEFT  
key 126   META_RIGHT  
key 127   MENU  
key 128   MEDIA_STOP  

我们继续看下KeyLayoutMap::load这个函数,这个函数我们就不细说了。

[cpp]
view plain
copy





status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) {  
    outMap->clear();  
  
    Tokenizer* tokenizer;  
    status_t status = Tokenizer::open(filename, &tokenizer);//通过这个函数获取Tokenizer  
    if (status) {  
        ALOGE("Error %d opening key layout map file %s.", status, filename.string());  
    } else {  
        sp<KeyLayoutMap> map = new KeyLayoutMap();  
        if (!map.get()) {  
            ALOGE("Error allocating key layout map.");  
            status = NO_MEMORY;  
        } else {  
#if DEBUG_PARSER_PERFORMANCE  
            nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);  
#endif  
            Parser parser(map.get(), tokenizer);//解析  
            status = parser.parse();  
#if DEBUG_PARSER_PERFORMANCE  
            nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;  
            ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",  
                    tokenizer->getFilename().string(), tokenizer->getLineNumber(),  
                    elapsedTime / 1000000.0);  
#endif  
            if (!status) {  
                *outMap = map;  
            }  
        }  
        delete tokenizer;  
    }  
    return status;  
}  

1.2 KeyboardInputMapper的processKey函数

下面我们再来看KeyboardInputMapper::process中的processKey函数

[cpp]
view plain
copy





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()) {  
        policyFlags |= POLICY_FLAG_WAKE;  
    }  
  
    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);  
}  

这个函数,一开始还是再把扫描码转成键盘码,后面主要调用了notifyKey函数。

getListener函数如下

[cpp]
view plain
copy





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

而notifyKey函数如下,就是把对象放在一个变量中。

[cpp]
view plain
copy





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

二、InputReader的loopOnce函数

最后我们再回到InputReader的loopOnce函数,我们主要看最后mQueuedListener->flush函数。

[cpp]
view plain
copy





void InputReader::loopOnce() {  
    int32_t oldGeneration;  
    int32_t timeoutMillis;  
    bool inputDevicesChanged = false;  
    Vector<InputDeviceInfo> inputDevices;  
    { // acquire lock  
        AutoMutex _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);  
  
    { // 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) {  
#if DEBUG_RAW_EVENTS  
                ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);  
#endif  
                mNextTimeout = LLONG_MAX;  
                timeoutExpiredLocked(now);  
            }  
        }  
  
        if (oldGeneration != mGeneration) {  
            inputDevicesChanged = true;  
            getInputDevicesLocked(inputDevices);  
        }  
    } // release lock  
  
    // Send out a message that the describes the changed input devices.  
    if (inputDevicesChanged) {  
        mPolicy->notifyInputDevicesChanged(inputDevices);  
    }  
  
    // Flush queued events out to the listener.  
    // This must happen outside of the lock because the listener could potentially call  
    // back into the InputReader's methods, such as getScanCodeState, or become blocked  
    // on another thread similarly waiting to acquire the InputReader lock thereby  
    // resulting in a deadlock.  This situation is actually quite plausible because the  
    // listener is actually the input dispatcher, which calls into the window manager,  
    // which occasionally calls into the input reader.  
    mQueuedListener->flush();  
}  

QueuedInputListener::flush函数在文件InputListener.cpp中。

[cpp]
view plain
copy





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

这个函数中遍历之前我们在每个InputMapper存入的NotifyArgs对象,最后调用了NotifyArgs对象的notify函数

之前我们的NotifyArgs对象是NotifyKeyArgs对象,这个notify是个虚函数,就到NotifyKeyArgs::notify

[cpp]
view plain
copy





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

而这个listener,是新建QueuedInputListener的时候传进来的

[cpp]
view plain
copy





mQueuedListener = new QueuedInputListener(listener);  

listener是InputReader里面传过来的,所以我们知道InputReader是在InputManager中创建的

[cpp]
view plain
copy





InputReader::InputReader(const sp<EventHubInterface>& eventHub,  
        const sp<InputReaderPolicyInterface>& policy,  
        const sp<InputListenerInterface>& listener) :  

我们看下InputManager的构造函数,传进来的InputDispatcher

[cpp]
view plain
copy





InputManager::InputManager(  
        const sp<EventHubInterface>& eventHub,  
        const sp<InputReaderPolicyInterface>& readerPolicy,  
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {  
    mDispatcher = new InputDispatcher(dispatcherPolicy);  
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);  
    initialize();  
}  

所以最后这个listener是InputDispatcher,因此最后就是调用的InputDispatcher的notifyKey函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  INPUT