Original Event Process
-
Keyboard 类型事件的加工处理
-
Touch 类型事件的加工处理
Keyboard 类型事件的加工处理
KeyboardInputMapper的配置
InputDevice创建完成后,对通过InputReader下的mConfig成员对其进行策略配置,以影响原始输入事件的处理行为,InputDevice的
InputMapper同样是在此时进行配置的。
/frameworks/native/services/inputflinger/InputReader.h
[InputReader.h–>InputReaderConfiguration]
/**
获取屏幕信息
*/
bool getDisplayInfo(bool external, DisplayViewport* outViewport) const;
/**
设置屏幕信息
*/
void setDisplayInfo(bool external, const DisplayViewport& viewport);
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReaderConfiguration::configure()]
/**
当屏幕旋转状态发生变化时,需要更新InputReaderConfiguration,并对KeyboardInputMapper进行重新配置
*/
void KeyboardInputMapper::configure(nsecs_t when,
const InputReaderConfiguration* config, uint32_t changes) {
InputMapper::configure(when, config, changes);
if (!changes) { // first time only
// Configure basic parameters.
configureParameters();
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
DisplayViewport v;
/**
调整屏幕旋转方向
*/
if (config->getDisplayInfo(false /*external*/, &v)) {
mOrientation = v.orientation;
} else {
mOrientation = DISPLAY_ORIENTATION_0;
}
} else {
mOrientation = DISPLAY_ORIENTATION_0;
}
}
}
扫描码到虚拟键值的映射
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReaderConfiguration::process()]
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
/**
按键类的原始输入事件类型EV_KEY(0x01)
*/
case EV_KEY: {
/**
事件代码保存了键盘的扫描码
*/
int32_t scanCode = rawEvent->code;
int32_t usageCode = mCurrentHidUsage;
mCurrentHidUsage = 0;
if (isKeyboardOrGamepadKey(scanCode)) {
/**
调用processKey处理事件
*/
processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
}
break;
}
case EV_MSC: {
if (rawEvent->code == MSC_SCAN) {
mCurrentHidUsage = rawEvent->value;
}
break;
}
case EV_SYN: {
if (rawEvent->code == SYN_REPORT) {
mCurrentHidUsage = 0;
}
}
}
}
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReaderConfiguration::processKey()]
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
int32_t usageCode) {
int32_t keyCode;
int32_t keyMetaState;
uint32_t policyFlags;
/**
通过EventHub的mapKey()函数进行映射
输入:scanCode, usageCode, mMetaState
输出:&keyCode, &keyMetaState, &policyFlags
*/
if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
&keyCode, &keyMetaState, &policyFlags)) {
/**
如果映射失败,则用AKEYCODE_UNKNOWN作为事件的虚拟键值
*/
keyCode = AKEYCODE_UNKNOWN;
keyMetaState = mMetaState;
policyFlags = 0;
}
if (down) {
// Rotate key codes according to orientation if needed.
if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
keyCode = rotateKeyCode(keyCode, mOrientation);
}
// Add key down.
ssize_t keyDownIndex = findKeyDown(scanCode);
if (keyDownIndex >= 0) {
// key repeat, be sure to use same keycode as before in case of rotation
keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
} else {
// key down
if ((policyFlags & POLICY_FLAG_VIRTUAL)
&& mContext->shouldDropVirtualKey(when,
getDevice(), keyCode, scanCode)) {
return;
}
if (policyFlags & POLICY_FLAG_GESTURE) {
mDevice->cancelTouch(when);
}
mKeyDowns.push();
KeyDown& keyDown = mKeyDowns.editTop();
keyDown.keyCode = keyCode;
keyDown.scanCode = scanCode;
}
mDownTime = when;
} else {
// Remove key down.
ssize_t keyDownIndex = findKeyDown(scanCode);
if (keyDownIndex >= 0) {
// key up, be sure to use same keycode as before in case of rotation
keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
mKeyDowns.removeAt(size_t(keyDownIndex));
} else {
// key was not actually down
ALOGI("Dropping key up from device %s because the key was not down. "
"keyCode=%d, scanCode=%d",
getDeviceName().string(), keyCode, scanCode);
return;
}
}
if (updateMetaStateIfNeeded(keyCode, down)) {
// If global meta state changed send it along with the key.
// If it has not changed then we'll use what keymap gave us,
// since key replacement logic might temporarily reset a few
// meta bits for given key.
keyMetaState = mMetaState;
}
nsecs_t downTime = mDownTime;
// Key down on external an keyboard should wake the device.
// We don't do this for internal keyboards to prevent them from waking up in your pocket.
// For internal keyboards, the key layout file should specify the policy flags for
// each wake key individually.
// TODO: Use the input device configuration to control this behavior more finely.
if (down && getDevice()->isExternal()) {
policyFlags |= POLICY_FLAG_WAKE;
}
if (mParameters.handlesKeyRepeat) {
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
if (down && !isMetaKey(keyCode)) {
getContext()->fadePointer();
}
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
getListener()->notifyKey(&args);
}
EventHub从扫描码映射为虚拟键值
在EventHub通过openDeviceLocked()函数加载输入设备时,会根据设备提供的事件位掩码位设备分配类别。当设备的类别位
INPUT_DEVICE_CLASS_KEYBOARD/JOYSTICK时,说明此设备可以上报EV_KEY类型的原始事件,因此openDeviceLocked()
函数会调用EventHub::loadKeyMapLocked()函数位此设备加载键盘布局配置文件。键盘布局配置文件的路径是由设备配置文件
中的keyboard.layout项与设备厂商信息联合产生的位于/system/usr/input/下的一个.kl文件。这个.kl文件的每一行
描述了一个从扫描码到虚拟键值的映射信息。.kl文件中一行的典型格式如下:
key <scan_code> <key_name> <policy_flag>
KEY ,关键字,表示本行描述了一个键盘映射
scan_code,十进制的扫描码
key_name,虚拟键值名称
policy_flag,表明此键所附加的策略名称
例如:
key 158 BACK WAKE_DROPPED
loadKeyMapLocked()函数分析.kl文件
/frameworks/native/services/inputflinger/EventHub.cpp
[EventHub.cpp–>EventHub::loadKeyMapLocked()]
status_t EventHub::loadKeyMapLocked(Device* device) {
return device->keyMap.load(device->identifier, device->configuration);
}
/frameworks/native/services/inputflinger/EventHub.h
private:
struct Device {
Device* next;
......
KeyMap keyMap;
......
};
/frameworks/native/libs/input/Keyboard.cpp
[Keyboard.cpp–>Keyboard::KeyMap::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)) {
/**
1. 加载keyboard.layout
*/
status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
if (status == NAME_NOT_FOUND) {
LOGE("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)) {
/**
2. 加载keyboard.characterMap
*/
status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
if (status == NAME_NOT_FOUND) {
LOGE("Configuration for keyboard device '%s' requested keyboard character "
"map '%s' but it was not found.",
deviceIdenfifier.name.string(), keyLayoutName.string());
}
}
if (isComplete()) {
return OK;
}
}
/**
3. 调用probeKeyMap
*/
// 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!
LOGE("Could not determine key map for device '%s' and no default key maps were found!",
deviceIdenfifier.name.string());
return NAME_NOT_FOUND;
}
1. 加载keyboard.layout
/frameworks/native/libs/input/Keyboard.cpp
[Keyboard.cpp–> KeyMap::loadKeyLayout()]
/**
加载keyboard.layout,生成keyLayoutMap
*/
status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
const String8& name) {
/**
1. 获取keyLayoutMap存放路径
*/
String8 path(getPath(deviceIdentifier, name,
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
if (path.isEmpty()) {
return NAME_NOT_FOUND;
}
/**
2. 加载KeyLayoutMap
*/
KeyLayoutMap* map;
status_t status = KeyLayoutMap::load(path, &map);
if (status) {
return status;
}
/**
3. 设置keyLayoutFile,赋值keyLayoutMap
*/
keyLayoutFile.setTo(path);
keyLayoutMap = map;
return OK;
}
1.1 获取keyLayoutMap存放路径
/frameworks/native/libs/input/Keyboard.cpp
[Keyboard.cpp–> KeyMap::getPath()]
/**
deviceIdentifier:
name:
type: INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1,/* .kl file */(/frameworks/native/include/input/InputDevice.h)
*/
String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
const String8& name, InputDeviceConfigurationFileType type) {
return name.isEmpty()
? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
: getInputDeviceConfigurationFilePathByName(name, type);
}
/frameworks/native/libs/input/InputDevice.cpp
[Input.cpp–>Input::getInputDeviceConfigurationFilePathByDeviceIdentifier()]
String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
const InputDeviceIdentifier& deviceIdentifier,
InputDeviceConfigurationFileType type) {
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
if (deviceIdentifier.version != 0) {
// Try vendor product version.
String8 versionPath(getInputDeviceConfigurationFilePathByName(
String8::format("Vendor_%04x_Product_%04x_Version_%04x",
deviceIdentifier.vendor, deviceIdentifier.product,
deviceIdentifier.version),
type));
if (!versionPath.isEmpty()) {
return versionPath;
}
}
// Try vendor product.
String8 productPath(getInputDeviceConfigurationFilePathByName(
String8::format("Vendor_%04x_Product_%04x",
deviceIdentifier.vendor, deviceIdentifier.product),
type));
if (!productPath.isEmpty()) {
return productPath;
}
}
// Try device name.
return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
}
/frameworks/native/libs/input/InputDevice.cpp
[Input.cpp–>Input::getInputDeviceConfigurationFilePathByName()]
String8 getInputDeviceConfigurationFilePathByName(
const String8& name, InputDeviceConfigurationFileType type) {
// Search system repository.
String8 path;
path.setTo(getenv("ANDROID_ROOT"));
path.append("/usr/");
appendInputDeviceConfigurationFileRelativePath(path, name, type);
#if DEBUG_PROBE
LOGD("Probing for system provided input device configuration file: path='%s'", path.string());
#endif
if (!access(path.string(), R_OK)) {
#if DEBUG_PROBE
LOGD("Found");
#endif
return path;
}
// Search user repository.
// TODO Should only look here if not in safe mode.
path.setTo(getenv("ANDROID_DATA"));
path.append("/system/devices/");
appendInputDeviceConfigurationFileRelativePath(path, name, type);
#if DEBUG_PROBE
LOGD("Probing for system user input device configuration file: path='%s'", path.string());
#endif
if (!access(path.string(), R_OK)) {
#if DEBUG_PROBE
LOGD("Found");
#endif
return path;
}
// Not found.
#if DEBUG_PROBE
LOGD("Probe failed to find input device configuration file: name='%s', type=%d",
name.string(), type);
#endif
return String8();
}
1.2. 加载KeyLayoutMap
/frameworks/native/libs/input/KeyLayoutMap.cpp
[KeyLayoutMap.cpp–>KeyLayoutMap::load()]
status_t KeyLayoutMap::load(const String8& filename, KeyLayoutMap** outMap) {
*outMap = NULL;
Tokenizer* tokenizer;
/**
打开filename
*/
status_t status = Tokenizer::open(filename, &tokenizer);
if (status) {
LOGE("Error %d opening key layout map file %s.", status, filename.string());
} else {
/**
新建一个KeyLayoutMap
*/
KeyLayoutMap* map = new KeyLayoutMap();
if (!map) {
LOGE("Error allocating key layout map.");
status = NO_MEMORY;
} else {
#if DEBUG_PARSER_PERFORMANCE
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
#endif
/**
解析KeyLayoutMap
*/
Parser parser(map, tokenizer);
status = parser.parse();
#if DEBUG_PARSER_PERFORMANCE
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
LOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
elapsedTime / 1000000.0);
#endif
/**
解析成功,赋值给outMap
*/
if (status) {
delete map;
} else {
*outMap = map;
}
}
delete tokenizer;
}
return status;
}
2. 加载keyboard.characterMap
/frameworks/native/libs/input/Keyboard.cpp
[Keyboard.cpp–> KeyMap::loadKeyCharacterMap()]
/**
加载keyboard.layout,生成KeyCharacterMap
*/
status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
const String8& name) {
String8 path(getPath(deviceIdentifier, name,
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
if (path.isEmpty()) {
return NAME_NOT_FOUND;
}
KeyCharacterMap* map;
status_t status = KeyCharacterMap::load(path, &map);
if (status) {
return status;
}
keyCharacterMapFile.setTo(path);
keyCharacterMap = map;
return OK;
}
3. 调用probeKeyMap
/frameworks/native/libs/input/Keyboard.cpp
[Keyboard.cpp–> KeyMap::probeKeyMap()]
bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
const String8& keyMapName) {
if (!haveKeyLayout()) {
loadKeyLayout(deviceIdentifier, keyMapName);
}
if (!haveKeyCharacterMap()) {
loadKeyCharacterMap(deviceIdentifier, keyMapName);
}
return isComplete();
}
/frameworks/native/include/input/Keyboard.h
[Keyboard.cpp–> KeyMap]
class KeyMap {
public:
String8 keyLayoutFile;
KeyLayoutMap* keyLayoutMap;
String8 keyCharacterMapFile;
KeyCharacterMap* keyCharacterMap;
KeyMap();
~KeyMap();
status_t load(const InputDeviceIdentifier& deviceIdenfier,
const PropertyMap* deviceConfiguration);
inline bool haveKeyLayout() const {
return !keyLayoutFile.isEmpty();
}
inline bool haveKeyCharacterMap() const {
return !keyCharacterMapFile.isEmpty();
}
inline bool isComplete() const {
return haveKeyLayout() && haveKeyCharacterMap();
}
private:
bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
const String8& name);
String8 getPath(const InputDeviceIdentifier& deviceIdentifier,
const String8& name, InputDeviceConfigurationFileType type);
};
总结
loadKeyMapLocked()函数分析.kl文件,每一行都将名称转换为虚拟键值后存储在Key的结构体中,所得到的Key结构体集合以扫描码为键
存储在KeyLayoutMap对象中,于是EventHub::mapKey()函数可以根据设备Id找到对应的KeyLayoutMap,进而根据扫描码找到对应的key结
体中所保存的虚拟键值(传出参数keycode)以及策略值(传出参数flag),从而完成从扫描码到虚拟键值的映射工作。
/frameworks/native/services/inputflinger/EventHub.cpp
[EventHub.cpp–>EventHub::mapKey()]
status_t EventHub::mapKey(int32_t deviceId, int scancode,
int32_t* outKeycode, uint32_t* outFlags) const
{
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device && device->keyMap.haveKeyLayout()) {
status_t err = device->keyMap.keyLayoutMap->mapKey(scancode, outKeycode, outFlags);
if (err == NO_ERROR) {
return NO_ERROR;
}
}
if (mBuiltInKeyboardId != -1) {
device = getDeviceLocked(mBuiltInKeyboardId);
if (device && device->keyMap.haveKeyLayout()) {
status_t err = device->keyMap.keyLayoutMap->mapKey(scancode, outKeycode, outFlags);
if (err == NO_ERROR) {
return NO_ERROR;
}
}
}
*outKeycode = 0;
*outFlags = 0;
return NAME_NOT_FOUND;
}
/frameworks/native/libs/input/KeyLayoutMap.cpp
[EventHub.cpp–>EventHub::mapKey()]
status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const {
ssize_t index = mKeys.indexOfKey(scanCode);
if (index < 0) {
#if DEBUG_MAPPING
LOGD("mapKey: scanCode=%d ~ Failed.", scanCode);
#endif
*keyCode = AKEYCODE_UNKNOWN;
*flags = 0;
return NAME_NOT_FOUND;
}
const Key& k = mKeys.valueAt(index);
*keyCode = k.keyCode;
*flags = k.flags;
#if DEBUG_MAPPING
LOGD("mapKey: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags);
#endif
return NO_ERROR;
}
按键事件的加工处理
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReaderConfiguration::processKey()]
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
int32_t usageCode) {
int32_t keyCode;
int32_t keyMetaState;
uint32_t policyFlags;
/**
通过EventHub的mapKey()函数进行映射
输入:scanCode, usageCode, mMetaState
输出:&keyCode, &keyMetaState, &policyFlags
*/
if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
&keyCode, &keyMetaState, &policyFlags)) {
/**
如果映射失败,则用AKEYCODE_UNKNOWN作为事件的虚拟键值
*/
keyCode = AKEYCODE_UNKNOWN;
keyMetaState = mMetaState;
policyFlags = 0;
}
if (down) {
/**
1. 当按下时,首先需要根据屏幕的方向对按键的虚拟键值进行旋转变换
*/
// Rotate key codes according to orientation if needed.
if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
keyCode = rotateKeyCode(keyCode, mOrientation);
}
/**
2. KeyboardInputMapper维护了一个KeyDown结构体的集合。当按键按下去是会生成
一个保存了扫描码与键值的KeyDown对象并添加到集合中。通过扫描码对集合的查找结果
表明了按键是否是重复按下
*/
// Add key down.
ssize_t keyDownIndex = findKeyDown(scanCode);
if (keyDownIndex >= 0) {
/**
对于重复按下的按键,需要确保后续的处理过程中的虚拟键值和第一次按下时一致,以免
重复的过程中屏幕方向的变化导致虚拟键值的变化,使得后续InputDeispatcher无法正
常识别重复按键的动作
*/
// key repeat, be sure to use same keycode as before in case of rotation
keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
} else {
// key down
if ((policyFlags & POLICY_FLAG_VIRTUAL)
&& mContext->shouldDropVirtualKey(when,
getDevice(), keyCode, scanCode)) {
return;
}
if (policyFlags & POLICY_FLAG_GESTURE) {
mDevice->cancelTouch(when);
}
/**
生成KeyDown结构体并添加到集合中
*/
mKeyDowns.push();
KeyDown& keyDown = mKeyDowns.editTop();
keyDown.keyCode = keyCode;
keyDown.scanCode = scanCode;
}
mDownTime = when;
} else {
/**
对于抬起的按键,将对应的KeyDown对象从集合中移除
*/
// Remove key down.
ssize_t keyDownIndex = findKeyDown(scanCode);
if (keyDownIndex >= 0) {
// key up, be sure to use same keycode as before in case of rotation
keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
mKeyDowns.removeAt(size_t(keyDownIndex));
} else {
// key was not actually down
ALOGI("Dropping key up from device %s because the key was not down. "
"keyCode=%d, scanCode=%d",
getDeviceName().string(), keyCode, scanCode);
return;//如果设备几点上报一个并未按下的按键的抬起事件,将忽略该事件
}
}
/**
设置metaState(是指控制键的按下状态。控制键有左右Shift、Alt Ctrl Fn CapsLock NumLock
SCrollLock等,这些按键被按下或者抬起是,mMetaState会将相应的位置为1或者0)
*/
if (updateMetaStateIfNeeded(keyCode, down)) {
// If global meta state changed send it along with the key.
// If it has not changed then we'll use what keymap gave us,
// since key replacement logic might temporarily reset a few
// meta bits for given key.
keyMetaState = mMetaState;
}
nsecs_t downTime = mDownTime;
// Key down on external an keyboard should wake the device.
// We don't do this for internal keyboards to prevent them from waking up in your pocket.
// For internal keyboards, the key layout file should specify the policy flags for
// each wake key individually.
// TODO: Use the input device configuration to control this behavior more finely.
if (down && getDevice()->isExternal()) {
policyFlags |= POLICY_FLAG_WAKE;
}
if (mParameters.handlesKeyRepeat) {
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
if (down && !isMetaKey(keyCode)) {
getContext()->fadePointer();
}
/**
将所有的按键事件信息封装为NotifyKeyArgs对象,并将此对象通知给listener
*/
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
getListener()->notifyKey(&args);
}
/frameworks/base/services/input/InputReader.cpp
[InputReader.cpp–>InputReader::KeyboardInputMapper::findKeyDown]
ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
size_t n = mKeyDowns.size();
for (size_t i = 0; i < n; i++) {
if (mKeyDowns[i].scanCode == scanCode) {
return i;
}
}
return -1;
}
/frameworks/native/services/inputflinger/InputReader.h
struct KeyDown {
int32_t keyCode;
int32_t scanCode;
};
Vector<KeyDown> mKeyDowns; // keys that are down
Touch 类型事件的加工处理
Touch类型事件的信息与原始事件的组织方式
TouchInputMapper体系
1.TouchInputMapper: 负责在接收到EV_SYN事件时调用子类的syncTouch()函数,另子类将获取的描述点击事件的信息存储到其
mCurrentRawPointerData对象中。将mCurrentRwaPointerData解析为ACTION_DOWAN, ACTION_MOVE, ACTION_UP等高级点击
事件后,将其交付给InputDispather.
2.MultiTouchInputMapper: 主要为了实现syncTouch()函数,目的是将MultiTouchMotionAccumulator收集道德点击事件信息
存储到mCurrentRawPointerData中。另外,MultiTouchInputMapper也负责从EventHub中获取与多点触控相关的设备配置信息,如
所支持的触控点的数量以及各项信息的取值范围、精度、噪音偏移量等指标。
3.MultiTouchMotionAccumulator: 多点触控信息的累加器。他负责接收处理每条EV_ABS事件,并将事件中携带的点击信息保存下来
并在EV_SYN事件到来是提供给MultiTouchInputMapper.
4.SingleTouchInputMapper与SingleTouchMotionAccumulator,负责单点触控事件的收集与整合工作。
MultiTouchInputMapper配置
MultiTouchInputMapper及其累加器的构造函数没有什么内容。事件信息收集与整合所需要的信息的获取位于其配置过程中,也就是其
基类TouchInputMapper的configure()函数中。
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReader::TouchInputMapper::configure()]
void TouchInputMapper::configure(nsecs_t when,
const InputReaderConfiguration* config, uint32_t changes) {
InputMapper::configure(when, config, changes);
mConfig = *config;
if (!changes) { // first time only
// Configure basic parameters.
configureParameters();
// Configure common accumulators.
mCursorScrollAccumulator.configure(getDevice());
mTouchButtonAccumulator.configure(getDevice());
// Configure absolute axis information.
configureRawPointerAxes();
// Prepare input device calibration.
parseCalibration();
resolveCalibration();
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) {
// Update location calibration to reflect current settings
updateAffineTransformation();
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
// Update pointer speed.
mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
}
bool resetNeeded = false;
if (!changes || (changes & (InputReaderConfiguration::CHANGE_DISPLAY_INFO
| InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT
| InputReaderConfiguration::CHANGE_SHOW_TOUCHES
| InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
// Configure device sources, surface dimensions, orientation and
// scaling factors.
configureSurface(when, &resetNeeded);
}
if (changes && resetNeeded) {
// Send reset, unless this is the first time the device has been configured,
// in which case the reader will call reset itself after all mappers are ready.
getDevice()->notifyReset(when);
}
}
TouchInputMapper的configure()函数的调用函数和KeyboardInputMapper一样。在TouchInputMapper被创建之后对
configure()的第一次调用中,TouchInputMapper会调用子类的configureRawPointerAxes()函数用以获取由设备节点
提供的信息。这些信息包裹输入设备所支持的触控点数量以及各项信息的取值范围、精度、干扰偏移量等指标。他们是通过
EventHub,使用ioctl方式获取的。一项信息的技术指标由RawAbsoluteAxisInfo结构描述:
/* Describes an absolute axis. */
struct RawAbsoluteAxisInfo {
bool valid; // true if the information is valid, false otherwise
int32_t minValue; // minimum value
int32_t maxValue; // maximum value
int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8
int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
int32_t resolution; // resolution in units per mm or radians per mm
inline void clear() {
valid = false;
minValue = 0;
maxValue = 0;
flat = 0;
fuzz = 0;
resolution = 0;
}
};
例如,对设备的X坐标这项信息而言,RawAbsoluteAxisInfo的minValue和maxValue表示事件上报的X坐标范围,Resolution
表示传感器在每毫米距离中可以产生的X坐标点的数量(ppm).X坐标与Y坐标的这些技术指标构建了传感器的物理坐标系。而对压力
值这项信息而言,如果其RawAbsoluteAxisInfo的valid值为false,则表示此设备不支持识别压力值。当然,这些字段并不是对
所有类型的信息都有效,例如对于传感器所支持的触控点数量这项信息,仅有valid和maxValue两个字段有效,value为true表示
设备支持通过slot协议进行触控点索引的识别,而masValue则表示最大触控点的数量为maxValue + 1.
所有信息的技术指标被MultiTouchInputMapper保存在一个名为mRawPointerAxes的RawPointerAxes结构体中。
另外,TouchInputMapper的configure()调用的configureSurface()函数会通过DisplayViewPort获取屏幕的方向以及屏幕
坐标系的信息。屏幕方向的不同会导致触控位置的X和Y两个方向的颠倒或交换。通过计算屏幕坐标系与传感器物理坐标系之间的差
异,使得在事件到来时可以将点击位置从传感器的坐标系转换到屏幕的坐标系中。
总结
MultiTouchInputMapper的configureRawPointerAxes()函数获取来自设备几天的各项触控信息的技术指标。同时,这些指标
构架了传感器的物理坐标系
TouchInputMapper的configureSurface()函数获取来自DisplayViewPort的屏幕方向以及屏幕坐标系的信息,并计算物理坐标
系到屏幕坐标系的差异信息。
点击事件信息的收集
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReader::MultiTouchInputMapper::process()]
void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
TouchInputMapper::process(rawEvent);
mMultiTouchMotionAccumulator.process(rawEvent);
}
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReader::TouchInputMapper::process()]
void TouchInputMapper::process(const RawEvent* rawEvent) {
mCursorButtonAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
mTouchButtonAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
sync(rawEvent->when);
}
}
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReader::MultiTouchMotionAccumulator::process()]
void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
/**
点击原始输入事件的事件类型为EV_ABS
*/
if (rawEvent->type == EV_ABS) {
bool newSlot = false;
if (mUsingSlotsProtocol) {
if (rawEvent->code == ABS_MT_SLOT) {
/**
代码为ABS_MT_SLOT的事件指明后续事件对应的触控点的索引
*/
mCurrentSlot = rawEvent->value;
newSlot = true;
}
} else if (mCurrentSlot < 0) {
mCurrentSlot = 0;
}
if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
#if DEBUG_POINTERS
if (newSlot) {
ALOGW("MultiTouch device emitted invalid slot index %d but it "
"should be between 0 and %d; ignoring this slot.",
mCurrentSlot, mSlotCount - 1);
}
#endif
} else {
/**
根据触控点的索引获取一个slot对象。随后收集的信息豆浆放置在这个Slot对象中
*/
Slot* slot = &mSlots[mCurrentSlot];
/**
提取事件中携带的信息,斌存储在触控点对应的Slot对象中
*/
switch (rawEvent->code) {//原始输入事件的代码指明了他所携带的信息类型
case ABS_MT_POSITION_X:
slot->mInUse = true;
slot->mAbsMTPositionX = rawEvent->value;
break;
case ABS_MT_POSITION_Y:
slot->mInUse = true;
slot->mAbsMTPositionY = rawEvent->value;
break;
case ABS_MT_TOUCH_MAJOR:
slot->mInUse = true;
slot->mAbsMTTouchMajor = rawEvent->value;
break;
case ABS_MT_TOUCH_MINOR:
slot->mInUse = true;
slot->mAbsMTTouchMinor = rawEvent->value;
slot->mHaveAbsMTTouchMinor = true;
break;
case ABS_MT_WIDTH_MAJOR:
slot->mInUse = true;
slot->mAbsMTWidthMajor = rawEvent->value;
break;
case ABS_MT_WIDTH_MINOR:
slot->mInUse = true;
slot->mAbsMTWidthMinor = rawEvent->value;
slot->mHaveAbsMTWidthMinor = true;
break;
case ABS_MT_ORIENTATION:
slot->mInUse = true;
slot->mAbsMTOrientation = rawEvent->value;
break;
case ABS_MT_TRACKING_ID:
if (mUsingSlotsProtocol && rawEvent->value < 0) {
// The slot is no longer in use but it retains its previous contents,
// which may be reused for subsequent touches.
slot->mInUse = false;
} else {
slot->mInUse = true;
slot->mAbsMTTrackingId = rawEvent->value;
}
break;
case ABS_MT_PRESSURE:
slot->mInUse = true;
slot->mAbsMTPressure = rawEvent->value;
break;
case ABS_MT_DISTANCE:
slot->mInUse = true;
slot->mAbsMTDistance = rawEvent->value;
break;
case ABS_MT_TOOL_TYPE:
slot->mInUse = true;
slot->mAbsMTToolType = rawEvent->value;
slot->mHaveAbsMTToolType = true;
break;
}
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
// MultiTouch Sync: The driver has returned all data for *one* of the pointers.
mCurrentSlot += 1;
}
}
MultiTouchMotionAccumulator保存了一个名为mSlots的对象数组
/frameworks/native/services/inputflinger/InputReader.h
class Slot {
public:
inline bool isInUse() const { return mInUse; }
inline int32_t getX() const { return mAbsMTPositionX; }
inline int32_t getY() const { return mAbsMTPositionY; }
inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; }
inline int32_t getTouchMinor() const {
return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; }
inline int32_t getToolMajor() const { return mAbsMTWidthMajor; }
inline int32_t getToolMinor() const {
return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; }
inline int32_t getOrientation() const { return mAbsMTOrientation; }
inline int32_t getTrackingId() const { return mAbsMTTrackingId; }
inline int32_t getPressure() const { return mAbsMTPressure; }
inline int32_t getDistance() const { return mAbsMTDistance; }
inline int32_t getToolType() const;
private:
friend class MultiTouchMotionAccumulator;
bool mInUse;
bool mHaveAbsMTTouchMinor;
bool mHaveAbsMTWidthMinor;
bool mHaveAbsMTToolType;
int32_t mAbsMTPositionX;
int32_t mAbsMTPositionY;
int32_t mAbsMTTouchMajor;
int32_t mAbsMTTouchMinor;
int32_t mAbsMTWidthMajor;
int32_t mAbsMTWidthMinor;
int32_t mAbsMTOrientation;
int32_t mAbsMTTrackingId;
int32_t mAbsMTPressure;
int32_t mAbsMTDistance;
int32_t mAbsMTToolType;
Slot();
void clear();
};
Slot* mSlots;
Slot对象是收集特定触控点的点击信息的场所,而其在数组中的索引就是触控点的Id.MultiTouchMotionAccumulator::process()
函数中,当输入设备开始上报某一触控点的信息时,首先会发送一条类型为EV_ABS,代码为ABS_MT_SLOT事件,这个事件的值表明随后
携带点击信息的事件属于哪一个触控点。携带信息的事件到来时,起事件代码执行他所携带的信息类型,MultiTouchMotionAccumulator
从mSlots数组中以触控点的id为索引将对应的Slot对象取出,并将信息值保存在Slot的对应字段中。随着携带信息的事件不断到来,
Slot对象中的信息也渐渐丰满起来。当设备将这个触控点的点击信息全部上报完毕,会发送一个EV_SYN+SYN_REPORT事件启动TouchInput-
Mapper的信息整合与加工工作,或者发送另一个事件代码为ABS_MT_SLOT的事件,开始上报另一个触控点的信息。
携带点击信息的原始输入事件格式如下:
Type,取值为EV_ABS,表明属于点击事件
Code,取值为ABS_MT_XXX,其中的XXX表明了信息的类型
Value,信息的值
而在一个点击信息的收集过程中所受到的原始输入事件序列如下
EV_ABS ABS_MT_SLOT 触控点1
EV_ABS 信息1 信息1的值
EV_ABS 信息2 信息2的值
EV_ABS ABS_MT_SLOT 触控点2
......//触控点2的信息以及更多的触控点
EV_SYN SYN_REPORT
因此整个点击事件信息的收集过程从指定触控点的事件开会,以一条EV_SYN SYN_REPORT事件结束。在这个过程后,MultiTouchMotionAccumulator
便在mSlots数组中保存了一个或多个触控点的点击信息。
这个通过ABS_MT_SLOT事件代码有设备显示指明触控点的方式称为显示识别方式,也叫slot协议方式。这种方式的好处是灵活多变,而坏处是,当有0号
触控点处于活动状态时(也就是单点操作)仍需要一个ABS_MT_SLOT事件,这是一种浪费。
另外一种识别方式为隐式识别。在隐式识别方式下,代表触控地信息的事件由EV_SYN+SYN_MT_REPORT的事件分隔(注意不是标志开始进行信息整合的代码
SYN_REPORT),分隔的第一批事件携带的信息被认为属于0号索引点的触控点,随后事件组所属的触控点索引依次增加1.最后使用EV_SYN+SYN_MT_REPORT
事件通知开始进行信息的整合工作,因此,隐式识别下的原始输入事件序列如下:
EV_ABS 信息1 信息1的值 //0号触控点的信息
EV_ABS 信息2 信息2的值
EV_SYN SYN_MT_REPORT
...... //1号触控点的信息
EV_SYN SYN_MT_REPORT
EV_ABS 信息1 信息1的值 //2号触控点的信息
EV_SYN SYN_REPORT
隐式识别的好处在于,单点操作时,不需要额外的ABS_MT_SLOT事件对触控点索引进行指明,从而减少单点操作情况下原始输入事件数量与处理事件。而缺
也很明显,在多点操作过程中,如果低索引值的触控点陆续抬起,仅剩下一个高索引值的触控点处于活动状态时,在上报其信息之前,仍需相应数量EV_SYN
+SYN_MT_REPORT事件进行补齐,这也是一种浪费
因此这两种方式各有利弊,一个多点触控设备采取暗中方式应当更具其应用场景进行合适选择
点击事件信息的整合、变换与高级事件的生成
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReader::TouchInputMapper::process()]
void TouchInputMapper::process(const RawEvent* rawEvent) {
mCursorButtonAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
mTouchButtonAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
/**
通过sync函数开始整合
*/
sync(rawEvent->when);
}
}
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReader::TouchInputMapper::sync()]
void TouchInputMapper::sync(nsecs_t when) {
const RawState* last = mRawStatesPending.isEmpty() ?
&mCurrentRawState : &mRawStatesPending.top();
// Push a new state.
mRawStatesPending.push();
RawState* next = &mRawStatesPending.editTop();
next->clear();
next->when = when;
// Sync button state.
next->buttonState = mTouchButtonAccumulator.getButtonState()
| mCursorButtonAccumulator.getButtonState();
// Sync scroll
next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
mCursorScrollAccumulator.finishSync();
/**
1 调用子类的syncTouch
*/
// Sync touch
syncTouch(when, next);
// Assign pointer ids.
if (!mHavePointerIds) {
assignPointerIds(last, next);
}
#if DEBUG_RAW_EVENTS
ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
"hovering ids 0x%08x -> 0x%08x",
last->rawPointerData.pointerCount,
next->rawPointerData.pointerCount,
last->rawPointerData.touchingIdBits.value,
next->rawPointerData.touchingIdBits.value,
last->rawPointerData.hoveringIdBits.value,
next->rawPointerData.hoveringIdBits.value);
#endif
/**
2 调用processRawTouches
*/
processRawTouches(false /*timeout*/);
}
1 调用子类的syncTouch
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReader::MultiTouchInputMapper::syncTouch()]
/**
将mSlots数组中的触控点信息转存到RawPointerData::Pointer中
*/
void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
size_t outCount = 0;
BitSet32 newPointerIdBits;
for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
const MultiTouchMotionAccumulator::Slot* inSlot =
mMultiTouchMotionAccumulator.getSlot(inIndex);
if (!inSlot->isInUse()) {
continue;
}
if (outCount >= MAX_POINTERS) {
#if DEBUG_POINTERS
ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
"ignoring the rest.",
getDeviceName().string(), MAX_POINTERS);
#endif
break; // too many fingers!
}
RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
outPointer.x = inSlot->getX();
outPointer.y = inSlot->getY();
outPointer.pressure = inSlot->getPressure();
outPointer.touchMajor = inSlot->getTouchMajor();
outPointer.touchMinor = inSlot->getTouchMinor();
outPointer.toolMajor = inSlot->getToolMajor();
outPointer.toolMinor = inSlot->getToolMinor();
outPointer.orientation = inSlot->getOrientation();
outPointer.distance = inSlot->getDistance();
outPointer.tiltX = 0;
outPointer.tiltY = 0;
outPointer.toolType = inSlot->getToolType();
if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
outPointer.toolType = mTouchButtonAccumulator.getToolType();
if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
}
}
bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE
&& (mTouchButtonAccumulator.isHovering()
|| (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
outPointer.isHovering = isHovering;
// Assign pointer id using tracking id if available.
mHavePointerIds = true;
int32_t trackingId = inSlot->getTrackingId();
int32_t id = -1;
if (trackingId >= 0) {
for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {
uint32_t n = idBits.clearFirstMarkedBit();
if (mPointerTrackingIdMap[n] == trackingId) {
id = n;
}
}
if (id < 0 && !mPointerIdBits.isFull()) {
id = mPointerIdBits.markFirstUnmarkedBit();
mPointerTrackingIdMap[id] = trackingId;
}
}
if (id < 0) {
mHavePointerIds = false;
outState->rawPointerData.clearIdBits();
newPointerIdBits.clear();
} else {
outPointer.id = id;
outState->rawPointerData.idToIndex[id] = outCount;
outState->rawPointerData.markIdBit(id, isHovering);
newPointerIdBits.markBit(id);
}
outCount += 1;
}
outState->rawPointerData.pointerCount = outCount;
mPointerIdBits = newPointerIdBits;
mMultiTouchMotionAccumulator.finishSync();
}
2 调用processRawTouches
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReader::TouchInputMapper::processRawTouches()]
void TouchInputMapper::processRawTouches(bool timeout) {
if (mDeviceMode == DEVICE_MODE_DISABLED) {
/**
如果设备禁用,则不做处理
*/
// Drop all input if the device is disabled.
mCurrentRawState.clear();
mRawStatesPending.clear();
return;
}
// Drain any pending touch states. The invariant here is that the mCurrentRawState is always
// valid and must go through the full cook and dispatch cycle. This ensures that anything
// touching the current state will only observe the events that have been dispatched to the
// rest of the pipeline.
const size_t N = mRawStatesPending.size();
size_t count;
for(count = 0; count < N; count++) {
const RawState& next = mRawStatesPending[count];
// A failure to assign the stylus id means that we're waiting on stylus data
// and so should defer the rest of the pipeline.
if (assignExternalStylusId(next, timeout)) {
break;
}
// All ready to go.
clearStylusDataPendingFlags();
mCurrentRawState.copyFrom(next);
if (mCurrentRawState.when < mLastRawState.when) {
mCurrentRawState.when = mLastRawState.when;
}
cookAndDispatch(mCurrentRawState.when);
}
if (count != 0) {
mRawStatesPending.removeItemsAt(0, count);
}
if (mExternalStylusDataPending) {
if (timeout) {
nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
clearStylusDataPendingFlags();
mCurrentRawState.copyFrom(mLastRawState);
#if DEBUG_STYLUS_FUSION
ALOGD("Timeout expired, synthesizing event with new stylus data");
#endif
cookAndDispatch(when);
} else if (mExternalStylusFusionTimeout == LLONG_MAX) {
mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
}
}
}
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReader::TouchInputMapper::cookAndDispatch()]
void TouchInputMapper::cookAndDispatch(nsecs_t when) {
// Always start with a clean state.
mCurrentCookedState.clear();
// Apply stylus buttons to current raw state.
applyExternalStylusButtonState(when);
// Handle policy on initial down or hover events.
bool initialDown = mLastRawState.rawPointerData.pointerCount == 0
&& mCurrentRawState.rawPointerData.pointerCount != 0;
uint32_t policyFlags = 0;
bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;
if (initialDown || buttonsPressed) {
// If this is a touch screen, hide the pointer on an initial down.
if (mDeviceMode == DEVICE_MODE_DIRECT) {
getContext()->fadePointer();
}
if (mParameters.wake) {
policyFlags |= POLICY_FLAG_WAKE;
}
}
// Consume raw off-screen touches before cooking pointer data.
// If touches are consumed, subsequent code will not receive any pointer data.
if (consumeRawTouches(when, policyFlags)) {
mCurrentRawState.rawPointerData.clear();
}
/**
*/
// Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure
// with cooked pointer data that has the same ids and indices as the raw data.
// The following code can use either the raw or cooked data, as needed.
cookPointerData();
// Apply stylus pressure to current cooked state.
applyExternalStylusTouchState(when);
/**
*/
// Synthesize key down from raw buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);
// Dispatch the touches either directly or by translation through a pointer on screen.
if (mDeviceMode == DEVICE_MODE_POINTER) {
for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits);
!idBits.isEmpty(); ) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
|| pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
mCurrentCookedState.stylusIdBits.markBit(id);
} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER
|| pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
mCurrentCookedState.fingerIdBits.markBit(id);
} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {
mCurrentCookedState.mouseIdBits.markBit(id);
}
}
for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits);
!idBits.isEmpty(); ) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
|| pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
mCurrentCookedState.stylusIdBits.markBit(id);
}
}
// Stylus takes precedence over all tools, then mouse, then finger.
PointerUsage pointerUsage = mPointerUsage;
if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
mCurrentCookedState.mouseIdBits.clear();
mCurrentCookedState.fingerIdBits.clear();
pointerUsage = POINTER_USAGE_STYLUS;
} else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
mCurrentCookedState.fingerIdBits.clear();
pointerUsage = POINTER_USAGE_MOUSE;
} else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||
isPointerDown(mCurrentRawState.buttonState)) {
pointerUsage = POINTER_USAGE_GESTURES;
}
/**
*/
dispatchPointerUsage(when, policyFlags, pointerUsage);
} else {
if (mDeviceMode == DEVICE_MODE_DIRECT
&& mConfig.showTouches && mPointerController != NULL) {
mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
mPointerController->setButtonState(mCurrentRawState.buttonState);
mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex,
mCurrentCookedState.cookedPointerData.touchingIdBits);
}
if (!mCurrentMotionAborted) {
dispatchButtonRelease(when, policyFlags);
dispatchHoverExit(when, policyFlags);
dispatchTouches(when, policyFlags);
dispatchHoverEnterAndMove(when, policyFlags);
dispatchButtonPress(when, policyFlags);
}
if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
mCurrentMotionAborted = false;
}
}
/**
*/
// Synthesize key up from raw buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);
// Clear some transient state.
mCurrentRawState.rawVScroll = 0;
mCurrentRawState.rawHScroll = 0;
// Copy current touch to last touch in preparation for the next cycle.
mLastRawState.copyFrom(mCurrentRawState);
mLastCookedState.copyFrom(mCurrentCookedState);
}
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReader::TouchInputMapper::cookPointerData()]
void TouchInputMapper::cookPointerData() {
uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;
mCurrentCookedState.cookedPointerData.clear();
mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;
mCurrentCookedState.cookedPointerData.hoveringIdBits =
mCurrentRawState.rawPointerData.hoveringIdBits;
mCurrentCookedState.cookedPointerData.touchingIdBits =
mCurrentRawState.rawPointerData.touchingIdBits;
if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
mCurrentCookedState.buttonState = 0;
} else {
mCurrentCookedState.buttonState = mCurrentRawState.buttonState;
}
// Walk through the the active pointers and map device coordinates onto
// surface coordinates and adjust for display orientation.
for (uint32_t i = 0; i < currentPointerCount; i++) {
const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
// Size
float touchMajor, touchMinor, toolMajor, toolMinor, size;
switch (mCalibration.sizeCalibration) {
case Calibration::SIZE_CALIBRATION_GEOMETRIC:
case Calibration::SIZE_CALIBRATION_DIAMETER:
case Calibration::SIZE_CALIBRATION_BOX:
case Calibration::SIZE_CALIBRATION_AREA:
if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {
touchMajor = in.touchMajor;
touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
toolMajor = in.toolMajor;
toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
size = mRawPointerAxes.touchMinor.valid
? avg(in.touchMajor, in.touchMinor) : in.touchMajor;
} else if (mRawPointerAxes.touchMajor.valid) {
toolMajor = touchMajor = in.touchMajor;
toolMinor = touchMinor = mRawPointerAxes.touchMinor.valid
? in.touchMinor : in.touchMajor;
size = mRawPointerAxes.touchMinor.valid
? avg(in.touchMajor, in.touchMinor) : in.touchMajor;
} else if (mRawPointerAxes.toolMajor.valid) {
touchMajor = toolMajor = in.toolMajor;
touchMinor = toolMinor = mRawPointerAxes.toolMinor.valid
? in.toolMinor : in.toolMajor;
size = mRawPointerAxes.toolMinor.valid
? avg(in.toolMajor, in.toolMinor) : in.toolMajor;
} else {
ALOG_ASSERT(false, "No touch or tool axes. "
"Size calibration should have been resolved to NONE.");
touchMajor = 0;
touchMinor = 0;
toolMajor = 0;
toolMinor = 0;
size = 0;
}
if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) {
uint32_t touchingCount =
mCurrentRawState.rawPointerData.touchingIdBits.count();
if (touchingCount > 1) {
touchMajor /= touchingCount;
touchMinor /= touchingCount;
toolMajor /= touchingCount;
toolMinor /= touchingCount;
size /= touchingCount;
}
}
if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) {
touchMajor *= mGeometricScale;
touchMinor *= mGeometricScale;
toolMajor *= mGeometricScale;
toolMinor *= mGeometricScale;
} else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) {
touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0;
touchMinor = touchMajor;
toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0;
toolMinor = toolMajor;
} else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) {
touchMinor = touchMajor;
toolMinor = toolMajor;
}
mCalibration.applySizeScaleAndBias(&touchMajor);
mCalibration.applySizeScaleAndBias(&touchMinor);
mCalibration.applySizeScaleAndBias(&toolMajor);
mCalibration.applySizeScaleAndBias(&toolMinor);
size *= mSizeScale;
break;
default:
touchMajor = 0;
touchMinor = 0;
toolMajor = 0;
toolMinor = 0;
size = 0;
break;
}
// Pressure
float pressure;
switch (mCalibration.pressureCalibration) {
case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
pressure = in.pressure * mPressureScale;
break;
default:
pressure = in.isHovering ? 0 : 1;
break;
}
// Tilt and Orientation
float tilt;
float orientation;
if (mHaveTilt) {
float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale;
float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale;
orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle));
tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle));
} else {
tilt = 0;
switch (mCalibration.orientationCalibration) {
case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
orientation = in.orientation * mOrientationScale;
break;
case Calibration::ORIENTATION_CALIBRATION_VECTOR: {
int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
int32_t c2 = signExtendNybble(in.orientation & 0x0f);
if (c1 != 0 || c2 != 0) {
orientation = atan2f(c1, c2) * 0.5f;
float confidence = hypotf(c1, c2);
float scale = 1.0f + confidence / 16.0f;
touchMajor *= scale;
touchMinor /= scale;
toolMajor *= scale;
toolMinor /= scale;
} else {
orientation = 0;
}
break;
}
default:
orientation = 0;
}
}
// Distance
float distance;
switch (mCalibration.distanceCalibration) {
case Calibration::DISTANCE_CALIBRATION_SCALED:
distance = in.distance * mDistanceScale;
break;
default:
distance = 0;
}
// Coverage
int32_t rawLeft, rawTop, rawRight, rawBottom;
switch (mCalibration.coverageCalibration) {
case Calibration::COVERAGE_CALIBRATION_BOX:
rawLeft = (in.toolMinor & 0xffff0000) >> 16;
rawRight = in.toolMinor & 0x0000ffff;
rawBottom = in.toolMajor & 0x0000ffff;
rawTop = (in.toolMajor & 0xffff0000) >> 16;
break;
default:
rawLeft = rawTop = rawRight = rawBottom = 0;
break;
}
// Adjust X,Y coords for device calibration
// TODO: Adjust coverage coords?
float xTransformed = in.x, yTransformed = in.y;
mAffineTransform.applyTo(xTransformed, yTransformed);
// Adjust X, Y, and coverage coords for surface orientation.
float x, y;
float left, top, right, bottom;
switch (mSurfaceOrientation) {
case DISPLAY_ORIENTATION_90:
x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
right = float(rawBottom- mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
orientation -= M_PI_2;
if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) {
orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
}
break;
case DISPLAY_ORIENTATION_180:
x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;
left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
orientation -= M_PI;
if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) {
orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
}
break;
case DISPLAY_ORIENTATION_270:
x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;
y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
orientation += M_PI_2;
if (mOrientedRanges.haveOrientation && orientation > mOrientedRanges.orientation.max) {
orientation -= (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
}
break;
default:
x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
break;
}
// Write output coords.
PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];
out.clear();
out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);
out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);
out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor);
out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt);
out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);
if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left);
out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top);
out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right);
out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom);
} else {
out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor);
out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
}
// Write output properties.
PointerProperties& properties =
mCurrentCookedState.cookedPointerData.pointerProperties[i];
uint32_t id = in.id;
properties.clear();
properties.id = id;
properties.toolType = in.toolType;
// Write id index.
mCurrentCookedState.cookedPointerData.idToIndex[id] = i;
}
}
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReader::synthesizeButtonKeys()]
static void synthesizeButtonKeys(InputReaderContext* context, int32_t action,
nsecs_t when, int32_t deviceId, uint32_t source,
uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState) {
synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,
lastButtonState, currentButtonState,
AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,
lastButtonState, currentButtonState,
AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
}
static void synthesizeButtonKey(InputReaderContext* context, int32_t action,
nsecs_t when, int32_t deviceId, uint32_t source,
uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState,
int32_t buttonState, int32_t keyCode) {
if (
(action == AKEY_EVENT_ACTION_DOWN
&& !(lastButtonState & buttonState)
&& (currentButtonState & buttonState))
|| (action == AKEY_EVENT_ACTION_UP
&& (lastButtonState & buttonState)
&& !(currentButtonState & buttonState))) {
NotifyKeyArgs args(when, deviceId, source, policyFlags,
action, 0, keyCode, 0, context->getGlobalMetaState(), when);
context->getListener()->notifyKey(&args);
}
}
/frameworks/native/services/inputflinger/InputReader.cpp
[InputReader.cpp–>InputReader]
dispatchPointerUsage(when, policyFlags, pointerUsage);
dispatchButtonRelease(when, policyFlags);
dispatchHoverExit(when, policyFlags);
dispatchTouches(when, policyFlags);
dispatchHoverEnterAndMove(when, policyFlags);
dispatchButtonPress(when, policyFlags);
流程总结:
首先转存子类进行触控点的点击信息。TouchInputMapper使用一个名为mCurrentRawState的rawPointerData对象来接收子类的触控
点的点击信息。这个收集过程由syncTouch()函数完成.RawPointerData类其实是其内部类Pointer类的集合。Pointer类保存了一个触
控点的点击事件信息,并且它和MultiTouchInputMapper的Slot对象内容是一致的。因此SyncTouch()的主要工作就是将mSlots中存有
点击信息的slot对象转存到mCurrentRawState所保持的Pointer对象中。
将收集到的触控点信息转存到mCurrentRawState中后,通过调用cookPointerData()函数完成触控点信息从传感器物理坐标系到屏幕坐标
系的变换过程。变换的依据是在TouchInputMapper配置过程中所获取的屏幕坐标系信息以及传感器物理坐标系信息的差异。
坐标系转换完成后通过dispatchTouch()函数将当前的触控点信息与上次的触控点嘻嘻进行对比,为每个触控点生成ACTION_DOWN,ACTION_
MOVE, ACTION_UP等高级触摸事件,然后与KeyboardInputMapper一样,将事件封装成NotifyMotionArgs对象,并通过getListener->
notifyMotion()函数,准备将事件提交给InputDispatcher进行派发。
生成高级触摸事件时使用了位操作。通过前后两次触控点列表的对比,如果本次的一个触控点没有出现在上次的触控点列表中,则表示触控点
刚刚被按下,于是便产生ACTION_DOWN。如果上一次的一个触控点没有出现在本次的触控点列表中,则表示此触控点刚刚被抬起,于是便产生
ACTION_UP。当一个触控点同事出现在前后两次列表中,便产生ACTION_MOVE.
最后将本次触控点信息mCurrentRawState保存到mLastRawState中,以便下次生成高级触摸事件时进行比对。
Touch类型事件处理总结
设备节点上报复杂输入事件的方式是使每个原始输入事件描述输入动作的一项信息,并在最后使用一个EV_SYN + SYN_REPORT事件表示输入
动作的的信息上报完毕。
在多点触控操作是,通过原始输入事件势必触控点索引的方式有显示与隐式两种。显示是由输入设备通过一条EV_ABS+ABS_MT_SLOT事件指
明随后的原始输入事件所属触控点索引。而隐式的方式是通过EV_SYN+SYN_MT_REPORT事件将属于不懂触控点的事件阻隔开,并约定事件组
的顺序就是触控点的索引
MultiTouchMotionAccumulator收集事件信息的方式。它根据触控点的索引从mSlots数组中获取触控点对应的Slot对象,并将原始输入
事件所携带的触控点信息保存在Slot对象相依的字段中。当EV_SYN+SYN_REPORT到来时,其mSlots数组中变保存了所有触控点的点击信息。
触摸事件的坐标转换与高级触摸事件的生成方式,TouchInputMapper::sync()函数将触摸点信息冲传感器物理坐标系转换到屏幕坐标系,
而高级触摸事件的生成则依赖与前后两次触控点列表的对比结果。
InputReader到InputDispatcher
无论是Keyboard类型事件还是Touch类型事件,他们在InputReader子系统中的终点都是通过InputMapper::getListener()所获取的继承
自InputListenerInterface接口的对象,它是输入事件从InputReader到InputDispatcher的通道。getListener()函数返回的对象正是
InputReader::loopOnce()函数中最后一步的mQueuedListener.
这个类型为QueuedInputListener的对象在InputReader的构造函数中被创建,并封装了同样继承子InputListenerInterface接口的Input-
Dispatcher.
QueuedInputListener避免了在原始事件的加工过程中向InputDispatcher进行事件的提交,而是将事件信息缓存起来。在InputReader::loopOnce()
的末尾,也就是InputReader处理完抽取自EventHub的所有原始输入事件之后,QueuedInputListener::flush()函数的调用将缓存的事件
取出,并提交给InputDispathcer.
为什么要避免在原始事件的加工过程中实时地项InputDispatcher提交事件呢?
InputDispathcer会在线程中不断地将派发队列中的事件派发给合适的窗口。当派发队列为空时,为了节约资源,InputDispatcher的线程
就会就如休眠状态,当InputReader把新事件注入InputDispatcher的派发队列是,就需要将休眠的InputDispatcher唤醒以进行派发工作。
于是问题就出现了,假如InputDispatcher派发一个事件的速度快于InputReader加工一个原始事件的速度,则一次InputReader线程循环所产生
的多个事件会导致InputDispatcher的线程多次休眠与唤醒。
QueuedInputListener的存在,使得InputReader所产生的输入事件尽可能密集的注入InputDispather派发队列中,从而减少Input-
Dispatcher休眠与唤醒的次数。
另外,InputListenerInterface接口定义了三个接口:
1. notifyKey()
2. notifyMotion()
3. notifySwitch
这三个接口是InputReader输出事件的出口,同时体现了InputReader事件输出的三种基本类型
按键类型
手势类型
开关类型
这三种类型的事件分别由
NotifyKeyArgs
NotifyMotionArgs
NotifySwitchArgs
因此可以说, EventHub的RawEvent是InputReader的输入,而上述三个结构体则是InputReader的输出