小工具知识网

当前位置: 首页 > 行业知识 > 安卓11-HDMI插拔检测流程

行业知识

安卓11-HDMI插拔检测流程

2026-11-08 20:47:26 来源:互联网转载

hdmi从插入到拔出经过底层一系列检测到应用层,应用层获取hdmi插入状态后又会做出一系列相应的动作,下面梳理了从应用层到底层一步步追踪到芯片的hpd-pin的检测过程。其大致原理就是framework层通过检测/sys/class/extcon/hdmi/state 来获取hdmi插入与否,具体更新这个状态的地方再kernel层,kernel层通过一个dw_hdmi_connector_detect轮询函数不断的查询hpd状态然后更新sys/class/extcon/hdmi/state,后面分析了hdmi插入拔出状态改变后系统所做的其它动作。

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java +775    private class HdmiVideoExtconUEventObserver extends ExtconStateObserver {        private static final String HDMI_EXIST = "HDMI=1";        private static final String NAME = "hdmi";        private final ExtconInfo mHdmi = new ExtconInfo(NAME);  //判断/sys/class/extcon/hdmi文件是否存在        private boolean init() {            boolean plugged = false;            try {                plugged = parseStateFromFile(mHdmi); //解析sys/class/extcon/hdmi/state这个文件内容            } catch (FileNotFoundException e) {                Slog.w(TAG, mHdmi.getStatePath()                        + " not found while attempting to determine initial state", e);            } catch (IOException e) {                Slog.e(                        TAG,                        "Error reading " + mHdmi.getStatePath()                                + " while attempting to determine initial state",                        e);            }            startObserving(mHdmi);  //开始监控/sys/class/extcon/hdmi            return plugged;        }        @Override        public void updateState(ExtconInfo extconInfo, String eventName, Boolean state) {//通过这里更新hdmi plug状态,这个转态来自hal层                 //这里的变化来自kernel层dw-hdmi.cmDefaultDisplayPolicy.setHdmiPlugged(state);        }        @Override        public Boolean parseState(ExtconInfo extconIfno, String state) {            // extcon event state changes from kernel4.9            // new state will be like STATE=HDMI=1            return state.contains(HDMI_EXIST);   //判断这里是否sys/class/extcon/hdmi/state  HDMI=1 是的话返回true        }    }     void initializeHdmiStateInternal() {        boolean plugged = false;        // watch for HDMI plug messages if the hdmi switch exists        if (new File("/sys/devices/virtual/switch/hdmi/state").exists()) {            mHDMIObserver.startObserving("DEVPATH=/devices/virtual/switch/hdmi");            final String filename = "/sys/class/switch/hdmi/state";            FileReader reader = null;            try {                reader = new FileReader(filename);                char[] buf = new char[15];                int n = reader.read(buf);                if (n > 1) {                    plugged = 0 != Integer.parseInt(new String(buf, 0, n - 1));                }            } catch (IOException ex) {                Slog.w(TAG, "Couldn't read hdmi state from " + filename + ": " + ex);            } catch (NumberFormatException ex) {                Slog.w(TAG, "Couldn't read hdmi state from " + filename + ": " + ex);            } finally {                if (reader != null) {                    try {                        reader.close();                    } catch (IOException ex) {                    }                }            }        } else if (ExtconUEventObserver.extconExists()   //走这里  判断sys/class/extcon是否存在                && ExtconUEventObserver.namedExtconDirExists(HdmiVideoExtconUEventObserver.NAME)) {            Log.i("fan","xtconUEventObserver.extconExists");            HdmiVideoExtconUEventObserver observer = new HdmiVideoExtconUEventObserver();//新建一个hdmi观察者,检测hdmi hpd引脚的变化            plugged = observer.init();            mHDMIObserver = observer;        } else if (localLOGV) {            Slog.v(TAG, "Not observing HDMI plug state because HDMI was not found.");        }        // This dance forces the code in setHdmiPlugged to run.        // Always do this so the sticky intent is stuck (to false) if there is no hdmi.        mDefaultDisplayPolicy.setHdmiPlugged(plugged, true /* force */);    }    frameworks/base/services/core/java/com/android/server/ExtconStateObserver.java       public void onUEvent(ExtconInfo extconInfo, UEvent event) {        if (LOG) Slog.d(TAG, extconInfo.getName() + " UEVENT: " + event);        String name = event.get("NAME");        S state = parseState(extconInfo, event.get("STATE"));        Slog.d("fan","onUEvent get name="+name+"state="+state);        if (state != null) {            updateState(extconInfo, name, state);        }    }public abstract S parseState(ExtconInfo extconInfo, String state);  //在PhoneWindowManager.java里实现判断sys/class/extcon/hdmi/state  是否与HDMI=1相等frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java    String ACTION_HDMI_PLUGGED = "android.intent.action.HDMI_PLUGGED";    public void setHdmiPlugged(boolean plugged) {        setHdmiPlugged(plugged, false /* force */);    }    public void setHdmiPlugged(boolean plugged, boolean force) {        if (force || mHdmiPlugged != plugged) {            mHdmiPlugged = plugged;            mService.updateRotation(true /* alwaysSendConfiguration */, true /* forceRelayout */);            final Intent intent = new Intent(ACTION_HDMI_PLUGGED);            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);            intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged);            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);//通知系统hdmi插入状态        }    }    boolean isHdmiPlugged() {        return mHdmiPlugged;    }    frameworks/base/services/core/java/com/android/server/ExtconUEventObserver.java         public static boolean extconExists() {        File extconDir = new File("/sys/class/extcon"); //检查这个文件是否存在,对应上面的else if (ExtconUEventObserver.extconExists()         return extconDir.exists() && extconDir.isDirectory();    }kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.cstatic const struct drm_connector_funcs dw_hdmi_connector_funcs = {        .fill_modes = drm_helper_probe_single_connector_modes,        .detect = dw_hdmi_connector_detect, //通过这里检测hdmi变化        .destroy = drm_connector_cleanup,        .force = dw_hdmi_connector_force,        .reset = drm_atomic_helper_connector_reset,        .set_property = dw_hdmi_connector_set_property,        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,        .atomic_set_property = dw_hdmi_atomic_connector_set_property,        .atomic_get_property = dw_hdmi_atomic_connector_get_property,};static enum drm_connector_statusdw_hdmi_connector_detect(struct drm_connector *connector, bool force){        struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,                                             connector);        enum drm_connector_status result;        if (!hdmi->force_logo) {                mutex_lock(&hdmi->mutex);                hdmi->force = DRM_FORCE_UNSPECIFIED;                dw_hdmi_update_power(hdmi);                dw_hdmi_update_phy_mask(hdmi);                mutex_unlock(&hdmi->mutex);        }        result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);        if (result == connector_status_connected)                extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, true);        else                extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false);.....kernel/drivers/extcon/extcon.cint extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, bool state){        int ret, index;        unsigned long flags;        index = find_cable_index_by_id(edev, id);        if (index lock, flags);        ret = is_extcon_changed(edev, index, state);        spin_unlock_irqrestore(&edev->lock, flags);        if (!ret)                return 0;        ret = extcon_set_state(edev, id, state);        if (ret fd;if (fd[0] = 0)pfd[0].events = POLLIN | POLLRDNORM | POLLPRI;while (true) {                usleep(1000);int err = poll(&pfd[0], 1, 20);if (!err) {continue;} else if(err > 0) {if (!ctx->enable || !ctx->system_control)continue;ALOGD("poll revent:%02x\n", pfd[0].revents);memset(&cec_event, 0, sizeof(hdmi_event_t));if (pfd[0].revents & (POLLIN)) {struct cec_msg cecframe;ALOGD("poll receive msg\n");ret = ioctl(pfd[0].fd, CEC_RECEIVE, &cecframe);if (!ret) {cec_event.type = HDMI_EVENT_CEC_MESSAGE;cec_event.dev = &ctx->device;cec_event.cec.initiator = (cec_logical_address_t)(cecframe.msg[0] >> 4);cec_event.cec.destination = (cec_logical_address_t)(cecframe.msg[0] & 0x0f);cec_event.cec.length = cecframe.len - 1;cec_event.cec.body[0] = cecframe.msg[1];if (!validcecmessage(cec_event)) {for (ret = 0; ret event_callback)ctx->event_callback(&cec_event, ctx->cec_arg);} else {ALOGE("%s cec_event length > 15 ", __func__);}} else {ALOGE("%s hdmi cec read error", __FUNCTION__);}}if (pfd[0].revents & (POLLPRI)) {int state = -1;struct cec_event event;ALOGI("poll receive event\n");ret = ioctl(pfd[0].fd, CEC_DQEVENT, &event);//取得一个cec事件,然后判断事件的状态,此部分内容在内核层if (!ret) {ALOGD("event:%d\n", event.event);if (event.event == CEC_EVENT_PIN_HPD_LOW) {//获取底层hpdin管教状态ALOGI("CEC_EVENT_PIN_HPD_LOW\n");ctx->hotplug = false;cec_event.type = HDMI_EVENT_HOT_PLUG;cec_event.dev = &ctx->device;cec_event.hotplug.connected = HDMI_NOT_CONNECTED;cec_event.hotplug.port_id = HDMI_CEC_PORT_ID;if (ctx->event_callback)ctx->event_callback(&cec_event, ctx->cec_arg);} else if (event.event == CEC_EVENT_PIN_HPD_HIGH) {//高为连接ALOGI("CEC_EVENT_PIN_HPD_HIGH\n");ctx->hotplug = true;cec_event.type = HDMI_EVENT_HOT_PLUG;cec_event.dev = &ctx->device;cec_event.hotplug.connected = HDMI_CONNECTED;cec_event.hotplug.port_id = HDMI_CEC_PORT_ID;if (ctx->event_callback)ctx->event_callback(&cec_event, ctx->cec_arg);} else if (event.event == CEC_EVENT_STATE_CHANGE) {ALOGD("adapt state change,phy_addr:%x,flags:%x\n", event.state_change.phys_addr, event.flags);/* * Before cec HAL is initialized, hdmi hpd state may be * changed. So we should confirm the hpd status * after cec is initialized(Kernel will report * CEC_EVENT_FL_INITIAL_STATE to notify HAL that * initialization is done). */if (event.flags & CEC_EVENT_FL_INITIAL_STATE) {ALOGD("cec adapter init complete, get connect state\n");ctx->hotplug = get_hpd_state_from_node(ctx);ctx->cec_init = true;/* * Framework will start la polling when box turn on, * In addition, as soon as framewrok receives hdmi * plug in, it will start la polling immediately. * There is not need to report plug in event if hdmi * is connecting when box turn on. So we should report * hdmi plug out only. */if (!ctx->hotplug)report_hdp_event(ctx, ctx->hotplug);}ctx->phy_addr = event.state_change.phys_addr;}} else {ALOGE("%s cec event get err, ret:%d\n", __func__, ret);}}} else {ALOGE("%s: cec poll failed errno: %s", __FUNCTION__,strerror(errno));continue;}}return NULL;}Kernel/drivers/media/cec/cec-api.c static long cec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case CEC_DQEVENT:                return cec_dqevent(adap, fh, block, parg);  //hal层调用这里获取一个cec事件,既然有获取事件就有把事件放入事件队列的地方 drivers/media/cec/cec-adap.c                void cec_queue_pin_hpd_event(struct cec_adapter *adap, bool is_high, ktime_t ts){        struct cec_event ev = {                .event = is_high ? CEC_EVENT_PIN_HPD_HIGH :                                   CEC_EVENT_PIN_HPD_LOW,        };        struct cec_fh *fh;        if (!adap)                return;        /* hdmi HPD may occur before devnode is registered */        if (!adap->devnode.registered)                return;        mutex_lock(&adap->devnode.lock);        list_for_each_entry(fh, &adap->devnode.fhs, list)                cec_queue_event_fh(fh, &ev, ktime_to_ns(ts)); //插入一个cec事件,把这个事件放入到ece事件队列,供hal层获取,hal层获取后传到framework层        mutex_unlock(&adap->devnode.lock);}hardware/rockchip/hdmicec/hdmicec_event.cppstatic void report_hdp_event(hdmi_cec_context_t* ctx, bool hpd){        hdmi_event_t cec_event;        cec_event.type = HDMI_EVENT_HOT_PLUG;        cec_event.dev = &ctx->device;        if (hpd)                cec_event.hotplug.connected = HDMI_CONNECTED;        else                cec_event.hotplug.connected = HDMI_NOT_CONNECTED;        cec_event.hotplug.port_id = HDMI_CEC_PORT_ID;        if (ctx->event_callback)                ctx->event_callback(&cec_event, ctx->cec_arg);}最终通过event_callback通知应用层,下面查找下callbackhardware/rockchip/hdmicec/hdmi_cec.cppstatic void hdmi_cec_register_event_callback(const struct hdmi_cec_device* dev,                                             event_callback_t callback, void* arg){        struct hdmi_cec_context_t* ctx = (struct hdmi_cec_context_t*)dev;        ALOGI("%s", __func__);        ctx->event_callback = callback;        ctx->cec_arg = arg;}static int hdmi_cec_device_open(const struct hw_module_t* module, const char* name,                                struct hw_device_t** device){  ....  dev->device.register_event_callback = hdmi_cec_register_event_callback; //在HdmiCec.cpp完成注册  ......hardware/interfaces/tv/cec/1.0/default/HdmiCec.cpp Return HdmiCec::setCallback(const sp& callback) {  //接口在IHdmiCecCallback.hal中    if (mCallback != nullptr) {        mCallback->unlinkToDeath(this);        mCallback = nullptr;    }    if (callback != nullptr) {        mCallback = callback;        mCallback->linkToDeath(this, 0 /*cookie*/);        mDevice->register_event_callback(mDevice, eventCallback, nullptr);  //注册回调通知framework层 eventCallback看hardware/interfaces/tv/cec/1.0/default/HdmiCec.h    }    return Void();}IHdmiCec* HIDL_FETCH_IHdmiCec(const char* hal) {    hdmi_cec_device_t* hdmi_cec_device;    int ret = 0;    const hw_module_t* hw_module = nullptr;    ret = hw_get_module (HDMI_CEC_HARDWARE_MODULE_ID, &hw_module);    if (ret == 0) {        ret = hdmi_cec_open (hw_module, &hdmi_cec_device);        if (ret != 0) {            LOG(ERROR)  0,                    .portId = static_cast(event->hotplug.port_id)                };                mCallback->onHotplugEvent(hotplugEvent);  //实现在HdmiCecController.java 看下面            }        }  frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecController.java import android.hardware.tv.cec.V1_0.IHdmiCecCallback;private IHdmiCec mHdmiCec;    final class HdmiCecCallback extends IHdmiCecCallback.Stub { //接口在hal层IHdmiCecCallback.hal        @Override        public void onCecMessage(CecMessage message) throws RemoteException {            byte[] body = new byte[message.body.size()];            for (int i = 0; i  handleIncomingCecCommand(message.initiator, message.destination, body));        }        @Override        public void onHotplugEvent(HotplugEvent event) throws RemoteException {            runOnServiceThread(() -> handleHotplug(event.portId, event.connected));        }    }    private final HdmiControlService mService;    @ServiceThreadOnly    private void handleHotplug(int port, boolean connected) {        assertRunOnServiceThread();        HdmiLogger.debug("Hotplug event:[port:%d, connected:%b]", port, connected);        addHotplugEventToHistory(port, connected);        mService.onHotplug(port, connected);    }    private void init(NativeWrapper nativeWrapper) {        mIoHandler = new Handler(mService.getIoLooper());        mControlHandler = new Handler(mService.getServiceLooper());        nativeWrapper.setCallback(new HdmiCecCallback());    }   public void setCallback(HdmiCecCallback callback) {  //应用层调用            try {                mHdmiCec.setCallback(callback);            } catch (RemoteException e) {                HdmiLogger.error("Couldn't initialise tv.cec callback : ", e);            }        }frameworks/base/services/core/java/com/android/server/hdmi/HdmiControlService.java void onHotplug(int portId, boolean connected) {        assertRunOnServiceThread();        if (connected && !isTvDevice()                && getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {            if (isSwitchDevice()) {                initPortInfo();                HdmiLogger.debug("initPortInfo for switch device when onHotplug from tx.");            }            ArrayList localDevices = new ArrayList();            for (int type : mLocalDevices) {                if (type == HdmiDeviceInfo.DEVICE_PLAYBACK                        && isHdmiCecNeverClaimPlaybackLogicAddr) {                    continue;                }                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);            }        }    }    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,            HdmiHotplugEvent event) {        try {            listener.onReceived(event);        } catch (RemoteException e) {            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);        }    }frameworks/base/core/java/android/hardware/hdmi/HdmiControlManager.java    private IHdmiHotplugEventListener getHotplugEventListenerWrapper(            final HotplugEventListener listener) {        return new IHdmiHotplugEventListener.Stub() {            @Override            public void onReceived(HdmiHotplugEvent event) {                listener.onReceived(event);;            }        };    }    private final class ClientHotplugEventListener implements HotplugEventListener {        @Override        public void onReceived(HdmiHotplugEvent event) {            List ports = new ArrayList();            try {                ports = mService.getPortInfo();            } catch (RemoteException e) {                throw e.rethrowFromSystemServer();            }            if (ports.isEmpty()) {                Log.e(TAG, "Can't find port info, not updating connected status. "                        + "Hotplug event:" + event);                return;            }            // If the HDMI OUT port is plugged or unplugged, update the mLocalPhysicalAddress            for (HdmiPortInfo port : ports) {                if (port.getId() == event.getPort()) {                    if (port.getType() == HdmiPortInfo.PORT_OUTPUT) {                        setLocalPhysicalAddress(                                event.isConnected()                                ? port.getAddress()                                : INVALID_PHYSICAL_ADDRESS);                    }                    break;                }            }        }    }                  

上一篇:宝塔面板怎么开放函数?

下一篇:wpf 由于尚未生成某些自定义元素,设计视图无法正确显示。