Android耳機(jī)插拔檢測(framework篇)原理解析
基本原理
在輸入設(shè)備驅(qū)動(input_dev)中,一般通過輪詢或者中斷方式獲取輸入事件的原始值(raw value),經(jīng)過處理后再使用input_evnet()函數(shù)上報;
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 支持的設(shè)備類型
當(dāng)前支持的具體外設(shè)設(shè)備如下:
,另外一種uevent事件上報。
輸入子系統(tǒng)(InputEvent):可以上報按鍵事件也可以上報開關(guān)事件,事件類型包括headset\headPhone\Lineout。對于輸入設(shè)備都需要指定能產(chǎn)生同步類EV_SYN;switch class子系統(tǒng),通過uevent向用戶空間發(fā)送數(shù)據(jù),Android中有個線程專門監(jiān)聽此類事件。使用switch dev子系統(tǒng)時,名字必須要設(shè)置為"h2w",Android系統(tǒng)監(jiān)聽 /sys/class/switch/h2s這個虛擬設(shè)備。
Android系統(tǒng)中最終使用哪種方式?
可以通過配置Android系統(tǒng)中配置文件:在Android系統(tǒng)中默認(rèn)是使用UEvent的方式。不過一般廠商會在自己的xml文件中進(jìn)行配置。
frameworks/base/core/res/res/values/config.xml或者是device/eswin/common/overlay/frameworks/base/core/res/res/values/config.xml
第二個文件會覆蓋第一個文件,修改文件中的**true**變量,該值為true時使用的是tvinput子系統(tǒng),false為ueven機(jī)制。
在InputManagerService.java的構(gòu)造函數(shù)中,config_useDevInputEventForAudioJack的值的初始化mUseDevInputEventForAudioJack決定采用那種方式。所以最終采用的是tvinput子系統(tǒng)的方式。
路徑:frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
三 代碼流程
第一個階段 事件信息上報
input子系統(tǒng)通過inputReader開始讀取事件并處理。(記住這里的InputLisenerInterface listener)
路徑:/frameworks/native/services/inputflinger/reader/InputReader.cpp
InputRead構(gòu)造函數(shù)如下:
對于InputReadThread:
1)啟動循環(huán)后執(zhí)行mReader->loopOnce(),loopOnce()中會調(diào)用mEventHub→getEvents讀取事件;
2)調(diào)用processEventsLocked()處理事件;
3)調(diào)用mPolicy->notifyInputDeviceChanged()用InputManagerService的代理通過Handler發(fā)送MSG_DELIVER_INPUT_DEVICES_CHANGED消息,通知輸入設(shè)備發(fā)生了變化;
4)調(diào)用mQueuedListener->flush(),將事件隊列中的所有事件交給在InputReader中注冊過的InputDispatcher
獲取事件;2)處理事件;3)傳遞事件
1)獲取事件
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE)主要是從DEVICE_PATH = /dev/input獲取kernel的event,這里的事件不僅包含了input,也包含了輸入設(shè)備的add/remove。
(后續(xù)補(bǔ)充)
2)處理事件
通過getEvents()函數(shù)從驅(qū)動中獲取到事件后,調(diào)用processEventsLocked()函數(shù)開始處理:
InputReader.cpp->InputReader::processEventsLocked()
路徑:***
根據(jù)不同的deviceIdc從mDevice中獲取到對應(yīng)的Device,獲取到Device后,判斷該Device是否合法以及是否需要被忽略,如果不是,接下來需要調(diào)用deivce->process()進(jìn)行事件處理。
InputDevice,cpp->InputDevice:process()
路徑:frameworks/native/services/inputflinger/reader/InputDevice.cpp
該函數(shù)中,會依次處理同一個device產(chǎn)生的普通輸入事件,然后通過for_each_mapper_in_subdevice()進(jìn)行轉(zhuǎn)換調(diào)用InputMapper.process()進(jìn)行事件處理。
for_each_mapper_in_subdevice:
路徑:frameworks/native/services/inputflinger/reader/mapper/
從該路徑下的mapper類可以看出,Android將輸入設(shè)備分為以下幾種類型:
● CursorInputMapper :鼠標(biāo)
ExternalStylusInputMapper : 觸控筆
JoystickInputMapper :游戲桿
KeyboardInputMapper :鍵盤
KeyMouseInputMapper :通常由一個手持設(shè)備組成,具有鍵盤和觸控板/鼠標(biāo)的功能,適用于需要鍵盤輸入和鼠標(biāo)操作的情況,如在移動設(shè)備上進(jìn)行文字輸入和瀏覽。
RotaryEncoderInputMapper :旋轉(zhuǎn)編碼器輸入設(shè)備,一種用于測量旋轉(zhuǎn)運(yùn)動的設(shè)備。
SwitchInputMapper : 開關(guān)
TouchInputMapper 、MultiTouchInputMapper、SingleTouchInputMapper:觸摸屏
VibratorInputMapper :震動器,嚴(yán)格意義上是輸出設(shè)備
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; } 實(shí)際上調(diào)用是InputReadContext中的getListener()函數(shù)
路徑: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
最終將相關(guān)事件信息存放到mArgsQueue隊列中。
3)傳遞事件
InputListener.cpp->QueuedInputListener::flush()
路徑:frameworks\native\services\inputflinger\InputListener.cpp
在flush()函數(shù)中,依次取出mArgsQueue隊列中的數(shù)據(jù),調(diào)用NotifyArgs args->notify(mInnerListener)進(jìn)行處理。
NotifySwitchArgs::notify
void NotifySwitchArgs::notify(const sp& listener) const { listener->notifySwitch(this); }
調(diào)用的是 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,調(diào)用的是mClassifier→notifySwitch()
InputClassifier::notifySwitch
void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
// pass through
mListener->notifySwitch(args);
}
構(gòu)造函數(shù):
InputClassifier::InputClassifier(const sp& listener) : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
可以觀察到,mListener(listener)還是上面帶下來的參數(shù)InputDispatcher::mDispatcher,也就是mClassifier→notifySwitch()最終調(diào)用的是InputDispatcher::notifySwitch()
InputDispatcher.cpp->InputDispatcher::notifySwitch()
路徑:frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
構(gòu)造函數(shù):
可以觀察在InputDispatcherPolicyInterface中是個虛函數(shù),最終實(shí)現(xiàn)在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
全局搜索下同名函數(shù)“notifySwitch”
InputManagerService.java->InputManagerService::notifySwitch()
路徑:frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
根據(jù)從xml中獲取到的mUseDevInputEventForAudioJack = true,進(jìn)入到
WiredAccessoryManager.java->WiredAccessoryManager::notifyWiredAccessoryChanged()
路徑:frameworks/base/services/core/java/com/android/server/WiredAccessoryManager.java
WiredAccessoryManager.java->WiredAccessoryManager::updateLocked()
更新耳機(jī)狀態(tài),這里又分了多種耳機(jī): usb_headset_anlg、usb_headset_dgtl 、h2w_headset
設(shè)置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()
該函數(shù)主要分為三步:
a)handleDeviceConnection() 確保設(shè)備連接并向下設(shè)置設(shè)備支持的參數(shù);
b)sendDeviceConnectionIntent() 向上發(fā)送設(shè)備狀態(tài)
c) updataAudioRoutes() 更新Audio路由
a)AudioDeviceInventory::handleDeviceConnection()
AudioDeviceInventory.java->AudioDeviceInventory::handleDeviceConnection()
路徑:frameworks\base\services\core\java\com\android\server\audio\AudioDeviceInventory.java
如果設(shè)備已經(jīng)連接了
通過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);**通過調(diào)用setParameters()通知所有的hardware module,有新的設(shè)備正在處理中:
class AudioPolicyClientInterface:
路徑:frameworks/av/services/audiopolicy/AudioPolicyInterface.h
AudioPolicyClientInterface實(shí)現(xiàn)在AudioPolicyClientImpl.cpp中,調(diào)用到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()
發(fā)送intent去通知音頻外設(shè)的狀態(tài)變化。
路徑:frameworks/base/services/core/java/com/android/server/audio/AudioDeviceInventory.java
c)updateAudioRoutes()
更新音頻路徑
路徑:frameworks/base/services/core/java/com/android/server/audio/AudioDeviceInventory.java
根據(jù)不同的device設(shè)置connType并和前一次的mainType進(jìn)行比較是否需要更新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處理流程走到一樣,還區(qū)分兩者)
AudioDeviceInventory.java->onReportNewRoute()
路徑:frameworks\base\services\core\java\com\android\server\audio\AudioDeviceInventory.java
MediaRouter.java->dispatchAudioRoutesChanged()
其中的mIsBluetoothA2dpOn = mAudioService.isBluetoothA2dpOn()獲取當(dāng)前BT設(shè)備的狀態(tài),會調(diào)用到AudioDeviceBroker.java->isBluetoothA2dpOn()
MediaRouter.java->updateAduioRoutes()
判斷路由信息是否發(fā)生了改變。
到此這篇關(guān)于Android耳機(jī)插拔檢測(framework篇)原理解析的文章就介紹到這了,更多相關(guān)Android耳機(jī)插拔檢測內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android編程記錄ListView標(biāo)記行狀態(tài)的方法
這篇文章主要介紹了Android編程記錄ListView標(biāo)記行狀態(tài)的方法,結(jié)合實(shí)例分析了ListView標(biāo)記的相關(guān)實(shí)現(xiàn)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-11-11Android?JetPack組件的支持庫Databinding詳解
DataBinding是Google發(fā)布的一個數(shù)據(jù)綁定框架,它能夠讓開發(fā)者減少重復(fù)性非常高的代碼,如findViewById這樣的操作。其核心優(yōu)勢是解決了數(shù)據(jù)分解映射到各個view的問題,在MVVM框架中,實(shí)現(xiàn)的View和Viewmode的雙向數(shù)據(jù)綁定2022-08-08Android編程實(shí)現(xiàn)應(yīng)用獲取包名、版本號、權(quán)限等信息的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)應(yīng)用獲取包名、版本號、權(quán)限等信息的方法,涉及Android針對應(yīng)用相關(guān)信息的獲取操作實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-02-02解決Android Studio 代碼自動提示突然失效的問題
這篇文章主要介紹了解決Android Studio 代碼自動提示突然失效的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03Android自定義View實(shí)現(xiàn)鐘擺效果進(jìn)度條PendulumView
這篇文章主要介紹了Android自定義View實(shí)現(xiàn)鐘擺效果進(jìn)度條PendulumView,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-09-09