Android input 输入系统学习

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: false

Input 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.3

Input 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.0ms


shell@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 event0
crw-rw---- root     input     13,  65 1970-01-01 08:00 event1
crw-rw---- root     input     13,  66 1970-01-01 08:00 event2
crw-rw---- root     input     13,  67 1970-01-02 08:01 event3
crw-rw---- root     input     13,   0 1970-01-01 08:00 js0
crw-rw---- root     input     13,  63 1970-01-01 08:00 mice
crw-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。







你可能感兴趣的:(android,input,KL)