android input system(frameworks) analysis -- InputManager (1)

对input系统在framework层的分析从一次触摸屏丢失上报事件开始:由于设备节点/dev/input/input0存在,而且getevent能响应点触摸屏的动作,所以把问题定位到了EventHub和InputManager这一层。


InputManager的结构很简单,对外开放

    virtual status_t start();
    virtual status_t stop();
    virtual sp<InputReaderInterface> getReader();
    virtual sp<InputDispatcherInterface> getDispatcher();

四个成员函数,内部带有私有成员

    sp<InputReaderInterface> mReader;
    sp<InputReaderThread> mReaderThread;
    sp<InputDispatcherInterface> mDispatcher;
    sp<InputDispatcherThread> mDispatcherThread;
其构造过程也很简单:

    mDispatcher = new InputDispatcher(dispatcherPolicy);  -- 根据输入的参数构造一个dispatcher
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher); -- 根据参数构造一个inputreader
    initialize();

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader); -- 产生reader线程用于从设备节点读取input event
    mDispatcherThread = new InputDispatcherThread(mDispatcher); -- 产生dispachter线程分发event
}
InputManager的start主要就是让这两个线程跑起来:

status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
               ...
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
               ...
    return OK;
}

====================================================================================================================

接着先来分析InputReader:

InputReaderThread run之后会重复的调用

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

亦即mReader->loopOnce();

void InputReader::loopOnce() {
    RawEvent rawEvent;
    mEventHub->getEvent(& rawEvent);
    process(& rawEvent);
}


InputReader上来就找EventHub要数据(getEvent()),所以我们要先看看EventHub的getEvent都做了些什么工作:

bool EventHub::getEvent(RawEvent* outEvent)
{
    outEvent->deviceId = 0;
          ...

    // 第一次调用getEvent的时候,需要调用openPlatformInput来搜索输入系统来查找可用的设备
    if (!mOpened) {
        mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
        mOpened = true;
        mNeedToSendFinishedDeviceScan = true;
    }
}
bool EventHub::openPlatformInput(void)
{
    // 为poll()调用作准备
    mFDCount = 1;
    mFDs = (pollfd *)calloc(1, sizeof(mFDs[0]));
    mDevices = (device_t **)calloc(1, sizeof(mDevices[0]));
    mFDs[0].events = POLLIN;
    mFDs[0].revents = 0;
    mDevices[0] = NULL;
          ...
    // 上面有赋值device_path = "/dev/input"
    res = scanDir(device_path);
          ...
    return true;
}


接着又绕到scanDir()里面,这个函数无非就是对/dev/input这个目录的每个文件执行openDevice(),后者才是真正干活的:

int EventHub::openDevice(const char *deviceName) {
    int version;
    int fd;
    struct pollfd *new_mFDs;
    device_t **new_devices;
    char **new_device_names;
    char name[80];
    char location[80];
    char idstr[80];
    struct input_id id;

    LOGV("Opening device: %s", deviceName);

    AutoMutex _l(mLock);
    // 根据路径打开文件获取version, id, name等信息
    fd = open(deviceName, O_RDWR);
    if(fd < 0) ...
    if(ioctl(fd, EVIOCGVERSION, &version)) 
        ...
    if(ioctl(fd, EVIOCGID, &id)) 
        ...
    if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) 
        ...
    // check to see if the device is on our excluded list - 如果此设备在排除名单内,忽略它
    List<String8>::iterator iter = mExcludedDevices.begin();
    List<String8>::iterator end = mExcludedDevices.end();
    for ( ; iter != end; iter++) {
        ...
    }
    // 读取location, idstr等信息
    if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) 
        ...
    if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) 
        ...
    // 将设备文件设置为非阻塞
    if (fcntl(fd, F_SETFL, O_NONBLOCK)) 
        ...
    // 在mDeviceById数组中寻找“空位”,扩充mDeviceById的空间并把新device加到尾部
    int devid = 0;
    while (devid < mNumDevicesById) {
        if (mDevicesById[devid].device == NULL) {
            break;
        }
        devid++;
    }
    if (devid >= mNumDevicesById) {
        device_ent* new_devids = (device_ent*)realloc(mDevicesById,
                sizeof(mDevicesById[0]) * (devid + 1));
        if (new_devids == NULL) {
            LOGE("out of memory");
            return -1;
        }
        mDevicesById = new_devids;
        mNumDevicesById = devid+1;
        mDevicesById[devid].device = NULL;
        mDevicesById[devid].seq = 0;
    }

    mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
    if (mDevicesById[devid].seq == 0) {
        mDevicesById[devid].seq = 1<<SEQ_SHIFT;
    }
    // 为后面用poll()来获取kernel层上报的input event作准备
    new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1));
    new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1));
    if (new_mFDs == NULL || new_devices == NULL) {
        LOGE("out of memory");
        return -1;
    }
    mFDs = new_mFDs;
    mDevices = new_devices;

    device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name);
    if (device == NULL) {
        LOGE("out of memory");
        return -1;
    }

    device->fd = fd;
    mFDs[mFDCount].fd = fd;
    mFDs[mFDCount].events = POLLIN;
    mFDs[mFDCount].revents = 0;

    // Figure out the kinds of events the device reports.
    // 分辨本device的输入类型: keypad? touchscreen? gamepad?
    uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
    memset(key_bitmask, 0, sizeof(key_bitmask));
    // 测试本device是否为键盘,是的话把key_bitmask保存下来
    LOGV("Getting keys...");
    if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
       ...
    }
    // 是否为轨迹球或鼠标
    // See if this is a trackball (or mouse).
    if (test_bit(BTN_MOUSE, key_bitmask)) {
        ...
    }
    // 是否为touchscreen,是否支持多点触摸
    // See if this is a touch pad.
    uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)];
    memset(abs_bitmask, 0, sizeof(abs_bitmask));
    LOGV("Getting absolute controllers...");
    if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) {
        ...
    }

#ifdef EV_SW
    // 是否为switch设备,这个宏没有打开
#endif
    // 如果是键盘的话,加载keypad-layout,即/usr/keylayout/中的xxx.kl文件
    if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
           ...
        status_t status = device->layoutMap->load(keylayoutFilename);
        if (status) {
            LOGE("Error %d loading key layout.", status);
        }
           ...
        // tell the world about the devname (the descriptive name) - 找一个首选keyboard出来
        if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {
            // the built-in keyboard has a well-known device ID of 0,
            // this device better not go away.
            mHaveFirstKeyboard = true;
            mFirstKeyboardId = device->id;
            property_set("hw.keyboards.0.devname", name);
        } else {
            // ensure mFirstKeyboardId is set to -something-.
            if (mFirstKeyboardId == 0) {
                mFirstKeyboardId = device->id;
            }
        }
        // 如果键盘有数字键,方向键,游戏控制键等特殊按钮,都一一标记到classes中
        if (hasKeycodeLocked(device, AKEYCODE_Q)) 
            ...
        if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
                hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
                hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
                hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
                hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
            device->classes |= INPUT_DEVICE_CLASS_DPAD;
        }
        
        // See if this device has a gamepad.
        for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
            if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
                device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
                break;
            }
        }

        LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
                device->id, name, propName, keylayoutFilename);
    }

    // 如果不知道这个device是啥类型,忽略之
    if (device->classes == 0) {
       ...
    }

    // 加到mDevicesById和mOpeningDevices中,回头getEvent()会处理mOpenningDevices链表中的新增device

    mDevicesById[devid].device = device;
    device->next = mOpeningDevices;
    mOpeningDevices = device;
    mDevices[mFDCount] = device;

    mFDCount++;
    return 0;
}



初始化折腾一番后,又回到了getEvent中来,getEvent除了在第一此进入需要加设备外,其主体就是一个大循环:

bool EventHub::getEvent(RawEvent* outEvent)
{
      ....

    // main loop here - 主循环体
    for (;;) {
        // Report any devices that had last been removed. - 当有设备从系统中移除时
        if (mClosingDevices != NULL) {
             ...
        }

	// Report any devices that had last been added - 当有设备加入到系统中时
        if (mOpeningDevices != NULL) {
            device_t* device = mOpeningDevices;
            LOGV("Reporting device opened: id=0x%x, name=%s\n",
                 device->id, device->path.string());
            mOpeningDevices = device->next;
            if (device->id == mFirstKeyboardId) {
                outEvent->deviceId = 0;
            } else {
                outEvent->deviceId = device->id;
            }
            outEvent->type = DEVICE_ADDED;
            outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
            mNeedToSendFinishedDeviceScan = true;
            return true; // 一个设备上报一个DEVICE_ADDED事件
        }

	// After finish scanning all input devices in system, send finished siganal at boot time 
        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan = false;
            outEvent->type = FINISHED_DEVICE_SCAN;
            outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
            return true;
        }

        // 下面为普通event获取和上报过程
             ... ...
    }
}

从上面的代码段可知,对于mOpeningDevices中的device,getEvent()调用都是每读取一个马上上报DEVICE_ADDED事件并返回。所以如果我们的系统中有一个keypad和一个touchscreen,则在扫描阶段就会有3次getEvent()返回:键盘的DEVICE_ADDED,触摸屏的DEVICE_ADDED和表示扫描完成的FINISHED_DEVICE_SCAN。















评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值