【Android】输入路由

【Android】输入路由

Android,可以通过配置 input-port-associations.xml,将Display和Input(屏幕输入)节点关联起来。

<ports>
    <port display="0" input="linduoDev-1.0/input0" />
    <port display="1" input="linduoDev-2.0/input0" />
</ports>

display="0"指定屏幕接口,input="linduoDev-1.0/input0"指定输入设备接口。可以通过下述命令查看配置关系

adb shell dumpsys input

相关源码流程

解析input-port-associations.xml
  • IMS(InputManagerService.java)
private static final String PORT_ASSOCIATIONS_PATH = 
	"etc/input-port-associations.xml";

private static Map<String, Integer> loadStaticInputPortAssociations() {        
	final File baseDir = Environment.getVendorDirectory();
    final File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH);

    try {
        final InputStream stream = new FileInputStream(confFile);
        return ConfigurationProcessor.processInputPortAssociations(stream);
    } catch (FileNotFoundException e) {
        // Most of the time, file will not exist, which is expected.
    } catch (Exception e) {
        Slog.e(TAG, "Could not parse '" + confFile.getAbsolutePath() + "'", e);
    }

    return new HashMap<>();
}
  • 解析的内容后的格式为<key>:InputPort<value>:DipslayPort
@VisibleForTesting    
static Map<String, Integer> processInputPortAssociations(InputStream xml) throws Exception {
    Map<String, Integer> associations = new HashMap<String, Integer>();
    {
        TypedXmlPullParser parser = Xml.resolvePullParser(xml);
        XmlUtils.beginDocument(parser, "ports");

        while (true) {
            XmlUtils.nextElement(parser);
            String entryName = parser.getName();
            if (!"port".equals(entryName)) {
                break;
            }
            String inputPort = parser.getAttributeValue(null, "input");
            String displayPortStr = parser.getAttributeValue(null, "display");
            if (TextUtils.isEmpty(inputPort) || TextUtils.isEmpty(displayPortStr)) {
                continue;
            }
            try {
                int displayPort = Integer.parseUnsignedInt(displayPortStr);
                associations.put(inputPort, displayPort);
            } catch (NumberFormatException e) {
                Slog.wtf(TAG, "Display port should be an integer");
            }
        }
    }
    return associations;
}
  • IMS通过JNI Callback,将InputPort:DisplayPort信息,传输到IMS的Native层。
// Native callback    
private String[] getInputPortAssociations() {
	final Map<String, Integer> associations = new HashMap<>(mStaticAssociations);
    // merge the runtime associations.synchronized 
    (mAssociationsLock) {
        associations.putAll(mRuntimeAssociations);
    }

    return flatten(associations);
}
  • JNI代码对应(com_android_server_input_InputManagerService.cpp)。读取InputPort、DisplayPort的配置。并设置DisplayViewPort
void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) {    
	ATRACE_CALL();
    // 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']
    // So we unpack accordingly here.outConfig->portAssociations.clear();
    jobjectArray portAssociations = jobjectArray(env->CallObjectMethod(mServiceObj,
            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);
    }

    { // acquire lockAutoMutex _l(mLock);

        outConfig->pointerVelocityControlParameters.scale = exp2f(mLocked.pointerSpeed
                * POINTER_SPEED_EXPONENT);
        outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled;

        outConfig->showTouches = mLocked.showTouches;

        outConfig->pointerCapture = mLocked.pointerCapture;

        outConfig->setDisplayViewports(mLocked.viewports);

        outConfig->defaultPointerDisplayId = mLocked.pointerDisplayId;

        outConfig->disabledDevices = mLocked.disabledInputDevices;
    } // release lock
}
  • InputReader通过调用getReaderConfiguration获取到配置信息。然后通知InputDevice配置信息。
void InputReader::refreshConfigurationLocked(uint32_t changes) {    
	mPolicy->getReaderConfiguration(&mConfig);
    mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);

    if (!changes) return;

    ALOGI("Reconfiguring input devices, changes=%s",
          InputReaderConfiguration::changesToString(changes).c_str());
    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

    if (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) {
        updatePointerDisplayLocked();
    }

    if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
        mEventHub->requestReopenDevices();
    } else {
        for (auto& devicePair : mDevices) {
            std::shared_ptr<InputDevice>& device = devicePair.second;
            device->configure(now, &mConfig, changes);
        }
    }

    if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) {
        const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
                                                   mConfig.pointerCapture);
        mQueuedListener->notifyPointerCaptureChanged(&args);
    }
}
  • 在InputDevice的configure中。拿到InputDevice关联的ViewPort
void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
                            uint32_t changes) {
       if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
            bool enabled = (config->disabledDevices.find(mId) 
            	== config->disabledDevices.end());
            if (mAssociatedDisplayPort) {
                mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort);
            } else if (mAssociatedDisplayUniqueId != std::nullopt) {
                mAssociatedViewport =
                        config->getDisplayViewportByUniqueId(*mAssociatedDisplayUniqueId);
            }

            if (changes) {
                // For first-time configuration, only allow device to be disabled after mappers have
                // finished configuring. This is because we need to read some of the properties from
                // the device's open fd.
                setEnabled(enabled, when);
            }
        }
}
  • DisplayViewPort为Input派发提供信息,该数据结构信息如下
enum class ViewportType : int32_t {
    INTERNAL = 1,
    EXTERNAL = 2,
    VIRTUAL = 3,
};

/*
 * Describes how coordinates are mapped on a physical display.
 * See com.android.server.display.DisplayViewport.
 */struct DisplayViewport {
    int32_t displayId; // -1 if invalidint32_t orientation;
    int32_t logicalLeft;
    int32_t logicalTop;
    int32_t logicalRight;
    int32_t logicalBottom;
    int32_t physicalLeft;
    int32_t physicalTop;
    int32_t physicalRight;
    int32_t physicalBottom;
    int32_t deviceWidth;
    int32_t deviceHeight;
    bool isActive;
    std::string uniqueId;
    // The actual (hardware) port that the associated display is connected to.// Not all viewports will have this specified.std::optional<uint8_t> physicalPort;
    ViewportType type;
 }
  • InputReader会根据InputPort找到对应的InputDevice,将对应的Input信息传输给InputDevice,然后InputDevice根据自己的DisplayViewPort,将Touch派发给对应的DisplayID(TouchInputMapper.cpp)
void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
	uint32_t source, int32_t action, int32_t actionButton,
	int32_t flags, int32_t metaState, int32_t buttonState,
	int32_t edgeFlags, const PointerProperties* properties,
	const PointerCoords* coords, const uint32_t* idToIndex,
	BitSet32 idBits, int32_t changedId, float xPrecision,
	float yPrecision, nsecs_t downTime) {

	const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
	const int32_t deviceId = getDeviceId();
	std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
	std::for_each(frames.begin(), frames.end(),
				  [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
	NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
						  policyFlags, action, actionButton, flags, metaState, buttonState,
						  MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
						  pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
						  downTime, std::move(frames));
	getListener()->notifyMotion(&args);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林多

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值