输入事件关联显示设备
inputevent 的 displayId 是怎么来的?此文章就是介绍 displayid 与 Inputevent 的关系。display 是由 DisplayManagerService 管理的,我们先从 DMS 开始。
显示管理服务(DisplayManagerService)
-
有屏幕添加时 DMS 会发送一个 MSG_REQUEST_TRAVERSAL 的 Handle 消息。
该消息意在通过 DMS 里持有的 WMS 的本地服务去调用 WMS 的 requestTraversal() 。 -
WMS 窗口遍历的时候会去调用 DMS 的 performTraversalInternal 方法,由 WMS里的 applySurfaceChangesTransaction()方法调用。
-
DMS 会去配置每一块 device 的信息,其中就包括 viewport。详见 configureDisplayLocked() 、 populateViewportLocked()。
// Update the corresponding viewport.
if ((info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
viewportType = VIEWPORT_INTERNAL;
} else if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
viewportType = VIEWPORT_EXTERNAL;
} else if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL
&& !TextUtils.isEmpty(info.uniqueId)) {
viewportType = VIEWPORT_VIRTUAL;
} else {
Slog.i(TAG, "Display " + info + " does not support input device matching.");
return;
}
//这里的 ViewPort 里有 device 的 uniqueId,用于 event 匹配 displayID。
private void populateViewportLocked(int viewportType, int displayId, DisplayDevice device, String uniqueId) {
final DisplayViewport viewport = getViewportLocked(viewportType, uniqueId);
device.populateViewportLocked(viewport);
viewport.valid = true;
viewport.displayId = displayId;//更新Displayid.
}
public final void populateViewportLocked(DisplayViewport viewport) {
viewport.orientation = mCurrentOrientation;
if (mCurrentLayerStackRect != null) {
viewport.logicalFrame.set(mCurrentLayerStackRect);
} else {
viewport.logicalFrame.setEmpty();
}
if (mCurrentDisplayRect != null) {
viewport.physicalFrame.set(mCurrentDisplayRect);
} else {
viewport.physicalFrame.setEmpty();
}
boolean isRotated = (mCurrentOrientation == Surface.ROTATION_90
|| mCurrentOrientation == Surface.ROTATION_270);
DisplayDeviceInfo info = getDisplayDeviceInfoLocked();
viewport.deviceWidth = isRotated ? info.height : info.width;
viewport.deviceHeight = isRotated ? info.width : info.height;
viewport.uniqueId = info.uniqueId;
if (info.address instanceof DisplayAddress.Physical) {
viewport.physicalPort = ((DisplayAddress.Physical) info.address).getPort();
} else {
viewport.physicalPort = null;
}
}
-
DMS 会发送一条 MSG_UPDATE_VIEWPORT 消息去设置 viewport,详见 performTraversalLocked()。
-
IMS 的 本地服务 就会将这些 viewport 信息传递到 JNI 层。
-
最终 native 层会去刷新配置将 viewport 信息保存到全局 InputReaderConfiguration 中,详见requestRefreshConfiguration()。
初始化时配置文件的解析
该配置文件是放置在 idc 下,以设备名称命名的 idc 文件。
- 解析配置文件时机。
// frameworks/native/services/inputflinger/EventHub.cpp
status_t EventHub::openDeviceLocked(const char* devicePath) {
... ...
Device* device = new Device(fd, deviceId, devicePath, identifier);
// Load the configuration file for the device.
loadConfigurationLocked(device);
// Determine whether the device is external or internal.
if (isExternalDeviceLocked(device)) {
device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
}
... ...
}
- 具体解析配置文件的功能函数
void EventHub::loadConfigurationLocked(Device* device) {
// 查找文件
device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
if (device->configurationFile.empty()) {
ALOGD("No input device configuration file found for device '%s'.",
device->identifier.name.c_str());
} else {
//找到了解析文件到configuration。
status_t status = PropertyMap::load(String8(device->configurationFile.c_str()),
&device->configuration);
if (status) {
ALOGE("Error loading input device configuration file for device '%s'. "
"Using default configuration.",
device->identifier.name.c_str());
}
}
}
- 从 odm、vendor、System里去查找文件是否存。
std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
const InputDeviceIdentifier& deviceIdentifier,
InputDeviceConfigurationFileType type) {
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
if (deviceIdentifier.version != 0) {
// Try vendor product version.
std::string versionPath = getInputDeviceConfigurationFilePathByName(
StringPrintf("Vendor_%04x_Product_%04x_Version_%04x",
deviceIdentifier.vendor, deviceIdentifier.product,
deviceIdentifier.version),
type);
if (!versionPath.empty()) {
return versionPath;
}
}
// Try vendor product.
std::string productPath = getInputDeviceConfigurationFilePathByName(
StringPrintf("Vendor_%04x_Product_%04x",
deviceIdentifier.vendor, deviceIdentifier.product),
type);
if (!productPath.empty()) {
return productPath;
}
}
// Try device name.
return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type);
}
std::string getInputDeviceConfigurationFilePathByName(
const std::string& name, InputDeviceConfigurationFileType type) {
// Search system repository.
std::string path;
// Treblized input device config files will be located /odm/usr or /vendor/usr.
const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")};
for (size_t i = 0; i < size(rootsForPartition); i++) {
if (rootsForPartition[i] == nullptr) {
continue;
}
path = rootsForPartition[i];
path += "/usr/";
appendInputDeviceConfigurationFileRelativePath(path, name, type);
if (!access(path.c_str(), R_OK)) {
return path;
}
}
// Search user repository.
// TODO Should only look here if not in safe mode.
path = "";
char *androidData = getenv("ANDROID_DATA");
if (androidData != nullptr) {
path += androidData;
}
path += "/system/devices/";
appendInputDeviceConfigurationFileRelativePath(path, name, type);
if (!access(path.c_str(), R_OK)) {
return path;
}
// Not found.
return "";
}
输入端口与显示端口的配置
在刷新配置的同时会去读取手机目录 /etc/input-port-associations.xml里的内容,该文件是配置输入端口与显示端口的关联关系。
代码详见 IMS 的 getInputPortAssociations()。
// Associations between input ports and display ports
// The java method packs the information in the following manner:
// Original data: [{'inputPort1': '1'}, {'inputPort2': '2'}]
// Received data: ['inputPort1', '1', 'inputPort2', '2']
<ports>
<port display="0" input="usb-xhci-hcd.0.auto-1.1/input0" />
<port display="1" input="usb-xhci-hcd.0.auto-1.2/input0" />
</ports>
配置文件解析行为
// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) {
... ...
outConfig->portAssociations.clear();
//Java层读取 input-port-associations.xml 文件
jobjectArray portAssociations = jobjectArray(env->CallStaticObjectMethod(
gServiceClassInfo.clazz, gServiceClassInfo.getInputPortAssociations));
if (!checkAndClearExceptionFromCallback(env, "getInputPortAssociations") && portAssociations) {
jsize length = env->GetArrayLength(portAssociations);
for (jsize i = 0; i < length / 2; i++) {
std::string inputPort = getStringElementFromJavaArray(env, portAssociations, 2 * i);
std::string displayPortStr =
getStringElementFromJavaArray(env, portAssociations, 2 * i + 1);
uint8_t displayPort;
// Should already have been validated earlier, but do it here for safety.
bool success = ParseUint(displayPortStr, &displayPort);
if (!success) {
ALOGE("Could not parse entry in port configuration file, received: %s",
displayPortStr.c_str());
continue;
}
outConfig->portAssociations.insert({inputPort, displayPort});//存上。
}
env->DeleteLocalRef(portAssociations);
}
... ...
}
分配显示端口
void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) {
... ...
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
// In most situations, no port will be specified.
mAssociatedDisplayPort = std::nullopt;
// Find the display port that corresponds to the current input port.
const std::string& inputPort = mIdentifier.location;
if (!inputPort.empty()) {
const std::unordered_map<std::string, uint8_t>& ports = config->portAssociations;
const auto& displayPort = ports.find(inputPort);
if (displayPort != ports.end()) {
mAssociatedDisplayPort = std::make_optional(displayPort->second);
}
}
}
... ...
}
ViewPort 查找
ViewPort 的查找会从三个方面去查找——显示端口、唯一的显示ID、显示端口类型。当这些都找不到时才会创建一个 displayid 为 -1,viewtype 为 VIEWPORT_INTERNAL 的 newViewport。
/**
* Determine which DisplayViewport to use.
* 1. If display port is specified, return the matching viewport. If matching viewport not
* found, then return.
* 2. If a device has associated display, get the matching viewport by either unique id or by
* the display type (internal or external).
* 3. Otherwise, use a non-display viewport.
*/
std::optional<DisplayViewport> TouchInputMapper::findViewport() {
if (mParameters.hasAssociatedDisplay) {
const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
if (displayPort) {
// Find the viewport that contains the same port
std::optional<DisplayViewport> v = mConfig.getDisplayViewportByPort(*displayPort);
if (!v) {
ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
"but the corresponding viewport is not found.",
getDeviceName().c_str(), *displayPort);
}
return v;
}
if (!mParameters.uniqueDisplayId.empty()) {
return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId);
}
ViewportType viewportTypeToUse;
if (mParameters.associatedDisplayIsExternal) {
viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
} else {
viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
}
std::optional<DisplayViewport> viewport =
mConfig.getDisplayViewportByType(viewportTypeToUse);
if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) {
ALOGW("Input device %s should be associated with external display, "
"fallback to internal one for the external viewport is not found.",
getDeviceName().c_str());
viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
}
return viewport;
}
DisplayViewport newViewport;
// Raw width and height in the natural orientation.
int32_t rawWidth = mRawPointerAxes.getRawWidth();
int32_t rawHeight = mRawPointerAxes.getRawHeight();
newViewport.setNonDisplayViewport(rawWidth, rawHeight);
return std::make_optional(newViewport);
}
本文来自博客园,作者:寒风凛凛,转载请注明原文链接:https://www.cnblogs.com/dongxiaofat/p/15426403.html