从Hal开始,具体的code不贴了,只贴下接口。

1.注册接口

hardware\libhardware\include\hardware\hdmi_cec.h

    /** (*register_event_callback)() registers a callback that HDMI-CEC HAL* can later use for incoming CEC messages or internal HDMI events.* When calling from C++, use the argument arg to pass the calling object.* It will be passed back when the callback is invoked so that the context* can be retrieved.*/void (*register_event_callback)(const struct hdmi_cec_device* dev,event_callback_t callback, void* arg);

2.IHdmiCec callback接口回调实现

Z:\p\hardware\interfaces\tv\cec\1.0\default\HdmiCec.h

如果是HDMI_EVENT_HOT_PLUG,则会带上connected boolean值和对应的portId。如果是盒子的话,portId就是1,TV的话就是从1-4.

    static void eventCallback(const hdmi_event_t* event, void* /* arg */) {if (mCallback != nullptr && event != nullptr) {if (event->type == HDMI_EVENT_CEC_MESSAGE) {size_t length = std::min(event->cec.length,static_cast<size_t>(MaxLength::MESSAGE_BODY));CecMessage cecMessage {.initiator = static_cast<CecLogicalAddress>(event->cec.initiator),.destination = static_cast<CecLogicalAddress>(event->cec.destination),};cecMessage.body.resize(length);for (size_t i = 0; i < length; ++i) {cecMessage.body[i] = static_cast<uint8_t>(event->cec.body[i]);}mCallback->onCecMessage(cecMessage);} else if (event->type == HDMI_EVENT_HOT_PLUG) {HotplugEvent hotplugEvent {.connected = event->hotplug.connected > 0,.portId = static_cast<uint32_t>(event->hotplug.port_id)};mCallback->onHotplugEvent(hotplugEvent);}}}

3.android framework jni

会访问到java侧的HdmiCecController的handleHotplug方法。

Z:\p\frameworks\base\services\core\jni\com_android_server_hdmi_HdmiCecController.cpp

Return<void> HdmiCecController::HdmiCecCallback::onHotplugEvent(const HotplugEvent& event) {sp<HdmiCecEventHandler> handler(new HdmiCecEventHandler(mController, event));mController->mLooper->sendMessage(handler, HdmiCecEventHandler::EventType::HOT_PLUG);return Void();
}
    void propagateHotplugEvent(const HotplugEvent& event) {// Note that this method should be called in service thread.JNIEnv* env = AndroidRuntime::getJNIEnv();jint port = static_cast<jint>(event.portId);jboolean connected = (jboolean) event.connected;env->CallVoidMethod(mController->getCallbacksObj(),gHdmiCecControllerClassInfo.handleHotplug, port, connected);checkAndClearExceptionFromCallback(env, __FUNCTION__);}

4. HdmiCecController

Z:\p\frameworks\base\services\core\java\com\android\server\hdmi\HdmiCecController.java

在打开log.tag.HDMI=DEBUG开关以后,会有port和connected的打印。

    /*** Called by native when a hotplug event issues.*/@ServiceThreadOnlyprivate void handleHotplug(int port, boolean connected) {assertRunOnServiceThread();HdmiLogger.debug("Hotplug event:[port:%d, connected:%b]", port, connected);mService.onHotplug(port, connected);}

5.HdmiControlService

①如果不是tv,就重新分配逻辑地址。

②调用HdmiCecLocalDevice的onHotplug回调,tv会调用HotplugDetectionAction进行设备列表的处理,其他类型的设备则可能会进行唤醒的处理。

③广播HotplugEvent。

广播HotplugEvent是后续事件传输过程的关键,因为下面会交给TIF来处理,更新TvInputManagerService里面Hdmi和Hardware相关设备的更新和对应接口add、remove的回调。

    /*** Called when a new hotplug event is issued.** @param portId hdmi port number where hot plug event issued.* @param connected whether to be plugged in or not*/@ServiceThreadOnlyvoid onHotplug(int portId, boolean connected) {assertRunOnServiceThread();if (connected && !isTvDevice()) {ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();for (int type : mLocalDevices) {HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);if (localDevice == null) {localDevice = HdmiCecLocalDevice.create(this, type);localDevice.init();}localDevices.add(localDevice);}allocateLogicalAddress(localDevices, INITIATED_BY_HOTPLUG);}for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {device.onHotplug(portId, connected);}announceHotplugEvent(portId, connected);}
    private void announceHotplugEvent(int portId, boolean connected) {HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);synchronized (mLock) {for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {invokeHotplugEventListenerLocked(record.mListener, event);}}}
        @Overridepublic void addHotplugEventListener(final IHdmiHotplugEventListener listener) {enforceAccessPermission();HdmiControlService.this.addHotplugEventListener(listener);}
private void addHotplugEventListener(final IHdmiHotplugEventListener listener) {final HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);try {listener.asBinder().linkToDeath(record, 0);} catch (RemoteException e) {Slog.w(TAG, "Listener already died");return;}synchronized (mLock) {mHotplugEventListenerRecords.add(record);}// Inform the listener of the initial state of each HDMI port by generating// hotplug events.runOnServiceThread(new Runnable() {@Overridepublic void run() {synchronized (mLock) {if (!mHotplugEventListenerRecords.contains(record)) return;}for (HdmiPortInfo port : mPortInfo) {HdmiHotplugEvent event = new HdmiHotplugEvent(port.getId(),mCecController.isConnected(port.getId()));synchronized (mLock) {invokeHotplugEventListenerLocked(listener, event);}}}});}

6.TvInputHardwareManager添加回调以及回调实现

Z:\p\frameworks\base\services\core\java\com\android\server\tv\TvInputHardwareManager.java

    public void onBootPhase(int phase) {if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {IHdmiControlService hdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(Context.HDMI_CONTROL_SERVICE));if (hdmiControlService != null) {try {hdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);......}} }

如果这个Hdmi设备有对应的TVInputHardwareInfo,一般都是有的啦。dumpsys tv_input就会看到从hdmi_port, 就能找到对应的deviceId,再从deviceId找到InputId,这样就完成HDMIDeviceInfo《-》TVInputInfo的过渡了。

  mHardwareList:TvInputHardwareInfo {id=1, type=3, audio_type=-2147450880, audio_addr=, cable_connection_status=0}TvInputHardwareInfo {id=5, type=9, audio_type=-2147483616, audio_addr=, hdmi_port=1, cable_connection_status=0}TvInputHardwareInfo {id=6, type=9, audio_type=-2147483616, audio_addr=, hdmi_port=2, cable_connection_status=0}TvInputHardwareInfo {id=7, type=9, audio_type=-2147483616, audio_addr=, hdmi_port=3, cable_connection_status=0}TvInputHardwareInfo {id=16, type=2, audio_type=-2147467264, audio_addr=, cable_connection_status=0}
  mHardwareInputIdMap: deviceId -> inputId1: com.droidlogic.tvinput/.services.AV1InputService/HW15: com.droidlogic.tvinput/.services.Hdmi1InputService/HW56: com.droidlogic.tvinput/.services.Hdmi2InputService/HW67: com.droidlogic.tvinput/.services.Hdmi3InputService/HW716: com.droidlogic.tvinput/.services.ADTVInputService/HW16

如果能找到对应的InputId,那么就开始TvInputHardware 状态变更的处理。

private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {@Overridepublic void onReceived(HdmiHotplugEvent event) {synchronized (mLock) {mHdmiStateMap.put(event.getPort(), event.isConnected());TvInputHardwareInfo hardwareInfo =findHardwareInfoForHdmiPortLocked(event.getPort());Slog.w(TAG, "onReceived HdmiHotplugEvent " + event + " hardwareInfo " + hardwareInfo);if (hardwareInfo == null) {return;}String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());Slog.w(TAG, "onReceived inputId " + inputId);if (inputId == null) {return;}// No HDMI hotplug does not necessarily mean disconnected, as old devices may// not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to// denote unknown state.int state = event.isConnected()? INPUT_STATE_CONNECTED: INPUT_STATE_CONNECTED_STANDBY;mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();}}}
    interface Listener {void onStateChanged(String inputId, int state);void onHardwareDeviceAdded(TvInputHardwareInfo info);void onHardwareDeviceRemoved(TvInputHardwareInfo info);void onHdmiDeviceAdded(HdmiDeviceInfo device);void onHdmiDeviceRemoved(HdmiDeviceInfo device);void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device);}private class ListenerHandler extends Handler {private static final int STATE_CHANGED = 1;private static final int HARDWARE_DEVICE_ADDED = 2;private static final int HARDWARE_DEVICE_REMOVED = 3;private static final int HDMI_DEVICE_ADDED = 4;private static final int HDMI_DEVICE_REMOVED = 5;private static final int HDMI_DEVICE_UPDATED = 6;@Overridepublic final void handleMessage(Message msg) {switch (msg.what) {case STATE_CHANGED: {String inputId = (String) msg.obj;int state = msg.arg1;mListener.onStateChanged(inputId, state);break;}case HARDWARE_DEVICE_ADDED: {TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;mListener.onHardwareDeviceAdded(info);break;}case HARDWARE_DEVICE_REMOVED: {TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;mListener.onHardwareDeviceRemoved(info);break;}case HDMI_DEVICE_ADDED: {HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;mListener.onHdmiDeviceAdded(info);break;}case HDMI_DEVICE_REMOVED: {HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;mListener.onHdmiDeviceRemoved(info);break;}case HDMI_DEVICE_UPDATED: {HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;String inputId;synchronized (mLock) {inputId = mHdmiInputIdMap.get(info.getId());}if (inputId != null) {mListener.onHdmiDeviceUpdated(inputId, info);} else {Slog.w(TAG, "Could not resolve input ID matching the device info; "+ "ignoring.");}break;}default: {Slog.w(TAG, "Unhandled message: " + msg);break;}}

mListener是在TvInputHardwareManager初始化的时候由TvInputManagerServive创建的。

    public TvInputHardwareManager(Context context, Listener listener) {mContext = context;mListener = listener;mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);mHal.init();}

7. TvInputManagerService

Z:\p\frameworks\base\services\core\java\com\android\server\tv\TvInputManagerService.java

    private final class HardwareListener implements TvInputHardwareManager.Listener {@Overridepublic void onStateChanged(String inputId, int state) {synchronized (mLock) {setStateLocked(inputId, state, mCurrentUserId);}}@Overridepublic void onHardwareDeviceAdded(TvInputHardwareInfo info) {synchronized (mLock) {Slog.d(TAG, "onHardwareDeviceAdded " + info);UserState userState = getOrCreateUserStateLocked(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);}}}}@Overridepublic void onHardwareDeviceRemoved(TvInputHardwareInfo info) {synchronized (mLock) {Slog.d(TAG, "onHardwareDeviceRemoved " + info);UserState userState = getOrCreateUserStateLocked(mCurrentUserId);// Broadcast the event to all hardware inputs.for (ServiceState serviceState : userState.serviceStateMap.values()) {if (!serviceState.isHardware || serviceState.service == null) continue;try {serviceState.service.notifyHardwareRemoved(info);} catch (RemoteException e) {Slog.e(TAG, "error in notifyHardwareRemoved", e);}}}}@Overridepublic void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {synchronized (mLock) {UserState userState = getOrCreateUserStateLocked(mCurrentUserId);// Broadcast the event to all hardware inputs.for (ServiceState serviceState : userState.serviceStateMap.values()) {if (!serviceState.isHardware || serviceState.service == null) continue;try {serviceState.service.notifyHdmiDeviceAdded(deviceInfo);} catch (RemoteException e) {Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);}}}}@Overridepublic void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {synchronized (mLock) {UserState userState = getOrCreateUserStateLocked(mCurrentUserId);// Broadcast the event to all hardware inputs.for (ServiceState serviceState : userState.serviceStateMap.values()) {if (!serviceState.isHardware || serviceState.service == null) continue;try {serviceState.service.notifyHdmiDeviceRemoved(deviceInfo);} catch (RemoteException e) {Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);}}}}@Overridepublic void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) {synchronized (mLock) {Integer state;switch (deviceInfo.getDevicePowerStatus()) {case HdmiControlManager.POWER_STATUS_ON:state = INPUT_STATE_CONNECTED;break;case HdmiControlManager.POWER_STATUS_STANDBY:case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON:case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY:state = INPUT_STATE_CONNECTED_STANDBY;break;case HdmiControlManager.POWER_STATUS_UNKNOWN:default:state = null;break;}if (state != null) {setStateLocked(inputId, state, mCurrentUserId);}}}}
    private void setStateLocked(String inputId, int state, int userId) {UserState userState = getOrCreateUserStateLocked(userId);TvInputState inputState = userState.inputMap.get(inputId);if (inputState == null ||inputState.info == null) {Slog.d(TAG,"Can't get input info,exit!!!!!");return;}ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());int oldState = inputState.state;inputState.state = state;Slog.d(TAG,"setStateLocked oldState " + oldState + " new " + inputState.state);if (serviceState != null && serviceState.service == null&& (!serviceState.sessionTokens.isEmpty() || serviceState.isHardware)) {// We don't notify state change while reconnecting. It should remain disconnected.Slog.d(TAG,"Can't get input info when disconnected!");return;}if (oldState != state) {notifyInputStateChangedLocked(userState, inputId, state, null);}}

最终会调用注册在HdmiControlService里面的ITvInputManagerCallback的onInputStateChanged回调方法,ITvInputManagerCallback则是可以通过TvInputManager注册的回调。

    private void notifyInputStateChangedLocked(UserState userState, String inputId,int state, ITvInputManagerCallback targetCallback) {if (DEBUG) {Slog.d(TAG, "notifyInputStateChangedLocked(inputId=" + inputId+ ", state=" + state + ")");}if (targetCallback == null) {for (ITvInputManagerCallback callback : userState.callbackSet) {try {callback.onInputStateChanged(inputId, state);} catch (RemoteException e) {Slog.e(TAG, "failed to report state change to callback", e);}}} else {try {targetCallback.onInputStateChanged(inputId, state);} catch (RemoteException e) {Slog.e(TAG, "failed to report state change to callback", e);}}}

参考log:

130|console:/ # logcat -c
ce -s TvInputHardwareManager -s TvInputManagerService                         <
--------- beginning of system
--------- beginning of main
01-06 21:14:11.978  3640  3640 D TvInputManagerService: setStateLocked oldState 0 new 1
01-06 21:14:11.978  3640  3640 D TvInputManagerService: notifyInputStateChangedLocked(inputId=com.droidlogic.tvinput/.services.Hdmi1InputService/HW5, state=1)
01-06 21:14:12.182  3640  3640 W TvInputHardwareManager: onReceived HdmiHotplugEvent android.hardware.hdmi.HdmiHotplugEvent@6a58b2b hardwareInfo TvInputHardwareInfo {id=5, type=9, audio_type=-2147483616, audio_addr=, hdmi_port=1, cable_connection_status=0}
01-06 21:14:12.182  3640  3640 W TvInputHardwareManager: onReceived inputId com.droidlogic.tvinput/.services.Hdmi1InputService/HW5
01-06 21:14:12.183  3640  3640 D TvInputManagerService: setStateLocked oldState 1 new 1
01-06 21:14:12.631  3640  3640 W TvInputHardwareManager: onStatusChanged CEC: id: 8 logical_address: 0x08 device_type: 4 vendor_id: 32837 display_name: Playback_2 power_status: 0 physical_address: 0x1000 port_id: 1 status 2

这一节并没有看到onHdmiDeviceAdded和onHardwareDeviceAdded的调用,下一节理清楚。

TIF 和Hdmi cec hotplug热插拔事件过程梳理一相关推荐

  1. hdmi cec简介 开发指导

    一.hdmi cec简介 CEC,全称是Consumer Electronics Control,消费类电子控制.为用户环境中所有通过HDMI线连接的家庭视听设备提供高级控制功能的一种协议,用户通过一 ...

  2. 现在无法开始异步操作。异步操作只能在异步处理程序或模块中开始,或在页生存期中的特定事件过程中开始...

    异常处理汇总-后端系列 http://www.cnblogs.com/dunitian/p/4523006.html 这篇没啥技术含量,用来小记一番 错误信息 "System.Invalid ...

  3. HDMI CEC小结

    HDMI CEC算是一个相当庞大的系统,想了解还要从HDMI接口信号啊.物理地址啊.逻辑地址啊等等HDMI基础的东西说起. 不过可以简单的这么理解,在HDMI CEC最小系统里,所有通过HDMI连接在 ...

  4. HDMI CEC协议

    1 前言 本文档仅作为本人记录使用,主要根据工作使用及<HDMI Specification 1.4a.pdf>进行终结得出,若有不足会后续补充. 2 CEC简介 CEC(Consumer ...

  5. Android cec设备,Android和HDMI CEC

    是的,这是完全有可能做的,但它是非常可笑的. HDMI CEC像公共汽车一样工作.通常在两个方向. 你的问题: >是的.虽然我使用JNI访问实际工作的本机代码.您可能可以纯粹在Java中执行此操 ...

  6. SprinBoot集成disConf的过程梳理

    SprinBoot集成disConf的过程梳理: 打开pom.xml文件,添加disconf-client的依赖关系,工程会自动下载Disconf客户端的jar包 <dependency> ...

  7. 在linux下使用udev获取热插拔(hotplug)事件

    udev是一种工具,它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等,设备文件通常放在/dev目录下.使用udev后,在/dev目录下就只包含系统中真正存在的设备.udev ...

  8. linux下热插拔事件的产生是怎样通知到用户空间,kobject_uevent_env之uevent【转】...

    转自:http://blog.csdn.net/myarrow/article/details/8259888 1.kobject, ktype, kset 1) kobject: 代表sysfs中的 ...

  9. HDMI CEC协议简介

    一. 概述 1. HDMI HDMI(High-Definition Multimedia Interface,高清多媒体接口),是一种专用的音频/视频接口,用于发送未压缩的视频数据和压缩/未压缩的音 ...

最新文章

  1. Miner3D Enterprise 企业版
  2. 过滤特征_机器学习深度研究:特征选择中几个重要的统计学概念
  3. 岳阳学计算机软件,岳阳学java专业学校排名
  4. 【Nacos】Nacos MySQL 配置后无法登录 愚蠢的问题
  5. app推广广告词热点
  6. c++ thread(2.1)---join()
  7. 关于正则表达式中^和$
  8. django -- url 的 命名空间
  9. 用python调用r包
  10. 计算机如何通过手机连接网络打印机,怎样连接网络打印机【图文教程】
  11. cocos creator飞机大战总结
  12. FlexBox 行间距
  13. 中科大凸优化P345678 Chapter2Conve Set
  14. php生成手机桌面图标,教你把手机桌面图标和名字,更改成自己喜欢的
  15. 利用python进行数据分析数据_利用Python进行数据分析
  16. 今年大厂门槛高?阿里内部5份专属PDF带你激流勇进,厚积薄发,勇夺offer
  17. 秋月梨文案:秋月梨水果活动文案,水果店卖秋月梨文案
  18. Hermitian matrix专题一
  19. 继续在深圳发展还是回武汉呢?
  20. 国外LEAD真实收入,没有那么暴利,但是也不低!

热门文章

  1. 谷歌浏览器提示:您要访问的网站包含恶意软件(解决方案)
  2. 数学分析(4):自然数集的序关系
  3. yii2restful规范的api使用
  4. 实战 | 把学校的网站搞了!
  5. 天梯选拔:先序序列创建二叉树,输出先序序列、中序序列、后序序列并输出叶子结点数
  6. 在php代码中内嵌javascript代码
  7. eWebEditor编辑器asp.net版
  8. 极光推送:java后台向APP推送消息(android,ios极光推送消息)
  9. 2345看图王去掉“今日热点”弹窗的步骤
  10. 反向链接的类型(转)