TIF 和Hdmi cec hotplug热插拔事件过程梳理二
这次梳理下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 extends Handler {
private static final int STATE_CHANGED = 1;
private static final int HARDWARE_DEVICE_ADDED = 2;
private static final int HARDWARE_DEVICE_REMOVED = 3;
private static final int HDMI_DEVICE_ADDED = 4;
private static final int HDMI_DEVICE_REMOVED = 5;
private static final int HDMI_DEVICE_UPDATED = 6;
@Override
public final void handleMessage(Message msg) {
switch (msg.what) {
case STATE_CHANGED: {
String inputId = (String) msg.obj;
int state = msg.arg1;
mListener.onStateChanged(inputId, state);
break;
}
case HARDWARE_DEVICE_ADDED: {
TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
mListener.onHardwareDeviceAdded(info);
break;
}
case HARDWARE_DEVICE_REMOVED: {
TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
mListener.onHardwareDeviceRemoved(info);
break;
}
case HDMI_DEVICE_ADDED: {
HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
mListener.onHdmiDeviceAdded(info);
break;
}
case HDMI_DEVICE_REMOVED: {
HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
mListener.onHdmiDeviceRemoved(info);
break;
}
case HDMI_DEVICE_UPDATED: {
HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
String inputId;
synchronized (mLock) {
inputId = mHdmiInputIdMap.get(info.getId());
}
if (inputId != null) {
mListener.onHdmiDeviceUpdated(inputId, info);
} else {
Slog.w(TAG, "Could not resolve input ID matching the device info; "
+ "ignoring.");
}
break;
}
default: {
Slog.w(TAG, "Unhandled message: " + msg);
break;
}
}
}
}
4.TvInputManagerService
同样也是HardwareListener,但是onHdmiDeviceAdded,后续不是交给TvInputManager去回调注册的TvInputManagerCallback,而是会调用保存的serviceState.service.notifyHdmiDeviceAdded(deviceInfo);serviceState.service是客户端比如TvView 在createSession时会和对应的TvInputService建立联系,TvInputManagerService会通过bind的方式连接TvInputService,也就是说下面就是通知TvInputService hdmi 设备的状态发生了变化。
private final class HardwareListener implements TvInputHardwareManager.Listener {
@Override
public void onStateChanged(String inputId, int state) {
synchronized (mLock) {
setStateLocked(inputId, state, mCurrentUserId);
}
}
@Override
public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
synchronized (mLock) {
Slog.d(TAG, "onHardwareDeviceAdded " + info);
UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
if (!serviceState.isHardware || serviceState.service == null) continue;
try {
serviceState.service.notifyHardwareAdded(info);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHardwareAdded", e);
}
}
}
}
@Override
public void onHardwareDeviceRemoved(TvInputHardwareInfo info) {
synchronized (mLock) {
Slog.d(TAG, "onHardwareDeviceRemoved " + info);
UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
if (!serviceState.isHardware || serviceState.service == null) continue;
try {
serviceState.service.notifyHardwareRemoved(info);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHardwareRemoved", e);
}
}
}
}
@Override
public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
if (!serviceState.isHardware || serviceState.service == null) continue;
try {
serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
}
}
}
}
@Override
public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
if (!serviceState.isHardware || serviceState.service == null) continue;
try {
serviceState.service.notifyHdmiDeviceRemoved(deviceInfo);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
}
}
}
}
@Override
public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) {
synchronized (mLock) {
Integer state;
switch (deviceInfo.getDevicePowerStatus()) {
case HdmiControlManager.POWER_STATUS_ON:
state = INPUT_STATE_CONNECTED;
break;
case HdmiControlManager.POWER_STATUS_STANDBY:
case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON:
case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY:
state = INPUT_STATE_CONNECTED_STANDBY;
break;
case HdmiControlManager.POWER_STATUS_UNKNOWN:
default:
state = null;
break;
}
if (state != null) {
setStateLocked(inputId, state, mCurrentUserId);
}
}
}
}
在连接TvInputService的InputServiceConnection实现的onServiceConnected方法里面,会将TvInputService的BpBinder保存起来,并注册ServiceCallback。
private final class InputServiceConnection implements ServiceConnection {
private final ComponentName mComponent;
private final int mUserId;
private InputServiceConnection(ComponentName component, int userId) {
mComponent = component;
mUserId = userId;
}
@Override
public void onServiceConnected(ComponentName component, IBinder service) {
if (DEBUG) {
Slog.d(TAG, "onServiceConnected(component=" + component + ")");
}
synchronized (mLock) {
UserState userState = mUserStates.get(mUserId);
if (userState == null) {
// The user was removed while connecting.
mContext.unbindService(this);
return;
}
ServiceState serviceState = userState.serviceStateMap.get(mComponent);
serviceState.service = ITvInputService.Stub.asInterface(service);
// Register a callback, if we need to.
if (serviceState.isHardware && serviceState.callback == null) {
serviceState.callback = new ServiceCallback(mComponent, mUserId);
try {
serviceState.service.registerCallback(serviceState.callback);
} catch (RemoteException e) {
Slog.e(TAG, "error in registerCallback", e);
}
}
ServiceCallback的作用是,在TvInputManagerService通过onHdmiDeviceAdded调用到TvInputService以后,再回调给TvInputManagerService继续处理。
private final class ServiceCallback extends ITvInputServiceCallback.Stub {
private final ComponentName mComponent;
private final int mUserId;
ServiceCallback(ComponentName component, int userId) {
mComponent = component;
mUserId = userId;
}
private void ensureHardwarePermission() {
if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("The caller does not have hardware permission");
}
}
private void ensureValidInput(TvInputInfo inputInfo) {
if (inputInfo.getId() == null || !mComponent.equals(inputInfo.getComponent())) {
throw new IllegalArgumentException("Invalid TvInputInfo");
}
}
private void addHardwareInputLocked(TvInputInfo inputInfo) {
ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
serviceState.hardwareInputMap.put(inputInfo.getId(), inputInfo);
buildTvInputListLocked(mUserId, null);
}
public void addHardwareInput(int deviceId, TvInputInfo inputInfo) {
ensureHardwarePermission();
Slog.d(TAG, "addHardwareInput " + deviceId);
Exception ex = new Exception("addHardwareInput");
ex.printStackTrace();
ensureValidInput(inputInfo);
synchronized (mLock) {
mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo);
addHardwareInputLocked(inputInfo);
}
}
public void addHdmiInput(int id, TvInputInfo inputInfo) {
ensureHardwarePermission();
ensureValidInput(inputInfo);
synchronized (mLock) {
mTvInputHardwareManager.addHdmiInput(id, inputInfo);
addHardwareInputLocked(inputInfo);
}
}
public void removeHardwareInput(String inputId) {
ensureHardwarePermission();
Slog.d(TAG, "removeHardwareInput " + inputId);
Exception ex = new Exception("removeHardwareInput");
ex.printStackTrace();
synchronized (mLock) {
ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
if (removed) {
buildTvInputListLocked(mUserId, null);
mTvInputHardwareManager.removeHardwareInput(inputId);
} else {
Slog.e(TAG, "failed to remove input " + inputId);
}
}
}
}
连接TvInputService的位置,在TvInputManagerService createSession和removeSession时,都可能会重新建立或者移除和TvInputService的连接。
private void updateServiceConnectionLocked(ComponentName component, int userId) {
UserState userState = getOrCreateUserStateLocked(userId);
ServiceState serviceState = userState.serviceStateMap.get(component);
if (serviceState == null) {
return;
}
if (serviceState.reconnecting) {
if (!serviceState.sessionTokens.isEmpty()) {
// wait until all the sessions are removed.
return;
}
serviceState.reconnecting = false;
}
boolean shouldBind;
if (userId == mCurrentUserId) {
shouldBind = !serviceState.sessionTokens.isEmpty() || serviceState.isHardware;
} else {
// For a non-current user,
// if sessionTokens is not empty, it contains recording sessions only
// because other sessions must have been removed while switching user
// and non-recording sessions are not created by createSession().
shouldBind = !serviceState.sessionTokens.isEmpty();
}
if (serviceState.service == null && shouldBind) {
// This means that the service is not yet connected but its state indicates that we
// have pending requests. Then, connect the service.
if (serviceState.bound) {
// We have already bound to the service so we don't try to bind again until after we
// unbind later on.
return;
}
if (DEBUG) {
Slog.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")");
}
Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
serviceState.bound = mContext.bindServiceAsUser(
i, serviceState.connection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
new UserHandle(userId));
} else if (serviceState.service != null && !shouldBind) {
// This means that the service is already connected but its state indicates that we have
// nothing to do with it. Then, disconnect the service.
if (DEBUG) {
Slog.d(TAG, "unbindService(service=" + component + ")");
}
mContext.unbindService(serviceState.connection);
userState.serviceStateMap.remove(component);
}
}
下面看看TvInputService的处理。
5.TvInputService
Z:\p\frameworks\base\media\java\android\media\tv\TvInputService.java
@Override
public final IBinder onBind(Intent intent) {
return new ITvInputService.Stub() {
@Override
public void registerCallback(ITvInputServiceCallback cb) {
if (cb != null) {
mCallbacks.register(cb);
}
}
@Override
public void unregisterCallback(ITvInputServiceCallback cb) {
if (cb != null) {
mCallbacks.unregister(cb);
}
}
@Override
public void createSession(InputChannel channel, ITvInputSessionCallback cb,
String inputId) {
if (channel == null) {
Log.w(TAG, "Creating session without input channel");
}
if (cb == null) {
return;
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = channel;
args.arg2 = cb;
args.arg3 = inputId;
mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget();
}
@Override
public void createRecordingSession(ITvInputSessionCallback cb, String inputId) {
if (cb == null) {
return;
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = cb;
args.arg2 = inputId;
mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_RECORDING_SESSION, args)
.sendToTarget();
}
@Override
public void notifyHardwareAdded(TvInputHardwareInfo hardwareInfo) {
Log.d(TAG, "notifyHardwareAdded " + hardwareInfo);
mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HARDWARE_INPUT,
hardwareInfo).sendToTarget();
}
@Override
public void notifyHardwareRemoved(TvInputHardwareInfo hardwareInfo) {
Log.d(TAG, "notifyHardwareRemoved " + hardwareInfo);
mServiceHandler.obtainMessage(ServiceHandler.DO_REMOVE_HARDWARE_INPUT,
hardwareInfo).sendToTarget();
}
@Override
public void notifyHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
Log.d(TAG, "notifyHdmiDeviceAdded " + deviceInfo);
mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HDMI_INPUT,
deviceInfo).sendToTarget();
}
@Override
public void notifyHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
Log.d(TAG, "notifyHdmiDeviceRemoved " + deviceInfo);
mServiceHandler.obtainMessage(ServiceHandler.DO_REMOVE_HDMI_INPUT,
deviceInfo).sendToTarget();
}
};
}
首先是TvInputService具体的实现类重写onHdmiDeviceAdded方法,然后再是进行广播这个添加的TvInputInfo。
case DO_ADD_HDMI_INPUT: {
Log.d(TAG, "DO_ADD_HDMI_INPUT");
HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
TvInputInfo inputInfo = onHdmiDeviceAdded(deviceInfo);
if (inputInfo != null) {
broadcastAddHdmiInput(deviceInfo.getId(), inputInfo);
}
return;
}
ITvInputServiceCallback在TvInputManagerService建立和TvInputService的连接时就注册好了,所以下面其实是又交给TvInputManagerService进行后续的TvInputInfo相关的处理。
private void broadcastAddHdmiInput(int id, TvInputInfo inputInfo) {
int n = mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
try {
mCallbacks.getBroadcastItem(i).addHdmiInput(id, inputInfo);
} catch (RemoteException e) {
Log.e(TAG, "error in broadcastAddHdmiInput", e);
}
}
mCallbacks.finishBroadcast();
}
6.回到TvInputManagerService
首先是添加TvInputhardwareManager内部的设备信息,然后是再添加hardwareInput。
private final class ServiceCallback extends ITvInputServiceCallback.Stub {
public void addHdmiInput(int id, TvInputInfo inputInfo) {
ensureHardwarePermission();
ensureValidInput(inputInfo);
synchronized (mLock) {
mTvInputHardwareManager.addHdmiInput(id, inputInfo);
addHardwareInputLocked(inputInfo);
}
}
这里一样在添加完信息会可能会产生StateChange的回调,交给之前通过TvInputManager 注册的TvInputManagerCallback处理。
public void addHardwareInput(int deviceId, TvInputInfo info) {
synchronized (mLock) {
String oldInputId = mHardwareInputIdMap.get(deviceId);
if (oldInputId != null) {
Slog.w(TAG, "Trying to override previous registration: old = "
+ mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
+ info + ":" + deviceId);
}
mHardwareInputIdMap.put(deviceId, info.getId());
mInputMap.put(info.getId(), info);
// Process pending state changes
// For logical HDMI devices, they have information from HDMI CEC signals.
for (int i = 0; i < mHdmiStateMap.size(); ++i) {
TvInputHardwareInfo hardwareInfo =
findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
if (hardwareInfo == null) {
continue;
}
String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
if (inputId != null && inputId.equals(info.getId())) {
// No HDMI hotplug does not necessarily mean disconnected, as old devices may
// not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
// denote unknown state.
int state = mHdmiStateMap.valueAt(i)
? INPUT_STATE_CONNECTED
: INPUT_STATE_CONNECTED_STANDBY;
mHandler.obtainMessage(
ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
return;
}
}
// For the rest of the devices, we can tell by the cable connection status.
Connection connection = mConnections.get(deviceId);
if (connection != null) {
mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
connection.getInputStateLocked(), 0, info.getId()).sendToTarget();
}
}
}
TvInputManagerService内部则是会更新对应的ServiceState里面的TVInputInfo,并通过关键函数buildTvInputListLocked重建TvInput列表。
private void addHardwareInputLocked(TvInputInfo inputInfo) {
ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
serviceState.hardwareInputMap.put(inputInfo.getId(), inputInfo);
buildTvInputListLocked(mUserId, null);
}
不管是因为hdmi设备的增加,还是移除,都是完全重新的通过PackageManager重新查询所有的创建的TvInputService,如果有新添加的,如果没有就updateServiceConnectionLocked创建对应的ServiceState建立和它的binder连接就创建之,并且添加到保存的InputIdMap中。反之如果原来有现在没了,就移除之。最后触发notifyInputAddedLocked来通知观察者。
private void buildTvInputListLocked(int userId, String[] updatedPackages) {
UserState userState = getOrCreateUserStateLocked(userId);
userState.packageSet.clear();
if (DEBUG) Slog.d(TAG, "buildTvInputList");
Exception ex = new Exception("buildTvInputListLocked");
ex.printStackTrace();
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> services = pm.queryIntentServicesAsUser(
new Intent(TvInputService.SERVICE_INTERFACE),
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
userId);
List<TvInputInfo> inputList = new ArrayList<>();
for (ResolveInfo ri : services) {
ServiceInfo si = ri.serviceInfo;
if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
+ android.Manifest.permission.BIND_TV_INPUT);
continue;
}
ComponentName component = new ComponentName(si.packageName, si.name);
if (hasHardwarePermission(pm, component)) {
ServiceState serviceState = userState.serviceStateMap.get(component);
if (serviceState == null) {
// New hardware input found. Create a new ServiceState and connect to the
// service to populate the hardware list.
serviceState = new ServiceState(component, userId);
userState.serviceStateMap.put(component, serviceState);
updateServiceConnectionLocked(component, userId);
} else {
inputList.addAll(serviceState.hardwareInputMap.values());
}
} else {
try {
TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build();
inputList.add(info);
} catch (Exception e) {
Slog.e(TAG, "failed to load TV input " + si.name, e);
continue;
}
}
userState.packageSet.add(si.packageName);
}
Map<String, TvInputState> inputMap = new HashMap<>();
for (TvInputInfo info : inputList) {
if (DEBUG) {
Slog.d(TAG, "add " + info.getId());
}
TvInputState inputState = userState.inputMap.get(info.getId());
if (inputState == null) {
inputState = new TvInputState();
}
inputState.info = info;
inputMap.put(info.getId(), inputState);
}
for (String inputId : inputMap.keySet()) {
if (!userState.inputMap.containsKey(inputId)) {
notifyInputAddedLocked(userState, inputId);
} else if (updatedPackages != null) {
// Notify the package updates
ComponentName component = inputMap.get(inputId).info.getComponent();
for (String updatedPackage : updatedPackages) {
if (component.getPackageName().equals(updatedPackage)) {
updateServiceConnectionLocked(component, userId);
notifyInputUpdatedLocked(userState, inputId);
break;
}
}
}
}
for (String inputId : userState.inputMap.keySet()) {
if (!inputMap.containsKey(inputId)) {
TvInputInfo info = userState.inputMap.get(inputId).info;
ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
if (serviceState != null) {
abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId);
}
notifyInputRemovedLocked(userState, inputId);
}
}
userState.inputMap.clear();
userState.inputMap = inputMap;
}
这里就是通知新的InputId的添加和移除,也是ITvInputManagerCallback。
private void notifyInputAddedLocked(UserState userState, String inputId) {
if (DEBUG) {
Slog.d(TAG, "notifyInputAddedLocked(inputId=" + inputId + ")");
}
for (ITvInputManagerCallback callback : userState.callbackSet) {
try {
callback.onInputAdded(inputId);
} catch (RemoteException e) {
Slog.e(TAG, "failed to report added input to callback", e);
}
}
}
private void notifyInputRemovedLocked(UserState userState, String inputId) {
if (DEBUG) {
Slog.d(TAG, "notifyInputRemovedLocked(inputId=" + inputId + ")");
}
for (ITvInputManagerCallback callback : userState.callbackSet) {
try {
callback.onInputRemoved(inputId);
} catch (RemoteException e) {
Slog.e(TAG, "failed to report removed input to callback", e);
}
}
}
比如Input移除通知的栈
01-06 03:55:26.632 3640 4395 W System.err: java.lang.Exception: notifyInputRemovedLocked onInputRemoved com.droidlogic.tvinput/.services.Hdmi2InputService/HDMI240008
01-06 03:55:26.632 3640 4395 W System.err: at com.android.server.tv.TvInputManagerService.notifyInputRemovedLocked(TvInputManagerService.java:771)
01-06 03:55:26.632 3640 4395 W System.err: at com.android.server.tv.TvInputManagerService.buildTvInputListLocked(TvInputManagerService.java:344)
01-06 03:55:26.632 3640 4395 W System.err: at com.android.server.tv.TvInputManagerService.access$400(TvInputManagerService.java:102)
01-06 03:55:26.632 3640 4395 W System.err: at com.android.server.tv.TvInputManagerService$ServiceCallback.removeHardwareInput(TvInputManagerService.java:2426)
01-06 03:55:26.633 3640 4395 W System.err: at android.media.tv.ITvInputServiceCallback$Stub.onTransact(ITvInputServiceCallback.java:86)
01-06 03:55:26.633 3640 4395 W System.err: at android.os.Binder.execTransact(Binder.java:731)
添加的栈
01-07 00:04:04.584 3640 5236 W System.err: java.lang.Exception: notifyInputAddedLocked onInputAdded com.droidlogic.tvinput/.services.Hdmi1InputService/HDMI100008
01-07 00:04:04.584 3640 5236 W System.err: at com.android.server.tv.TvInputManagerService.notifyInputAddedLocked(TvInputManagerService.java:756)
01-07 00:04:04.584 3640 5236 W System.err: at com.android.server.tv.TvInputManagerService.buildTvInputListLocked(TvInputManagerService.java:323)
01-07 00:04:04.584 3640 5236 W System.err: at com.android.server.tv.TvInputManagerService.access$400(TvInputManagerService.java:102)
01-07 00:04:04.584 3640 5236 W System.err: at com.android.server.tv.TvInputManagerService$ServiceCallback.addHardwareInputLocked(TvInputManagerService.java:2393)
01-07 00:04:04.584 3640 5236 W System.err: at com.android.server.tv.TvInputManagerService$ServiceCallback.addHdmiInput(TvInputManagerService.java:2413)
01-07 00:04:04.584 3640 5236 W System.err: at android.media.tv.ITvInputServiceCallback$Stub.onTransact(ITvInputServiceCallback.java:78)
01-07 00:04:04.585 3640 5236 W System.err: at android.os.Binder.execTransact(Binder.java:731)