zoukankan      html  css  js  c++  java
  • Android系统--输入系统(十一)Reader线程_简单处理

    Android系统--输入系统(十一)Reader线程_简单处理

    1. 引入

    Reader线程主要负责三件事情
    • 获得输入事件
    • 简单处理
    • 上传给Dispatch线程
    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); //进行简单处理
        }
    
        ......
    
        mQueuedListener->flush();
    }
    
    前面我们已经分析其如何获取输入事件以及涉及中重要的数据结构,本次博文主要阐述其如何处理输入事件。Reader线程只是对输入事件进行简单的处理,大多数复杂处理由Dispatch线程负责处理,在后面的博文会具体分析Dispatch线程。

    2. Reader线程的简单处理概述

    根据所获得的RawEvent中的type参数进行处理
    • ADD Device
    • Remove Device
    • 真正的输入事件
    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分支对真正的事件进行处理
        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: %d Count: %d", batchSize, count);
    #endif
            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); // can't happen
                break;
            }
        }
            count -= batchSize;
            rawEvent += batchSize;
        }
    }
    

    3. 插入输入设备处理

    3.1 创建InpuDevice对象
    InputReader.cpp
    void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
        ......
    
        InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes); //首先创建InputDevice对象
         device->configure(when, &mConfig, 0);
        device->reset(when);
    
        if (device->isIgnored()) {
            ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
                identifier.name.string());
    } else {
            ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,
                identifier.name.string(), device->getSources());
        }
    
        mDevices.add(deviceId, device); //将InputDevice添加到mDevice中,mDevice记录管理输入设备
        bumpGenerationLocked();
    }
    
    扩展:我们知道在输入系统中我们使用EeventHub类管理多个输入设备(之前博文已经具体分析),为什么在InputReader中又要构建一个mDevices来记录管理多个输入设备呢? 即Android输入系统当中采用分层作用。
    输入系统中的分层作用:
    (1)EventHub中有mDvices类记录管理一个输入设备,主要作用是读取事件
    mDevices类
    - fd : int //设备节点所打开的文件句柄
    - identifier : const InputDeviceIdentifier //记录厂商信息,存储了设备的供应商、型号等信息
    - keyBitmask[] : uint8_t
    - configurationFile : String8 //IDC文件名
    - configuration : PropertyMap* //IDC属性:(内嵌OR外接)设备
    - keyMap : KeyMap //保存配置文件(kl,kcm)
    
    (2)InputReader中有mDvices链表中使用InputDevice对象记录管理一个输入设备,主要用来处理事件
    InputDevice类
    - InputReaderContext* mContext;
    - int32_t mId; //通过mId从EventHub中找到对应的输入设备
    - Vector<InputMapper*> mMappers; //处理上报的事件
    
    总结:InputDevice主要负责处理事件,并不需要关心设备的具体信息,其中具体信息由EventHub中mDevice类记录,当需要设备信息时,可以通过mId找到对应设备,提取信息
    3.2 根据输入设备类别,添加Mapper,主要负责之后的按键映射。
    InputReader.cpp
    
    InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
            const InputDeviceIdentifier& identifier, uint32_t classes) {
        InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
                controllerNumber, identifier, classes);
    
        // External devices.
        if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
            device->setExternal(true);
        }
    
        // Switch-like devices.
        if (classes & INPUT_DEVICE_CLASS_SWITCH) {
            device->addMapper(new SwitchInputMapper(device));
        }
    
        // Vibrator-like devices.
        if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
            device->addMapper(new VibratorInputMapper(device));
        }
    
        // Keyboard-like devices.
        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;
        }
    
        if (keyboardSource != 0) {
            device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
        }
    
        // Cursor-like devices.
        if (classes & INPUT_DEVICE_CLASS_CURSOR) {
            device->addMapper(new CursorInputMapper(device));
        }
    
        // Touchscreens and touchpad devices.
        if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
            device->addMapper(new MultiTouchInputMapper(device));
        } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
            device->addMapper(new SingleTouchInputMapper(device));
        }
    
        // Joystick-like devices.
        if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
            device->addMapper(new JoystickInputMapper(device));
        }
    
        return device;
    }
    
    总的来说,添加新设备的过程就是这样子,拿键盘输入来说,首先创建一个InputDevice类记录,并且它添加KeyboardInputMapper对象,使用该对象进行处理,移除过程与添加过程类似就不具体分析了。

    4. 真正的输入设备上报事件处理

    (1)设备产生的输入事件,主要是通过调用processEventsForDeviceLocked函数进行处理
    InputReader.cpp
    void InputReader::processEventsForDeviceLocked(int32_t deviceId,
    const RawEvent* rawEvents, size_t count) {
        ......
    
        device->process(rawEvents, count); //直接调用process进行处理
    }
    
    (2)process函数中将InputDvices对象中的mMappers一个个取出来,调用其process函数。
    InputReader.cpp
    void InputDevice::process(const RawEvent* rawEvents, size_t count) {
        size_t numMappers = mMappers.size();
        for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
    #if DEBUG_RAW_EVENTS
        ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld",
            rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
            rawEvent->when);
    #endif
    
        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
    #if DEBUG_RAW_EVENTS
            ALOGD("Recovered from input event buffer overrun.");
    #endif
            } else {
    #if DEBUG_RAW_EVENTS
                ALOGD("Dropped input event while waiting for next input sync.");
    #endif
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ALOGI("Detected input event buffer overrun for device %s.", getName().string());
            mDropUntilNextSync = true;
            reset(rawEvent->when);
    } else {
    
            //将InputDvices对象中的mMappers一个个取出来,调用其process函数
                for (size_t i = 0; i < numMappers; i++) {
                  InputMapper* mapper = mMappers[i]; //
                  mapper->process(rawEvent);
               }
            }
        }
    }
    
    (3)这里我们用键盘的process函数进行分析
    - 进行内核扫描码转化Android系统所需要的按键码,转化成功则处理该按键
    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;
            }
        }
    }
    }
    
    - 扫描码转化过程(具体在上次博文已经论述):
    EventHub.cpp
    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.
            //首先使用KCM文件进行映射,映射过程在上次博文已经详细分析
            sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
        if (kcm != NULL) {
            if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
                *outFlags = 0;
                return NO_ERROR;
            }
    }
    
        // Check the key layout next.
        //其次使用keylayout文件进行映射
        if (device->keyMap.haveKeyLayout()) {
            if (!device->keyMap.keyLayoutMap->mapKey(
            scanCode, usageCode, outKeycode, outFlags)) {
                return NO_ERROR;
            }
        }
    }
    
        *outKeycode = 0;
        *outFlags = 0;
        return NAME_NOT_FOUND;
    
    - 在Reader线程中,只是将内核上报的扫描码,转化为Android系统所使用的按键码,然后重新构造一个args,将其发给下一级的Dispatch线程进行处理。
    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,
        down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
        AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
    
        getListener()->notifyKey(&args); //通知Listener处理,Dispatch线程会监听该事件,并处理,下次博文会具体分析
    }
    

    5. 总结Reader线程

    1. 在输入子系统中,Java程序调用C程序时,会构造一个NativeInputManager对象

    2. NativeInputManager对象包括:

      • mReader (实现具体功能)
      • mReaderThread(实现简单循环)
      • mDispatcher
      • mDispatcherThread
    3. mReader具体功能

      3.1 mReader使用EventHub管理多个输入设备,多个输入设备存储在mDevices容器中

      • mDevices中有一个Device对象,表示具体的输入设备

      • mDevices对象中有keyMap对象,用于进行内核扫描码转化为Android具体按键

      3.2 在Reader线程中,使用Epoll机制来检测设备节点的添加和删除,以及该设备节点是否有数据产生,如果有数据,就会读到数据,将读到的数据会构建为一个RawEvent类中,并进行处理,处理的过程便是根据RawEvent中的类型进行本篇博文所述的处理,就不在赘述。

    6. Reader线程简单处理各类调用关系图

  • 相关阅读:
    高德地图
    微信小程序蓝牙
    微信小程序请求封装
    create-react-app配置less
    浏览器渲染原理及流程
    输入网址到呈现网页发生的过程
    cookie的理解
    浏览器本地存储
    cookie,localStorage,sessionStorage区别
    关于this指向
  • 原文地址:https://www.cnblogs.com/lkq1220/p/6810126.html
Copyright © 2011-2022 走看看