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構建完成。
Tags: Android interface hardware public super
文章來源: