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
對映檔案的載入 從上面的命令可以整體把握按鍵的對映情況,但是如何載入,我們可以看一下按鍵的處理樞紐,也即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 目錄下資訊,如下:
使用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 版權宣告:本文為博主原創文章,轉載請附上博文連結!