1. 程式人生 > >Android Input 輸入系統學習

Android Input 輸入系統學習

Android輸入裝置的對映 使用指令dumpsys input 可以看到以下資訊 130|[email protected]_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    

[email protected]_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 目錄下資訊,如下:

[email protected]_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。 ---------------------  作者:Tony_ke  來源:CSDN  原文:https://blog.csdn.net/kehyuanyu/article/details/48573211  版權宣告:本文為博主原創文章,轉載請附上博文連結!