目前大多数音频视频设备采用红外遥控器,由于距离、角度、障碍物等的影响,红外遥控器的应用受到了很大限制。蓝牙无线通信技术可以实现传统红外遥控全部应用功能,而且客服了红外遥控器的局限性。蓝牙音频视频遥控应用框架(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。
---------------------
版权声明:本文为CSDN博主「就爱吃鲜橙」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/OswinWang/article/details/60961298