这次梳理下Hdmi设备增删的事件是如何传递到TvInputService,以及TvInputManagerService的处理差异。
1.HdmiCecLocalDeviceTv
HdmiControlService在收到HdmiCecController的onHotplug回调后,会再依次调用设备列表中的各个HdmiCecLocalDevice的onHotplug方法。以Tv为例,它会发起HotplugDetectionAction来轮询cec总线上的每一个逻辑地址,来判断是否需要添加或者删除设备。
Z:\p\frameworks\base\services\core\java\com\android\server\hdmi\HdmiCecLocalDeviceTv.java
@Override
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {
assertRunOnServiceThread();
if (!connected) {
removeCecSwitches(portId);
}
// Tv device will have permanent HotplugDetectionAction.
List<HotplugDetectionAction> hotplugActions = getActions(HotplugDetectionAction.class);
if (!hotplugActions.isEmpty()) {
// Note that hotplug action is single action running on a machine.
// "pollAllDevicesNow" cleans up timer and start poll action immediately.
// It covers seq #40, #43.
hotplugActions.get(0).pollAllDevicesNow();
} else {
HdmiLogger.debug("start poll devices for hotplug");
addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
}
}
2.HotplugDetectionAction
Z:\p\frameworks\base\services\core\java\com\android\server\hdmi\HotplugDetectionAction.java
private void pollAllDevices() {
Slog.v(TAG, "Poll all devices.");
pollDevices(new DevicePollingCallback() {
@Override
public void onPollingFinished(List<Integer> ackedAddress) {
checkHotplug(ackedAddress, false);
}
}, Constants.POLL_ITERATION_IN_ORDER
| Constants.POLL_STRATEGY_REMOTES_DEVICES, HdmiConfig.HOTPLUG_DETECTION_RETRY);
}
addDevice是通过发送GET_PHSYCAL_ADDRESS message收到响应后再添加的。
private void addDevice(int addedAddress) {
// Sending <Give Physical Address> will initiate new device action.
sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(),
addedAddress));
}
tv handleReportPhysicalAddress
HdmiDeviceInfo deviceInfo = new HdmiDeviceInfo(address, path, getPortId(path), type,
Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(address));
addCecDevice(deviceInfo);
下面就是关键addCecDevice,这里会调用注册在HdmiControlService里面的IHdmiDeviceEventListener来触发DEVICE_EVENT_ADD_DEVICE和DEVICE_EVENT_REMOVE_DEVICE事件。
/**
* Called when a device is newly added or a new device is detected or
* existing device is updated.
*
* @param info device info of a new device.
*/
@ServiceThreadOnly
final void addCecDevice(HdmiDeviceInfo info) {
assertRunOnServiceThread();
Slog.d(TAG, "addCecDevice " + info);
HdmiDeviceInfo old = addDeviceInfo(info);
if (info.getLogicalAddress() == mAddress) {
// The addition of TV device itself should not be notified.
return;
}
if (old == null) {
invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
} else if (!old.equals(info)) {
invokeDeviceEventListener(old, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
}
}
3.TvInputHardwareManager
和hotplug一样,都是在TvInputManagerService创建的TvInputHardwareManager里面初始化时创建的监听。
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
IHdmiControlService hdmiControlService = IHdmiControlService.Stub.asInterface(
ServiceManager.getService(Context.HDMI_CONTROL_SERVICE));
if (hdmiControlService != null) {
try {
hdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
hdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
以add为例,首先会将这个HdmiDeviceInfo添加到保存好的列表里面,并发送 ListenerHandler.HDMI_DEVICE_ADDED继续处理。
private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
@Override
public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
Slog.w(TAG, "onStatusChanged " + deviceInfo + " status " + status);
if (!deviceInfo.isSourceType()) return;
synchronized (mLock) {
int messageType = 0;
Object obj = null;
switch (status) {
case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
if (findHdmiDeviceInfo(deviceInfo.getId()) == null) {
mHdmiDeviceList.add(deviceInfo);
} else {
Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
return;
}
messageType = ListenerHandler.HDMI_DEVICE_ADDED;
obj = deviceInfo;
break;
}
case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
return;
}
messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
obj = deviceInfo;
break;
}
case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
return;
}
mHdmiDeviceList.add(deviceInfo);
messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
obj = deviceInfo;
break;
}
}
Message msg = mHandler.obtainMessage(messageType, 0, 0, obj);
if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
msg.sendToTarget();
} else {
mPendingHdmiDeviceEvents.add(msg);
}
}
}
和hotplug调用InputManagerService的 onStateChanged方法一样,这里会调用onHdmiDeviceAdded方法。需要注意的一点是,实际上add和remove这两个接口的调用相当频繁,在每一次HdmiDeviceInfo发生变更时基本上就会触发一次add和remove,比如原来没拿到vendorId现在拿到了,就更新下。所以这并不能对应实际上的热插拔。在这里进行热插拔的处理是不合适的。
private class ListenerHandler e