博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 4.x耳机插拔检测实现方法
阅读量:4283 次
发布时间:2019-05-27

本文共 5265 字,大约阅读时间需要 17 分钟。

本文基于Android 4.4撰写,另外也参看了一下4.2,机制相同,也许细节方面会有所不同,这里以4.4为主。

Android耳机插拔可以有两个机制实现:

1. InputEvent

2. UEvent

其中UEvent是Android系统默认的耳机插拔机制,所以我这里最终代码是基于UEvent实现的,对于InputEvent机制只是大概看了看,并没有具体实现,因此不能保证一定正确,寻求解决方法的同学可以直接移步只对UEvent方式的介绍。

1. 耳机检测的硬件原理

首先我们看看耳机检测的原理。一般的耳机检测包含普通的耳机检测和带mic的耳机检测两种,这两种耳机统称为Headset,而对于不带mic的耳机,一般称之为Headphone。

对于Headset装置的插入检测,一般通过Jack即耳机插座来完成,大致的原理是使用带检测机械结构的耳机插座,将检测脚连到可GPIO中断上,当耳机插入时,耳机插头的金属会碰到检测脚,使得检测脚的电平产生变化,从而引起中断。这样就可以在中断处理函数中读取GPIO的的值,进一步判断出耳机是插入还是拔出。

而对于Headset是否带mic的检测,需要通过codec附加的micbias电流的功能,具体可以参考我的下一篇文章。

2. 两种方式的切换

前面提到Android默认提供了两种解决方法,那么一定也提供了两种方式的切换,这个提供切换的设置名为config_useDevInputEventForAudioJack,对Android源代码进行全局搜索,可以看到它在frameworks/base/core/res/res/values/config.xml中,默认为false,即不使用InputEvent方式,另外在包的厂商相关的文件夹中也找到了相关的设置,如下:

/android/4.4/device/asus/flo/overlay/frameworks/base/core/res/res/values/config.xml

false

/android/4.4/device/samsung/manta/overlay/frameworks/base/core/res/res/values/config.xml

true

/android/4.4/device/asus/deb/overlay/frameworks/base/core/res/res/values/config.xml

false

/android/4.4/device/lge/hammerhead/overlay/frameworks/base/core/res/res/values/config.xml

true

/android/4.4/device/lge/mako/overlay/frameworks/base/core/res/res/values/config.xml

true

可以看到有些厂商的确是使用了InputEvent的方式来进行耳机检测。具体对这个变量的修改是在device下还是frameworks下我想应该都可以,device下可能更好。

3. InputEvent

1) 上层的大概机制

InputEvent部分的大概机制可以在网上搜索文章,具体流程我也不是特别清楚,这里大概说一下。

InputEvent的处理主要在InputManagerService.java中。在InputManagerService构造函数中,通过如下函数,

mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);

判断当前是否通过InputEvent实现耳机插拔检测。

当Android得到InputEvent后,会调用InputManagerService.java中notifySwitch的函数,进而转至WiredAccessoryManager.java文件中的notifyWiredAccessoryChanged函数,之后的流程就和UEvent相同了,在后续会讲到。

2) Kernel层的机制

Kernel层对耳机插拔InputEvent处理主要是通过input_report_key/input_report_switch来实现,而在实际使用中,ASOC已经为我们封装好了相应Jack接口函数,只要符合规范就可以拿来使用。下面列出几个常用的接口函数。

int snd_soc_jack_new(structsnd_soc_codec *codec, const char *id, int type, struct snd_soc_jack *jack)

生成一个新的jack对象,定义其被检测的类型,即可能插入的设备类型。一般定义为SND_JACK_HEADSET,其余也可以根据接口支持种类添加SND_JACK_LINEOUT,SND_JACK_AVOUT等。

这个函数中调用了snd_jack_new,而在snd_jack_new中可以看到调用 input_allocate_device()分配了input device,就可以在后续产生input event了。

int snd_soc_jack_add_pins(structsnd_soc_jack *jack, int count, struct snd_soc_jack_pin *pins)

将之前定义好的pins加入dapm widgets中,方便dapm统一管理。这一步和InputEvent没有一定联系,可以不调用,主要是可以将耳机插座定义为widgets加入dapm进行省电管理。

void snd_soc_jack_report(structsnd_soc_jack *jack, int status, int mask)

汇报jack插拔状态,主要完成以下两个工作:

 a) 根据插入拔出状态更新前面通过snd_soc_jack_add_pins加入的dapm pin的状态,对其进行上电下电管理。

? b) 调用snd_jack_report,在其中通过input_report_key/input_report_switch来向上层汇报input event。

基于上面的函数,可以用以下做法来实现基于InputEvent机制的耳机插拔检测:

a) snd_soc_jack_new 创建jack对象

b) snd_soc_jack_add_pins将其加入到dapm wigets中

c) 通过request irq申请耳机插拔中断,在中断处理函数中通过检测线高低电平判断耳机是插入还是拔出,通过读取codec寄存器来判断是headset还是headphone

d) 根据判断结果调用snd_soc_jack_report发送InputEvent

此外,ASOC还提供了一个封装好的函数来实现上述c)和d)步骤的功能:

int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, struct snd_soc_jack_gpio *gpios)

该函数通过标准GPIO驱动申请GPIO及GPIO对应中断,并提供了统一的中断处理函数来汇报事件。此函数只适用于耳机中断接至GPIO且GPIO驱动为Linux标准驱动的情况下,并且不支持mic检测,因此不建议使用。

4. UEvent

UEvent机制比较简单,它基于switch driver,switch driver会在Android建立耳机插拔的目录/sys/devices/virtual/switch/h2w,在此目录下有个设备结点名为state,driver通过更新state的值,从而通知Android上层耳机状态的改变。

1) Android上层机制

针对UEvent机制,Android上层在WiredAccessoryManager.java中实现。

在这个文件中,从UEventObserver中继承了类WiredAccessoryObserver,在makeObservedUEventList中将要观察的事件加入到UEvent中:

if(!mUseDevInputEventForAudioJack) {

uei = new UEventInfo(NAME_H2W,BIT_HEADSET, BIT_HEADSET_NO_MIC);

……

……

}

可以看到,只有当不使用InputEvent时才添加UEvent事件,NAME_H2W就是headphone对应的switch driver的名字。BIT_HEADSET和BIT_HEADSET_NO_MIC是state结点的两个值,分别表示有mic和无mic的耳机。

当UEvent事件到来时,类WiredAccessoryObserver中重载的onUEvent函数会被回调,从而调用updateStateLocked(devPath,name, state) ,其中state的值就是通过/sys/devices/virtual/switch/h2w/state结点来获得。

最后,程序会进入setDeviceStateLocked函数中处理,在setDeviceStateLocked中根据state的值设置device,然后调用mAudioManager.setWiredDeviceConnectionState,最后进入AudioPolicyManagerBase::setDeviceConnectionState。

2) Kernel层的机制

前面说过,基于UEvent的耳机检测机制需要实现一只switchdriver,它会建立一个用于耳机插拔检测的目录/sys/devices/virtual/switch/h2w,在此目录下有个设备结点名为state,switch driver通过更新state的值,从而通知Android上层耳机状态的改变。

switch driver的目录在 kernel的drivers/staging/android/switch目录下,可以从目录名称中看到这只driver是为了Android专门产生的。在switch目录下有两个已有文件,switch_class.c是switch driver的内部实现,它提供了switch driver所需的一些API;switch_gpio.c是一个例子,它实现了一个基于GPIO中断的switch driver。

另外,在drivers/switch目录下也有同样的文件,不同之处是两者在Android下生成的结点的位置不同,如果要按照drivers/switch目录下的switch driver来实现,需要更改WiredAccessoryManager.java文件。

下面讲讲如何添加switch driver。添加switch driver很简单,可以仿照switch_gpio.c,大致步骤如下:

 a) 在drivers/staging/android/switch目录下新建一个platform driver,其中包含一个全局变量struct switch_dev sdev,即要注册的switch device。

b) 在platformdriver的probe函数中调用switch_dev_register将前面的sdev注册到系统中。

int switch_dev_register(struct switch_dev *sdev)

c) 申请用于耳机检测的中断处理函数。对于耳机插拔来说,由于用户的插拔快慢等可能产生多次中断,所以一般是在中断处理函数中实现一个延时工作队列,即INIT_DELAYED_WORK,在队列的回调函数中来进行实际判断。

d) 当中断发生后,通过switch_set_state设置state节点的值,这个值要和WiredAccessoryManager.java文件中定义的一致,可参看BIT_HEADSET和BIT_HEADSET_NO_MIC的定义。目前是0表示无耳机插入,1表示带Mic的耳机,2表示不带Mic的耳机。

void switch_set_state(struct switch_dev *sdev, int state)

我们进一步看看switch_set_state这个函数,在这个函数中调用了kobject_uevent_env/kobject_uevent,这两个函数就是kernel通过uevent来通知user space的核心函数了。

转载地址:http://ycngi.baihongyu.com/

你可能感兴趣的文章
springboot pom文件设置<packaging>pom</packaging> 对于application.yml无法加载读取的问题
查看>>
springboot加载resouce下面的静态文件,templates目录的访问,以及经过controller后跳转页面问题
查看>>
shiro的通过md5+salt+hash散列进行注册操作
查看>>
springboot整合applicationContext实现上下文获取实例bean
查看>>
shiro目前问题记录
查看>>
shiro实现本地内存Ehcache实现将菜单权限进行缓存
查看>>
shiro使用redis实现将菜单权限进行缓存
查看>>
cmd窗口下执行jar包 logger.info输出乱码,out语句输出正常解决办法
查看>>
springboot启动初始化实例,后面进行使用
查看>>
shiro实现加载验证码
查看>>
springboot 搭建多模块调用以及打包执行
查看>>
shiro实现不使用密码加密器进行登录
查看>>
权限管理系统笔记
查看>>
java8 新特性 拼接字符串
查看>>
springboot中mybaits自动返回新增数据的主键
查看>>
shiro的使用freemark实现前端控制权限
查看>>
权限管理系统中功能权限&数据权限以及权限模块的实现
查看>>
shiro中LoginUrl与UnauthorizedUrl的作用
查看>>
权限管理系统之数据权限表RBAC
查看>>
shiro权限执行逻辑流程
查看>>