耳机插拔流程

1.1      耳机

在Android系统中,有线耳机分两种,一种带mic,一种不带mic,带mic的耳机被称为Headset,不带mic的耳机被称为HeadPhone。在audio.h中,有以下几个设备来表示耳机:

AUDIO_DEVICE_OUT_WIRED_HEADSET             = 0x4,

AUDIO_DEVICE_OUT_WIRED_HEADPHONE           = 0x8,

AUDIO_DEVICE_IN_WIRED_HEADSET         = AUDIO_DEVICE_BIT_IN | 0x10,

对于Headset的插入检测,一般通过jack耳机插座来完成,大概原理是使用带检测机构的耳机插座,将检测脚连到GPIO中断上,耳机插入时,是的检测脚的电平变化,引起中断。通过GPIO的值判断耳机是插入还是拔出。

1.2      Uevent

目前使用的耳机插拔事件使用的是UEvent。但是也可以使用InputEvent,这种设置在frameworks/base/core/res/res/values/config.xml中,设置项为

<boolname="config_useDevInputEventForAudioJack">false</bool>

值为false,所以目前kernel是用Uevent来通知应用层耳机插拔的。

什么是Uevent:

1.    内核通知应用的一种方式

2.    目前使用socket进行内核和应用的通信

3.    uevent就是一个特殊格式的字符串,如下,这应该是插入鼠标的Uevent

"[email protected]/class/input/input9/mouse2\0    // message

ACTION=add\0                         // action type

DEVPATH=/class/input/input9/mouse2\0 // path in /sys

SUBSYSTEM=input\0                    // subsystem (class)

SEQNUM=1064\0                        // sequence number

PHYSDEVPATH=/devices/pci0000:00/0000:00:1d.1/usb2/2-2/2-2:1.0\0  // device path in /sys

PHYSDEVBUS=usb\0      // bus

PHYSDEVDRIVER=usbhid\0 // driver

MAJOR=13\0            // major number

MINOR=34\0",          // minor number

内核发送uevent使用kobject_uevent_env,它还有一个wrapper是kobject_uevent,这个函数使用的比较多

1.3      UEvent是怎么工作的?

1.3.1       Switch驱动

Switch是在android中实现的一个module,可动态加载,是一个驱动。
Switch相关的代码在在drivers\switch\switch_class.c和drivers\switch\switch_gpio.c中,其中switch_gpio基于platform_device框架,在switch_class.c中,Export了以下两个函数供switch_gpio使用

EXPORT_SYMBOL_GPL(switch_dev_register);

EXPORT_SYMBOL_GPL(switch_dev_unregister);

而Switch_gpio.c是驱动模块栈的更靠近设备的一层,这个设备就是gpio,初始化函数是

gpio_switch_probe{

switch_dev_register(&switch_data->sdev);

gpio_request(switch_data->gpio,pdev->name);

gpio_direction_input(switch_data->gpio);

gpio_to_irq(switch_data->gpio);

request_irq//申请GPIO中断

gpio_switch_work

}

这货是个platform驱动,它会调用

static int __init gpio_switch_init(void)

{

returnplatform_driver_register(&gpio_switch_driver);

}

把自己注册成一个platform驱动。Platform是一种虚拟总线,和spi,emmc等都是类似的。

基于platform device/driver框架,在probe函数中完成初始化,包括获取gpio的使用权限,设置gpio方向为输入,注册switch_dev设备,为gpio分配中断,指定中断服务程序,初始化一个gpio_switch_work工作,最后读取gpio初始状态。

在switch_class.c中,调用switch_set_state->switch_gpio_print_state输出GPIO状态到 sysfs。

Sysfs是一个虚拟的文件系统,由linux内核提供,通过使用虚拟文件,sysfs向用户空间公布了关于内核子系统的信息,硬件设备和相关的设备驱动。但是这个文件系统并不是被上层直接使用的,它仅仅是作为比较直观的方式提供给human
being检查状态用的。

而真正发送uevent是在switch_class.c的这个函数里:

void switch_set_state(struct switch_dev *sdev,int state)

最终通过kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp)发送。

kobject_uevent_env的流程如下:

1.    调用kobject_uevent_env之前,系统已经调用uevent_net_init(struct net
*net)完成了初始化工作,也就是创建了socket,并创建了netlink,如下:

netlink_kernel_create(net,NETLINK_KOBJECT_UEVENT,1, NULL, NULL, THIS_MODULE);

sock_create_lite(PF_NETLINK,SOCK_DGRAM, unit, &sock)

__netlink_create(&init_net,sock, cb_mutex, unit)

2.    netlink_broadcast_filtered,netlink广播uevent信息。

sk_for_each_bound

do_one_broadcast(sk,&info);

上面提到了Netlink socket,下面看看这是啥:

Netlink socket是一种Linux特有的socket,用于实现用户进程与内核进程之间通信的一种特殊的进程间通信方式(IPC)
,也是网络应用程序与内核通信的最常用的接口。

Netlink 是一种在内核和用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API
就能使用 Netlink
提供的强大功能,内核态需要使用专门的内核 API
来使用 Netlink。

1.3.2       Netlink Socket通信

用户空间程序只需要创建一个NETLINK socket描述符,就可以侦听Uevent了,如下:

sockfd=socket(AF_NETLINK,SOCK_RAW,NETLINK_KOBJECT_UEVENT);

AF_NETLINK 是一种“domain”,定义了通信使用的协议,比如我们通常使用的IPV4协议。

<sys/socket.h>定义的所有的协议如下:


AF_UNIX,


AF_LOCAL


AF_INET


IPv4


AF_INET6


IPv6


AF_IPX


IPX


AF_NETLINK


Kernel


AF_X25


ITU-T


AF_AX25


Amateur


AF_ATMPVC


Access


AF_APPLETALK


AppleTal


AF_PACKET


Low


AF_ALG


Interface

事实上,Framework就是写了一个很简单的socket网络程序:

在uevent.c (hardware\libhardware_legacy\uevent)中,看函数uevent_init,

s =socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

setsockopt(s,SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));

bind(s,(struct sockaddr *) &addr, sizeof(addr)

再看uevent_next_event(char* buffer, intbuffer_length)

int count =recv(fd, buffer, buffer_length, 0);

其实就是读取socket的内容到buffer中。

1.3.3       观察者模式

拿到socket的内容后,需要通知对它感兴趣的模块。这里就使用了观察者模式。Android在frameworks/base/services/java/com/android/server/WiredAccessoryManager. java中实现对UEvent的处理。在这个文件中,从UEventObserver中继承了类WiredAccessoryObserver,在makeObservedUEventList中将要观察的事件加入到UEvent系统中:

UeventObserver持有一个静态的UEventThread对象,所有子类Observer都共享一个Thread。

private staticUEventThread sThread;

UeventThread是一个静态类,有一个Array:

private finalArrayList<Object> mKeysAndObservers = new ArrayList<Object>();

各种子类Observer都添加key和observer到mKeysAndObservers。比如,WiredAccessoryObserver添加(“DEVPATH=
/devices/virtual/switch/h2w”,this),(“DEVPATH=
/devices/virtual/switch/usb_audio”,this),(“DEVPATH=
/devices/virtual/switch/hdmi”,this)到mKeysAndObservers。这个Thread不断的去读取AF_NETLINK中的数据,并对注册进去的key字符串进行匹配,匹配上的调用改key对应的observer的onUevent函数。

如何匹配的?

private voidsendEvent(String message){

for(;;){

if(message.contains(key))

mTempObserversToSignal.add(observer);

}

for (;;) {

final UEventObserverobserver = mTempObserversToSignal.get(i);

observer.onUEvent(event);

}

}

这个onUEvent经过一系列无聊的调用,最终调到

AudioManager::
setWiredDeviceConnectionState(intdevice, int state, String name),自此进入Audio子系统

1.4      Audio子系统

最终会到Audio Policy中的setDeviceConnectionState来,它有两个参数,一个表示设备id,另一个表示该设备是插入还是拔出。比如设备id
4代表的是HEADSET,而当state是AUDIO_POLICY_DEVICE_STATE_AVAILABLE时表示插入,当AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE时表示拔出。当设备有hw
module与之相对应时(getModuleForDevice),会增加或减少mAvailableOutputDevices和mAvailableInputDevices。

时间: 2024-10-12 04:52:53

耳机插拔流程的相关文章

Android4.&#215;耳机插拔检测

Android4.2耳机插拔检测实现方法 1. 耳机检测的硬件原理 一般的耳机检测包含普通的耳机检测和带mic的耳机检测两种,这两种耳机统称为Headset,而对于不带mic的耳机,一般称之为Headphone. 对于Headset装置的插入检测,一般通过Jack即耳机插座来完成,大致的原理是使用带检测机械结构的耳机插座,将检测脚连到可GPIO中断上,当耳机插入时,耳机插头的金属会碰到检测脚,使得检测脚的电平产生变化,从而引起中断.这样就可以在中断处理函数中读取GPIO的的值,进一步判断出耳机是

音乐播放-后台-耳机控制-耳机插拔

1.首先需要引用系统Framework – AVFoundation,然后在AppDelegate的应用启动事件里面添加以下代码: AVAudioSession *session = [AVAudioSession sharedInstance]; [session setCategory:AVAudioSessionCategoryPlayback error:nil] [session setActive:YES error:nil] AVAudioSessionCategoryPlayba

Android底层开发之耳机插拔与音频通道切换实例

Android底层开发之耳机插拔与音频通道切换实例 由于使用的是耳机 麦克分离式的耳机,所以要分别上报事件.在Android系统层耳机插孔的检测是基于/sys/class/switch/h2w/state的值来判断的(以4.4.4_r2为例子位于WiredAccessoryManager.java). 只要在内核中实现一个「或真或假」的基于switch类的h2w开关.Android系统就可以监听到插拔信息. 在播放音乐的时候插入耳机,使用tinymix(参考:Android音频底层调试-基于ti

Linux ALSA 音频系统:物理链路篇

1. Overview 硬件平台及软件版本: Kernel - 3.4.5 SoC - Samsung exynos CODEC - WM8994 Machine - goni_wm8994 Userspace - tinyalsa Linux ALSA 音频系统架构大致如下: +--------+ +--------+ +--------+ |tinyplay| |tinycap | |tinymix | +--------+ +--------+ +--------+ | ^ ^ V | V

[深入理解Android卷一全文-第七章]深入理解Audio系统

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该由于纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. 第7章  深入理解Audio系统 本章主要内容 ·  具体分析AudioTrack. ·  具体分析AudioFlinger. ·  具体分析AudioPolicyService. 本章涉及的源代码文件名称及位置 以下是本章分析的源代码文件名称及其位置. ·  AudioTrack.java framewor

Android从零开发目录

育知2016全新课程体系 全明星讲师团倾力打造,专业成就非凡,价值与收获倍增 阶段 科目 主要内容 课时 Android编程基础——JavaSE阶段 Java语言入门 1.介绍计算机的基本使用和DOS常用命令 2.Java概述和开发环境 3.关键字.标识符.常量与变量 4.数据类型和运算符 5.流程控制与数组 6.方法的定义及格式.正确的调用方法                     一周 OOP编程 1.面向对象编程(OOP).类和对象, 2.成员变量和局部变量.方法(方法重载).匿名对象

Android的系统服务一览

System_Server进程 运行在system server进程中的服务比较多,这是整个Android框架的基础 Native服务 SurfaceFlinger 这是framebuffer合成的服务,将各个应用程序及应用程序中的逻辑窗口图像数据(surface)合成到一个物理窗口中显示(framebuffer)的服务程序 Java服务:http://hovertree.com/menu/java/这部分的服务大部分都有一个供应用进程使用的manager类,这就是一个RPC调用,用户通过调用x

Android4.4 耳机检测分析

在ALSA架构中,ASOC是由3个部分组成:Platform.CODEC & Machine.而耳机检测一般是在Machine driver里实现,当然也可以在CODEC driver里实现.耳机插拔接口Jack的结构图如图1所示(iPhone和Nokia耳机MIC & GROUND正好相反). 图1.Jack结构图 耳机检测信号HP_DET#可以通过以下有两种方式传递到SOC端: 1.直接将HP_DET#信号接到SOC端: 2.将HP_DET#信号先接到CODEC端,再有CODEC端GP

Android中的Audio播放:控制Audio输出通道切换

Audio 输出通道有很多,Speaker.headset.bluetooth A2DP等.通话或播放音乐等使用Audio输出过程中,可能发生Audio输出通道的切换.比如,插入有线耳机播放音乐时,声音是从耳机发出的:而此时拔出耳机,Audio输出通道会发生切换.如果音乐播放器不做处理,Audio输出是被切换到扬声器的,声音直接从Speaker发出.我们在编写程序时,要捕获并按照需求来处理这样的事,本文就是讲解如何处理的. Android中可以通过android.media.AudioManag