Android TV Input Framework(TIF)--构建TV input list
这部分主要介绍tvinput是怎么来的,源码目录:frameworks/base/services/core/java/com/android/server/tv
TvInputManagerService治理着体系的种种输进,TV Input首要分为三品种型:hardware input:首要包括TV内建的种种输进端心,比方tuner、component, composite, hdmi。非hdmi
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.Callbackpublic 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 updatesComponentName 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" /> <service android:name="com.montage.tvinput.dvb.DvbTvInputService" android:enabled="true" android:permission="android.permission.BIND_TV_INPUT" android:protectionLevel="signatureOrSystem" android:exported="true"> <intent-filter> <action android:name="android.media.tv.TvInputService" /> </intent-filter> <meta-data android:name="android.media.tv.input" android:resource="@xml/dvbc_tv_input" /> </service> /*******************************/ TvInputService { public static final String SERVICE_INTERFACE = "android.media.tv.TvInputService"; public static final String SERVICE_META_DATA = "android.media.tv.input"; }
对于内建的硬件输入端口,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);}@Overridepublic 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构建完成。
备注:在原文章的基础上会对某些地方进行补充和扩展。
原文章:http://www.07net01.com/program/2016/02/1276341.html;
Android TV Input Framework(TIF)--构建TV input list相关推荐
- Android TV Input Framework(Android TV 一)
前言 Android TV是Android 5.0新的内容,当前国内的智能电视大部分都是基于Android系统的,Android TV作为事实上的标准,它的推出必将极大的影响下一代智能电视的开发. 近 ...
- Android TV框架 TIF(Android TV Input Framework)入门实践
Tamic/CSDN http://blog.csdn.net/sk719887916/article/details/53645615 做TV开发一段时间了,国内目前关于这方面的资料并不多,这里我来 ...
- Android知识点 200 —— framework/base/cmds 常见的am命令,input,pm命令
文章原文:http://www.360doc.com/content/11/0510/00/4154133_115595135.shtml 返回知识列表:Android知识点list /framewo ...
- [译]利用 Android 构建 TV 的未来
原文地址:Building for the future of TV with Android 原文作者:Rachel Berk 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/ ...
- android studio3.12,Android Studio V3.12环境下TV开发教程(六)提供卡片视图
Android Studio V3.12环境下TV开发教程 文章源自:光谷佳武 https://blog.csdn.net/jiawuhan/article/details/80619656 在上一课 ...
- Android电视切换回放,Android Studio V3.12环境下TV开发教程(五)建立电视回放应用...
Android Studio V3.12环境下TV开发教程 文章源自:光谷佳武 https://blog.csdn.net/jiawuhan/article/details/80619382 浏览和播 ...
- Android WebView 支持H5图片上传input type=file
2019独角兽企业重金招聘Python工程师标准>>> Android WebView 缓存处理 Android WebView 支持H5图片上传<input type=&qu ...
- 分析Android引起的“电视智能化”之TV用UI的现状与未来
转自:http://www.kokojia.com/article/18560.html Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由G ...
- Android模拟器学framework和driver之传感器篇3(Android HAL)
Android模拟器学framework和driver之传感器篇3(Android HAL) 前面,带着大家一起写了一个temperature sensor的驱动,已经一个测试tool来测试这个驱动, ...
- Android 通过python实现自动化构建打包上传加固
Android 通过python实现自动化构建打包上传加固 实现需求: 基于gradle命令,通过python实现多渠道,多环境打包,上传蒲公英,360加固等 经过学习调研完成步骤如下: 一,通过gr ...
最新文章
- mysql 查询不使用索引_简单的mySQL查询不使用索引
- 【ubuntu-qt-dlib】 配置问题 (一) undefined reference to `XPending'
- 陈省身数学奖获得者张继平:怎样才能学好数学?
- win8 apache php mysql_windows8.1下Apache+Php+MySQL配置步骤
- 【OpenCV】OpenCV函数精讲之 -- 命名空间
- java 工作一年_干java工作了快一年,到底会了什么
- 优先队列-二叉堆-堆排序原理-Java相关API
- java 托管 非托管_java jni调用 非托管 dll
- [转载] python计时函数timeit.timeit()使用小结
- 大数据 数据库 评测_中国信通院公布第九批大数据产品能力评测结果,65款产品通过...
- 计算一幅图像的信噪比
- 10.2 项目干系人管理
- 使用pycharm出现黄色框的情况
- java纲要_幼儿园综合素质笔试大纲
- 深度学习系统框架的演进趋势
- 目前计算机无法显示的四叠字,四叠字列表(共19个),还能用的四叠字大全,带拼音,部分注释!...
- 互联网思维——迭代思维
- MATLAB——新建、删除或移动文件夹
- 2017计算机研究生专业排名,2017年USNews美国大学计算机硕士研究生专业排名TOP110...
- 2C4T与4C4T在计算密集型任务下的效率对比