蓝牙耳机按键在Android侧的处理流程

来源:互联网 发布:python递归函数怎么写 编辑:程序博客网 时间:2024/05/16 08:09

    目前大多数音频视频设备采用红外遥控器,由于距离、角度、障碍物等的影响,红外遥控器的应用受到了很大限制。蓝牙无线通信技术可以实现传统红外遥控全部应用功能,而且客服了红外遥控器的局限性。蓝牙音频视频遥控应用框架(Audio Video Remote Control Profile,AVRCP)就是实现无线遥控功能的规范。

     蓝牙耳机按键的处理在android层主要就是对BT的AVRCP profile的处理。下面我们来具体看下。

    上层对接收到的AVRCP命令的处理有两部分。

       1、对metadata, play status and event notification的处理。(avrcp.java)

       2、对播放、暂停、停止、上一首、下一首的按键处理(快进和快退不在这里处理,是第一种情况)。使用uinput模拟输入设备转化为input event。

现在我们就来分别分析一下上述的两种处理。


一、avrcp音频属性的处理

AVRCP主要负责以下的功能:

1、 接受来自蓝牙耳机的请求,并返回响应;

2、 当用户直接用手机操作音乐时,将状态更新到蓝牙耳机;

3、 将手机音量的改变通知蓝牙耳机,将蓝牙耳机音量的改变通知手机audio;

上面的功能主要是通过回调函数、Native函数和AvrcpMessageHandler类实现的。回调函数负责将来自蓝牙耳机的消息通知给手机,Native函数返回响应给蓝牙耳机。

主要包含以下回调方法:

方法

介绍

getRcFeatrues

Avrcp是否支持absolute volume

getPlayStatus

获取播放状态

getElementAttr

获取音乐属性信息

registerNotification

获取register notification响应

volumeChangeCallback

蓝牙耳机音量改变

handlePassthroughCmd

处理passthrough命令

主要包含以下native方法:

方法

介绍

classInitNative

初始化回调方法

initNative

获取avrcp接口实例,并调用init初始化

cleanupNative

销毁avrcp接口实例

getPlayStatusRspNative

播放状态的响应

getElementAttrRspNative

获取音乐属性信息的响应

registerNotificationRspPlayStatusNative

响应register notification

registerNotificationRspTrackChangeNative

响应register notification

registerNotificationRspPlayPosNative

响应register notification

setVolumeNative

设置蓝牙耳机的音量


二、蓝牙耳机按键在android侧的映射处理 

AVRCP的按键定义:

代码路径在frameworks/base/data/keyboards/AVRCP.kl

key 200   MEDIA_PLAY_PAUSE    WAKE

key 201   MEDIA_PLAY_PAUSE    WAKE

key 166   MEDIA_STOP          WAKE

key 163   MEDIA_NEXT          WAKE

key 165   MEDIA_PREVIOUS      WAKE

key 168   MEDIA_REWIND        WAKE

key 208   MEDIA_FAST_FORWARD  WAKE

前面的key 值是在蓝牙耳机中定义的key code ,后面的MEDIA_xx是在android中定义的key code。

在google remote中,android接收端接收socket发来的IR CODE,然后将IRCODE模拟出来发给系统处理,这就是google remote接收端的原理。这里使用的是使用uinput模拟输入设备。通过 send_key函数,使用uinput桥接,发送input event。uinput原理是利用内核现有的uinput驱动,通过内核驱动uinput来发送input event。

avrcp按键映射成inputevent处理过程:

Thread: InputDeviceReader是整个Input过程的控制中心(在Android4.x中,这个thread在InputManager.cpp中)。这个thread通过EventHub::getEvent()来读Linux的 /dev/input目录下的Input Devices,从而得到硬件的key input。基于一个与每个Input Device关联的key layout map,key input会被map成Android能够识别的Key Code,比如一个AVRCP Input Device的key input值200就会被map成MEDIA_PLAY。

需要注意的是,在EventHub::getEvnet()读Input Devices之前,需要判断Devices是否已经被打开。如果没有就需要去调用EventHub::openPlatformInput()去扫描 /dev/input/下有哪些设备,然后打开这些设备并载入它们各自的keylayout map(/frameworks/base/data/keyboards/ 中的*.kl文件就是用于定义key layout map的,其中的AVRCP.kl就定义了Bluetooth AVRCP Input Device的key layout map。在Android Device的root fs中,这些.kl文件会被放在/system/usr/keylayout/目录)。这些被打开的设备的file descriptor和device_t指针会分别保存在mFDs和mDevices中。

InputDeviceReaderThread得到Key Code之后,会交给Java层的代码来处理。最终由PhoneWindowManager.java中的interceptKeyBeforeQueueing()创建一个消息 MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK,经过消息的转发和处理最后在dispatchMediaKeyEven函数里创建并发送ACTION_MEDIA_BUTTON intent广播出去。

发送ACTION_MEDIA_BUTTON的代码路径:

frameworks/base/media/java/android/media/MediaFocusControl.java

接收ACTION_MEDIA_BUTTON的代码路径在:

packages/apps/Music/src/com/android/music/MediaButtonIntentReceiver.java

在文件MediaButtonIntentReceiver里会把按键转化为MediaPlaybackService的command,然后启动MediaPlaybackService对这些command进行处理,包括音乐进行暂停、播放、停止等操作。

MediaPlaybackService的代码路径:

packages/apps/Music/src/com/android/music/MediaPlaybackService.java

在 onStartCommand里对音乐播放器进行暂停、播放、停止等操作。


三、蓝牙耳机按键的hci log分析

简单说一下发送命令的处理过程,主要分为两步,按键按下发送一个Pass though命令,按键弹起的时候,会再次发送一个Pass though命令,第一个命令的State_flag为Button Pushed,第二个命令的state_flag为Button Released。每个命令都需要对方的回应,如果接受,则会回应Accepted,否则返回Rejected。下面就以蓝牙耳机的播放键为例子来说明一下。

播放按键的BT log如下:


如上图,1463帧的Role为CT,代表为蓝牙耳机,向手机发送play命令,手机回应accepted,然后,CT发送按键弹起的命令,收到ACCEPTED,整个播放的log就结束了。上面的NOTIFY,是蓝牙耳机发送的同步命令,手机会发送CHANGED进行响应,如果在100ms内,不能进行响应,则会先发送INTERIM进行告知,再发送CHANGED。

 

0 0
原创粉丝点击