Android HDMI audio设备插拔事件

一.Android audio设备插拔事件检测

1.1两种机制的切换

常规Android系统Audio可插拔设备有:耳机(headset、headphones)、USB Audio设备、HDMI Audio设备,以耳机为例:Android耳机插拔可以有两个机制实现:

  1. InputEvent
  2. UEvent

Android 系统中根据Android10.0/frameworks/base/core/res/res/values/config.xml中 config_useDevInputEventForAudioJack配置项决定InputEvent或UEvent,默认为false,即不使用InputEvent方式。

1.2 Android 耳机插拔事件处理流程

在这里插入图片描述

1.3 InputEvent机制

InputEvent的处理主要在frameworks/base/services/java/com/android/server/input/InputManagerService.java
该类的成员mUseDevInputEventForAudioJack决定选择 InputEvent 还是 UEvent,
在InputManagerService构造函数中会读取config.xml 中config_useDevInputEventForAudioJack值。

/** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
309    final boolean mUseDevInputEventForAudioJack;
310
311    public InputManagerService(Context context) {
312        this.mContext = context;
313        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
314
315        mUseDevInputEventForAudioJack =
316                context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
317        Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
318                + mUseDevInputEventForAudioJack);
319      ........

327    }

InputManagerService当收到native inputevent事件会调用函数:notifySwitch

// Native callback.
1753    private void notifySwitch(long whenNanos, int switchValues, int switchMask) {                         
1759        ........// 代码省略
1769        if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
1770            mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
1771                    switchMask);
1772        }
1774        ........// 代码省略
1782    }

然后转到frameworks/base/services/core/java/com/android/server/WiredAccessoryManager.java 的
notifyWiredAccessoryChanged中

public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask) {
146        ........// 代码省略
182            updateLocked(NAME_H2W, "",
183                    (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset);
184        }
185    }

private void updateLocked(String newName, String address, int newState) {
210        // Retain only relevant bits

216        ........// 代码省略

270        Message msg;
271        // send a combined name, address string separated by |
272        if (newName.startsWith(NAME_DP_AUDIO)) {
273            int pseudoHeadsetState = mHeadsetState;
274            if (dpBitState && (newDpState != 0)) {
275                // One DP already connected, so allow request to connect second.
276                pseudoHeadsetState = mHeadsetState & (~BIT_HDMI_AUDIO);
277            }
278            msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState,
279                                         pseudoHeadsetState,
280                                         NAME_DP_AUDIO+"/"+address);
281
282            if ((headsetState == 0) && (mDpCount != 0)) {
283                // Atleast one DP is connected, so keep mHeadsetState's DP bit set.
284                headsetState = headsetState | BIT_HDMI_AUDIO;
285            }
286        } else {
287            msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState,
288                                         mHeadsetState,
289                                         newName+"/"+address);
290        }
291        mHandler.sendMessage(msg);
292
293        mHeadsetState = headsetState;
294    }

private final Handler mHandler = new Handler(Looper.myLooper(), null, true) {
297        @Override
298        public void handleMessage(Message msg) {
299            switch (msg.what) {
300                case MSG_NEW_DEVICE_STATE:
301                    setDevicesState(msg.arg1, msg.arg2, (String) msg.obj);
302                    mWakeLock.release();
303                    break;
304                ........// 代码省略
308            }
309        }
310    };
311
312    private void setDevicesState(
313            int headsetState, int prevHeadsetState, String headsetNameAddr) {
314        synchronized (mLock) {
315            int allHeadsets = SUPPORTED_HEADSETS;
316            for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
317                if ((curHeadset & allHeadsets) != 0) {
318                    setDeviceStateLocked(curHeadset, headsetState, prevHeadsetState,
319                                         headsetNameAddr);
320                    allHeadsets &= ~curHeadset;
321                }
322            }
323        }
324    }
325
326    private void setDeviceStateLocked(int headset,
327            int headsetState, int prevHeadsetState, String headsetNameAddr) {
328        
               ........// 代码省略
338
339            if (headset == BIT_HEADSET) {
340                outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET;
341                inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET;
342            } else if (headset == BIT_HEADSET_NO_MIC) {
343                outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
344            } else if (headset == BIT_LINEOUT) {
345                outDevice = AudioManager.DEVICE_OUT_LINE;
346            } else if (headset == BIT_USB_HEADSET_ANLG) {
347                outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
348            } else if (headset == BIT_USB_HEADSET_DGTL) {
349                outDevice = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
350            } else if (headset == BIT_HDMI_AUDIO) {
351                outDevice = AudioManager.DEVICE_OUT_HDMI;
352            } else {
353                Slog.e(TAG, "setDeviceState() invalid headset type: " + headset);
354                return;
355            }
356
361
362            String[] hs = headsetNameAddr.split("/");
363            if (outDevice != 0) {
368              mAudioManager.setWiredDeviceConnectionState(outDevice, state,
369                                                             (hs.length > 1 ? hs[1] : ""), hs[0]);
370            }
371            if (inDevice != 0) {
372
373              mAudioManager.setWiredDeviceConnectionState(inDevice, state,
374                                                           (hs.length > 1 ? hs[1] : ""), hs[0]);
375            }
376        }
377    }
378

最终将设备插拔事件上报到AudioManager,最终完成设备切换。

二.Android HDMI audio设备插拔事件检测

2.1 HDMI Audio设备插拔机制选择之kernel上报事件

通过上述流程分析,发现 inputevent 机制组要处理的设备类型有BIT_HEADSET、BIT_HEADSET_NO_MIC、BIT_LINEOUT这三种类型,所以HDMI Audio设备插拔事件应该选择UEvent 机制,而在Android Q中 kernel 3.5 以后改用 Extcon 取代switch class,所以HDMI Audio最终采用Extcon节点状态检测插拔事件。
首先确定何时建立插拔事件,那就要看下kernel 中 HDMI 驱动逻辑,以我目前用的lt9611芯片为例:首先驱动probe时,注册extcon:调用devm_extcon_hdmi_dev_register;然后当中断发生时同步state状态:调用extcon_set_state_sync;具体如下:

static int lt9611_probe(struct i2c_client *client,
	 const struct i2c_device_id *id)
{
	........// 代码省略
	
	ret = lt9611_gpio_configure(pdata, true);// 配置gpio引脚
	
	........// 代码省略
	
	ret = request_threaded_irq(pdata->irq, NULL, lt9611_irq_thread_handler,
		IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lt9611", pdata); // 注册中断
	
	i2c_set_clientdata(client, pdata);// 设置初始参数
	dev_set_drvdata(&client->dev, pdata);// 设置初始参数

	........// 代码省略
	
	ret = lt9611_sysfs_init(&client->dev);
	
	........// 代码省略

	/* create switch device to update audio modules */
	sdev_audio =  extcon_dev_allocate(hdmi_ext_disp_supported_cable); // 分配extcon资源
	if (!sdev_audio)
		goto err_sysfs_init;

	ret = extcon_dev_register(sdev_audio); //注册extcon
	sdev_audio->name = "hdmi_audio";

    // 同步初始状态
	lt9611_write(pdata, 0xff, 0x82);
	lt9611_read(pdata, 0x5e, &reg_val, 1);
	connected  = (reg_val & BIT(2));
	pr_info("connected = %d\n", !!connected);
	extcon_set_state_sync(sdev_audio, EXTCON_DISP_HDMI, !!connected);

	return ret;

当中断发生时(HDMI 插入/拔出),同步extcon节点状态:

static irqreturn_t lt9611_irq_thread_handler(int irq, void *dev_id)
{
	........// 代码省略

	 /* hpd changed low */
	if (irq_flag3 & 0x80) {
		pr_info("hdmi cable disconnected\n");
		........// 代码省略
		extcon_set_state_sync(sdev_audio, EXTCON_DISP_HDMI, 0);
	}
	 /* hpd changed high */
	if (irq_flag3 & 0x40) {
		pr_info("hdmi cable connected\n");
        ........// 代码省略
		extcon_set_state_sync(sdev_audio, EXTCON_DISP_HDMI, 1);
	}
	........// 代码省略

	return IRQ_HANDLED;
}

下面具体看下devm_extcon_dev_allocate、devm_extcon_hdmi_dev_register、extcon_set_state_sync实现。
代码路径 kernel/msm-4.9/drivers/extcon/extcon.c

1044 struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable)
1045{
1046	struct extcon_dev *edev;
1047
1048	if (!supported_cable)
1049		return ERR_PTR(-EINVAL);
1050
1051	edev = kzalloc(sizeof(*edev), GFP_KERNEL);// 分配extcon资源
1052	if (!edev)
1053		return ERR_PTR(-ENOMEM);
1054
1055	edev->max_supported = 0;
1056	edev->supported_cable = supported_cable;
1057
1058	return edev;
1059}

其中supported_cable在驱动中定义:

static const unsigned int hdmi_ext_disp_supported_cable[] = {
	EXTCON_DISP_HDMI,
	EXTCON_NONE,
};

再看extcon_dev_register

1080 int extcon_dev_register(struct extcon_dev *edev)
1081{
1082	........// 代码省略
1090
1091	if (!edev || !edev->supported_cable)
1092		return -EINVAL;
1093
1094	for (; edev->supported_cable[index] != EXTCON_NONE; index++);
1095
1096	edev->max_supported = index;
1097	if (index > SUPPORTED_CABLE_MAX) {
1098		dev_err(&edev->dev,
1099			"exceed the maximum number of supported cables\n");
1100		return -EINVAL;
1101	}
1102
1103	edev->dev.class = extcon_class;
1104	edev->dev.release = extcon_dev_release;
1105
1106	edev->name = dev_name(edev->dev.parent);// 设置name
1107	if (IS_ERR_OR_NULL(edev->name)) {
1108		dev_err(&edev->dev,
1109			"extcon device name is null\n");
1110		return -EINVAL;
1111	}
1112	dev_set_name(&edev->dev, "extcon%lu",
1113			(unsigned long)atomic_inc_return(&edev_no)); // 设置节点路径
1115    
1116     ........// 代码省略
1117
1234	ret = device_register(&edev->dev);// device注册
1235	if (ret) {
1236		put_device(&edev->dev);
1237		goto err_dev;
1238	}
1239     ........// 代码省略
1269
1270	return 0;
1290}

最后来看下extcon_set_state_sync

594 int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id,
595				bool cable_state)
596{
597	int ret, index;
598	unsigned long flags;
599
600	index = find_cable_index_by_id(edev, id);
601	if (index < 0)
602		return index;
603
604	/* Check whether the external connector's state is changed. */
605	spin_lock_irqsave(&edev->lock, flags);
606	ret = is_extcon_changed(edev, index, cable_state);
607	spin_unlock_irqrestore(&edev->lock, flags);
608	if (!ret)
609		return 0;
610
611	ret = extcon_set_state(edev, id, cable_state);
612	if (ret < 0)
613		return ret;
614
615	return extcon_sync(edev, id);
616}

实现上述代码,就会在/sys/class/extcon生成extcon?节点,其中state和name比较重要

xxxx:/sys/class/extcon/extcon1 # ls
cable.0 cable.1 device name power state subsystem uevent 

2.2 HDMI Audio设备插拔机制选择之framework插拔事件接收

根据之前描述,framework中WiredAccessoryManager.java主要负责对插拔事件的管理
首先来看下WiredAccessoryManager的构造函数;

100 public WiredAccessoryManager(Context context, InputManagerService inputManager) {
101        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
102        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryManager");
103        mWakeLock.setReferenceCounted(false);
104        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
105        mInputManager = inputManager;
106
107        mUseDevInputEventForAudioJack =
108                context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
109
110        mExtconObserver = new WiredAccessoryExtconObserver();//接受extcon事件
111        mObserver = new WiredAccessoryObserver();//接受uevent事件,已经废弃
112    }

对于HDMI extcon事件管理应该分为两个部分:1.系统启动时对连接状态的判断;2.系统启动后对插拔动作的判断,首先先来看第一种:
当Android系统启动时,WiredAccessoryManager对象就会创建,同时会调用onSystemReady函数

113 private void onSystemReady() {
114        // Inputevent 事件初始状态    
115        if (mUseDevInputEventForAudioJack) {
116            .......// 代码省略
128            notifyWiredAccessoryChanged(0, switchValues,
129                    SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT);
130        }
131
132        // extcon和Uevent事件状态
133        if (ExtconUEventObserver.extconExists() && mExtconObserver.uEventCount() > 0) {
134            if (mUseDevInputEventForAudioJack) {
135                Log.w(TAG, "Both input event and extcon are used for audio jack,"
136                        + " please just choose one.");
137            }
138            mExtconObserver.init();
139        } else {
140            mObserver.init();
141        }
142    }

可以看出当 ExtconUEventObserver.extconExists() && mExtconObserver.uEventCount() > 0 时会调用mExtconObserver.init();那么如何才能使之成立呢,那就要看WiredAccessoryExtconObserver构造函数。

744 WiredAccessoryExtconObserver() {
745            mExtconInfos = ExtconInfo.getExtconInfos(".*audio.*");
746        }

然后会调用getExtconInfos初始化mExtconInfos

93 public static List<ExtconInfo> getExtconInfos(@Nullable String regex) {
94            if (!extconExists()) {
95                return new ArrayList<>(0);  // Always return a new list.
96            }
97            Pattern p = regex == null ? null : Pattern.compile(regex);
98            File file = new File("/sys/class/extcon");// 查选节点路径
99            File[] files = file.listFiles();
100            if (files == null) {
101                Slog.wtf(TAG, file + " exists " + file.exists() + " isDir " + file.isDirectory()
102                        + " but listFiles returns null. "
103                        + SELINUX_POLICIES_NEED_TO_BE_CHANGED);
104                return new ArrayList<>(0);  // Always return a new list.
105            } else {
106                ArrayList list = new ArrayList(files.length);
107                for (File f : files) {
108                    String name = f.getName();// 获取节点目录名并与regex做比较
109                    if (p == null || p.matcher(name).matches()) {
110                        ExtconInfo uei = new ExtconInfo(name);
111                        list.add(uei);
112                        if (LOG) Slog.d(TAG, name + " matches " + regex);
113                    } else {
114                        if (LOG) Slog.d(TAG, name + " does not match " + regex);
115                    }
116                }
117                return list;
118            }
119        }

同上述代码可以看出,该函数会从/sys/class/extcon路径下找寻有无regex目录,到现在可以发现存在一下两个问题:1.此时regex为".audio.",表示带audio的任何目录,而通过上文extcon节点创建目录为:/sys/class/extcon/extcon#,#号为顺序号不固定,所以再回看extcon节点目录创建的地方

1112	dev_set_name(&edev->dev, "extcon%lu",
1113			(unsigned long)atomic_inc_return(&edev_no)); // 设置节点路径
1115    

如果调用原生extcon注册接口创建只可能为extcon#目录,于是我想到两个修改方案:

1.修改原生接口实现:

1112	dev_set_name(&edev->dev, "extcon%lu",
1113			(unsigned long)atomic_inc_return(&edev_no)); // 设置节点路径
1115
1112	dev_set_name(&edev->dev, "audio%lu",
1113			(unsigned long)atomic_inc_return(&edev_no)); // 设置节点路径为audio
1115

但是,考虑到,除了HDMI audio extcon时间外其他extcon 例如usb host也调用此接口,所以此方案不可行。
2. 模拟原生接口,实现extcon注册过程:

1080int extcon_dev_register(struct extcon_dev *edev)
1081{
1082	........// 代码省略
1112	dev_set_name(&edev->dev, "extcon%lu",
1113			(unsigned long)atomic_inc_return(&edev_no)); // 设置节点路径
1115    ........// 代码省略
1290}
1080int extcon_hdmi_dev_register(struct extcon_dev *edev)
1081{
1082	........// 代码省略e
1112	dev_set_name(&edev->dev, "audio%lu",
1113			(unsigned long)atomic_inc_return(&edev_no)); // 设置节点路径audio
1115    ........// 代码省略
1290}

函数内部实现只改动节点路径audio,其他不变,此方案验证可行。

xxxx:/sys/class/extcon # ls
audio0 extcon0 extcon1

同时观查插拔HDMI 设备state节点变化

插入
xxxx:cat /sys/class/extcon/audio0/state
HDMI=1
拔出
xxxx:cat /sys/class/extcon/audio0/state
HDMI=0

然后,再重新回到WiredAccessoryExtconObserver,现在虽然extcon节点已经创建,并与WiredAccessoryExtconObserver,然后使得if (ExtconUEventObserver.extconExists() && mExtconObserver.uEventCount() > 0) 条件满足,然后调用mExtconObserver.init()

749 private void init() {
750            for (ExtconInfo extconInfo : mExtconInfos) {
751                Pair<Integer, Integer> state = null;
752                try {
753                    state = parseStateFromFile(extconInfo);
754                } catch (FileNotFoundException e) {
755                    Slog.w(TAG, extconInfo.getStatePath()
756                            + " not found while attempting to determine initial state", e);
757                } catch (IOException e) {
758                    Slog.e(
759                            TAG,
760                            "Error reading " + extconInfo.getStatePath()
761                                    + " while attempting to determine initial state",
762                            e);
763                }
764                if (state != null) {
765                    updateState(extconInfo, extconInfo.getName(), state);
766                }
767                if (LOG) Slog.d(TAG, "observing " + extconInfo.getName());
768                startObserving(extconInfo);
769            }
770
771        }

然后读取state状态

45 public S parseStateFromFile(ExtconInfo extconInfo) throws IOException {
46        String statePath = extconInfo.getStatePath();
47        return parseState(
48                extconInfo,
49                FileUtils.readTextFile(new File(statePath), 0, null).trim());
50    }

但此时抛出异常,通过分析发现由于selinux机制导致,没有权限访问该目录,所以尝试加入selinux权限,过程比较痛苦,这里不详细介绍,有兴趣可以查询selinux相关知识,这里只提一点:

尝试在/sys/class/extcon/audio0(/.*)? 失败,查看原因为改路径存在链接,实际路径为:
/sys/devices/platform/soc/7af6000.i2c/i2c-6/6-003b/extcon/audio0(/.*)?

以上具体针对系统实际情况作修改。

完成上述爬坑过程,最终获取到节点state状态值,然后调用updateState,并startObserving这个节点变化。

792 public void updateState(ExtconInfo extconInfo, String name,
793                Pair<Integer, Integer> maskAndState) {
794            synchronized (mLock) {
795                int mask = maskAndState.first;
796                int state = maskAndState.second;
797                updateLocked(name, "", mHeadsetState & ~(mask & ~state) | (mask & state));
798                return;
799            }
800        }
*/
209    private void updateLocked(String newName, String address, int newState) {
210        // Retain only relevant bits
211        int headsetState = newState & SUPPORTED_HEADSETS;
212        int newDpState = newState & BIT_HDMI_AUDIO;
213        int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG;
214        int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL;
215        int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT);
216        ........// 代码省略
228
229        if (mHeadsetState == headsetState && !newName.startsWith(NAME_DP_AUDIO)) {
230            Log.e(TAG, "No state change.");
231            return;
232        }
233
234        // reject all suspect transitions: only accept state changes from:
235        // - a: 0 headset to 1 headset
236        // - b: 1 headset to 0 headset
237        if (h2w_headset == (BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) {
238            Log.e(TAG, "Invalid combination, unsetting h2w flag");
239            h2wStateChange = false;
240        }
241        ........// 代码省略
269
270        Message msg;
271        // send a combined name, address string separated by |
272        ........// 代码省略
286        } else {
287            msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState,
288                                         mHeadsetState,
289                                         newName+"/"+address);
290        }
291        mHandler.sendMessage(msg);
292
293        mHeadsetState = headsetState;
294    }
296 private final Handler mHandler = new Handler(Looper.myLooper(), null, true) {
297        @Override
298        public void handleMessage(Message msg) {
299            switch (msg.what) {
300                case MSG_NEW_DEVICE_STATE:
301                    setDevicesState(msg.arg1, msg.arg2, (String) msg.obj);
302                    mWakeLock.release();
303                    break;
304                ........// 代码省略
308            }
309        }
310    };
312 private void setDevicesState(
313            int headsetState, int prevHeadsetState, String headsetNameAddr) {
314        synchronized (mLock) {
315            int allHeadsets = SUPPORTED_HEADSETS;
316            for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
317                if ((curHeadset & allHeadsets) != 0) {
318                    setDeviceStateLocked(curHeadset, headsetState, prevHeadsetState,
319                                         headsetNameAddr);
320                    allHeadsets &= ~curHeadset;
321                }
322            }
323        }
324    }
private void setDeviceStateLocked(int headset,
327            int headsetState, int prevHeadsetState, String headsetNameAddr) {
328        if ((headsetState & headset) != (prevHeadsetState & headset)) {
329            int outDevice = 0;
330            int inDevice = 0;
331            int state;
332
333            if ((headsetState & headset) != 0) {
334                state = 1;
335            } else {
336                state = 0;
337            }
338
339            if (headset == BIT_HEADSET) {
340                outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET;
341                inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET;
342            } else if (headset == BIT_HEADSET_NO_MIC) {
343                outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
344            } else if (headset == BIT_LINEOUT) {
345                outDevice = AudioManager.DEVICE_OUT_LINE;
346            } else if (headset == BIT_USB_HEADSET_ANLG) {
347                outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
348            } else if (headset == BIT_USB_HEADSET_DGTL) {
349                outDevice = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
350            } else if (headset == BIT_HDMI_AUDIO) {
351                outDevice = AudioManager.DEVICE_OUT_HDMI;
352            } else {
353                Slog.e(TAG, "setDeviceState() invalid headset type: " + headset);
354                return;
355            }
356
357            ........// 代码省略
361
362            String[] hs = headsetNameAddr.split("/");
363            if (outDevice != 0) {
364                ........// 代码省略
368                mAudioManager.setWiredDeviceConnectionState(outDevice, state,
369                                                             (hs.length > 1 ? hs[1] : ""), hs[0]);
370            }
371            if (inDevice != 0) {
372
373              mAudioManager.setWiredDeviceConnectionState(inDevice, state,
374                                                           (hs.length > 1 ? hs[1] : ""), hs[0]);
375            }
376        }
377    }

最终完成系统启动是的HDMI audio设备状态上报;
下面来看下,HDMI audio设备插拔事件上报:
当uevent事件检测到HDMI audio设备插拔事件时,WiredAccessoryExtconObserver通过 onUEvent得知,WiredAccessoryExtconObserver继承的是ExtconStateObserver.java。

public void onUEvent(ExtconInfo extconInfo, UEvent event) {
54        if (LOG) Slog.d(TAG, extconInfo.getName() + " UEVENT: " + event);
55        String name = event.get("NAME");
56        S state = parseState(extconInfo, event.get("STATE"));
57        if (state != null) {
58            updateState(extconInfo, name, state);
59        }
60    }

也会调用updateState函数,然后完成插拔事件上报;

最终通过AudioManager的setWiredDeviceConnectionState接口将HDMI Audio devices状态上报值framework层,完成audio竞合、设备切换,具体内容会在以后文章中体现。

本文为原创文章,转载请注明出处!!!!

  • 4
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值