原文在
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: a718a782d34bc767f46c232dd527998ea7fd
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: 8f43d929a9472e8dcd48a6c41e2435e8eaff35
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: 2b7a30c0f74e1362d8ef86c5e4f12150af666f
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: 065b1db3afd7f700b4049a883d405d76233a40
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=20457.2ms
DeviceResetEvent(deviceId=3), policyFlags=0x00000000, age=20457.2ms
DeviceResetEvent(deviceId=2), policyFlags=0x00000000, age=20457.2ms
DeviceResetEvent(deviceId=1), policyFlags=0x00000000, age=20457.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在按键性能优化时,就可以起到作用。
使用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;
}
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- ryyc.cn 版权所有 湘ICP备2023022495号-3
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务