这部分主要介绍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>}

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

  1. 查找所有的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";
    }
    

  2. 对于内建的硬件输入端口,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相关推荐

  1. Android TV Input Framework(Android TV 一)

    前言 Android TV是Android 5.0新的内容,当前国内的智能电视大部分都是基于Android系统的,Android TV作为事实上的标准,它的推出必将极大的影响下一代智能电视的开发. 近 ...

  2. Android TV框架 TIF(Android TV Input Framework)入门实践

    Tamic/CSDN http://blog.csdn.net/sk719887916/article/details/53645615 做TV开发一段时间了,国内目前关于这方面的资料并不多,这里我来 ...

  3. Android知识点 200 —— framework/base/cmds 常见的am命令,input,pm命令

    文章原文:http://www.360doc.com/content/11/0510/00/4154133_115595135.shtml 返回知识列表:Android知识点list /framewo ...

  4. [译]利用 Android 构建 TV 的未来

    原文地址:Building for the future of TV with Android 原文作者:Rachel Berk 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/ ...

  5. android studio3.12,Android Studio V3.12环境下TV开发教程(六)提供卡片视图

    Android Studio V3.12环境下TV开发教程 文章源自:光谷佳武 https://blog.csdn.net/jiawuhan/article/details/80619656 在上一课 ...

  6. Android电视切换回放,Android Studio V3.12环境下TV开发教程(五)建立电视回放应用...

    Android Studio V3.12环境下TV开发教程 文章源自:光谷佳武 https://blog.csdn.net/jiawuhan/article/details/80619382 浏览和播 ...

  7. Android WebView 支持H5图片上传input type=file

    2019独角兽企业重金招聘Python工程师标准>>> Android WebView 缓存处理 Android WebView 支持H5图片上传<input type=&qu ...

  8. 分析Android引起的“电视智能化”之TV用UI的现状与未来

    转自:http://www.kokojia.com/article/18560.html Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由G ...

  9. Android模拟器学framework和driver之传感器篇3(Android HAL)

    Android模拟器学framework和driver之传感器篇3(Android HAL) 前面,带着大家一起写了一个temperature sensor的驱动,已经一个测试tool来测试这个驱动, ...

  10. Android 通过python实现自动化构建打包上传加固

    Android 通过python实现自动化构建打包上传加固 实现需求: 基于gradle命令,通过python实现多渠道,多环境打包,上传蒲公英,360加固等 经过学习调研完成步骤如下: 一,通过gr ...

最新文章

  1. mysql 查询不使用索引_简单的mySQL查询不使用索引
  2. 【ubuntu-qt-dlib】 配置问题 (一) undefined reference to `XPending'
  3. 陈省身数学奖获得者张继平:怎样才能学好数学?
  4. win8 apache php mysql_windows8.1下Apache+Php+MySQL配置步骤
  5. 【OpenCV】OpenCV函数精讲之 -- 命名空间
  6. java 工作一年_干java工作了快一年,到底会了什么
  7. 优先队列-二叉堆-堆排序原理-Java相关API
  8. java 托管 非托管_java jni调用 非托管 dll
  9. [转载] python计时函数timeit.timeit()使用小结
  10. 大数据 数据库 评测_中国信通院公布第九批大数据产品能力评测结果,65款产品通过...
  11. 计算一幅图像的信噪比
  12. 10.2 项目干系人管理
  13. 使用pycharm出现黄色框的情况
  14. java纲要_幼儿园综合素质笔试大纲
  15. 深度学习系统框架的演进趋势
  16. 目前计算机无法显示的四叠字,四叠字列表(共19个),还能用的四叠字大全,带拼音,部分注释!...
  17. 互联网思维——迭代思维
  18. MATLAB——新建、删除或移动文件夹
  19. 2017计算机研究生专业排名,2017年USNews美国大学计算机硕士研究生专业排名TOP110...
  20. 2C4T与4C4T在计算密集型任务下的效率对比

热门文章

  1. 人类的终极目标是什么?
  2. 纤亿通谈-单模和多模光纤跳线有哪些不同之处?
  3. 为伊消得人憔悴,衣带渐宽终不悔(2)
  4. awgn matlab,Matlab实现加性高斯白噪声信道(AWGN)下的digital调制格式识别分类
  5. String的intern()详解
  6. Excel 冻结窗格
  7. To King Cover
  8. unity的立方贴图
  9. 按蚂蚁金服面试不过,就因为不会RPC服务超时排查思路?
  10. 使用Arduino开发板和颜色传感器区分不同颜色