本文分析设备扫描的过程,最主要的就是如何为输入设备建立数据。
EventHub 扫描输入设备
// EventHub.cpp
void EventHub::scanDevicesLocked() {
status_t result;
std::error_code errorCode;
// 扫描 /dev/input 目录下的设备文件
if (std::filesystem::exists(DEVICE_INPUT_PATH, errorCode)) {
result = scanDirLocked(DEVICE_INPUT_PATH);
if (result < 0) {
ALOGE("scan dir failed for %s", DEVICE_INPUT_PATH);
}
} else {
// ...
}
// ...
}
status_t EventHub::scanDirLocked(const std::string& dirname) {
// 遍历 /dev/input 的目录项
for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
openDeviceLocked(entry.path());
}
return 0;
}
void EventHub::openDeviceLocked(const std::string& devicePath) {
for (const auto& [deviceId, device] : mDevices) {
if (device->path == devicePath) {
return; // device was already registered
}
}
char buffer[80];
ALOGV("Opening device: %s", devicePath.c_str());
// 1. 打开设备文件
int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
ALOGE("could not open %s, %s\n", devicePath.c_str(), strerror(errno));
return;
}
// 2. InputDeviceIdentifier 保存输入设备的信息
InputDeviceIdentifier identifier;
// 获取设备名字
if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
ALOGE("Could not get device name for %s: %s", devicePath.c_str(), strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
// 保存设备名
identifier.name = buffer;
}
// 如果输入设备在排除列表中,那么关闭设备文件,那么系统就不会收到此输入设备的事件
// mExcludedDevices 来自于上层 InputManagerService
for (size_t i = 0; i < mExcludedDevices.size(); i++) {
const std::string& item = mExcludedDevices[i];
if (identifier.name == item) {
ALOGI("ignoring event id %s driver %s\n", devicePath.c_str(), item.c_str());
close(fd);
return;
}
}
// ... 省略一大堆的填充 identifier 的代码
// 递增地分配一个 device id
int32_t deviceId = mNextDeviceId++;
// 3. 创建 Device,代表一个输入设备
// Device 保存了刚才填充的输入设备信息 identifier
std::unique_ptr<Device> device =
std::make_unique<Device>(fd, deviceId, devicePath, identifier,
obtainAssociatedDeviceLocked(devicePath));
// 下面开始向 Device 中填充数据
// 3.1 加载输入设备的配置文件
// Device::configurationFile 保存配置文件路径
// Device::configuration 保存配置文件数据
device->loadConfigurationLocked();
// 3.2 获取输入设备能报告哪些类型的事件
// 例如,触摸屏能上报 type 为 EV_ABS 的坐标事件,按键设备能上报 type 为 EV_KEY 的按键事件
device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
device->readDeviceBitMask(EVIOCGBIT(EV_ABS, 0), device->absBitmask);
device->readDeviceBitMask(EVIOCGBIT(EV_REL, 0), device->relBitmask);
device->readDeviceBitMask(EVIOCGBIT(EV_SW, 0), device->swBitmask);
device->readDeviceBitMask(EVIOCGBIT(EV_LED, 0), device->ledBitmask);
device->readDeviceBitMask(EVIOCGBIT(EV_FF, 0), device->ffBitmask);
device->readDeviceBitMask(EVIOCGBIT(EV_MSC, 0), device->mscBitmask);
device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask);
// 3.3 判断输入设备类型,保存到 device->classes
bool haveKeyboardKeys =
device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1);
bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) ||
device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI);
bool haveStylusButtons = device->keyBitmask.test(BTN_STYLUS) ||
device->keyBitmask.test(BTN_STYLUS2) || device->keyBitmask.test(BTN_STYLUS3);
if (haveKeyboardKeys || haveGamepadButtons || haveStylusButtons) {
// 键盘类型
device->classes |= InputDeviceClass::KEYBOARD;
}
// ...省略其他类型的判断代码
if (device->absBitmask.test(ABS_MT_POSITION_X) && device->absBitmask.test(ABS_MT_POSITION_Y)) {
if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) {
// 多点触摸类型
device->classes |= (InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT);
if (device->propBitmask.test(INPUT_PROP_POINTER) &&
!device->keyBitmask.any(BTN_TOOL_PEN, BTN_TOOL_FINGER) && !haveStylusButtons) {
device->classes |= InputDeviceClass::TOUCHPAD;
}
}
// Is this an old style single-touch driver?
} else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
device->absBitmask.test(ABS_Y)) {
// 单点触摸类型
device->classes |= InputDeviceClass::TOUCH;
} else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) &&
!device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) {
// ...
}
// ....
// 4. 注意,键盘类型的输入设备,还需要加载按键映射文件
status_t keyMapStatus = NAME_NOT_FOUND;
if (device->classes.any(InputDeviceClass::KEYBOARD | InputDeviceClass::JOYSTICK |
InputDeviceClass::SENSOR)) {
keyMapStatus = device->loadKeyMapLocked();
}
// ...
// 5. epoll 监听输入设备的输入事件
if (registerDeviceForEpollLocked(*device) != OK) {
return;
}
// 6. 对 KEYBOARD 和 SENSOR 两种类型的设备,向驱动发送一些配置要求
// 例如,对于 KEYBOARD 输入设备,禁止驱动的 key repeat,由输入系统自己模拟
device->configureFd();
ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=%s, "
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
deviceId, fd, devicePath.c_str(), device->identifier.name.c_str(),
device->classes.string().c_str(), device->configurationFile.c_str(),
device->keyMap.keyLayoutFile.c_str(), device->keyMap.keyCharacterMapFile.c_str(),
toString(mBuiltInKeyboardId == deviceId));
// 6. mOpeningDevices 保存输入设备 Device
addDeviceLocked(std::move(device));
}
// 对于 keyboard 类型的输入设备,禁止驱动的 key repeat 功能,由 Input 系统自己生成 key repeat 事件
void EventHub::Device::configureFd() {
// Set fd parameters with ioctl, such as key repeat, suspend block, and clock type
if (classes.test(InputDeviceClass::KEYBOARD)) {
// 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", path.c_str(), strerror(errno));
}
}
// 》。。
}
EventHub 扫描输入设备,创建了代表输入设备的 Device,然后向 Device 中填充数据,Device 最终保存到 EventHub::mOpeningDevices 中 。
可以通过 dumpsys input 查看向 Device 中填充的数据
Event Hub State:
...
// 触摸屏
5: XXX_ts
// 一个输入设备可以多多种类型
Classes: KEYBOARD | TOUCH | TOUCH_MT | BATTERY
Path: /dev/input/event5
Enabled: true
Descriptor: 037b4b9dd8120bd57454eabdb9b1462c7738b2d3
Location:
ControllerNumber: 0
UniqueId: xxx_ts
Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000, bluetoothAddress=<not set>
KeyLayoutFile: /system/usr/keylayout/Generic.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
// 输入设备配置文件
ConfigurationFile:
VideoDevice: <none>
SysfsDevicePath: /sys/devices/virtual
KeyState (pressed): <none>
AbsState: ABS_MT_SLOT=0, ABS_MT_TOUCH_MAJOR=12, ABS_MT_TOUCH_MINOR=12, ABS_MT_POSITION_X=4512, ABS_MT_POSITION_Y=10223, ABS_MT_TRACKING_ID=-1
// 电源键的输入设备
9: pmic_pwrkey
Classes: KEYBOARD
Path: /dev/input/event1
Enabled: true
Descriptor: 39f0cc2fda6f8287fd99ffa0db53ae80f4837e18
Location: pmic_pwrkey/input0
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000, bluetoothAddress=<not set>
// 这两个都是输入设备的按键映射文件
KeyLayoutFile: /system/usr/keylayout/Generic.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
// 输入设备配置文件
ConfigurationFile:
VideoDevice: <none>
SysfsDevicePath: /sys/devices/platform/soc/c42d000.qcom,spmi/spmi-0/0-00/c42d000.qcom,spmi:qcom,pmk8550@0:pon_hlos@1300/c42d000.qcom,spmi:qcom,pmk8550@0:pon_hlos@1300:pwrkey
KeyState (pressed): <none>
EventHub 生成合成事件
std::vector<RawEvent> EventHub::getEvents(int timeoutMillis) {
std::scoped_lock _l(mLock);
std::array<input_event, EVENT_BUFFER_SIZE> readBuffer;
std::vector<RawEvent> events;
bool awoken = false;
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
// Reopen input devices if needed.
if (mNeedToReopenDevices) {
// ...
}
// Report any devices that had last been added/removed.
for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
// ...
}
// mNeedToScanDevices 初始化的值为 true
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
// 1. 扫描输入设备
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
// 扫描设备时,mOpeningDevices 保存了所有打开的输入设备
while (!mOpeningDevices.empty()) {
std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
mOpeningDevices.pop_back();
ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
// 2. 为打开的输入设备,向 events 中填充一个类型为 DEVICE_ADDED 的事件
events.push_back({
.when = now,
.deviceId = deviceId,
.type = DEVICE_ADDED,
});
for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
it++) {
// ...
}
// mDevices 以 id 为 KEY,保存 Device
auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
if (!inserted) {
ALOGW("Device id %d exists, replaced.", device->id);
}
// 表示需要发送一个完成扫描事件
mNeedToSendFinishedDeviceScan = true;
if (events.size() == EVENT_BUFFER_SIZE) {
break;
}
}
// 3. 扫描完成,向 events 中填充一个类型为 FINISHED_DEVICE_SCAN 的事件
if (mNeedToSendFinishedDeviceScan) {
mNeedToSendFinishedDeviceScan = false;
events.push_back({
.when = now,
.type = FINISHED_DEVICE_SCAN,
});
if (events.size() == EVENT_BUFFER_SIZE) {
break;
}
}
// Grab the next input event.
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) {
// ...
}
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
// ...
}
if (deviceChanged) {
continue;
}
// 4. events 中已经有事件了,跳出无限循环
if (!events.empty() || awoken) {
break;
}
mPendingEventIndex = 0;
mLock.unlock(); // release lock before poll
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
mLock.lock(); // reacquire lock after poll
if (pollResult == 0) {
// Timed out.
mPendingEventCount = 0;
break;
}
if (pollResult < 0) {
// ...
} else {
// Some events occurred.
mPendingEventCount = size_t(pollResult);
}
}
// 返回事件给 InputReader
return events;
}
EventHub 扫描输入设备后,向 events 中填充了两种类型的事情
- 为每一个打开的输入设备,添加一个类型为 DEVICE_ADDED 的事件。
- 设备扫描完成后,还补充了一个 FINISHED_DEVICE_SCAN 的事件。
由于这些事件都不是来自于输入设备,而是输入系统自己生成的,因此这些事件被称之为合成事件(synthetic event),而输入设备产生的事件,例如,按键事件/触摸事件,称之为元事件(raw event)。
InputReader 处理设备扫描的合成事件
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
// Copy some state so that we can access it outside the lock later.
bool inputDevicesChanged = false;
std::vector<InputDeviceInfo> inputDevices;
// 保存需要发送给下一环的事件
std::list<NotifyArgs> notifyArgs;
{ // acquire lock
std::scoped_lock _l(mLock);
oldGeneration = mGeneration;
timeoutMillis = -1;
auto changes = mConfigurationChangesToRefresh;
if (changes.any()) {
// ...
} else if (mNextTimeout != LLONG_MAX) {
// ...
}
} // release lock
// 1. 从 EventHub 获取事件
std::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis);
{ // acquire lock
std::scoped_lock _l(mLock);
mReaderIsAliveCondition.notify_all();
// 2. 处理事件
if (!events.empty()) {
// 处理 EventHub 的事件时,也会生成新的事件 NotifyArgs,保存到 notifyArgs 中
notifyArgs += processEventsLocked(events.data(), events.size());
}
if (mNextTimeout != LLONG_MAX) {
// ...
// 3. 处理设备改变
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
inputDevices = getInputDevicesLocked();
// 3.1 notifyArgs 保存一个 NotifyInputDevicesChangedArgs 事件
// NotifyInputDevicesChangedArgs 不会发送给 InputDispatcher 处理,好像只是在 InputProcessor 中处理了
notifyArgs.emplace_back(
NotifyInputDevicesChangedArgs{mContext.getNextId(), inputDevices});
}
} // release lock
// 3.2 输入设备改变了,把所有输入设备信息发送给上层
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
for (const auto& args : notifyArgs) {
const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args);
if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) {
// ...
}
}
// 4. 把 notifyArgs 中的所有事件,保存到 mQueuedListener 的队列中
notifyAll(std::move(notifyArgs));
// 5. 刷新 mQueuedListener 队列,把所有事件发送给下一环
mQueuedListener.flush();
}
虽然说,InputReader 处理从 EventHub 获取的事件(RawEvent)时,会生成新的事件(NotifyArgs)。但是,对于 InputReader 处理设备扫描而生成的合成事件,本文只关心 InputReader 是如何为输入设备建立数据,至于这期间生成的 NotifyArgs 事件是如何处理的,我只在代码中进行注释。
// InputReader.cpp
std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
std::list<NotifyArgs> out;
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type;
size_t batchSize = 1;
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
// ...
} else {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
// ...
case EventHubInterface::FINISHED_DEVICE_SCAN:
// 如下函数所展示
handleConfigurationChangedLocked(rawEvent->when);
break;
// ...
}
}
count -= batchSize;
rawEvent += batchSize;
}
return out;
}
// 处理 type 为 FINISHED_DEVICE_SCAN 的事件
void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
// Reset global meta state because it depends on the list of all configured devices.
updateGlobalMetaStateLocked();
// Enqueue configuration changed.
// 创建 NotifyConfigurationChangedArgs ,并保存到 mQueuedListener 的队列中
// 经过 InputDispatcher,最终通知上层 WMS 更新配置
mQueuedListener.notifyConfigurationChanged({mContext.getNextId(), when});
}
// 处理 type 为 DEVICE_ADDED 的事件
void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {
if (mDevices.find(eventHubId) != mDevices.end()) {
ALOGW("Ignoring spurious device added event for eventHubId %d.", eventHubId);
return;
}
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
// 1. InputReader 创建的输入设备的类型为 InputDevice
std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
// 2. 配置 InputDevice
notifyAll(device->configure(when, mConfig, /*changes=*/{}));
notifyAll(device->reset(when));
if (device->isIgnored()) {
ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' "
"(ignored non-input device)",
device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str());
} else {
ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=%s",
device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str(),
inputEventSourceToString(device->getSources()).c_str());
}
// 3. InputReader 使用 mDevices 保存输入设备数据
// mDevices : eventhub id -> InputDevice
mDevices.emplace(eventHubId, device);
// Add device to device to EventHub ids map.
// 4. InputReader 还使用 mDeviceToEventHubIdsMap 保存输入设备
// mDeviceToEventHubIdsMap : InputDevice -> vector<eventhub id>
const auto mapIt = mDeviceToEventHubIdsMap.find(device);
if (mapIt == mDeviceToEventHubIdsMap.end()) {
std::vector<int32_t> ids = {eventHubId};
mDeviceToEventHubIdsMap.emplace(device, ids);
} else {
// TODO: 一个 InputDevice 可以对应多个 eventhub id,这是怎么回事?
mapIt->second.push_back(eventHubId);
}
// mGeneration + 1
bumpGenerationLocked();
// ...
}
InputReader 处理设备扫描而生成的合成事件,为输入设备创建 InputDevice。然后对 InputDevice 进行配置,这个配置的过程,就是为了处理输入设备的事件而准备的。最后用两个数据结构 mDevices (EventHubId->InputDevice) 和 mDeviceToEventHubIdsMap (InputDevice -> vector<EventHubId>) 保存了 InputDevice。
创建 InputDevice
// InputReader.cpp
std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
int32_t eventHubId, const InputDeviceIdentifier& identifier) {
auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {
const InputDeviceIdentifier identifier2 =
devicePair.second->getDeviceInfo().getIdentifier();
return isSubDevice(identifier, identifier2);
});
std::shared_ptr<InputDevice> device;
if (deviceIt != mDevices.end()) {
device = deviceIt->second;
} else {
int32_t deviceId = (eventHubId < END_RESERVED_ID) ? eventHubId : nextInputDeviceIdLocked();
// 1.创建 InputDevice
// mContext 是 InputReader 的环境,可以从它获取 InputReader 的数据
device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),
identifier);
}
// 2. 在 InputDevice 中创建 InputMapper 集合
device->addEventHubDevice(eventHubId, mConfig);
return device;
}
InputReader 创建了自己的输入设备 InputDevice,并且在 InputDevice 中,还会根据输入设备类型,创建并保存各种 InputMapper, 如下
InputMapper 是为了对 raw event 进行加工。例如,把 motion event 的屏幕坐标,转换为逻辑屏坐标。
// InputDevice.cpp
void InputDevice::addEventHubDevice(int32_t eventHubId,
const InputReaderConfiguration& readerConfig) {
if (mDevices.find(eventHubId) != mDevices.end()) {
return;
}
// 创建 InputDeviceContext,它代表 InputDevice 的环境,它会传递给 InputMapper
std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
// 为 InputDevice 创建 InputMapper 集合
// 一个输入可能有多种类型,因此需要创建多个 InputMapper
std::vector<std::unique_ptr<InputMapper>> mappers = createMappers(*contextPtr, readerConfig);
// insert the context into the devices set
// mDevices 保存 InputDeviceContext 和 InputMapper 集合
// mDevices: EventHub device id -> { InputDeviceContext , vector<InputMapper> }
mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
// Must change generation to flag this device as changed
// InputReader::mGeneration + 1
bumpGeneration();
}
// 创建 InputMapper 集合
std::vector<std::unique_ptr<InputMapper>> InputDevice::createMappers(
InputDeviceContext& contextPtr, const InputReaderConfiguration& readerConfig) {
ftl::Flags<InputDeviceClass> classes = contextPtr.getDeviceClasses();
// 保存所有创建的 InputMapper
std::vector<std::unique_ptr<InputMapper>> mappers;
// ...
uint32_t keyboardSource = 0;
// 手机和平板的键盘类型的输入设备,就是无字母类型的键盘
int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
if (classes.test(InputDeviceClass::KEYBOARD)) {
keyboardSource |= AINPUT_SOURCE_KEYBOARD;
}
// ...
// 为键盘输入设备,创建 KeyboradMapper
if (keyboardSource != 0) {
// 对于手机平板来说 keyboardSource 为 AINPUT_SOURCE_KEYBOARD
mappers.push_back(createInputMapper<KeyboardInputMapper>(contextPtr, readerConfig,
keyboardSource, keyboardType));
}
// ...
if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
// ...
} else if (classes.test(InputDeviceClass::TOUCH_MT)) {
// 为多点触摸的设备创建 MultiTouchInputMapper
mappers.push_back(std::make_unique<MultiTouchInputMapper>(contextPtr, readerConfig));
} else if (classes.test(InputDeviceClass::TOUCH)) {
// 为单点触摸的设备创建 SingleTouchInputMapper
mappers.push_back(std::make_unique<SingleTouchInputMapper>(contextPtr, readerConfig));
}
// ...
return mappers;
}
配置 InputDevice
// InputDevice.cpp
// changes 此时为空
std::list<NotifyArgs> InputDevice::configure(nsecs_t when,
const InputReaderConfiguration& readerConfig,
ConfigurationChanges changes) {
std::list<NotifyArgs> out;
mSources = 0;
mClasses = ftl::Flags<InputDeviceClass>(0);
mControllerNumber = 0;
for_each_subdevice([this](InputDeviceContext& context) {
// 获取 input device 的类型
// 由 EventHub 实现
mClasses |= context.getDeviceClasses();
int32_t controllerNumber = context.getDeviceControllerNumber();
if (controllerNumber > 0) {
if (mControllerNumber && mControllerNumber != controllerNumber) {
ALOGW("InputDevice::configure(): composite device contains multiple unique "
"controller numbers");
}
mControllerNumber = controllerNumber;
}
});
mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL);
mHasMic = mClasses.test(InputDeviceClass::MIC);
using Change = InputReaderConfiguration::Change;
if (!changes.any() || !isIgnored()) {
// Full configuration should happen the first time configure is called
// and when the device type is changed. Changing a device type can
// affect various other parameters so should result in a
// reconfiguration.
if (!changes.any() || changes.test(Change::DEVICE_TYPE)) {
mConfiguration.clear();
for_each_subdevice([this](InputDeviceContext& context) {
// 加载输入设备的配置文件的数据
std::optional<PropertyMap> configuration =
getEventHub()->getConfiguration(context.getEventHubId());
if (configuration) {
// InputDevice::mConfiguration 保存输入输入设备配置文件中的数据
mConfiguration.addAll(&(*configuration));
}
});
mAssociatedDeviceType =
getValueByKey(readerConfig.deviceTypeAssociations, mIdentifier.location);
}
if (!changes.any() || changes.test(Change::KEYBOARD_LAYOUTS)) {
if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
// 从上层 KeyboardLayoutManager 加载 overlay keyboard layout
std::shared_ptr<KeyCharacterMap> keyboardLayout =
mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
bool shouldBumpGeneration = false;
for_each_subdevice(
[&keyboardLayout, &shouldBumpGeneration](InputDeviceContext& context) {
if (context.setKeyboardLayoutOverlay(keyboardLayout)) {
shouldBumpGeneration = true;
}
});
if (shouldBumpGeneration) {
bumpGeneration();
}
}
}
if (!changes.any() || changes.test(Change::DEVICE_ALIAS)) {
if (!(mClasses.test(InputDeviceClass::VIRTUAL))) {
// 从上层,加载输入设备别名
std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
if (mAlias != alias) {
mAlias = alias;
bumpGeneration();
}
}
}
if (changes.test(Change::ENABLED_STATE)) {
// ...
}
if (!changes.any() || changes.test(Change::DISPLAY_INFO)) {
// In most situations, no port or name will be specified.
mAssociatedDisplayPort = std::nullopt;
mAssociatedDisplayUniqueId = std::nullopt;
mAssociatedViewport = std::nullopt;
// Find the display port that corresponds to the current input port.
// 处理 display port 与 Input port 的绑定关系
const std::string& inputPort = mIdentifier.location;
if (!inputPort.empty()) {
// 端口绑定关系来自于上层
const std::unordered_map<std::string, uint8_t>& ports =
readerConfig.portAssociations;
const auto& displayPort = ports.find(inputPort);
if (displayPort != ports.end()) {
mAssociatedDisplayPort = std::make_optional(displayPort->second);
} else {
const std::unordered_map<std::string, std::string>& displayUniqueIds =
readerConfig.uniqueIdAssociations;
const auto& displayUniqueId = displayUniqueIds.find(inputPort);
if (displayUniqueId != displayUniqueIds.end()) {
mAssociatedDisplayUniqueId = displayUniqueId->second;
}
}
}
// If the device was explicitly disabled by the user, it would be present in the
// "disabledDevices" list. If it is associated with a specific display, and it was not
// explicitly disabled, then enable/disable the device based on whether we can find the
// corresponding viewport.
bool enabled =
(readerConfig.disabledDevices.find(mId) == readerConfig.disabledDevices.end());
if (mAssociatedDisplayPort) {
mAssociatedViewport =
readerConfig.getDisplayViewportByPort(*mAssociatedDisplayPort);
if (!mAssociatedViewport) {
ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
"but the corresponding viewport is not found.",
getName().c_str(), *mAssociatedDisplayPort);
enabled = false;
}
} else if (mAssociatedDisplayUniqueId != std::nullopt) {
mAssociatedViewport =
readerConfig.getDisplayViewportByUniqueId(*mAssociatedDisplayUniqueId);
if (!mAssociatedViewport) {
ALOGW("Input device %s should be associated with display %s but the "
"corresponding viewport cannot be found",
getName().c_str(), mAssociatedDisplayUniqueId->c_str());
enabled = false;
}
}
if (changes.any()) {
// ...
}
}
// 配置所有的 InputMapper
for_each_mapper([this, when, &readerConfig, changes, &out](InputMapper& mapper) {
// 参数 changes 为空
out += mapper.reconfigure(when, readerConfig, changes);
// InputDevice::mSources,是所有 InputMapper::mSource 的集合
mSources |= mapper.getSources();
});
// If a device is just plugged but it might be disabled, we need to update some info like
// axis range of touch from each InputMapper first, then disable it.
if (!changes.any()) {
out += setEnabled(readerConfig.disabledDevices.find(mId) ==
readerConfig.disabledDevices.end(),
when);
}
}
return out;
}
配置 InputDevice,有的根据 EventHub 中的数据配置,有的是根据上层的数据进行配置的,但是最主要是配置 InputMapper。
这里展示下按键输入设备和触摸屏的 dumpsys input 数据
Device 7: vivo_ts
EventHub Devices: [ 5 ]
Generation: 81
IsExternal: false
IsWaking: false
AssociatedDisplayPort: <none>
AssociatedDisplayUniqueIdByPort: <none>
AssociatedDisplayUniqueIdByDescriptor: <none>
HasMic: false
Sources: KEYBOARD | TOUCHSCREEN
KeyboardType: 1
ControllerNum: 0
Device 3: pmic_pwrkey
EventHub Devices: [ 9 ]
Generation: 69
IsExternal: false
IsWaking: false
AssociatedDisplayPort: <none>
AssociatedDisplayUniqueIdByPort: <none>
AssociatedDisplayUniqueIdByDescriptor: <none>
HasMic: false
Sources: KEYBOARD
KeyboardType: 1
ControllerNum: 0
KeyboardInputMapper 的配置
// KeyboardInputMapper.cpp
// 此时 changes 为空
std::list<NotifyArgs> KeyboardInputMapper::reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
ConfigurationChanges changes) {
// 父类方法,实现为空
std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
if (!changes.any()) { // first time only
// Configure basic parameters.
// 1.配置基本参数
configureParameters();
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
// 2.获取 DisplayViewPort
// 键盘类型的输入设备,一般不需要 DisplayViewPort,这里不展开讨论
mViewport = findViewport(config);
}
if (!changes.any() ||
changes.test(InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION)) {
// 来自于上层
std::optional<KeyboardLayoutInfo> newKeyboardLayoutInfo =
getValueByKey(config.keyboardLayoutAssociations, getDeviceContext().getLocation());
if (mKeyboardLayoutInfo != newKeyboardLayoutInfo) {
mKeyboardLayoutInfo = newKeyboardLayoutInfo;
bumpGeneration();
}
}
return out;
}
KeyboardInputMapper 配置基本参数,是从输入设备的配置文件获取数据的,如下
// KeyboardInputMapper.cpp
void KeyboardInputMapper::configureParameters() {
const PropertyMap& config = getDeviceContext().getConfiguration();
// keyboard.orientationAware 如果为 true,那么按键映射需要根据方向进行转换
mParameters.orientationAware = config.getBool("keyboard.orientationAware").value_or(false);
// keyboard.handlesKeyRepeat 如果为 true,表示输入设备自己产生 key repeat 事件,而不需要 Input 系统模拟 key repeat 事件
mParameters.handlesKeyRepeat = config.getBool("keyboard.handlesKeyRepeat").value_or(false);
// TODO: 暂时还不知道有什么用
mParameters.doNotWakeByDefault = config.getBool("keyboard.doNotWakeByDefault").value_or(false);
}
对于我的手机而言,输入设备是没有配置文件的,因此这些数据都为 false。
MultiTouchInputMapper 配置
手机或者平板的显示屏,基本上都是支持多点触摸,因此这里看来 MultiTouchInputMapper 的配置。
由于 MultiTouchInputMapper 没有实现 reconfigure(),因此看下它的父类 TouchInputMapper 的方法
// TouchInputMapper.cpp
// changes 此时为空
std::list<NotifyArgs> TouchInputMapper::reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
ConfigurationChanges changes) {
// 父类实现为空
std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
// 保存 InputReader 的配置
mConfig = config;
// Full configuration should happen the first time configure is called and
// when the device type is changed. Changing a device type can affect
// various other parameters so should result in a reconfiguration.
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DEVICE_TYPE)) {
// Configure basic parameters.
// 1. 配置基本参数
mParameters = computeParameters(getDeviceContext());
// Configure common accumulators.
mCursorScrollAccumulator.configure(getDeviceContext());
mTouchButtonAccumulator.configure();
// Configure absolute axis information.
// 2. 配置坐标系
// 子类 MultiTouchInputMapper 有实现
configureRawPointerAxes();
// Prepare input device calibration.
parseCalibration();
resolveCalibration();
}
if (!changes.any() ||
changes.test(InputReaderConfiguration::Change::TOUCH_AFFINE_TRANSFORMATION)) {
// Update location calibration to reflect current settings
updateAffineTransformation();
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED)) {
// Update pointer speed.
mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
}
using namespace ftl::flag_operators;
bool resetNeeded = false;
if (!changes.any() ||
changes.any(InputReaderConfiguration::Change::DISPLAY_INFO |
InputReaderConfiguration::Change::POINTER_CAPTURE |
InputReaderConfiguration::Change::POINTER_GESTURE_ENABLEMENT |
InputReaderConfiguration::Change::SHOW_TOUCHES |
InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE |
InputReaderConfiguration::Change::DEVICE_TYPE)) {
// Configure device sources, display dimensions, orientation and
// scaling factors.
// 3. 配置输入设备的转换矩阵
configureInputDevice(when, &resetNeeded);
}
if (changes.any() && resetNeeded) {
out += reset(when);
// 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.
// InputDispatcher::dispatchDeviceResetLocked() 处理这个事件,就是重置一些数据而已
out.emplace_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
}
return out;
}
TouchInputMapper( 子类有 MultiTouchInputMapper 和 SingleTouchInputMapper ) 的配置过程,主要有以下几点
- 配置基本参数,保存到 TouchInputMapper::parameters。这些参数,大部分来自输入设备的配置文件,还有一些来自于驱动。参考【配置基本参数】。
- 配置输入设备的坐标系。例如,x,y 轴的最小和最大坐标。参考【配置输入设备坐标系】。
- 配置输入设备的转换矩阵。 这个是为了把输入设备的坐标,先转换为物理显示屏的坐标,然后再转换为逻辑显示屏的坐标。参考【配置输入设备的转换矩阵】
配置基本参数
TouchInputMapper::Parameters TouchInputMapper::computeParameters(
const InputDeviceContext& deviceContext) {
Parameters parameters;
// Use the pointer presentation mode for devices that do not support distinct
// multitouch. The spot-based presentation relies on being able to accurately
// locate two or more fingers on the touch pad.
// 这是从驱动中获取的
parameters.gestureMode = deviceContext.hasInputProperty(INPUT_PROP_SEMI_MT)
? Parameters::GestureMode::SINGLE_TOUCH
: Parameters::GestureMode::MULTI_TOUCH;
// 加载输入设备配置文件的数据
const PropertyMap& config = deviceContext.getConfiguration();
std::optional<std::string> gestureModeString = config.getString("touch.gestureMode");
if (gestureModeString.has_value()) {
if (*gestureModeString == "single-touch") {
parameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
} else if (*gestureModeString == "multi-touch") {
parameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
} else if (*gestureModeString != "default") {
ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString->c_str());
}
}
// 对于触摸屏,类型为 TOUCH_SCREEN
parameters.deviceType = computeDeviceType(deviceContext);
parameters.hasButtonUnderPad = deviceContext.hasInputProperty(INPUT_PROP_BUTTONPAD);
// 注意,输入设备在没有配置文件的情况下,只要输入设备是触摸屏类型,parameters.orientationAware 就是为 true
parameters.orientationAware =
config.getBool("touch.orientationAware")
.value_or(parameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN);
parameters.orientation = ui::ROTATION_0;
std::optional<std::string> orientationString = config.getString("touch.orientation");
if (orientationString.has_value()) {
if (parameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
} else if (*orientationString == "ORIENTATION_90") {
parameters.orientation = ui::ROTATION_90;
} else if (*orientationString == "ORIENTATION_180") {
parameters.orientation = ui::ROTATION_180;
} else if (*orientationString == "ORIENTATION_270") {
parameters.orientation = ui::ROTATION_270;
} else if (*orientationString != "ORIENTATION_0") {
ALOGW("Invalid value for touch.orientation: '%s'", orientationString->c_str());
}
}
parameters.hasAssociatedDisplay = false;
parameters.associatedDisplayIsExternal = false;
if (parameters.orientationAware ||
parameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN ||
parameters.deviceType == Parameters::DeviceType::POINTER ||
(parameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION &&
deviceContext.getAssociatedViewport())) {
parameters.hasAssociatedDisplay = true;
if (parameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
parameters.associatedDisplayIsExternal = deviceContext.isExternal();
parameters.uniqueDisplayId = config.getString("touch.displayId").value_or("").c_str();
}
}
if (deviceContext.getAssociatedDisplayPort()) {
parameters.hasAssociatedDisplay = true;
}
// Initial downs on external touch devices should wake the device.
// Normally we don't do this for internal touch screens to prevent them from waking
// up in your pocket but you can enable it using the input device configuration.
// touch.wake 如果为 true,那么触摸可以唤醒屏幕
// 如果是外接设备,触摸也可以唤醒屏幕
parameters.wake = config.getBool("touch.wake").value_or(deviceContext.isExternal());
std::optional<int32_t> usiVersionMajor = config.getInt("touch.usiVersionMajor");
std::optional<int32_t> usiVersionMinor = config.getInt("touch.usiVersionMinor");
if (usiVersionMajor.has_value() && usiVersionMinor.has_value()) {
parameters.usiVersion = {
.majorVersion = *usiVersionMajor,
.minorVersion = *usiVersionMinor,
};
}
parameters.enableForInactiveViewport =
config.getBool("touch.enableForInactiveViewport").value_or(false);
return parameters;
}
可以通过 dumpsys input 看下基本参数
Device 7: xxx_ts
...
Touch Input Mapper (mode - DIRECT):
Parameters:
GestureMode: MULTI_TOUCH
DeviceType: TOUCH_SCREEN
AssociatedDisplay: hasAssociatedDisplay=true, isExternal=false, displayId=''
OrientationAware: true
Orientation: Rotation0
UsiVersion: <not set>
EnableForInactiveViewport: false
配置输入设备坐标系
// MultiTouchInputMapper.cpp
void MultiTouchInputMapper::configureRawPointerAxes() {
// 父类重置了 mRawPointerAxes
TouchInputMapper::configureRawPointerAxes();
// 获取坐标轴信息,保存到 mRawPointerAxes
// getAbsoluteAxisInfo 由 EventHub 实现,是从驱动获取的数据
getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
if (slotCount > MAX_SLOTS) {
ALOGW("MultiTouch Device %s reported %zu slots but the framework "
"only supports a maximum of %zu slots at this time.",
getDeviceName().c_str(), slotCount, MAX_SLOTS);
slotCount = MAX_SLOTS;
}
// 配置累加器
// 注意,最后一个参数,表示使用 slot 协议来上报数据
mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
/*usingSlotsProtocol=*/true);
} else {
// ...
}
}
MultiTouchInputMapper 配置的坐标轴信息,都保存到 mRawPointerAxes,可以 dumpsys input 看下数据
Input Reader State (Nums of device: 9):
...
Device 7: xxx_ts
...
Touch Input Mapper (mode - DIRECT):
Raw Touch Axes:
X: min=0, max=12599, flat=0, fuzz=0, resolution=0
Y: min=0, max=27999, flat=0, fuzz=0, resolution=0
Pressure: unknown range
TouchMajor: min=0, max=31, flat=0, fuzz=0, resolution=0
TouchMinor: min=0, max=31, flat=0, fuzz=0, resolution=0
ToolMajor: unknown range
ToolMinor: unknown range
Orientation: unknown range
Distance: unknown range
TiltX: unknown range
TiltY: unknown range
TrackingId: min=0, max=65535, flat=0, fuzz=0, resolution=0
Slot: min=0, max=9, flat=0, fuzz=0, resolution=0
MultiTouchInputMapper 还为触摸事件的累加器进行配置,如下
// MultiTouchMotionAccumulator.cpp
void MultiTouchMotionAccumulator::configure(const InputDeviceContext& deviceContext,
size_t slotCount, bool usingSlotsProtocol) {
// usingSlotsProtocol 为 true
mUsingSlotsProtocol = usingSlotsProtocol;
// 保存 slot 的数量
mSlots = std::vector<Slot>(slotCount);
mCurrentSlot = -1;
if (mUsingSlotsProtocol) {
int32_t initialSlot;
// EventHub 实现,从驱动中获取当前上报事件的 slot
if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
status == OK) {
mCurrentSlot = initialSlot;
} else {
ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
}
}
}
配置 MultiTouchMotionAccumulator,主要就是保存是否是使用 slot 协议,以及 slot 的数量。
配置输入设备的转换矩阵
// TouchInputMapper.cpp
void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
const DeviceMode oldDeviceMode = mDeviceMode;
resolveExternalStylusPresence();
// 1. 解析 device mode
if (mParameters.deviceType == Parameters::DeviceType::POINTER &&
// ...
} else if (isTouchScreen()) { // 触摸屏的 device type 是 TOUCH_SCREEN
mSource = AINPUT_SOURCE_TOUCHSCREEN;
// 触摸屏的 device mode 为 DIRECT
mDeviceMode = DeviceMode::DIRECT;
// ...
} else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION) {
// ...
} else {
// ...
}
// 2. 找到 DisplayViewport
const std::optional<DisplayViewport> newViewportOpt = findViewport();
// Ensure the device is valid and can be used.
if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
ALOGW("Touch device '%s' did not report support for X or Y axis! "
"The device will be inoperable.",
getDeviceName().c_str());
mDeviceMode = DeviceMode::DISABLED;
} else if (!newViewportOpt) {
ALOGI("Touch device '%s' could not query the properties of its associated "
"display. The device will be inoperable until the display size "
"becomes available.",
getDeviceName().c_str());
mDeviceMode = DeviceMode::DISABLED;
} else if (!mParameters.enableForInactiveViewport && !newViewportOpt->isActive) {
ALOGI("Disabling %s (device %i) because the associated viewport is not active",
getDeviceName().c_str(), getDeviceId());
mDeviceMode = DeviceMode::DISABLED;
}
// Raw width and height in the natural orientation.
const ui::Size rawSize{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
const int32_t rawXResolution = mRawPointerAxes.x.resolution;
const int32_t rawYResolution = mRawPointerAxes.y.resolution;
// Calculate the mean resolution when both x and y resolution are set, otherwise set it to 0.
const float rawMeanResolution =
(rawXResolution > 0 && rawYResolution > 0) ? (rawXResolution + rawYResolution) / 2 : 0;
const DisplayViewport& newViewport = newViewportOpt.value_or(kUninitializedViewport);
const bool viewportChanged = mViewport != newViewport;
bool skipViewportUpdate = false;
if (viewportChanged) {
const bool viewportOrientationChanged = mViewport.orientation != newViewport.orientation;
const bool viewportDisplayIdChanged = mViewport.displayId != newViewport.displayId;
// 保存 DisplayViewPort
mViewport = newViewport;
// 触摸屏的 device mode 是 DIRECT
if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
const auto oldDisplayBounds = mDisplayBounds;
mDisplayBounds = getNaturalDisplaySize(mViewport);
mPhysicalFrameInRotatedDisplay = {mViewport.physicalLeft, mViewport.physicalTop,
mViewport.physicalRight, mViewport.physicalBottom};
// TODO(b/257118693): Remove the dependence on the old orientation/rotation logic that
// uses mInputDeviceOrientation. The new logic uses the transforms calculated in
// computeInputTransforms().
// InputReader works in the un-rotated display coordinate space, so we don't need to do
// anything if the device is already orientation-aware. If the device is not
// orientation-aware, then we need to apply the inverse rotation of the display so that
// when the display rotation is applied later as a part of the per-window transform, we
// get the expected screen coordinates.
mInputDeviceOrientation = mParameters.orientationAware
? ui::ROTATION_0
: getInverseRotation(mViewport.orientation);
// For orientation-aware devices that work in the un-rotated coordinate space, the
// viewport update should be skipped if it is only a change in the orientation.
skipViewportUpdate = !viewportDisplayIdChanged && mParameters.orientationAware &&
mDisplayBounds == oldDisplayBounds && viewportOrientationChanged;
// Apply the input device orientation for the device.
// 计算输入设备的方向
mInputDeviceOrientation = mInputDeviceOrientation + mParameters.orientation;
// 3. 计算转换矩阵
computeInputTransforms();
} else {
// ...
}
}
// If moving between pointer modes, need to reset some state.
bool deviceModeChanged = mDeviceMode != oldDeviceMode;
if (deviceModeChanged) {
mOrientedRanges.clear();
}
// Create and preserve the pointer controller in the following cases:
const bool isPointerControllerNeeded =
// - when the device is in pointer mode, to show the mouse cursor;
(mDeviceMode == DeviceMode::POINTER) ||
// - when pointer capture is enabled, to preserve the mouse cursor position;
(mParameters.deviceType == Parameters::DeviceType::POINTER &&
mConfig.pointerCaptureRequest.enable) ||
// - when we should be showing touches;
(mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
// - when we should be showing a pointer icon for direct styluses.
(mDeviceMode == DeviceMode::DIRECT && mConfig.stylusPointerIconEnabled && hasStylus());
if (isPointerControllerNeeded) {
if (mPointerController == nullptr) {
mPointerController = getContext()->getPointerController(getDeviceId());
}
if (mConfig.pointerCaptureRequest.enable) {
mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
}
} else {
if (mPointerController != nullptr && mDeviceMode == DeviceMode::DIRECT &&
!mConfig.showTouches) {
mPointerController->clearSpots();
}
mPointerController.reset();
}
if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) {
ALOGI("Device reconfigured: id=%d, name='%s', size %s, orientation %d, mode %d, "
"display id %d",
getDeviceId(), getDeviceName().c_str(), toString(mDisplayBounds).c_str(),
mInputDeviceOrientation, mDeviceMode, mViewport.displayId);
configureVirtualKeys();
initializeOrientedRanges();
// Location
updateAffineTransformation();
if (mDeviceMode == DeviceMode::POINTER) {
// ....
}
// Inform the dispatcher about the changes.
*outResetNeeded = true;
bumpGeneration();
}
}
既然是要通过转换矩阵,把输入设备的坐标,最终转化为逻辑显示屏的坐标,那么首先得找到显示屏的相关坐标数据,这就是第二步所做的,如下
// TouchInputMapper.cpp
std::optional<DisplayViewport> TouchInputMapper::findViewport() {
// 对于触摸屏,通过导出的数据看,mParameters.hasAssociatedDisplay 为 true,mDeviceMode 为 DIRECT
if (mParameters.hasAssociatedDisplay && mDeviceMode != DeviceMode::UNSCALED) {
if (getDeviceContext().getAssociatedViewport()) {
// ...
}
const std::optional<std::string> associatedDisplayUniqueId =
getDeviceContext().getAssociatedDisplayUniqueId();
if (associatedDisplayUniqueId) {
// ...
}
// 触摸屏的 device mode 为 DIRECT
if (mDeviceMode == DeviceMode::POINTER) {
// ...
}
if (!mParameters.uniqueDisplayId.empty()) {
// ...
}
ViewportType viewportTypeToUse;
if (mParameters.associatedDisplayIsExternal) {
// ...
} else {
// 默认使用内置显示屏的 DisplayViewPort
viewportTypeToUse = ViewportType::INTERNAL;
}
std::optional<DisplayViewport> viewport =
mConfig.getDisplayViewportByType(viewportTypeToUse);
if (!viewport && viewportTypeToUse == ViewportType::EXTERNAL) {
// ...
}
return viewport;
}
// ...
}
触摸屏,默认与内置显示屏的 DisplayViewPort 关联。而这个 DisplayViewPort 是 DisplayManagerService 设置给 Input 系统的。
那么,这个 DisplayViewPort 有什么用呢? 可以看下上层的注释
/**
* Describes how the pixels of physical display device reflects the content of
* a logical display.
*
* This information is used by the input system to translate touch input from
* physical display coordinates into logical display coordinates.
*.
*/
public final class DisplayViewport {
根据上层的 DisplayViewport 注释所说,它的作用是把物理显示屏的坐标,转换为逻辑显示屏的坐标。但是,在 Input 系统中,还会利用它把输入设备的坐标,转换为物理显示屏的坐标,最终转换为逻辑显示屏的坐标,如下
// TouchInputMapper.cpp
void TouchInputMapper::computeInputTransforms() {
constexpr auto isRotated = [](const ui::Transform::RotationFlags& rotation) {
return rotation == ui::Transform::ROT_90 || rotation == ui::Transform::ROT_270;
};
// See notes about input coordinates in the inputflinger docs:
// //frameworks/native/services/inputflinger/docs/input_coordinates.md
// Step 1: Undo the raw offset so that the raw coordinate space now starts at (0, 0).
ui::Transform undoOffsetInRaw;
undoOffsetInRaw.set(-mRawPointerAxes.x.minValue, -mRawPointerAxes.y.minValue);
// Step 2: Rotate the raw coordinates to account for input device orientation. The coordinates
// will now be in the same orientation as the display in ROTATION_0.
// Note: Negating an ui::Rotation value will give its inverse rotation.
const auto inputDeviceOrientation = ui::Transform::toRotationFlags(-mParameters.orientation);
const ui::Size orientedRawSize = isRotated(inputDeviceOrientation)
? ui::Size{mRawPointerAxes.getRawHeight(), mRawPointerAxes.getRawWidth()}
: ui::Size{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
// When rotating raw values, account for the extra unit added when calculating the raw range.
const auto orientInRaw = ui::Transform(inputDeviceOrientation, orientedRawSize.width - 1,
orientedRawSize.height - 1);
// Step 3: Rotate the raw coordinates to account for the display rotation. The coordinates will
// now be in the same orientation as the rotated display. There is no need to rotate the
// coordinates to the display rotation if the device is not orientation-aware.
const auto viewportRotation = ui::Transform::toRotationFlags(-mViewport.orientation);
const auto rotatedRawSize = mParameters.orientationAware && isRotated(viewportRotation)
? ui::Size{orientedRawSize.height, orientedRawSize.width}
: orientedRawSize;
// When rotating raw values, account for the extra unit added when calculating the raw range.
const auto rotateInRaw = mParameters.orientationAware
? ui::Transform(viewportRotation, rotatedRawSize.width - 1, rotatedRawSize.height - 1)
: ui::Transform();
// Step 4: Scale the raw coordinates to the display space.
// - In DIRECT mode, we assume that the raw surface of the touch device maps perfectly to
// the surface of the display panel. This is usually true for touchscreens.
// - In POINTER mode, we cannot assume that the display and the touch device have the same
// aspect ratio, since it is likely to be untrue for devices like external drawing tablets.
// In this case, we used a fixed scale so that 1) we use the same scale across both the x and
// y axes to ensure the mapping does not stretch gestures, and 2) the entire region of the
// display can be reached by the touch device.
// - From this point onward, we are no longer in the discrete space of the raw coordinates but
// are in the continuous space of the logical display.
ui::Transform scaleRawToDisplay;
const float xScale = static_cast<float>(mViewport.deviceWidth) / rotatedRawSize.width;
const float yScale = static_cast<float>(mViewport.deviceHeight) / rotatedRawSize.height;
if (mDeviceMode == DeviceMode::DIRECT) {
// 矩阵中设置缩放,其实就是为了把输入设备坐标,转换为物理屏的坐标
scaleRawToDisplay.set(xScale, 0, 0, yScale);
} else if (mDeviceMode == DeviceMode::POINTER) {
const float fixedScale = std::max(xScale, yScale);
scaleRawToDisplay.set(fixedScale, 0, 0, fixedScale);
} else {
LOG_ALWAYS_FATAL("computeInputTransform can only be used for DIRECT and POINTER modes");
}
// Step 5: Undo the display rotation to bring us back to the un-rotated display coordinate space
// that InputReader uses.
const auto undoRotateInDisplay =
ui::Transform(viewportRotation, mViewport.deviceWidth, mViewport.deviceHeight)
.inverse();
// Now put it all together!
// The transform that maps the input device's raw coordinate space to the rotated display's
// coordinate space. This used to perform hit-testing of raw events with the physical frame in
// the rotated coordinate space. See mPhysicalFrameInRotatedDisplay.
mRawToRotatedDisplay = (scaleRawToDisplay * (rotateInRaw * (orientInRaw * undoOffsetInRaw)));
// The transform that maps the input device's raw coordinate space to the un-rotated display's
// coordinate space. InputReader generates events in the un-rotated display's coordinate space.
mRawToDisplay = (undoRotateInDisplay * mRawToRotatedDisplay);
mRawRotation = ui::Transform{mRawToDisplay.getOrientation()};
}
矩阵的知识,我已经还给我的大学老师了,这里就不献丑去分析原理,就展示下 dumpsys input 导出的数据吧
Device 7: vivo_ts
...
Viewport INTERNAL: displayId=0, uniqueId=local:4630946650788219010, port=130, orientation=0, logicalFrame=[0, 0, 1080, 2400], physicalFrame=[0, 0, 1260, 2800], deviceSize=[1260, 2800], isActive=[1]
DisplayBounds: 1260x2800
PhysicalFrameInRotatedDisplay: Rect{0, 0, 1260, 2800}
InputDeviceOrientation: Rotation0
Translation and Scaling Factors:
RawToDisplay Transform: (ROT_0) (SCALE )
0.1000 0.0000 0.0000
0.0000 0.1000 0.0000
0.0000 0.0000 1.0000
RawRotation Transform: (ROT_0) (IDENTITY)
总结
扫描输入设备,最终是为了给输入设备建立数据,我们最主要关心的是,这些数据来自哪里。至于这些数据有什么用途,等到我们碰到的时候,再来细细地体会。