Android耳机插拔检测(framework篇)原理解析

 更新时间:2024年11月26日 10:56:12   作者:呦呦呦是代码啊  
文章详细介绍了Android系统中音频设备插拔事件的处理流程,包括事件的获取、处理和上报,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧

基本原理
在输入设备驱动(input_dev)中,一般通过轮询或者中断方式获取输入事件的原始值(raw value),经过处理后再使用input_evnet()函数上报;
android native层的input flinger会去读这个event,读到后往android java层notify,notify给InputManagerService/WiredAccessoryManager,WiredAccessoryManager在处理这个msg。
涉及到的类文件:
● InputManagerService.java
./framework/base/services/core/java/com/android/server/input/InputManagerService.java
*WiredAccessoryManager.java.
./framework/base/services/core/java/com/android/server/WiredAccessoryManager.java
config.xml
./framework/base/core/res/res/values/config.xml
SystemServer.java
./framework/base/services/java/com/android/server/SystemServer.java
AudioManager.java
./base/media/java/android/media/AudioManager.java
AudioService.java
./base/media/java/android/media/AudioService.java

InputReader.cpp->InputReader::processEventsLocked()
InputDevice.cpp->InputDevice:process()
SwitchInputMapper.cpp->SwitchInputMapper::process()
InputMapper.h->InputMap::getListener()
InputListener.cpp->mQueuedListener->notifySwitch

3)传递事件
InputListener.cpp->QueuedInputListener::flush()
NotifySwitchArgs::notify
InputClassifier::notifySwitch
InputDispatcher.cpp->InputDispatcher::notifySwitch()
com_android_server_input_InputManagerService.cpp->NativeInputManager::notifySwitch()
InputManagerService.java->InputManagerService::notifySwitch()
WiredAccessoryManager.java->WiredAccessoryManager::notifyWiredAccessoryChanged()
WiredAccessoryManager.java->WiredAccessoryManager::updateLocked()
WiredAccessoryManager.java->WiredAccessoryManager::setDeviceStateLocked
AUdioManager.java->AudioManager::setWiredDeviceConnectionState()
AudioService.java->AudioSystem::setWiredDeviceConnectionState()
AudioDeviceInventory->AudioDeviceInventory::onSetWiredDeviceConnectionState()

a)AudioDeviceInventory::handleDeviceConnection()
AudioDeviceInventory.java->AudioDeviceInventory::handleDeviceConnection()
AudioSystem.cpp->AudioSystem::setDeviceConnectionState()
AudioPolicyManager.cpp->AudioPolicyManager::setDeviceConnectionState()
class AudioPolicyClientInterface:
AudioPolicyService.cpp->AudioPolicyService:: SET_PARAMETERS
AudioFlinger.cpp->AudioFlinger::setParameters()
DeviceHalHidl.cpp->DeviceHalHidl::setParameters()
Device.cpp->Device::halSetParameters()
audio_hw.c->audio_hal::adev_set_parameters()

b) sendDeviceConnectionIntent()

c)updateAudioRoutes()
AudioDeviceBroker.java->postReportNewRoutes()
AudioDeviceInventory.java->onReportNewRoute()
MediaRouter.java->dispatchAudioRoutesChanged()
MediaRouter.java->updateAduioRoutes()

二 插拔事件上报

2.1 支持的设备类型
当前支持的具体外设设备如下:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/1ffbd9aa88f343068239c77713363d02.png

2.2 上报方式

有两种上报插拔事件的方式,一种是输入子系统,另外一种uevent事件上报。

输入子系统(InputEvent):可以上报按键事件也可以上报开关事件,事件类型包括headset\headPhone\Lineout。对于输入设备都需要指定能产生同步类EV_SYN;switch class子系统,通过uevent向用户空间发送数据,Android中有个线程专门监听此类事件。使用switch dev子系统时,名字必须要设置为"h2w",Android系统监听 /sys/class/switch/h2s这个虚拟设备。
Android系统中最终使用哪种方式?
可以通过配置Android系统中配置文件:在Android系统中默认是使用UEvent的方式。不过一般厂商会在自己的xml文件中进行配置。
frameworks/base/core/res/res/values/config.xml或者是device/eswin/common/overlay/frameworks/base/core/res/res/values/config.xml
第二个文件会覆盖第一个文件,修改文件中的**true**变量,该值为true时使用的是tvinput子系统,false为ueven机制。
在InputManagerService.java的构造函数中,config_useDevInputEventForAudioJack的值的初始化mUseDevInputEventForAudioJack决定采用那种方式。所以最终采用的是tvinput子系统的方式。
路径:frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

三 代码流程

第一个阶段 事件信息上报

input子系统通过inputReader开始读取事件并处理。(记住这里的InputLisenerInterface listener)
路径:/frameworks/native/services/inputflinger/reader/InputReader.cpp
InputRead构造函数如下:

对于InputReadThread:
1)启动循环后执行mReader->loopOnce(),loopOnce()中会调用mEventHub→getEvents读取事件;
2)调用processEventsLocked()处理事件;
3)调用mPolicy->notifyInputDeviceChanged()用InputManagerService的代理通过Handler发送MSG_DELIVER_INPUT_DEVICES_CHANGED消息,通知输入设备发生了变化;
4)调用mQueuedListener->flush(),将事件队列中的所有事件交给在InputReader中注册过的InputDispatcher

获取事件;2)处理事件;3)传递事件

1)获取事件

size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE)主要是从DEVICE_PATH = /dev/input获取kernel的event,这里的事件不仅包含了input,也包含了输入设备的add/remove。
(后续补充)
2)处理事件

通过getEvents()函数从驱动中获取到事件后,调用processEventsLocked()函数开始处理:
InputReader.cpp->InputReader::processEventsLocked()
路径:***

根据不同的deviceIdc从mDevice中获取到对应的Device,获取到Device后,判断该Device是否合法以及是否需要被忽略,如果不是,接下来需要调用deivce->process()进行事件处理。
InputDevice,cpp->InputDevice:process()
路径:frameworks/native/services/inputflinger/reader/InputDevice.cpp

该函数中,会依次处理同一个device产生的普通输入事件,然后通过for_each_mapper_in_subdevice()进行转换调用InputMapper.process()进行事件处理。
for_each_mapper_in_subdevice:

路径:frameworks/native/services/inputflinger/reader/mapper/
从该路径下的mapper类可以看出,Android将输入设备分为以下几种类型:
● CursorInputMapper :鼠标
ExternalStylusInputMapper : 触控笔
JoystickInputMapper :游戏杆
KeyboardInputMapper :键盘
KeyMouseInputMapper :通常由一个手持设备组成,具有键盘和触控板/鼠标的功能,适用于需要键盘输入和鼠标操作的情况,如在移动设备上进行文字输入和浏览。
RotaryEncoderInputMapper :旋转编码器输入设备,一种用于测量旋转运动的设备。
SwitchInputMapper : 开关
TouchInputMapper 、MultiTouchInputMapper、SingleTouchInputMapper:触摸屏
VibratorInputMapper :震动器,严格意义上是输出设备
SwitchInputMapper.cpp->SwitchInputMapper::process()
路径:frameworks/native/services/inputflinger/reader/mapper/SwitchInputMapper.cpp

将所有的事件信息封装成一个NotifySwitchArgs对象。所以这个getLintener()是谁??
InputMapper.h->InputMap::getListener()
路径:frameworks/native/services/inputflinger/reader/mapper/InputMapper.h

而mDeviceContext又是一个InputDeviceContext类型的,可以看到:
inline InputReaderContext* getContext() { return mContext; } 实际上调用是InputReadContext中的getListener()函数
路径:frameworks\native\services\inputflinger\reader\InputReader.cpp
InputListenerInterface* InputReader::ContextImpl::getListener() {
return mReader->mQueuedListener.get();
}
所以getListener()->notifySwtch最终为mQueuedListener->notifySwitch(&args)
InputListener.cpp->mQueuedListener->notifySwitch
路径:frameworks/native/services/inputflinger/InputListener.cpp

最终将相关事件信息存放到mArgsQueue队列中。
3)传递事件

InputListener.cpp->QueuedInputListener::flush()
路径:frameworks\native\services\inputflinger\InputListener.cpp

在flush()函数中,依次取出mArgsQueue队列中的数据,调用NotifyArgs args->notify(mInnerListener)进行处理。
NotifySwitchArgs::notify
void NotifySwitchArgs::notify(const sp& listener) const { listener->notifySwitch(this); }
调用的是 listener->notifySwitch(this), 所有传入的mInnerListener是哪位??
路径:frameworks\native\services\inputflinger\InputListener.cpp
QueuedInputListener::QueuedInputListener(const sp& innerListener) : mInnerListener(innerListener) { }

InputManager::InputManager()
路径:frameworks/native/services/inputflinger/InputManager.cpp

所以上面的mInnerListener就是mClassifier,调用的是mClassifier→notifySwitch()
InputClassifier::notifySwitch
void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
// pass through
mListener->notifySwitch(args);
}
构造函数:
InputClassifier::InputClassifier(const sp& listener) : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
可以观察到,mListener(listener)还是上面带下来的参数InputDispatcher::mDispatcher,也就是mClassifier→notifySwitch()最终调用的是InputDispatcher::notifySwitch()
InputDispatcher.cpp->InputDispatcher::notifySwitch()
路径:frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp

构造函数:

可以观察在InputDispatcherPolicyInterface中是个虚函数,最终实现在NativeInputManager::notifySwitch()
com_android_server_input_InputManagerService.cpp->NativeInputManager::notifySwitch()
路径:frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

路径:frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

全局搜索下同名函数“notifySwitch”
InputManagerService.java->InputManagerService::notifySwitch()
路径:frameworks\base\services\core\java\com\android\server\input\InputManagerService.java

根据从xml中获取到的mUseDevInputEventForAudioJack = true,进入到

WiredAccessoryManager.java->WiredAccessoryManager::notifyWiredAccessoryChanged()
路径:frameworks/base/services/core/java/com/android/server/WiredAccessoryManager.java

WiredAccessoryManager.java->WiredAccessoryManager::updateLocked()
更新耳机状态,这里又分了多种耳机: usb_headset_anlg、usb_headset_dgtl 、h2w_headset

设置outputDevice = DEVICE_OUT_WIRED_HEADPHONE = 0x8
WiredAccessoryManager.java->WiredAccessoryManager::setDeviceStateLocked
路径:frameworks\base\services\core\java\com\android\server\WiredAccessoryManager.java

判断headphone是否带mic。
AUdioManager.java->AudioManager::setWiredDeviceConnectionState()
路径:frameworks/base/media/java/android/media/AudioManager.java

(涉及到Binder通信一堆东西)
AudioService.java->AudioSystem::setWiredDeviceConnectionState()
路径:frameworks/base/services/core/java/com/android/server/audio/AudioService.java

围绕着:
frameworks/base/services/core/java/com/android/server/audio/AudioDeviceBroker.java
frameworks/base/services/core/java/com/android/server/audio/AudioDeviceInventory.java
AudioDeviceBroker::setWiredDeviceConnectionState()
->AudioDeviceInventory::setWiredDeviceConnectionState()
→AudioDeviceBroker::postSetWiredDeviceConnectionState()
→AudioDeviceInventory::onSetWiredDeviceConnectionState()
AudioDeviceInventory->AudioDeviceInventory::onSetWiredDeviceConnectionState()

该函数主要分为三步:
a)handleDeviceConnection() 确保设备连接并向下设置设备支持的参数;
b)sendDeviceConnectionIntent() 向上发送设备状态
c) updataAudioRoutes() 更新Audio路由
a)AudioDeviceInventory::handleDeviceConnection()
AudioDeviceInventory.java->AudioDeviceInventory::handleDeviceConnection()
路径:frameworks\base\services\core\java\com\android\server\audio\AudioDeviceInventory.java

如果设备已经连接了
通过final int res = mAudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName, AudioSystem.AUDIO_FORMAT_DEFAULT);
通过AudioSystemAdapter.java->AudioSystem.java->android_media_AudioSystem.cpp->AudioSystem.cpp
AudioSystem.cpp->AudioSystem::setDeviceConnectionState()
路径:frameworks/av/media/libaudioclient/AudioSystem.cpp

AudioPolicyManager.cpp->AudioPolicyManager::setDeviceConnectionState()
路径:frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
→AudioPolicyManager::setDeviceConnectionStateInt()
路径:frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp

先看这个**broadcastDeviceConnectionState(dev, state);**通过调用setParameters()通知所有的hardware module,有新的设备正在处理中:

class AudioPolicyClientInterface:
路径:frameworks/av/services/audiopolicy/AudioPolicyInterface.h

AudioPolicyClientInterface实现在AudioPolicyClientImpl.cpp中,调用到AudioPolicyService中:
AudioPolicyService.cpp->AudioPolicyService:: SET_PARAMETERS
路径:frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp

AudioFlinger.cpp->AudioFlinger::setParameters()
路径:frameworks/av/services/audioflinger/AudioFlinger.cpp

DeviceHalHidl.cpp->DeviceHalHidl::setParameters()
路径:frameworks/av/media/libaudiohal/impl/DeviceHalHidl.cpp

Device.cpp->Device::halSetParameters()
路径:hardware/interfaces/audio/core/all-versions/default/Device.cpp

audio_hw.c->audio_hal::adev_set_parameters()

b) sendDeviceConnectionIntent()
发送intent去通知音频外设的状态变化。
路径:frameworks/base/services/core/java/com/android/server/audio/AudioDeviceInventory.java

c)updateAudioRoutes()
更新音频路径
路径:frameworks/base/services/core/java/com/android/server/audio/AudioDeviceInventory.java
根据不同的device设置connType并和前一次的mainType进行比较是否需要更新route

AudioDeviceBroker.java->postReportNewRoutes()
/package/ void postReportNewRoutes(boolean fromA2dp) {
sendMsgNoDelay(fromA2dp ? MSG_REPORT_NEW_ROUTES_A2DP : MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
}
handleMessage()
case MSG_REPORT_NEW_ROUTES:
case MSG_REPORT_NEW_ROUTES_A2DP:
synchronized (mDeviceStateLock) {
mDeviceInventory.onReportNewRoutes();
}
break;
(搞不懂这里,MSG_REPORT_NEW_ROUTES和MSG_REPORT_NEW_ROUTES_A2DP处理流程走到一样,还区分两者)
AudioDeviceInventory.java->onReportNewRoute()
路径:frameworks\base\services\core\java\com\android\server\audio\AudioDeviceInventory.java

MediaRouter.java->dispatchAudioRoutesChanged()

其中的mIsBluetoothA2dpOn = mAudioService.isBluetoothA2dpOn()获取当前BT设备的状态,会调用到AudioDeviceBroker.java->isBluetoothA2dpOn()

MediaRouter.java->updateAduioRoutes()
判断路由信息是否发生了改变。

到此这篇关于Android耳机插拔检测(framework篇)原理解析的文章就介绍到这了,更多相关Android耳机插拔检测内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Android开源项目PullToRefresh下拉刷新功能详解2

    Android开源项目PullToRefresh下拉刷新功能详解2

    这篇文章主要为大家进一步的介绍了Android开源项目PullToRefresh下拉刷新功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • Android 启动模式详细介绍

    Android 启动模式详细介绍

    这篇文章主要介绍了Android 启动模式详细介绍的相关资料,Activity一共有以下四种launchMode启动模式,这里一一做详解,需要的朋友可以参考下
    2016-12-12
  • Android自带倒计时控件Chronometer使用方法详解

    Android自带倒计时控件Chronometer使用方法详解

    这篇文章主要为大家详细介绍了Android自带倒计时控件Chronometer的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • android 获取手机内存及 内存可用空间的方法

    android 获取手机内存及 内存可用空间的方法

    下面小编就为大家带来一篇android 获取手机内存及SD卡内存可用空间的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • 超酷炫的Android碎纸机效果推荐

    超酷炫的Android碎纸机效果推荐

    这篇文章运用xml和java实现了Android版的碎纸机动画,效果非常好,推荐给有需要的小伙伴们使用。
    2016-07-07
  • Android中如何实现清空搜索框的文字

    Android中如何实现清空搜索框的文字

    本文主要介绍Android中实现清空搜索框的文字的方法。项目中的有关搜索的地方,加上清空文字的功能,目的是为了增加用户体验,使用户删除文本更加快捷。需要的朋友一起来看下吧
    2016-12-12
  • Android实现字母雨的效果

    Android实现字母雨的效果

    在学习Android的时候见到别人实现的黑客帝国的字母雨效果,感觉效果很炫,今天我们也来实现一下。
    2016-07-07
  • Android的HTTP操作库Volley的基本使用教程

    Android的HTTP操作库Volley的基本使用教程

    这篇文章主要介绍了Android的HTTP操作库Volley的基本使用教程,包括JSON请求与图片加载等用法的实例,需要的朋友可以参考下
    2016-05-05
  • android 判断横竖屏问题的详解

    android 判断横竖屏问题的详解

    本篇文章是对android中如何判断横竖屏的方法进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • Android WebView实现全屏播放视频

    Android WebView实现全屏播放视频

    WebView是Android系统中的原生控件,其主要功能与前端页面进行响应交互,快捷省时地实现如期的功能,相当于增强版的内置浏览器。这篇文章主要介绍的是利用WebView实现全屏播放视频的功能,感兴趣的小伙伴可以了解一下
    2021-12-12

最新评论