设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间10 现在时间10 缓存数据 只要你的心是晴的,人生就没有雨天。就像好事情总是发生在那些微笑着的人身上。调整心情,保持微笑。早安!

只要你的心是晴的,人生就没有雨天。就像好事情总是发生在那些微笑着的人身上。调整心情,保持微笑。早安!

查看: 850|回复: 0

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

[复制链接]

  离线 

TA的专栏

  • 打卡等级:热心大叔
  • 打卡总天数:224
  • 打卡月天数:0
  • 打卡总奖励:3964
  • 最近打卡:2025-04-12 11:50:01
等级头衔

等級:晓枫资讯-上等兵

在线时间
5 小时

积分成就
威望
0
贡献
454
主题
390
精华
0
金钱
5222
积分
890
注册时间
2023-1-3
最后登录
2025-5-31

发表于 2025-5-31 06:48:55 | 显示全部楼层 |阅读模式
基本原理
在输入设备驱动(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
1.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
2.png

三 代码流程

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

对于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)传递事件
4.png

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()
路径:***
5.png

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

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

路径: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
8.png

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

而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
10.png

最终将相关事件信息存放到mArgsQueue队列中。
3)传递事件
InputListener.cpp->QueuedInputListener::flush()
路径:frameworks\native\services\inputflinger\InputListener.cpp
11.png

在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) { }
12.png

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

所以上面的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
14.png

构造函数:
15.png

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

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

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

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

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

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

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

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

(涉及到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()
24.png

该函数主要分为三步:
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
25.png

如果设备已经连接了
通过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
26.png

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

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

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

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

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

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

audio_hw.c->audio_hal::adev_set_parameters()
33.png

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

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

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
36.png

MediaRouter.java->dispatchAudioRoutesChanged()
37.png

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

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

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表