TvInputManagerService管理着系统的各种输入,TV Input主要分为三种类型:
-
hardware input:主要包含TV内建的各种输入端口,比如tuner、component, composite, hdmi。
-
非hardware input: 视频点播等非内建的硬件端口属于这种类型。
-
HDMI logic input:带有HDMI CEC的设备属于这种类型。
TvInputManagerService由SystemServer创建,我们先看看它的构造方法
public TvInputManagerService(Context context) {
super(context);
...
mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
synchronized (mLock) {
mUserStates.put(mCurrentUserId, new UserState(mContext, mCurrentUserId));
}
}
在构造方法中会创建一下TvInputHardwareManager实例,并传入一个HardwareListener实例给TvInputHardwareManager。TvInputHardwareManager通过TvInputHal来获取TV硬件输入的各种状态,并通过HardwareListener通知TvInputManagerService。
class TvInputHardwareManager implements TvInputHal.Callback
public interface Callback {
public void onDeviceAvailable(
TvInputHardwareInfo info, TvStreamConfig[] configs);
public void onDeviceUnavailable(int deviceId);
public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs);
public void onFirstFrameCaptured(int deviceId, int streamId);
}
public TvInputHardwareManager(Context context, Listener listener) {
mContext = context;
mListener = listener;
...
mHal.init();
}
TvInputHardwareManager实现了TvInputHal.Callback接口,在构造方法中调用mHal.init()对TvInputHal进行初始化,在TvInputHal初始化过程中,所有TV内建的Input都会通过onDeviceAvailable通知给TvInputHardWareManager,我们看一下onDeviceAvailable的实现:
<code class="hljs r has-numbering"> public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) { synchronized (mLock) { <span class="hljs-keyword">...</span> buildHardwareListLocked(); mHandler.obtainMessage( ListenerHandler.HARDWARE_DEVICE_ADDED, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, info).sendToTarget(); <span class="hljs-keyword">...</span> } } }</code>
onDeviceAvailable会调用buildHardwareListLocked把Tv Input的信息放入一个链表,TV Input的信息用TvInputHardwareInfo类表示,
<code class="hljs cs has-numbering"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">buildHardwareListLocked</span>() { mHardwareList.clear(); <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < mConnections.size(); ++i) { mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked()); } }</code>
然后通过mHandler.obtainMessage(ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget()发送到handler线程处理,
<code class="hljs r has-numbering"> public final void handleMessage(Message msg) { <span class="hljs-keyword">switch</span> (msg.what) { <span class="hljs-keyword">...</span> case HARDWARE_DEVICE_ADDED: { TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj; mListener.onHardwareDeviceAdded(info); <span class="hljs-keyword">break</span>; } <span class="hljs-keyword">...</span></code>
还记得TvInputManagerService在创建TvInputHardwareManager的时候传入的HardwareListener吗?它就是mListener, HardwareListener的onHardwareDeviceAdded会被调用,
<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onHardwareDeviceAdded</span>(TvInputHardwareInfo info) { <span class="hljs-keyword">synchronized</span> (mLock) { UserState userState = getUserStateLocked(mCurrentUserId); <span class="hljs-comment">// Broadcast the event to all hardware inputs.</span> <span class="hljs-keyword">for</span> (ServiceState serviceState : userState.serviceStateMap.values()) { <span class="hljs-keyword">if</span> (!serviceState.isHardware || serviceState.service == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">continue</span>; <span class="hljs-keyword">try</span> { serviceState.service.notifyHardwareAdded(info); } <span class="hljs-keyword">catch</span> (RemoteException e) { Slog.e(TAG, <span class="hljs-string">"error in notifyHardwareAdded"</span>, e); } } } }</code>
到目前为止,我们只是对TvInputManagerService的构造方法进行分析,userState.serviceStateMap还是空的,所以这个时候onHardwareDeviceAdded被调用其实什么事情都没有做。
我们接着分析TvInputManagerService的初始化过程:
<code class="hljs java has-numbering"> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onBootPhase</span>(<span class="hljs-keyword">int</span> phase) { <span class="hljs-keyword">if</span> (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { registerBroadcastReceivers(); } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { <span class="hljs-keyword">synchronized</span> (mLock) { buildTvInputListLocked(mCurrentUserId, <span class="hljs-keyword">null</span>); buildTvContentRatingSystemListLocked(mCurrentUserId); } } mTvInputHardwareManager.onBootPhase(phase); }</code>
当第三方的app可以启动的时候,即phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START,调用buildTvInputListLocked开始构建Tv Input List。
<code class="hljs cs has-numbering"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">buildTvInputListLocked</span>(<span class="hljs-keyword">int</span> userId, String[] updatedPackages) { UserState userState = getUserStateLocked(userId); userState.packageSet.clear(); <span class="hljs-keyword">if</span> (DEBUG) Slog.d(TAG, <span class="hljs-string">"buildTvInputList"</span>); PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> services = pm.queryIntentServices( <span class="hljs-keyword">new</span> Intent(TvInputService.SERVICE_INTERFACE), PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); ---<<span class="hljs-number">1</span>> List<TvInputInfo> inputList = <span class="hljs-keyword">new</span> ArrayList<TvInputInfo>(); <span class="hljs-keyword">for</span> (ResolveInfo ri : services) { ServiceInfo si = ri.serviceInfo; <span class="hljs-keyword">if</span> (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) { Slog.w(TAG, <span class="hljs-string">"Skipping TV input "</span> + si.name + <span class="hljs-string">": it does not require the permission "</span> + android.Manifest.permission.BIND_TV_INPUT); <span class="hljs-keyword">continue</span>; } ComponentName component = <span class="hljs-keyword">new</span> ComponentName(si.packageName, si.name); <span class="hljs-keyword">if</span> (hasHardwarePermission(pm, component)) { ServiceState serviceState = userState.serviceStateMap.<span class="hljs-keyword">get</span>(component); <span class="hljs-keyword">if</span> (serviceState == <span class="hljs-keyword">null</span>) { <span class="hljs-comment">// We see this hardware TV input service for the first time; we need to</span> <span class="hljs-comment">// prepare the ServiceState object so that we can connect to the service and</span> <span class="hljs-comment">// let it add TvInputInfo objects to mInputList if there's any.</span> serviceState = <span class="hljs-keyword">new</span> ServiceState(component, userId); ---<<span class="hljs-number">2</span>> userState.serviceStateMap.put(component, serviceState); updateServiceConnectionLocked(component, userId); ---<<span class="hljs-number">3</span>> } <span class="hljs-keyword">else</span> { inputList.addAll(serviceState.inputList); ---<<span class="hljs-number">4</span>> } } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">try</span> { inputList.add(TvInputInfo.createTvInputInfo(mContext, ri));---<<span class="hljs-number">5</span>> } <span class="hljs-keyword">catch</span> (XmlPullParserException | IOException e) { Slog.e(TAG, <span class="hljs-string">"failed to load TV input "</span> + si.name, e); <span class="hljs-keyword">continue</span>; } } userState.packageSet.add(si.packageName); } Map<String, TvInputState> inputMap = <span class="hljs-keyword">new</span> HashMap<String, TvInputState>(); ---<<span class="hljs-number">6</span>> <span class="hljs-keyword">for</span> (TvInputInfo info : inputList) { <span class="hljs-keyword">if</span> (DEBUG) { Slog.d(TAG, <span class="hljs-string">"add "</span> + info.getId()); } TvInputState state = userState.inputMap.<span class="hljs-keyword">get</span>(info.getId()); <span class="hljs-keyword">if</span> (state == <span class="hljs-keyword">null</span>) { state = <span class="hljs-keyword">new</span> TvInputState(); } state.info = info; inputMap.put(info.getId(), state); } <span class="hljs-keyword">for</span> (String inputId : inputMap.keySet()) { <span class="hljs-keyword">if</span> (!userState.inputMap.containsKey(inputId)) { ---<<span class="hljs-number">7</span>> notifyInputAddedLocked(userState, inputId); } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (updatedPackages != <span class="hljs-keyword">null</span>) { <span class="hljs-comment">// Notify the package updates</span> ComponentName component = inputMap.<span class="hljs-keyword">get</span>(inputId).info.getComponent(); <span class="hljs-keyword">for</span> (String updatedPackage : updatedPackages) { <span class="hljs-keyword">if</span> (component.getPackageName().equals(updatedPackage)) { updateServiceConnectionLocked(component, userId); notifyInputUpdatedLocked(userState, inputId); <span class="hljs-keyword">break</span>; } } } } <span class="hljs-keyword">for</span> (String inputId : userState.inputMap.keySet()) { <span class="hljs-keyword">if</span> (!inputMap.containsKey(inputId)) { ---<<span class="hljs-number">8</span>> TvInputInfo info = userState.inputMap.<span class="hljs-keyword">get</span>(inputId).info; ServiceState serviceState = userState.serviceStateMap.<span class="hljs-keyword">get</span>(info.getComponent()); <span class="hljs-keyword">if</span> (serviceState != <span class="hljs-keyword">null</span>) { abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId); } notifyInputRemovedLocked(userState, inputId); } } userState.inputMap.clear(); userState.inputMap = inputMap; ---<<span class="hljs-number">9</span>> }</code>
这个方法比较长,关键行标上了序号,下面逐一解释:
1、查找所有的package中的service,如果package的service在AndroidManifest.xml中声明了如下属性:
<code class="hljs avrasm has-numbering"><span class="hljs-label">android:</span>permission=<span class="hljs-string">"android.permission.BIND_TV_INPUT"</span></code>那么就认为这个service代表一种Tv Input,这些service都继承自TvInputService。对于TV内建的输入端口,还要在package的AndroidManifest.xml声明
<code class="hljs xml has-numbering"><span class="hljs-tag"><<span class="hljs-title">uses-permission</span> <span class="hljs-attribute">android:name</span>=<span class="hljs-value">"android.permission.TV_INPUT_HARDWARE"</span> /></span></code>2、对于内建的硬件输入端口,TvInputManangerService会创建对应的ServiceState实例,并跟对应的Service建立连接,
<code class="hljs cs has-numbering"> <span class="hljs-keyword">private</span> <span class="hljs-title">ServiceState</span>(ComponentName component, <span class="hljs-keyword">int</span> userId) { <span class="hljs-keyword">this</span>.component = component; <span class="hljs-keyword">this</span>.connection = <span class="hljs-keyword">new</span> InputServiceConnection(component, userId); <span class="hljs-keyword">this</span>.isHardware = hasHardwarePermission(mContext.getPackageManager(), component); }</code>注意this.connection = new InputServiceConnection(component, userId),稍后会用到。
<code class="hljs r has-numbering"> private void updateServiceConnectionLocked(ComponentName component, int userId) { <span class="hljs-keyword">...</span> Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component); serviceState.bound = mContext.bindServiceAsUser( i, serviceState.connection, Context.BIND_AUTO_CREATE, new UserHandle(userId)); <span class="hljs-keyword">...</span> }</code>3.在updateServiceConnectionLocked中通过Intent跟service建立连接,这个时候inputList是空的,buildTvInputListLocked继续执行返回。当跟service连接成功以后,serviceState.connection.onServiceConnected会被回调,就是前面说的InputServiceConnection.onServiceConnected。
<code class="hljs r has-numbering">public void onServiceConnected(ComponentName component, IBinder service) { <span class="hljs-keyword">...</span> // Register a callback, <span class="hljs-keyword">if</span> we need to. <span class="hljs-keyword">if</span> (serviceState.isHardware && serviceState.callback == null) { serviceState.callback = new ServiceCallback(mComponent, mUserId); <span class="hljs-keyword">try</span> { serviceState.service.registerCallback(serviceState.callback); } catch (RemoteException e) { Slog.e(TAG, <span class="hljs-string">"error in registerCallback"</span>, e); } } <span class="hljs-keyword">...</span> <span class="hljs-keyword">if</span> (serviceState.isHardware) { List<TvInputHardwareInfo> hardwareInfoList = mTvInputHardwareManager.getHardwareList(); <span class="hljs-keyword">for</span> (TvInputHardwareInfo hardwareInfo : hardwareInfoList) { <span class="hljs-keyword">try</span> { serviceState.service.notifyHardwareAdded(hardwareInfo); } catch (RemoteException e) { Slog.e(TAG, <span class="hljs-string">"error in notifyHardwareAdded"</span>, e); } } List<HdmiDeviceInfo> deviceInfoList = mTvInputHardwareManager.getHdmiDeviceList(); <span class="hljs-keyword">for</span> (HdmiDeviceInfo deviceInfo : deviceInfoList) { <span class="hljs-keyword">try</span> { serviceState.service.notifyHdmiDeviceAdded(deviceInfo); } catch (RemoteException e) { Slog.e(TAG, <span class="hljs-string">"error in notifyHdmiDeviceAdded"</span>, e); } } } } }</code>
接着会向Service注册callback,后面会用到。然后通过mTvInputHardwareManager.getHardwareList获取之前初始化的时候保存的Hardware list,调用service的notifyHardwareAdded方法,我们看一下TvInputService的notifyHardwareAdded实现:
<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">notifyHardwareAdded</span>(TvInputHardwareInfo hardwareInfo) { mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HARDWARE_TV_INPUT, hardwareInfo).sendToTarget(); }</code>通知Handler线程DO_ADD_HARDWARE_TV_INPUT,
<code class="hljs r has-numbering">public final void handleMessage(Message msg) { <span class="hljs-keyword">switch</span> (msg.what) { <span class="hljs-keyword">...</span> case DO_ADD_HARDWARE_TV_INPUT: { TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj; TvInputInfo inputInfo = onHardwareAdded(hardwareInfo); <span class="hljs-keyword">if</span> (inputInfo != null) { broadcastAddHardwareTvInput(hardwareInfo.getDeviceId(), inputInfo); } <span class="hljs-keyword">return</span>; } <span class="hljs-keyword">...</span></code>然后service的onHardwareAdded方法被调用,并返回TvInputInfo,这代表一个TV Input,然后通过broadcastAddHardwareTvInput通知TvInputManagerService,
<code class="hljs cs has-numbering"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">broadcastAddHardwareTvInput</span>(<span class="hljs-keyword">int</span> deviceId, TvInputInfo inputInfo) { <span class="hljs-keyword">int</span> n = mCallbacks.beginBroadcast(); <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < n; ++i) { <span class="hljs-keyword">try</span> { mCallbacks.getBroadcastItem(i).addHardwareTvInput(deviceId, inputInfo); } <span class="hljs-keyword">catch</span> (RemoteException e) { Log.e(TAG, <span class="hljs-string">"Error while broadcasting."</span>, e); } } mCallbacks.finishBroadcast(); }</code>
我们在跟service建立连接以后,注册了callback,callback的addHardwareTvInput被调用,我们看一下TvInputManagerService中addHardwareTvInput的实现,
<code class="hljs java has-numbering"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addTvInputLocked</span>(TvInputInfo inputInfo) { ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); serviceState.inputList.add(inputInfo); buildTvInputListLocked(mUserId, <span class="hljs-keyword">null</span>); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addHardwareTvInput</span>(<span class="hljs-keyword">int</span> deviceId, TvInputInfo inputInfo) { ensureHardwarePermission(); ensureValidInput(inputInfo); <span class="hljs-keyword">synchronized</span> (mLock) { mTvInputHardwareManager.addHardwareTvInput(deviceId, inputInfo); addTvInputLocked(inputInfo); } }</code>addHardwareTvInput会调用addTvInputLocked,addTvInputLocked把TvInputInfo存入serviceState.inputList,接着调用buildTvInputListLocked重新构建Tv input list。
4.再次调用buildTvInputListLocked的时候,serviceState不为null,把serviceState.inputList放入到inputList。
5. 如果不是TV内建的硬件Input,直接创建TvInputInfo并放入InputList。
6. 每一个Tv Input都对应一个TvInputState,通过inputId在inputMap中索引。
7. 如果新构建的inputMap中的inputId在userState.inputMap中没有,表明这个TV input是新增加的,如果TV app有注册callback,那么TV app的onInputAdded会被调用。
8. 如果userState.inputMap中的inputId在新构建的inputMap中没有,表明这个TV Input被移除,如果TV app有注册callback,那么TV app的onInputRemoved会被调用。
9. userState.inputMap被新构建的inputMap替换,至此TV input list构建完成。