耳机插入上层处理流程分析
备注:本文主要分析 KERNEL以上的部分, KERNEL的处理见另外的文章。
1. WiredAccessoryManager
其构造函数中, NEW 了一个mObserver = new WiredAccessoryObserver();
这个类继承自 UEventObserver, 当KERNEL 检测到耳机状态发生变化时,会回调此类的方法:
@Override
public void onUEvent(UEventObserver.UEvent event) {
if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
try {
String devPath = event.get("DEVPATH");
String name = event.get("SWITCH_NAME");
int state = Integer.parseInt(event.get("SWITCH_STATE"));
synchronized (mLock) {
updateStateLocked(devPath, name, state);
}
} catch (NumberFormatException e) {
Slog.e(TAG, "Could not parse switch state from event " + event);
}
}
——》
private void updateStateLocked(String devPath, String name, int state) {
for (int i = 0; i < mUEventInfo.size(); ++i) {
UEventInfo uei = mUEventInfo.get(i);
if (devPath.equals(uei.getDevPath())) {
updateLocked(name, uei.computeNewHeadsetState(mHeadsetState, state));
return;
}
}
}
————-》
private void updateLocked(String newName, int newState) {
// Retain only relevant bits
int headsetState = newState & SUPPORTED_HEADSETS;
int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG;
int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL;
int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC);
boolean h2wStateChange = true;
boolean usbStateChange = true;
if (LOG) Slog.v(TAG, "newName=" + newName
+ " newState=" + newState
+ " headsetState=" + headsetState
+ " prev headsetState=" + mHeadsetState);
if (mHeadsetState == headsetState) {
Log.e(TAG, "No state change.");
return;
}
// reject all suspect transitions: only accept state changes from:
// - a: 0 headset to 1 headset
// - b: 1 headset to 0 headset
if (h2w_headset == (BIT_HEADSET | BIT_HEADSET_NO_MIC)) {
Log.e(TAG, "Invalid combination, unsetting h2w flag");
h2wStateChange = false;
}
// - c: 0 usb headset to 1 usb headset
// - d: 1 usb headset to 0 usb headset
if (usb_headset_anlg == BIT_USB_HEADSET_ANLG && usb_headset_dgtl == BIT_USB_HEADSET_DGTL) {
Log.e(TAG, "Invalid combination, unsetting usb flag");
usbStateChange = false;
}
if (!h2wStateChange && !usbStateChange) {
Log.e(TAG, "invalid transition, returning ...");
return;
}
mWakeLock.acquire();
Message msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState,
mHeadsetState, newName); // 往HANDLER发消息
mHandler.sendMessage(msg);
mHeadsetState = headsetState;
}
———–》
private final Handler mHandler = new Handler(Looper.myLooper(), null, true) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_NEW_DEVICE_STATE:
setDevicesState(msg.arg1, msg.arg2, (String)msg.obj);
mWakeLock.release();
}
}
};
——》
private void setDevicesState(
int headsetState, int prevHeadsetState, String headsetName) {
synchronized (mLock) {
int allHeadsets = SUPPORTED_HEADSETS;
for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
if ((curHeadset & allHeadsets) != 0) {
setDeviceStateLocked(curHeadset, headsetState, prevHeadsetState, headsetName);
allHeadsets &= ~curHeadset;
}
}
}
}
———–》
private void setDeviceStateLocked(int headset,
int headsetState, int prevHeadsetState, String headsetName) {
if ((headsetState & headset) != (prevHeadsetState & headset)) {
int device;
int state;
if ((headsetState & headset) != 0) {
state = 1;
} else {
state = 0;
}
if (headset == BIT_HEADSET) {
device = AudioManager.DEVICE_OUT_WIRED_HEADSET;
} else if (headset == BIT_HEADSET_NO_MIC){
device = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
} else if (headset == BIT_USB_HEADSET_ANLG) {
device = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
} else if (headset == BIT_USB_HEADSET_DGTL) {
device = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
} else if (headset == BIT_HDMI_AUDIO) {
device = AudioManager.DEVICE_OUT_AUX_DIGITAL;
} else {
Slog.e(TAG, "setDeviceState() invalid headset type: "+headset);
return;
}
if (LOG)
Slog.v(TAG, "device "+headsetName+((state == 1) ? " connected" : " disconnected"));
//ALPS00708321:add for AB=00->01, set device BIT_HEADSET will ingore and music will pause when plug in
// headset complete,so need delay to confirm set device BIT_HEADSET to Audio
if(prevHeadsetState == BIT_HEADSET_NO_MIC && headsetState == BIT_HEADSET && state == 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// Ingore
}
}
mAudioManager.setWiredDeviceConnectionState(device, state, headsetName);
// audio service 端处理
illegal_state = getIllegalHeadset();
if(49 == illegal_state) {
mHandler.postDelayed(new Runnable(){
public void run(){
illegal_state = getIllegalHeadset();
if(49 == illegal_state){
Slog.d(TAG, "show illegal Headset msg+++++++++++++");
showheadsetToast();
illegal_state = 0;
}else{
Slog.d(TAG, "don't show illegal Headset msg+++++++++++++");
illegal_state = 0;
}
}
},500);
//illegal_state = 0;
}
}
}
audioservice 端处理
——–》
public void setWiredDeviceConnectionState(int device, int state, String name) {
synchronized (mConnectedDevices) {
int delay = checkSendBecomingNoisyIntent(device, state);
queueMsgUnderWakeLock(mAudioHandler,
MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
device,
state,
name,
delay);
}
}
—————》
case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);
mAudioEventWakeLock.release();
break;
—————–》
private void onSetWiredDeviceConnectionState(int device, int state, String name)
{
synchronized (mConnectedDevices) {
Log.d(TAG,"onSetWiredDeviceConnectionState:" + "device:" + device + ",state:" + state);
/**
* M: do not broadcast if headset is connected for ALPS00850163 @{
*/
boolean shouldNotify = true;
if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
(device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
/// M: safe media @{
mHeadsetOrHeadphoneOn = false;
/// @}
/**
* M: do not setForceUse if headset is connected for
* ALPS00657344 @{
*/
boolean isHeadphoneOff = (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
boolean isHeadsetOn = mConnectedDevices
.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
if (isHeadphoneOff && isHeadsetOn) {
shouldNotify = false;
Log.d(TAG, "onSetWiredDeviceConnectionState() no need to setForceUse");
}
if (shouldNotify) {
setBluetoothA2dpOnInt(true);
}
/** @} */
}
boolean isUsb = ((device & AudioSystem.DEVICE_OUT_ALL_USB) != 0);
handleDeviceConnection((state == 1), device, (isUsb ? name : "")); 重点在这里
if (state != 0) {
if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
(device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE)) {
/// M: safe media @{
mHeadsetOrHeadphoneOn = true;
/// @}
setBluetoothA2dpOnInt(false);
}
}
if (!isUsb) {
sendDeviceConnectionIntent(device, state, name, shouldNotify);
}
———————-》
private boolean handleDeviceConnection(boolean connected, int device, String params) {
synchronized (mConnectedDevices) {
boolean isConnected = (mConnectedDevices.containsKey(device) &&
(params.isEmpty() || mConnectedDevices.get(device).equals(params)));
Log.d(TAG,"handleDeviceConnection:isConnected" + isConnected);
if (isConnected && !connected) {
AudioSystem.setDeviceConnectionState(device,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
mConnectedDevices.get(device));
Log.d(TAG, "handleDeviceConnection remove:" + "connected:" + connected + ",device:"
+ device);
mConnectedDevices.remove(device);
return true;
} else if (!isConnected && connected) {
/// M: if mhl is connecting to smartbook,we pass Params_SmartBook to native
AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE,
(device == AudioSystem.DEVICE_OUT_AUX_DIGITAL && whetherSmartBookConnected()) ? Params_SmartBook
: params);
Log.d(TAG, "handleDeviceConnection connect:" + "connected:" + connected
+ ",device:" + device);
mConnectedDevices.put(new Integer(device), params);
return true;
}
}
return false;
}
native service 大概流程分析
————–>
audiosystem.cpp
status_t AudioSystem::setDeviceConnectionState(audio_devices_t device,
audio_policy_dev_state_t state,
const char *device_address)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
const char *address = "";
if (aps == 0) return PERMISSION_DENIED;
if (device_address != NULL) {
address = device_address;
}
return aps->setDeviceConnectionState(device, state, address);
}
——–> AudioMTKPolicyManager.cpp
status_t AudioMTKPolicyManager::setDeviceConnectionState(audio_devices_t device,
AudioSystem::device_connection_state state,
const char *device_address)
{
case AudioSystem::DEVICE_STATE_AVAILABLE:
// register new device as available, 将新设备的标志置位
mAvailableOutputDevices = (audio_devices_t)(mAvailableOutputDevices | device);
。。。。。
checkOutputForAllStrategies(); // 根据策略
// outputs must be closed after checkOutputForAllStrategies() is executed
updateDevicesAndOutputs(); 设备状态发生了变化,更新每个策略下目前应该对应的输出设备
for (size_t i = 0; i < mOutputs.size(); i++)
{
// do not force device change on duplicated output because if device is 0, it will
// also force a device 0 for the two outputs it is duplicated to which may override
// a valid device selection on those outputs.
setOutputDevice(mOutputs.keyAt(i),
getNewDevice(mOutputs.keyAt(i), true /*fromCache*/),
!mOutputs.valueAt(i)->isDuplicated(),
0); 这个地方传进去的参数是 TRUE, 用上面更新过的策略对应的设备。
}
此方法后面还有 AUDIO INPUT 对应的处理, 我们假设是3段耳机插入, 略去。
——–》
uint32_t AudioMTKPolicyManager::setOutputDevice(audio_io_handle_t output,
audio_devices_t device,
bool force,
int delayMs)
{
路由, mpClientInterface实际上是 audiopolicyservice.
param.addInt(String8(AudioParameter::keyRouting), (int)device);
mpClientInterface->setParameters(output, param.toString(), delayMs);
设置音量
applyStreamVolumes(output, device, delayMs);
实际上 后期 applyStreamVolumes也是调用audiopolicyservice的方法来处理: mpClientInterface->setStreamVolume(AudioSystem::VOICE_CALL, volume, output, delayMs);
}
—————》
void AudioPolicyService::setParameters(audio_io_handle_t ioHandle,
const char *keyValuePairs,
int delayMs)
{
mAudioCommandThread->parametersCommand(ioHandle, keyValuePairs,
delayMs);
}
mAudioCommandThread 是在 AudioPolicyService 构造函数中 NEW的
————————–》
status_t AudioPolicyService::AudioCommandThread::parametersCommand(audio_io_handle_t ioHandle,
const char *keyValuePairs,
int delayMs)
{
status_t status = NO_ERROR;
AudioCommand *command = new AudioCommand();
command->mCommand = SET_PARAMETERS;
ParametersData *data = new ParametersData();
data->mIO = ioHandle;
data->mKeyValuePairs = String8(keyValuePairs);
command->mParam = data;
#ifdef MTK_AUDIO
Mutex::Autolock _t(mFunLock); //must get funlock;
#endif
Mutex::Autolock _l(mLock);
insertCommand_l(command, delayMs);
// 将新的命令插入一个序列容器中, 容器定义:Vector <AudioCommand *> mAudioCommands; // list of pending commands
ALOGD("AudioCommandThread() adding set parameter string %s, io %d ,delay %d",
keyValuePairs, ioHandle, delayMs);
mWaitWorkCV.signal(); // 唤醒 AudioCommandThread 线程, 开始处理命名
if (command->mWaitStatus) {
command->mCond.wait(mLock);
status = command->mStatus;
command->mCond.signal();
}
return status;
}
——————》
case SET_PARAMETERS: {
ParametersData *data = (ParametersData *)command->mParam;
ALOGV("AudioCommandThread() processing set parameters string %s, io %d",
data->mKeyValuePairs.string(), data->mIO);
AudioParameter param = AudioParameter(data->mKeyValuePairs);
float value = 0.0;
// use setmastervolume instead of setparameter
if((param.getFloat (String8("SetMasterVolume"), value)) == NO_ERROR){
MTK_ALOG_D("SET_PARAMETERS SetMasterVolume value = %f",value);
AudioSystem::setMasterVolume (value);
}
else{ 又到 AudioSystem 中,
command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs);
}
—————》
status_t AudioSystem::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
ret = af->setParameters(ioHandle, keyValuePairs);
}
———–》
status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
{
sp<ThreadBase> thread;
{
Mutex::Autolock _l(mLock);
thread = checkPlaybackThread_l(ioHandle);
if (thread == 0) {
thread = checkRecordThread_l(ioHandle);
} else if (thread == primaryPlaybackThread_l()) {
// indicate output device change to all input threads for pre processing
AudioParameter param = AudioParameter(keyValuePairs);
int value;
if ((param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) &&
(value != 0)) {
for (size_t i = 0; i < mRecordThreads.size(); i++) {
mRecordThreads.valueAt(i)->setParameters(keyValuePairs);
}
}
}
}
if (thread != 0) {
return thread->setParameters(keyValuePairs);
}
}
—–》
status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs)
{
status_t status;
ALOGV("ThreadBase::setParameters() %s", keyValuePairs.string());
Mutex::Autolock _l(mLock);
mNewParameters.add(keyValuePairs);
mWaitWorkCV.signal();
// wait condition with timeout in case the thread loop has exited
// before the request could be processed
if (mParamCond.waitRelative(mLock, kSetParametersTimeoutNs) == NO_ERROR) {
status = mParamStatus;
mWaitWorkCV.signal();
} else {
status = TIMED_OUT;
}
return status;
}
——–》
bool AudioFlinger::PlaybackThread::threadLoop()
{
在while loop 在有以下处理
if (checkForNewParameters_l()) {
cacheParameters_l();
}
}
———》
bool AudioFlinger::MixerThread::checkForNewParameters_l()
{
status = mOutput->stream->common.set_parameters(&mOutput->stream->common,keyValuePair.string());
}
mOutput 就对应到 AUDIO HAL 层设置路由
HAL层路由分析
—》 Audio_hw_hal.cpp (doov6732kk_pra\mediatek\platform\common\hardware\audio\policy_driver): out->stream.common.set_parameters = out_set_parameters;
我们只分析 STREAM OUT, 对于STREAM IN, 也是差不多。
static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
struct legacy_stream_out *out =
reinterpret_cast<struct legacy_stream_out *>(stream);
int val;
String8 s8 = String8(kvpairs);
AudioParameter parms = AudioParameter(String8(kvpairs));
/*
if (parms.getInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), val) == NO_ERROR) {
val = convert_audio_device(val, HAL_API_REV_2_0, HAL_API_REV_1_0);
parms.remove(String8(AUDIO_PARAMETER_STREAM_ROUTING));
parms.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), val);
s8 = parms.toString();
}
*/
return out->legacy_out->setParameters(s8);
}
———》
status_t AudioALSAStreamOut::setParameters(const String8 &keyValuePairs)
{
/// routing
if (param.getInt(keyRouting, value) == NO_ERROR)
{
param.remove(keyRouting);
mydevice = static_cast<audio_devices_t>(value);
ALOGD("%s(), mydevice 0x%x", __FUNCTION__, mydevice);
AudioAutoTimeoutLock _l(mLock);
if (mStreamOutType == STREAM_OUT_PRIMARY)
{
mStreamManager 的方法 routingOutputDevice 来设置硬件路由
status = mStreamManager->routingOutputDevice(mStreamAttributeSource.output_devices, static_cast<audio_devices_t>(value));
}
else if ((mStreamOutType == STREAM_OUT_HDMI_STEREO) || (mStreamOutType == STREAM_OUT_HDMI_MULTI_CHANNEL))
{
ALOGD("%s(), HDMI \"%s\"", __FUNCTION__, param.toString().string());
status = mStreamManager->routingOutputDevice(mStreamAttributeSource.output_devices, static_cast<audio_devices_t>(value));
}
else
{
ALOGW("%s(), NUM_STREAM_OUT_TYPE \"%s\"", __FUNCTION__, param.toString().string());
status = INVALID_OPERATION;
}
}
。。。。
}
————–》
status_t AudioALSAStreamManager::routingOutputDevice(const audio_devices_t current_output_devices, audio_devices_t output_devices)
{
for (size_t i = 0; i < mStreamOutVector.size(); i++)
{
if (mStreamOutVector[i]->getStreamAttribute()->output_devices == current_output_devices) // TODO(Harvey): or add group?
{
status = mStreamOutVector[i]->routing(output_devices); AudioALSAStreamOut的方法 routing
ASSERT(status == NO_ERROR);
}
}
}
————————————》
status_t AudioALSAStreamOut::routing(audio_devices_t output_devices)
{
if (mStandby == false)
{
ASSERT(output_devices != mStreamAttributeSource.output_devices); // TODO(Harvey): Could remove it after stress test
ASSERT(mPlaybackHandler != NULL);
//status = mPlaybackHandler->routing(output_devices);
//if (status != NO_ERROR)
if(mPlaybackHandler->getPlaybackHandlerType() == PLAYBACK_HANDLER_OFFLOAD)
{
status = pause();
AudioALSAHardwareResourceManager::getInstance()->stopOutputDevice(); 先STOP
}
else
{
status = close();
}
}
mStreamAttributeSource.output_devices = output_devices;
if(mPaused == true)
{
if(mPlaybackHandler->getPlaybackHandlerType() == PLAYBACK_HANDLER_OFFLOAD)
{
const stream_attribute_t *StreamAttributeTarget = mPlaybackHandler->getStreamAttributeTarget();
AudioALSAHardwareResourceManager::getInstance()->startOutputDevice(mStreamAttributeSource.output_devices, StreamAttributeTarget->sample_rate); 再start
status = resume();
}
}
if (is_lock_in_this_function == true)
{
mLock.unlock();
}
}
AudioALSAHardwareResourceManager 直接通过tiny alsa库调到KERNEL 去了。 KERNEL的东西就不分析了。