【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);
}