Android Input 输入系统学习


Android输入设备的映射

使用指令dumpsys input 可以看到以下信息
130|shell@cv6a638_base:/ # dumpsys input                                       INPUT MANAGER (dumpsys input)Event Hub State:  BuiltInKeyboardId: -2  Devices:    -1: Virtual      Classes: 0x40000023      Path: <virtual>      Descriptor: a718a782d34bc767f4689c232d64d527998ea7fd      Location:       ControllerNumber: 0      UniqueId: <virtual>      Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000      KeyLayoutFile: /system/usr/keylayout/Generic.kl      KeyCharacterMapFile: /system/usr/keychars/Virtual.kcm      ConfigurationFile:       HaveKeyboardLayoutOverlay: false    1: MStar Smart TV Keypad      Classes: 0x00000001      Path: /dev/input/event2      Descriptor: 8f43d929a9472e8dc54d48a6c41e2435e8eaff35      Location:       ControllerNumber: 0      UniqueId:       Identifier: bus=0x0006, vendor=0x3697, product=0x0002, version=0x0000      KeyLayoutFile: /system/usr/keylayout/Vendor_3697_Product_0002.kl      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm      ConfigurationFile:       HaveKeyboardLayoutOverlay: false    2: MCE IR Keyboard/Mouse (ir)      Classes: 0x0000000b      Path: /dev/input/event1      Descriptor: 2b764a30c0f74e1362d8ef86c5e4f12150af666f      Location: /input0      ControllerNumber: 0      UniqueId:       Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000      KeyLayoutFile: /system/usr/keylayout/Generic.kl      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm      ConfigurationFile:       HaveKeyboardLayoutOverlay: false    3: MStar Smart TV IR Receiver      Classes: 0x00000001      Path: /dev/input/event0      Descriptor: 0e50bdc18d3ae0b6f247100cbd99062d93c208eb      Location: /dev/ir      ControllerNumber: 0      UniqueId:       Identifier: bus=0x0018, vendor=0x3697, product=0x0001, version=0x0001      KeyLayoutFile: /system/usr/keylayout/Vendor_3697_Product_0001.kl      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm      ConfigurationFile:       HaveKeyboardLayoutOverlay: false    4: AVRCP      Classes: 0x80000001      Path: /dev/input/event3      Descriptor: 89065b1db3afd7f700b4049a883d405d76233a40      Location:       ControllerNumber: 0      UniqueId:       Identifier: bus=0x0005, vendor=0x0000, product=0x0000, version=0x0000      KeyLayoutFile: /system/usr/keylayout/AVRCP.kl      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm      ConfigurationFile:       HaveKeyboardLayoutOverlay: falseInput Reader State:  Device -1: Virtual    Generation: 2    IsExternal: false    Sources: 0x00000301    KeyboardType: 2    Keyboard Input Mapper:      Parameters:        HasAssociatedDisplay: false        OrientationAware: false      KeyboardType: 2      Orientation: 0      KeyDowns: 0 keys currently down      MetaState: 0x0      DownTime: 0  Device 1: MStar Smart TV Keypad    Generation: 9    IsExternal: false    Sources: 0x00000101    KeyboardType: 1    Keyboard Input Mapper:      Parameters:        HasAssociatedDisplay: false        OrientationAware: false      KeyboardType: 1      Orientation: 0      KeyDowns: 0 keys currently down      MetaState: 0x0      DownTime: 0  Device 2: MCE IR Keyboard/Mouse (ir)    Generation: 11    IsExternal: false    Sources: 0x00002103    KeyboardType: 2    Motion Ranges:      X: source=0x00002002, min=0.000, max=1919.000, flat=0.000, fuzz=0.000, resolution=0.000      Y: source=0x00002002, min=0.000, max=1079.000, flat=0.000, fuzz=0.000, resolution=0.000      PRESSURE: source=0x00002002, min=0.000, max=1.000, flat=0.000, fuzz=0.000, resolution=0.000    Keyboard Input Mapper:      Parameters:        HasAssociatedDisplay: false        OrientationAware: false      KeyboardType: 2      Orientation: 0      KeyDowns: 0 keys currently down      MetaState: 0x0      DownTime: 0    Cursor Input Mapper:      Parameters:        HasAssociatedDisplay: true        Mode: pointer        OrientationAware: false      XScale: 1.000      YScale: 1.000      XPrecision: 1.000      YPrecision: 1.000      HaveVWheel: false      HaveHWheel: false      VWheelScale: 1.000      HWheelScale: 1.000      Orientation: 0      ButtonState: 0x00000000      Down: false      DownTime: 0  Device 3: MStar Smart TV IR Receiver    Generation: 4    IsExternal: false    Sources: 0x00000101    KeyboardType: 1    Keyboard Input Mapper:      Parameters:        HasAssociatedDisplay: false        OrientationAware: false      KeyboardType: 1      Orientation: 0      KeyDowns: 0 keys currently down      MetaState: 0x0      DownTime: 0  Device 4: AVRCP    Generation: 12    IsExternal: true    Sources: 0x00000101    KeyboardType: 1    Keyboard Input Mapper:      Parameters:        HasAssociatedDisplay: false        OrientationAware: false      KeyboardType: 1      Orientation: 0      KeyDowns: 0 keys currently down      MetaState: 0x0      DownTime: 0  Configuration:    ExcludedDeviceNames: []    VirtualKeyQuietTime: 0.0ms    PointerVelocityControlParameters: scale=1.000, lowThreshold=500.000, highThreshold=3000.000, acceleration=3.000    WheelVelocityControlParameters: scale=1.000, lowThreshold=15.000, highThreshold=50.000, acceleration=4.000    PointerGesture:      Enabled: true      QuietInterval: 100.0ms      DragMinSwitchSpeed: 50.0px/s      TapInterval: 150.0ms      TapDragInterval: 300.0ms      TapSlop: 20.0px      MultitouchSettleInterval: 100.0ms      MultitouchMinDistance: 15.0px      SwipeTransitionAngleCosine: 0.3      SwipeMaxWidthRatio: 0.2      MovementSpeedRatio: 0.8      ZoomSpeedRatio: 0.3Input Dispatcher State:  DispatchEnabled: 1  DispatchFrozen: 0  FocusedApplication: name='AppWindowToken{42534628 token=Token{42532cb8 ActivityRecord{4255e728 u0 com.assem.launcher/.MainActivity t1}}}', dispatchingTimeout=5000.000ms  FocusedWindow: name='Window{425a9768 u0 com.assem.launcher/com.assem.launcher.MainActivity}'  TouchDown: false  TouchSplit: false  TouchDeviceId: -1  TouchSource: 0x00000000  TouchDisplayId: -1  TouchedWindows: <none>  Windows:    0: name='Window{424d0270 u0 NotificationPanel}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x01820300, type=0x000007e8, layer=211000, frame=[1152,-75][1920,1080], scale=1.000000, touchableRegion=[1152,-75][1920,1080], inputFeatures=0x00000000, ownerPid=2137, ownerUid=10003, dispatchingTimeout=5000.000ms    1: name='Window{42514f48 u0 KeyguardScrim}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x00110900, type=0x000007ed, layer=121000, frame=[0,0][1920,1080], scale=1.000000, touchableRegion=[0,0][1920,1080], inputFeatures=0x00000000, ownerPid=2067, ownerUid=1000, dispatchingTimeout=5000.000ms    2: name='Window{425a9768 u0 com.assem.launcher/com.assem.launcher.MainActivity}', displayId=0, paused=false, hasFocus=true, hasWallpaper=false, visible=true, canReceiveKeys=true, flags=0x01810520, type=0x00000001, layer=21005, frame=[0,0][1920,1080], scale=1.000000, touchableRegion=[0,0][1920,1080], inputFeatures=0x00000000, ownerPid=2301, ownerUid=1000, dispatchingTimeout=5000.000ms    3: name='Window{424c7c38 u0 com.android.systemui.ImageWallpaper}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x00000318, type=0x000007dd, layer=21000, frame=[0,0][1920,1280], scale=1.000000, touchableRegion=[0,0][1920,1280], inputFeatures=0x00000000, ownerPid=2137, ownerUid=10003, dispatchingTimeout=5000.000ms  MonitoringChannels:    0: 'WindowManager (server)'  RecentQueue: length=5    DeviceResetEvent(deviceId=-1), policyFlags=0x00000000, age=2045547.2ms    DeviceResetEvent(deviceId=3), policyFlags=0x00000000, age=2045547.2ms    DeviceResetEvent(deviceId=2), policyFlags=0x00000000, age=2045547.2ms    DeviceResetEvent(deviceId=1), policyFlags=0x00000000, age=2045547.2ms    DeviceResetEvent(deviceId=4), policyFlags=0x00000000, age=2033442.2ms  PendingEvent: <none>  InboundQueue: <empty>  Connections:    0: channelName='425a9768 com.assem.launcher/com.assem.launcher.MainActivity (server)', windowName='Window{425a9768 u0 com.assem.launcher/com.assem.launcher.MainActivity}', status=NORMAL, monitor=false, inputPublisherBlocked=false      OutboundQueue: <empty>      WaitQueue: <empty>    1: channelName='WindowManager (server)', windowName='monitor', status=NORMAL, monitor=true, inputPublisherBlocked=false      OutboundQueue: <empty>      WaitQueue: <empty>    2: channelName='42514f48 KeyguardScrim (server)', windowName='Window{42514f48 u0 KeyguardScrim}', status=NORMAL, monitor=false, inputPublisherBlocked=false      OutboundQueue: <empty>      WaitQueue: <empty>    3: channelName='424d0270 NotificationPanel (server)', windowName='Window{424d0270 u0 NotificationPanel}', status=NORMAL, monitor=false, inputPublisherBlocked=false      OutboundQueue: <empty>      WaitQueue: <empty>    4: channelName='424c7c38 com.android.systemui.ImageWallpaper (server)', windowName='Window{424c7c38 u0 com.android.systemui.ImageWallpaper}', status=NORMAL, monitor=false, inputPublisherBlocked=false      OutboundQueue: <empty>      WaitQueue: <empty>  AppSwitch: not pending  Configuration:    KeyRepeatDelay: 50.0ms    KeyRepeatTimeout: 500.0msshell@cv6a638_base:/ # 
我们可以看到在KeyLayoutFile条目中看到加载的按键映射文件,都是根据厂商ID和产品ID匹配对应文件的,例如vendor为0x3697,product 为0x0001,那么对应的配置文件为/system/usr/keylayout/Vendor_3697_Product_0001.kl另外该命令还很多信息可挖,例如KeyRepeatDelay: 50.0ms,KeyRepeatTimeout: 500.0ms在按键性能优化时,就可以起到作用。

映射文件的加载

从上面的命令可以整体把握按键的映射情况,但是如何加载,我们可以看一下按键的处理枢纽,也即EventHub.首先EventHub在启动之后会去搜索/dev/input目录下的设备文件,并读取相关设备信息, 主要用
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;}
openDeviceLocked分别会去读取/dev/input 目录下信息,如下:
shell@cv6a638_base:/ # ls /dev/input/ -al                                      crw-rw---- root     input     13,  64 1970-01-01 08:00 event0crw-rw---- root     input     13,  65 1970-01-01 08:00 event1crw-rw---- root     input     13,  66 1970-01-01 08:00 event2crw-rw---- root     input     13,  67 1970-01-02 08:01 event3crw-rw---- root     input     13,   0 1970-01-01 08:00 js0crw-rw---- root     input     13,  63 1970-01-01 08:00 micecrw-rw---- root     input     13,  32 1970-01-01 08:00 mouse0
这些设备中有鼠标,键盘,遥控器,滚动球等等,详细映射如下:
Event Hub State:  BuiltInKeyboardId: -2  Devices:    -1: Virtual      Classes: 0x40000023      Path: <virtual>      Descriptor: a718a782d34bc767f4689c232d64d527998ea7fd      Location:       ControllerNumber: 0      UniqueId: <virtual>      Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000      KeyLayoutFile: /system/usr/keylayout/Generic.kl      KeyCharacterMapFile: /system/usr/keychars/Virtual.kcm      ConfigurationFile:       HaveKeyboardLayoutOverlay: false    1: MStar Smart TV Keypad      Classes: 0x00000001      Path: /dev/input/event2      Descriptor: 8f43d929a9472e8dc54d48a6c41e2435e8eaff35      Location:       ControllerNumber: 0      UniqueId:       Identifier: bus=0x0006, vendor=0x3697, product=0x0002, version=0x0000      KeyLayoutFile: /system/usr/keylayout/Vendor_3697_Product_0002.kl      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm      ConfigurationFile:       HaveKeyboardLayoutOverlay: false    2: MCE IR Keyboard/Mouse (ir)      Classes: 0x0000000b      Path: /dev/input/event1      Descriptor: 2b764a30c0f74e1362d8ef86c5e4f12150af666f      Location: /input0      ControllerNumber: 0      UniqueId:       Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000      KeyLayoutFile: /system/usr/keylayout/Generic.kl      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm      ConfigurationFile:       HaveKeyboardLayoutOverlay: false    3: MStar Smart TV IR Receiver      Classes: 0x00000001      Path: /dev/input/event0      Descriptor: 0e50bdc18d3ae0b6f247100cbd99062d93c208eb      Location: /dev/ir      ControllerNumber: 0      UniqueId:       Identifier: bus=0x0018, vendor=0x3697, product=0x0001, version=0x0001      KeyLayoutFile: /system/usr/keylayout/Vendor_3697_Product_0001.kl      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm      ConfigurationFile:       HaveKeyboardLayoutOverlay: false    4: AVRCP      Classes: 0x80000001      Path: /dev/input/event3      Descriptor: 89065b1db3afd7f700b4049a883d405d76233a40      Location:       ControllerNumber: 0      UniqueId:       Identifier: bus=0x0005, vendor=0x0000, product=0x0000, version=0x0000      KeyLayoutFile: /system/usr/keylayout/AVRCP.kl      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm      ConfigurationFile:       HaveKeyboardLayoutOverlay: false
openDeviceLocked完成单个设备信息的读取,并组装成Device添加至mDevices(KeyedVector<int32_t, Device*>)中
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;    // Get device name.    if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {        //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));    } else {        buffer[sizeof(buffer) - 1] = '\0';        identifier.name.setTo(buffer);    }    // Check to see if the device is on our excluded list    for (size_t i = 0; i < mExcludedDevices.size(); i++) {        const String8& item = mExcludedDevices.itemAt(i);        if (identifier.name == item) {            ALOGI("ignoring event id %s driver %s\n", devicePath, item.string());            close(fd);            return -1;        }    }    // Get device driver version.    int driverVersion;    if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {        ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));        close(fd);        return -1;    }    // Get device identifier.    struct input_id inputId;    if(ioctl(fd, EVIOCGID, &inputId)) {        ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));        close(fd);        return -1;    }    identifier.bus = inputId.bustype;    identifier.product = inputId.product;    identifier.vendor = inputId.vendor;    identifier.version = inputId.version;    // Get device physical location.    if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {        //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));    } else {        buffer[sizeof(buffer) - 1] = '\0';        identifier.location.setTo(buffer);    }    // Get device unique id.    if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {        //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));    } else {        buffer[sizeof(buffer) - 1] = '\0';        identifier.uniqueId.setTo(buffer);    }    // Fill in the descriptor.    setDescriptor(identifier);    // Make file descriptor non-blocking for use with poll().    if (fcntl(fd, F_SETFL, O_NONBLOCK)) {        ALOGE("Error %d making device file descriptor non-blocking.", errno);        close(fd);        return -1;    }    // Allocate device.  (The device object takes ownership of the fd at this point.)    int32_t deviceId = mNextDeviceId++;    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);    ALOGV("add device %d: %s\n", deviceId, devicePath);    ALOGV("  bus:        %04x\n"         "  vendor      %04x\n"         "  product     %04x\n"         "  version     %04x\n",        identifier.bus, identifier.vendor, identifier.product, identifier.version);    ALOGV("  name:       \"%s\"\n", identifier.name.string());    ALOGV("  location:   \"%s\"\n", identifier.location.string());    ALOGV("  unique id:  \"%s\"\n", identifier.uniqueId.string());    ALOGV("  descriptor: \"%s\"\n", identifier.descriptor.string());    ALOGV("  driver:     v%d.%d.%d\n",        driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);    // Load the configuration file for the device.    loadConfigurationLocked(device);    // Figure out the kinds of events the device reports.    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);    ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);    ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);    ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);    ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);    ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);    ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);    // See if this is a keyboard.  Ignore everything in the button range except for    // joystick and gamepad buttons which are handled like keyboards for the most part.    bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC))            || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),                    sizeof_bit_array(KEY_MAX + 1));    bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),                    sizeof_bit_array(BTN_MOUSE))            || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),                    sizeof_bit_array(BTN_DIGI));    if (haveKeyboardKeys || haveGamepadButtons) {        device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;    }    // See if this is a cursor device such as a trackball or mouse.    if (test_bit(BTN_MOUSE, device->keyBitmask)            && test_bit(REL_X, device->relBitmask)            && test_bit(REL_Y, device->relBitmask)) {        device->classes |= INPUT_DEVICE_CLASS_CURSOR;    }    // MStar Android Patch Begin    // See if this is a cursor device of cywee company    if (test_bit(BTN_MOUSE, device->keyBitmask)            && test_bit(REL_X, device->relBitmask)            && test_bit(REL_Y, device->relBitmask)            && test_bit(REL_RX, device->relBitmask)            && test_bit(REL_RY, device->relBitmask)) {        device->classes |= INPUT_DEVICE_CLASS_CYWEE_CURSOR;    }    // MStar Android Patch End    // See if this is a touch pad.    // Is this a new modern multi-touch driver?    if (test_bit(ABS_MT_POSITION_X, device->absBitmask)            && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {        // Some joysticks such as the PS3 controller report axes that conflict        // with the ABS_MT range.  Try to confirm that the device really is        // a touch screen.        if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {            device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;        }    // Is this an old style single-touch driver?    } else if (test_bit(BTN_TOUCH, device->keyBitmask)            && test_bit(ABS_X, device->absBitmask)            && test_bit(ABS_Y, device->absBitmask)) {        device->classes |= INPUT_DEVICE_CLASS_TOUCH;    }    // See if this device is a joystick.    // Assumes that joysticks always have gamepad buttons in order to distinguish them    // from other devices such as accelerometers that also have absolute axes.    if (haveGamepadButtons) {        uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;        for (int i = 0; i <= ABS_MAX; i++) {            if (test_bit(i, device->absBitmask)                    && (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {                device->classes = assumedClasses;                break;            }        }    }    // Check whether this device has switches.    for (int i = 0; i <= SW_MAX; i++) {        if (test_bit(i, device->swBitmask)) {            device->classes |= INPUT_DEVICE_CLASS_SWITCH;            break;        }    }    // Check whether this device supports the vibrator.    if (test_bit(FF_RUMBLE, device->ffBitmask)) {        device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;    }    // Configure virtual keys.    if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {        // Load the virtual keys for the touch screen, if any.        // We do this now so that we can make sure to load the keymap if necessary.        status_t status = loadVirtualKeyMapLocked(device);        if (!status) {            device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;        }    }    // 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.        keyMapStatus = loadKeyMapLocked(device);    }    // Configure the keyboard, gamepad or virtual keyboard.    if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {        // Register the keyboard as a built-in keyboard if it is eligible.        if (!keyMapStatus                && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD                && isEligibleBuiltInKeyboard(device->identifier,                        device->configuration, &device->keyMap)) {            mBuiltInKeyboardId = device->id;        }        // 'Q' key support = cheap test of whether this is an alpha-capable kbd        if (hasKeycodeLocked(device, AKEYCODE_Q)) {            device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;        }        // See if this device has a DPAD.        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;            }        }        // Disable kernel key repeat since we handle it ourselves        unsigned int repeatRate[] = {0,0};        if (ioctl(fd, EVIOCSREP, repeatRate)) {            ALOGW("Unable to disable kernel key repeat for %s: %s", devicePath, strerror(errno));        }    }    // If the device isn't recognized as something we handle, don't monitor it.    if (device->classes == 0) {        ALOGV("Dropping device: id=%d, path='%s', name='%s'",                deviceId, devicePath, device->identifier.name.string());        delete device;        return -1;    }    // Determine whether the device is external or internal.    if (isExternalDeviceLocked(device)) {        device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;    }    if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_GAMEPAD)) {        device->controllerNumber = getNextControllerNumberLocked(device);    }    // Register with epoll.    struct epoll_event eventItem;    memset(&eventItem, 0, sizeof(eventItem));    eventItem.events = EPOLLIN;    eventItem.data.u32 = deviceId;    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;    }    // Enable wake-lock behavior on kernels that support it.    // TODO: Only need this for devices that can really wake the system.    bool usingSuspendBlockIoctl = !ioctl(fd, EVIOCSSUSPENDBLOCK, 1);    // Tell the kernel that we want to use the monotonic clock for reporting timestamps    // associated with input events.  This is important because the input system    // uses the timestamps extensively and assumes they were recorded using the monotonic    // clock.    //    // In older kernel, before Linux 3.4, there was no way to tell the kernel which    // clock to use to input event timestamps.  The standard kernel behavior was to    // record a real time timestamp, which isn't what we want.  Android kernels therefore    // contained a patch to the evdev_event() function in drivers/input/evdev.c to    // replace the call to do_gettimeofday() with ktime_get_ts() to cause the monotonic    // clock to be used instead of the real time clock.    //    // As of Linux 3.4, there is a new EVIOCSCLOCKID ioctl to set the desired clock.    // Therefore, we no longer require the Android-specific kernel patch described above    // as long as we make sure to set select the monotonic clock.  We do that here.    int clockId = CLOCK_MONOTONIC;    bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);    ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "            "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, "            "usingSuspendBlockIoctl=%s, usingClockIoctl=%s",         deviceId, fd, devicePath, device->identifier.name.string(),         device->classes,         device->configurationFile.string(),         device->keyMap.keyLayoutFile.string(),         device->keyMap.keyCharacterMapFile.string(),         toString(mBuiltInKeyboardId == deviceId),         toString(usingSuspendBlockIoctl), toString(usingClockIoctl));    addDeviceLocked(device);    return 0;}

使用ioctl(fd, EVIOCGID, &inputId)读取出设备标识信息赋给identifier,并创建Device,接着loadConfigurationLocked接在配置,最后使用addDeviceLocked添加设备信息。

按键布局文件(kl)加载

由上面分析指导,每个Device都对应一个设备,底层配置文件中的映射关系都需要load到android系统中才能被使用,我们可以先看EventHub的子类Device
struct Device {        Device* next;        int fd; // may be -1 if device is virtual        const int32_t id;        const String8 path;        const InputDeviceIdentifier identifier;        uint32_t classes;        uint8_t keyBitmask[(KEY_MAX + 1) / 8];        uint8_t absBitmask[(ABS_MAX + 1) / 8];        uint8_t relBitmask[(REL_MAX + 1) / 8];        uint8_t swBitmask[(SW_MAX + 1) / 8];        uint8_t ledBitmask[(LED_MAX + 1) / 8];        uint8_t ffBitmask[(FF_MAX + 1) / 8];        uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];        String8 configurationFile;        PropertyMap* configuration;        VirtualKeyMap* virtualKeyMap;        KeyMap keyMap;        sp<KeyCharacterMap> overlayKeyMap;        sp<KeyCharacterMap> combinedKeyMap;        bool ffEffectPlaying;        int16_t ffEffectId; // initially -1        int32_t controllerNumber;        int32_t timestampOverrideSec;        int32_t timestampOverrideUsec;        Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);        ~Device();        void close();        inline bool isVirtual() const { return fd < 0; }        const sp<KeyCharacterMap>& getKeyCharacterMap() const {            if (combinedKeyMap != NULL) {                return combinedKeyMap;            }            return keyMap.keyCharacterMap;        }    }

子类Device中函数KeyMap keyMap,该对象就是一个按键映射对象,接着分析如何加载
// Load the keymap for the device.status_t EventHub::loadKeyMapLocked(Device* device) {    return device->keyMap.load(device->identifier, device->configuration);}
其实进入loadKeyMapLocked有两各入口,createVirtualKeyboardLocked和openDeviceLocked,后者则是我们上面分析的一条路径。device->keyMap.load(device->identifier, device->configuration)也即进入Device::load
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);            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
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;}

/* Types of input device configuration files. */enum InputDeviceConfigurationFileType {    INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0,     /* .idc file */    INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1,        /* .kl file */    INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */};
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT就代表着.kl 类型文件,紧接着进入KeyLayoutMap::load(path, &keyLayoutMap)
status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) {    outMap->clear();    Tokenizer* tokenizer;    status_t status = Tokenizer::open(filename, &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;}

KeyLayoutMap::Parser完成.kl 文件内容的解析
status_t KeyLayoutMap::Parser::parse() {    while (!mTokenizer->isEof()) {#if DEBUG_PARSER        ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),                mTokenizer->peekRemainderOfLine().string());#endif        mTokenizer->skipDelimiters(WHITESPACE);        if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {            String8 keywordToken = mTokenizer->nextToken(WHITESPACE);            if (keywordToken == "key") {                mTokenizer->skipDelimiters(WHITESPACE);                status_t status = parseKey();                if (status) return status;            } else if (keywordToken == "axis") {                mTokenizer->skipDelimiters(WHITESPACE);                status_t status = parseAxis();                if (status) return status;            } else {                ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),                        keywordToken.string());                return BAD_VALUE;            }            mTokenizer->skipDelimiters(WHITESPACE);            if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {                ALOGE("%s: Expected end of line or trailing comment, got '%s'.",                        mTokenizer->getLocation().string(),                        mTokenizer->peekRemainderOfLine().string());                return BAD_VALUE;            }        }        mTokenizer->nextLine();    }    return NO_ERROR;}

至此按键文件的配置,加载,解析流程完毕。主要涉及几个核心类EventHub,KeyMap,KeyLayoutMap,InputDeviceInfo。






更多相关文章

  1. android 布局文件 layout_weight用法
  2. android 中xml文件中出现 Attr.value missing 错误
  3. 获得Android Linux系统增删文件的权限
  4. android 文件系统(
  5. 关于声明文件中android:process属性说明
  6. Ubuntu下连接Android设备
  7. Android 自定义View及其在布局文件中的使用示例(三):结合Android
  8. Android 4.4 SD卡文件读写变化

随机推荐

  1. android读取工程里文件并显示在界面
  2. 无法找到“android:Theme.Holo.Light.Dar
  3. Android 中的 Looper 对象
  4. android显示隐藏软键盘
  5. Eclipse项目导入AndroidStudio中文乱码问
  6. Android phonegap_2.8.1开发部署
  7. Android监听Home键和最近任务
  8. android系统服务启动顺序
  9. android获得系统时间(Handler)
  10. Android的JNI_OnLoad简介与应用