您的位置:首页 > 移动开发 > Android开发

Android TV Input Framework(TIF)--2 构建TV input list

2016-01-25 20:39 746 查看
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的实现:

public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
synchronized (mLock) {
...
buildHardwareListLocked();
mHandler.obtainMessage(
ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
...
}
}
}


onDeviceAvailable会调用buildHardwareListLocked把Tv Input的信息放入一个链表,TV Input的信息用TvInputHardwareInfo类表示,

private void buildHardwareListLocked() {
mHardwareList.clear();
for (int i = 0; i < mConnections.size(); ++i) {
mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
}
}


然后通过mHandler.obtainMessage(ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget()发送到handler线程处理,

public final void handleMessage(Message msg) {
switch (msg.what) {
...
case HARDWARE_DEVICE_ADDED: {
TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
mListener.onHardwareDeviceAdded(info);
break;
}
...


还记得TvInputManagerService在创建TvInputHardwareManager的时候传入的HardwareListener吗?它就是mListener, HardwareListener的onHardwareDeviceAdded会被调用,

public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
synchronized (mLock) {
UserState userState = getUserStateLocked(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);
}
}
}
}


到目前为止,我们只是对TvInputManagerService的构造方法进行分析,userState.serviceStateMap还是空的,所以这个时候onHardwareDeviceAdded被调用其实什么事情都没有做。

我们接着分析TvInputManagerService的初始化过程,

public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
registerBroadcastReceivers();
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
synchronized (mLock) {
buildTvInputListLocked(mCurrentUserId, null);
buildTvContentRatingSystemListLocked(mCurrentUserId);
}
}
mTvInputHardwareManager.onBootPhase(phase);
}


当第三方的app可以启动的时候,即phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START,调用buildTvInputListLocked开始构建Tv Input List。

private void buildTvInputListLocked(int userId, String[] updatedPackages) {
UserState userState = getUserStateLocked(userId);
userState.packageSet.clear();

if (DEBUG) Slog.d(TAG, "buildTvInputList");
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> services = pm.queryIntentServices(
new Intent(TvInputService.SERVICE_INTERFACE),
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);                       ---<1>
List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
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) {
// We see this hardware TV input service for the first time; we need to
// prepare the ServiceState object so that we can connect to the service and
// let it add TvInputInfo objects to mInputList if there's any.
serviceState = new ServiceState(component, userId);                     ---<2>
userState.serviceStateMap.put(component, serviceState);
updateServiceConnectionLocked(component, userId);    ---<3>
} else {
inputList.addAll(serviceState.inputList);            ---<4>
}
} else {
try {
inputList.add(TvInputInfo.createTvInputInfo(mContext, ri));---<5>
} catch (XmlPullParserException | IOException e) {
Slog.e(TAG, "failed to load TV input " + si.name, e);
continue;
}
}
userState.packageSet.add(si.packageName);
}

Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>();             ---<6>
for (TvInputInfo info : inputList) {
if (DEBUG) {
Slog.d(TAG, "add " + info.getId());
}
TvInputState state = userState.inputMap.get(info.getId());
if (state == null) {
state = new TvInputState();
}
state.info = info;
inputMap.put(info.getId(), state);
}

for (String inputId : inputMap.keySet()) {
if (!userState.inputMap.containsKey(inputId)) {              ---<7>
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)) {    ---<8>
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;               ---<9>
}


这个方法比较长,关键行标上了序号,下面逐一解释:

查找所有的package中的service,如果package的service在AndroidManifest.xml中声明了如下属性:

android:permission="android.permission.BIND_TV_INPUT"


那么就认为这个service代表一种Tv Input,这些service都继承自TvInputService。对于TV内建的输入端口,还要在package的AndroidManifest.xml声明

<uses-permission android:name="android.permission.TV_INPUT_HARDWARE" />


对于内建的硬件输入端口,TvInputManangerService会创建对应的ServiceState实例,并跟对应的Service建立连接,

private ServiceState(ComponentName component, int userId) {
this.component = component;
this.connection = new InputServiceConnection(component, userId);
this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
}


注意this.connection = new InputServiceConnection(component, userId),稍后会用到。

private void updateServiceConnectionLocked(ComponentName component, int userId) {
...
Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
serviceState.bound = mContext.bindServiceAsUser(
i, serviceState.connection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
...
}


3.在updateServiceConnectionLocked中通过Intent跟service建立连接,这个时候inputList是空的,buildTvInputListLocked继续执行返回。当跟service连接成功以后,serviceState.connection.onServiceConnected会被回调,就是前面说的InputServiceConnection.onServiceConnected。

public void onServiceConnected(ComponentName component, IBinder 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);
}
}
...
if (serviceState.isHardware) {
List<TvInputHardwareInfo> hardwareInfoList =
mTvInputHardwareManager.getHardwareList();
for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) {
try {
serviceState.service.notifyHardwareAdded(hardwareInfo);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHardwareAdded", e);
}
}

List<HdmiDeviceInfo> deviceInfoList =
mTvInputHardwareManager.getHdmiDeviceList();
for (HdmiDeviceInfo deviceInfo : deviceInfoList) {
try {
serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
} catch (RemoteException e) {
Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
}
}
}
}
}


接着会向Service注册callback,后面会用到。然后通过mTvInputHardwareManager.getHardwareList获取之前初始化的时候保存的Hardware list,调用service的notifyHardwareAdded方法,我们看一下TvInputService的notifyHardwareAdded实现:

public void notifyHardwareAdded(TvInputHardwareInfo hardwareInfo) {
mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HARDWARE_TV_INPUT,
hardwareInfo).sendToTarget();
}


通知Handler线程DO_ADD_HARDWARE_TV_INPUT,

public final void handleMessage(Message msg) {
switch (msg.what) {
...
case DO_ADD_HARDWARE_TV_INPUT: {
TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj;
TvInputInfo inputInfo = onHardwareAdded(hardwareInfo);
if (inputInfo != null) {
broadcastAddHardwareTvInput(hardwareInfo.getDeviceId(), inputInfo);
}
return;
}
...


然后service的onHardwareAdded方法被调用,并返回TvInputInfo,这代表一个TV Input,然后通过broadcastAddHardwareTvInput通知TvInputManagerService,

private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
int n = mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
try {
mCallbacks.getBroadcastItem(i).addHardwareTvInput(deviceId, inputInfo);
} catch (RemoteException e) {
Log.e(TAG, "Error while broadcasting.", e);
}
}
mCallbacks.finishBroadcast();
}


我们在跟service建立连接以后,注册了callback,callback的addHardwareTvInput被调用,我们看一下TvInputManagerService中addHardwareTvInput的实现,

private void addTvInputLocked(TvInputInfo inputInfo) {
ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
serviceState.inputList.add(inputInfo);
buildTvInputListLocked(mUserId, null);
}

@Override
public void addHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
ensureHardwarePermission();
ensureValidInput(inputInfo);
synchronized (mLock) {
mTvInputHardwareManager.addHardwareTvInput(deviceId, inputInfo);
addTvInputLocked(inputInfo);
}
}


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构建完成。

下面用序列图来展示一下整个过程

Created with Raphaël 2.1.0TvInputManagerServiceTvInputManagerServiceTvInputHardwareManagerTvInputHardwareManagerTvInputHalTvInputHalTvInputServiceTvInputServiceinitdeviceAvailableFromNativeonDeviceAvaliablebuildHardwareListLockedonHardwareDeviceAddedbuildTvInputListLockedupdateServiceConnectionLockedmContext.bindServiceAsUseronServiceConnectedregisterCallbackgetHardwareListnotifyHardwareAddedonHardwareAddedbroadcastAddHardwareTvInputaddHardwareTvInputaddTvInputLockedbuildTvInputListLockednotifyHdmiDeviceAddedonHdmiDeviceAddedbroadcastAddHdmiTvInputaddHdmiTvInputaddTvInputLockedbuildTvInputListLocked
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: