Android源码分析--AudioPolicyEngine设备路由(02)
2018-01-24 16:43
1321 查看
这篇中记住一个中心思想就是,流(Stream)决定策略(Strategy),策略决定设备(Device)。流在App层可以经常碰到,比如Music流,Notification流,Call流。为什么叫作策略,大概是因为中文翻译过来不大准确,最好的方式就是直接用Strategy表示。
先来看看流是什么,在下面的文件中定义
/system/media/audio/include/system/audio-base.h
/frameworks/base/media/java/android/media/AudioSystem.java
/frameworks/base/media/java/android/media/AudioManager.java
接下来分析上篇中提到的几个函数,路由策略定义在
/frameworks/av/services/audiopolicy/common/include/RoutingStrategy.h
从上面可以看到使用了mApmObserver变量,那么需要分析这个变量的作用,当具体分析AudioPolicyManager的时候再来分析这个变量。从变量名字中可以看到是通过这个mApmObserver变量获取可用的输出设备和可用的输入设备,它们存在一个DeviceVector向量内。这个outputs变量相当于输出通道集合,也是存储在SwAudioOutputCollection中。返回值是真正的设备选择的地方,设备是uint32_t类型,也就是上篇文章开始列举的设备。
这个函数就是通过比较确定那个设备优先,如果已经选择好设备就不会进行后面的选择,写的比较巧妙。分析每个case的意思
STRATEGY_TRANSMITTED_THROUGH_SPEAKER
只通过外放播放声音,可以看到它选择的设备是AUDIO_DEVICE_OUT_SPEAKER。
STRATEGY_SONIFICATION_RESPECTFUL
如果需要理解这个策略是什么意思,可以看上面的getStrategyForStream函数,可以看到它把AUDIO_STREAM_NOTIFICATION流转换成为STRATEGY_SONIFICATION_RESPECTFUL,这里可以理解成短信通知音,或者是程序的提示音。注意还有一个STRATEGY_SONIFICATION策略,从上面的代码中可以看到闹钟和来电铃声流会转换为这个策略。这两个策略之间的区别可以从带有的respectful后缀可以看出,这个策略更礼貌些,就是强制性或者优先级更小。
如果处在打电话的状态,就走STRATEGY_SONIFICATION策略。
device = getDeviceForStrategyInt(
STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);这个写法叫做迭代,函数调用自己,但是可以看出来迭代并不深。从else if的注释中还可以看到调用isStreamActiveRemotely和isStreamActive的意思,不懂这句英语的意思only informs about the activity of a stream, not if it's for local playback,后面需要详细对比两个函数,现在只需要知道这个函数就是判断传入的Stream参数是否是处于活跃状态,传入的后面一个常量参数前面已经有解释,就是相当于这个流停止之后的那么长时间仍然认为它是活跃的。可以看到当音乐在播放时,就选择音乐播放时的设备进行通知音的播放。
注释已经写的很清楚,虽然不知道具体应用的场景。
//user "safe" speaker if available instead of normal speaker to avoid triggering
//other acoustic safety mechanisms for notification
if ((device & AUDIO_DEVICE_OUT_SPEAKER) &&
(availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE;
device &= ~AUDIO_DEVICE_OUT_SPEAKER;
}STRATEGY_DTMF
沿用STRATEGY_MEDIA的策略,同时看到没有使用break,如果在通话状态就会走STRATEGY_PHONE策略。
STRATEGY_PHONE
通话策略下,判断是否强制蓝牙集成或者是外放播放播放,并且进行相关的设备选择。在没有蓝牙的情况下,依次选择的设备是headphone, wired headset, line, usb headset, usb device, earpiece。如果强制外放,就会走外放。
device = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER;STRATEGY_SONIFICATION
如果处在打电话的状态就让它走STRATEGY_PHONE策略,如果没有就走STRATEGY_ENFORCED_AUDIBLE策略。
STRATEGY_ENFORCED_AUDIBLE
强制输出,比如拍照音。
mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED如果这个判断成立就将device赋值为AUDIO_DEVICE_OUT_SPEAKER,如果连接蓝牙,则蓝牙和外放同时出声。
STRATEGY_ACCESSIBILITY
应该是针对特殊设备的。
STRATEGY_REROUTING
STRATEGY_MEDIA
这里重新定义device2是因为前面强制外放策略可能走这个媒体策略。这里的设备很多很多,不进行列举,碰到具体的问题的时候具体分析。
最后添加打印返回设备值。
另外一个非常重要的函数是输入设备的选择,类似的分析和输出设备的选择函数基本是相同的,都是通过switch case逻辑进行判断。
audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) const
{
const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices();
const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices();
const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
audio_devices_t availableDeviceTypes = availableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;
uint32_t device = AUDIO_DEVICE_NONE;
switch (inputSource) {
case AUDIO_SOURCE_VOICE_UPLINK:
if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) {
device = AUDIO_DEVICE_IN_VOICE_CALL;
break;
}
break;
case AUDIO_SOURCE_DEFAULT:
case AUDIO_SOURCE_MIC:
if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
} else if ((mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO) &&
(availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)) {
device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) {
device = AUDIO_DEVICE_IN_WIRED_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) {
device = AUDIO_DEVICE_IN_USB_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) {
device = AUDIO_DEVICE_IN_USB_DEVICE;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
device = AUDIO_DEVICE_IN_BUILTIN_MIC;
}
break;
case AUDIO_SOURCE_VOICE_COMMUNICATION:
// Allow only use of devices on primary input if in call and HAL does not support routing
// to voice call path.
if ((getPhoneState() == AUDIO_MODE_IN_CALL) &&
(availableOutputDevices.types() & AUDIO_DEVICE_OUT_TELEPHONY_TX) == 0) {
sp<AudioOutputDescriptor> primaryOutput = outputs.getPrimaryOutput();
availableDeviceTypes =
availableInputDevices.getDevicesFromHwModule(primaryOutput->getModuleHandle())
& ~AUDIO_DEVICE_BIT_IN;
}
switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) {
case AUDIO_POLICY_FORCE_BT_SCO:
// if SCO device is requested but no SCO device is available, fall back to default case
if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
break;
}
// FALL THROUGH
default: // FORCE_NONE
if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) {
device = AUDIO_DEVICE_IN_WIRED_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) {
device = AUDIO_DEVICE_IN_USB_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) {
device = AUDIO_DEVICE_IN_USB_DEVICE;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
device = AUDIO_DEVICE_IN_BUILTIN_MIC;
}
break;
case AUDIO_POLICY_FORCE_SPEAKER:
if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) {
device = AUDIO_DEVICE_IN_BACK_MIC;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
device = AUDIO_DEVICE_IN_BUILTIN_MIC;
}
break;
}
break;
case AUDIO_SOURCE_VOICE_RECOGNITION:
case AUDIO_SOURCE_UNPROCESSED:
case AUDIO_SOURCE_HOTWORD:
if (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO &&
availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) {
device = AUDIO_DEVICE_IN_WIRED_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) {
device = AUDIO_DEVICE_IN_USB_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) {
device = AUDIO_DEVICE_IN_USB_DEVICE;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
device = AUDIO_DEVICE_IN_BUILTIN_MIC;
}
break;
case AUDIO_SOURCE_CAMCORDER:
if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) {
device = AUDIO_DEVICE_IN_BACK_MIC;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
device = AUDIO_DEVICE_IN_BUILTIN_MIC;
}
break;
case AUDIO_SOURCE_VOICE_DOWNLINK:
case AUDIO_SOURCE_VOICE_CALL:
if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) {
device = AUDIO_DEVICE_IN_VOICE_CALL;
}
break;
case AUDIO_SOURCE_REMOTE_SUBMIX:
if (availableDeviceTypes & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
}
break;
case AUDIO_SOURCE_FM_TUNER:
if (availableDeviceTypes & AUDIO_DEVICE_IN_FM_TUNER) {
device = AUDIO_DEVICE_IN_FM_TUNER;
}
break;
default:
ALOGW("getDeviceForInputSource() invalid input source %d", inputSource);
break;
}
if (device == AUDIO_DEVICE_NONE) {
ALOGV("getDeviceForInputSource() no device found for source %d", inputSource);
if (availableDeviceTypes & AUDIO_DEVICE_IN_STUB) {
device = AUDIO_DEVICE_IN_STUB;
}
ALOGE_IF(device == AUDIO_DEVICE_NONE,
"getDeviceForInputSource() no default device defined");
}
ALOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device);
return device;
}比如录音的时候会走case AUDIO_SOURCE_MIC,按照优先级来划分的话从高到低是蓝牙,耳机,内置麦克风。
总结:
这里就已经讲完设备选择的流程,当然只是讲到它们的功能,具体的如何调用这些函数还在AudioPolicyManager中,AudioPolicyManager是个很大的架构,所以需要更多的章节去介绍。
下篇:
接下来将会从实际问题出发结合代码解决问题,问题的种类有很多,比如设备的切换错误,无声问题,杂音问题,声音外放的问题,音量大小的调节问题。
先来看看流是什么,在下面的文件中定义
/system/media/audio/include/system/audio-base.h
typedef enum { AUDIO_STREAM_DEFAULT = -1, // (-1) AUDIO_STREAM_MIN = 0, AUDIO_STREAM_VOICE_CALL = 0, AUDIO_STREAM_SYSTEM = 1, AUDIO_STREAM_RING = 2, AUDIO_STREAM_MUSIC = 3, AUDIO_STREAM_ALARM = 4, AUDIO_STREAM_NOTIFICATION = 5, AUDIO_STREAM_BLUETOOTH_SCO = 6, AUDIO_STREAM_ENFORCED_AUDIBLE = 7, AUDIO_STREAM_DTMF = 8, AUDIO_STREAM_TTS = 9, AUDIO_STREAM_ACCESSIBILITY = 10, AUDIO_STREAM_REROUTING = 11, AUDIO_STREAM_PATCH = 12, AUDIO_STREAM_PUBLIC_CNT = 11, // (ACCESSIBILITY + 1) AUDIO_STREAM_FOR_POLICY_CNT = 12, // PATCH AUDIO_STREAM_CNT = 13, // (PATCH + 1) } audio_stream_type_t;可以看到定义有12种流,这里还有一个比较巧妙的写法,就是流的数量定义AUDIO_STREAM_CNT放在最后。这个流在
/frameworks/base/media/java/android/media/AudioSystem.java
/* These values must be kept in sync with system/audio.h */ /* * If these are modified, please also update Settings.System.VOLUME_SETTINGS * and attrs.xml and AudioManager.java. */ /** Used to identify the default audio stream volume */ public static final int STREAM_DEFAULT = -1; /** Used to identify the volume of audio streams for phone calls */ public static final int STREAM_VOICE_CALL = 0; /** Used to identify the volume of audio streams for system sounds */ public static final int STREAM_SYSTEM = 1; /** Used to identify the volume of audio streams for the phone ring and message alerts */ public static final int STREAM_RING = 2; /** Used to identify the volume of audio streams for music playback */ public static final int STRE 4000 AM_MUSIC = 3; /** Used to identify the volume of audio streams for alarms */ public static final int STREAM_ALARM = 4; /** Used to identify the volume of audio streams for notifications */ public static final int STREAM_NOTIFICATION = 5; /** Used to identify the volume of audio streams for phone calls when connected on bluetooth */ public static final int STREAM_BLUETOOTH_SCO = 6; /** Used to identify the volume of audio streams for enforced system sounds in certain * countries (e.g camera in Japan) */ public static final int STREAM_SYSTEM_ENFORCED = 7; /** Used to identify the volume of audio streams for DTMF tones */ public static final int STREAM_DTMF = 8; /** Used to identify the volume of audio streams exclusively transmitted through the * speaker (TTS) of the device */ public static final int STREAM_TTS = 9;上面说的/system/audio.h就是指的audio-base.h,Android 8.0的代码和之前的有点差异,可以看到AudioSystem里面的Stream数值是和audio-base里面的数值是相同的。App层调用的接口类AudioManager中也有相关定义,写程序的时候需要指定Stream类型,后面讲到上层的时候再分析。
/frameworks/base/media/java/android/media/AudioManager.java
/** Used to identify the volume of audio streams for phone calls */ public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL; /** Used to identify the volume of audio streams for system sounds */ public static final int STREAM_SYSTEM = AudioSystem.STREAM_SYSTEM; /** Used to identify the volume of audio streams for the phone ring */ public static final int STREAM_RING = AudioSystem.STREAM_RING; /** Used to identify the volume of audio streams for music playback */ public static final int STREAM_MUSIC = AudioSystem.STREAM_MUSIC; /** Used to identify the volume of audio streams for alarms */ public static final int STREAM_ALARM = AudioSystem.STREAM_ALARM; /** Used to identify the volume of audio streams for notifications */ public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION; /** @hide Used to identify the volume of audio streams for phone calls when connected * to bluetooth */ public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO; /** @hide Used to identify the volume of audio streams for enforced system sounds * in certain countries (e.g camera in Japan) */ public static final int STREAM_SYSTEM_ENFORCED = AudioSystem.STREAM_SYSTEM_ENFORCED; /** Used to identify the volume of audio streams for DTMF Tones */ public static final int STREAM_DTMF = AudioSystem.STREAM_DTMF; /** @hide Used to identify the volume of audio streams exclusively transmitted through the * speaker (TTS) of the device */ public static final int STREAM_TTS = AudioSystem.STREAM_TTS; /** Used to identify the volume of audio streams for accessibility prompts */ public static final int STREAM_ACCESSIBILITY = AudioSystem.STREAM_ACCESSIBILITY;
接下来分析上篇中提到的几个函数,路由策略定义在
/frameworks/av/services/audiopolicy/common/include/RoutingStrategy.h
#pragma once namespace android { // Time in milliseconds after media stopped playing during which we consider that the // sonification should be as unobtrusive as during the time media was playing. #define SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY 5000 enum routing_strategy { STRATEGY_MEDIA, STRATEGY_PHONE, STRATEGY_SONIFICATION, STRATEGY_SONIFICATION_RESPECTFUL, STRATEGY_DTMF, STRATEGY_ENFORCED_AUDIBLE, STRATEGY_TRANSMITTED_THROUGH_SPEAKER, STRATEGY_ACCESSIBILITY, STRATEGY_REROUTING, NUM_STRATEGIES }; };将Stream转换为Strategy
routing_strategy Engine::getStrategyForStream(audio_stream_type_t stream) { // stream to strategy mapping switch (stream) { case AUDIO_STREAM_VOICE_CALL: case AUDIO_STREAM_BLUETOOTH_SCO: return STRATEGY_PHONE; case AUDIO_STREAM_RING: case AUDIO_STREAM_ALARM: return STRATEGY_SONIFICATION; case AUDIO_STREAM_NOTIFICATION: return STRATEGY_SONIFICATION_RESPECTFUL; case AUDIO_STREAM_DTMF: return STRATEGY_DTMF; default: ALOGE("unknown stream type %d", stream); case AUDIO_STREAM_SYSTEM: // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs // while key clicks are played produces a poor result case AUDIO_STREAM_MUSIC: return STRATEGY_MEDIA; case AUDIO_STREAM_ENFORCED_AUDIBLE: return STRATEGY_ENFORCED_AUDIBLE; case AUDIO_STREAM_TTS: return STRATEGY_TRANSMITTED_THROUGH_SPEAKER; case AUDIO_STREAM_ACCESSIBILITY: return STRATEGY_ACCESSIBILITY; case AUDIO_STREAM_REROUTING: return STRATEGY_REROUTING; } }将Usage转换为Strategy
routing_strategy Engine::getStrategyForUsage(audio_usage_t usage) { // usage to strategy mapping switch (usage) { case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: return STRATEGY_ACCESSIBILITY; case AUDIO_USAGE_MEDIA: case AUDIO_USAGE_GAME: case AUDIO_USAGE_ASSISTANT: case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: case AUDIO_USAGE_ASSISTANCE_SONIFICATION: return STRATEGY_MEDIA; case AUDIO_USAGE_VOICE_COMMUNICATION: return STRATEGY_PHONE; case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: return STRATEGY_DTMF; case AUDIO_USAGE_ALARM: case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: return STRATEGY_SONIFICATION; case AUDIO_USAGE_NOTIFICATION: case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: case AUDIO_USAGE_NOTIFICATION_EVENT: return STRATEGY_SONIFICATION_RESPECTFUL; case AUDIO_USAGE_UNKNOWN: default: return STRATEGY_MEDIA; } }从策略中获取设备
audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const { DeviceVector availableOutputDevices = mApmObserver->getAvailableOutputDevices(); DeviceVector availableInputDevices = mApmObserver->getAvailableInputDevices(); const SwAudioOutputCollection &outputs = mApmObserver->getOutputs(); return getDeviceForStrategyInt(strategy, availableOutputDevices, availableInputDevices, outputs); }
从上面可以看到使用了mApmObserver变量,那么需要分析这个变量的作用,当具体分析AudioPolicyManager的时候再来分析这个变量。从变量名字中可以看到是通过这个mApmObserver变量获取可用的输出设备和可用的输入设备,它们存在一个DeviceVector向量内。这个outputs变量相当于输出通道集合,也是存储在SwAudioOutputCollection中。返回值是真正的设备选择的地方,设备是uint32_t类型,也就是上篇文章开始列举的设备。
typedef uint32_t audio_devices_t;
audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, DeviceVector availableOutputDevices, DeviceVector availableInputDevices, const SwAudioOutputCollection &outputs) const { uint32_t device = AUDIO_DEVICE_NONE; uint32_t availableOutputDevicesType = availableOutputDevices.types(); switch (strategy) { case STRATEGY_TRANSMITTED_THROUGH_SPEAKER: device = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER; break; case STRATEGY_SONIFICATION_RESPECTFUL: if (isInCall()) { device = getDeviceForStrategyInt( STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs); } else if (outputs.isStreamActiveRemotely(AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { // while media is playing on a remote device, use the the sonification behavior. // Note that we test this usecase before testing if media is playing because // the isStreamActive() method only informs about the activity of a stream, not // if it's for local playback. Note also that we use the same delay between both tests device = getDeviceForStrategyInt( STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs); //user "safe" speaker if available instead of normal speaker to avoid triggering //other acoustic safety mechanisms for notification if ((device & AUDIO_DEVICE_OUT_SPEAKER) && (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) { device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE; device &= ~AUDIO_DEVICE_OUT_SPEAKER; } } else if (outputs.isStreamActive( AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { // while media is playing (or has recently played), use the same device device = getDeviceForStrategyInt( STRATEGY_MEDIA, availableOutputDevices, availableInputDevices, outputs); } else { // when media is not playing anymore, fall back on the sonification behavior device = getDeviceForStrategyInt( STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs); //user "safe" speaker if available instead of normal speaker to avoid triggering //other acoustic safety mechanisms for notification if ((device & AUDIO_DEVICE_OUT_SPEAKER) && (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) { device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE; device &= ~AUDIO_DEVICE_OUT_SPEAKER; } } break; case STRATEGY_DTMF: if (!isInCall()) { // when off call, DTMF strategy follows the same rules as MEDIA strategy device = getDeviceForStrategyInt( STRATEGY_MEDIA, availableOutputDevices, availableInputDevices, outputs); break; } // when in call, DTMF and PHONE strategies follow the same rules // FALL THROUGH case STRATEGY_PHONE: // Force use of only devices on primary output if: // - in call AND // - cannot route from voice call RX OR // - audio HAL version is < 3.0 and TX device is on the primary HW module if (getPhoneState() == AUDIO_MODE_IN_CALL) { audio_devices_t txDevice = getDeviceForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); sp<AudioOutputDescriptor> primaryOutput = outputs.getPrimaryOutput(); audio_devices_t availPrimaryInputDevices = availableInputDevices.getDevicesFromHwModule(primaryOutput->getModuleHandle()); audio_devices_t availPrimaryOutputDevices = primaryOutput->supportedDevices() & availableOutputDevices.types(); if (((availableInputDevices.types() & AUDIO_DEVICE_IN_TELEPHONY_RX & ~AUDIO_DEVICE_BIT_IN) == 0) || (((txDevice & availPrimaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) && (primaryOutput->getAudioPort()->getModuleVersionMajor() < 3))) { availableOutputDevicesType = availPrimaryOutputDevices; } } // for phone strategy, we first consider the forced use and then the available devices by // order of priority switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { case AUDIO_POLICY_FORCE_BT_SCO: if (!isInCall() || strategy != STRATEGY_DTMF) { device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT; if (device) break; } device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO; if (device) break; // if SCO device is requested but no SCO device is available, fall back to default case // FALL THROUGH default: // FORCE_NONE // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP if (!isInCall() && (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && (outputs.getA2dpOutput() != 0)) { device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; if (device) break; } device = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADSET; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_LINE; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_HEADSET; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_DEVICE; if (device) break; if (!isInCall()) { device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_ACCESSORY; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_DIGITAL; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; if (device) break; } device = availableOutputDevicesType & AUDIO_DEVICE_OUT_EARPIECE; break; case AUDIO_POLICY_FORCE_SPEAKER: // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to // A2DP speaker when forcing to speaker output if (!isInCall() && (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && (outputs.getA2dpOutput() != 0)) { device = availableOutputDe 12c19 vicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; if (device) break; } if (!isInCall()) { device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_ACCESSORY; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_DEVICE; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_DIGITAL; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; if (device) break; } device = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER; break; } break; case STRATEGY_SONIFICATION: // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by // handleIncallSonification(). if (isInCall()) { device = getDeviceForStrategyInt( STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs); break; } // FALL THROUGH case STRATEGY_ENFORCED_AUDIBLE: // strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION // except: // - when in call where it doesn't default to STRATEGY_PHONE behavior // - in countries where not enforced in which case it follows STRATEGY_MEDIA if ((strategy == STRATEGY_SONIFICATION) || (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)) { device = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER; } // if SCO headset is connected and we are told to use it, play ringtone over // speaker and BT SCO if (((availableOutputDevicesType & AUDIO_DEVICE_OUT_ALL_SCO) != 0) && (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] == AUDIO_POLICY_FORCE_BT_SCO)) { uint32_t device2 = AUDIO_DEVICE_NONE; device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT; if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET; } if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO; } if (device2 != AUDIO_DEVICE_NONE) { device |= device2; break; } } // The second device used for sonification is the same as the device used by media strategy // FALL THROUGH case STRATEGY_ACCESSIBILITY: if (strategy == STRATEGY_ACCESSIBILITY) { // do not route accessibility prompts to a digital output currently configured with a // compressed format as they would likely not be mixed and dropped. for (size_t i = 0; i < outputs.size(); i++) { sp<AudioOutputDescriptor> desc = outputs.valueAt(i); audio_devices_t devices = desc->device() & (AUDIO_DEVICE_OUT_HDMI | AUDIO_DEVICE_OUT_SPDIF | AUDIO_DEVICE_OUT_HDMI_ARC); if (desc->isActive() && !audio_is_linear_pcm(desc->mFormat) && devices != AUDIO_DEVICE_NONE) { availableOutputDevicesType = availableOutputDevices.types() & ~devices; } } availableOutputDevices = availableOutputDevices.getDevicesFromType(availableOutputDevicesType); if (outputs.isStreamActive(AUDIO_STREAM_RING) || outputs.isStreamActive(AUDIO_STREAM_ALARM)) { return getDeviceForStrategyInt( STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs); } if (isInCall()) { return getDeviceForStrategyInt( STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs); } } // For other cases, STRATEGY_ACCESSIBILITY behaves like STRATEGY_MEDIA // FALL THROUGH // FIXME: STRATEGY_REROUTING follow STRATEGY_MEDIA for now case STRATEGY_REROUTING: case STRATEGY_MEDIA: { uint32_t device2 = AUDIO_DEVICE_NONE; if (strategy != STRATEGY_SONIFICATION) { // no sonification on remote submix (e.g. WFD) if (availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, String8("0")) != 0) { device2 = availableOutputDevices.types() & AUDIO_DEVICE_OUT_REMOTE_SUBMIX; } } if (isInCall() && (strategy == STRATEGY_MEDIA)) { device = getDeviceForStrategyInt( STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs); break; } if ((device2 == AUDIO_DEVICE_NONE) && (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && (outputs.getA2dpOutput() != 0)) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; } if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; } } if ((device2 == AUDIO_DEVICE_NONE) && (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] == AUDIO_POLICY_FORCE_SPEAKER)) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER; } if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; } if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_LINE; } if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADSET; } if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_HEADSET; } if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_ACCESSORY; } if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_DEVICE; } if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; } if ((device2 == AUDIO_DEVICE_NONE) && (strategy != STRATEGY_SONIFICATION)) { // no sonification on aux digital (e.g. HDMI) device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_DIGITAL; } if ((device2 == AUDIO_DEVICE_NONE) && (mForceUse[AUDIO_POLICY_FORCE_FOR_DOCK] == AUDIO_POLICY_FORCE_ANALOG_DOCK)) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; } if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER; } int device3 = AUDIO_DEVICE_NONE; if (strategy == STRATEGY_MEDIA) { // ARC, SPDIF and AUX_LINE can co-exist with others. device3 = availableOutputDevicesType & AUDIO_DEVICE_OUT_HDMI_ARC; device3 |= (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPDIF); device3 |= (availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_LINE); } device2 |= device3; // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise device |= device2; // If hdmi system audio mode is on, remove speaker out of output list. if ((strategy == STRATEGY_MEDIA) && (mForceUse[AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO] == AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED)) { device &= ~AUDIO_DEVICE_OUT_SPEAKER; } } break; default: ALOGW("getDeviceForStrategy() unknown strategy: %d", strategy); break; } if (device == AUDIO_DEVICE_NONE) { ALOGV("getDeviceForStrategy() no device found for strategy %d", strategy); device = mApmObserver->getDefaultOutputDevice()->type(); ALOGE_IF(device == AUDIO_DEVICE_NONE, "getDeviceForStrategy() no default device defined"); } ALOGVV("getDeviceForStrategy() strategy %d, device %x", strategy, device); return device; }
这个函数就是通过比较确定那个设备优先,如果已经选择好设备就不会进行后面的选择,写的比较巧妙。分析每个case的意思
STRATEGY_TRANSMITTED_THROUGH_SPEAKER
只通过外放播放声音,可以看到它选择的设备是AUDIO_DEVICE_OUT_SPEAKER。
STRATEGY_SONIFICATION_RESPECTFUL
如果需要理解这个策略是什么意思,可以看上面的getStrategyForStream函数,可以看到它把AUDIO_STREAM_NOTIFICATION流转换成为STRATEGY_SONIFICATION_RESPECTFUL,这里可以理解成短信通知音,或者是程序的提示音。注意还有一个STRATEGY_SONIFICATION策略,从上面的代码中可以看到闹钟和来电铃声流会转换为这个策略。这两个策略之间的区别可以从带有的respectful后缀可以看出,这个策略更礼貌些,就是强制性或者优先级更小。
如果处在打电话的状态,就走STRATEGY_SONIFICATION策略。
device = getDeviceForStrategyInt(
STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);这个写法叫做迭代,函数调用自己,但是可以看出来迭代并不深。从else if的注释中还可以看到调用isStreamActiveRemotely和isStreamActive的意思,不懂这句英语的意思only informs about the activity of a stream, not if it's for local playback,后面需要详细对比两个函数,现在只需要知道这个函数就是判断传入的Stream参数是否是处于活跃状态,传入的后面一个常量参数前面已经有解释,就是相当于这个流停止之后的那么长时间仍然认为它是活跃的。可以看到当音乐在播放时,就选择音乐播放时的设备进行通知音的播放。
注释已经写的很清楚,虽然不知道具体应用的场景。
//user "safe" speaker if available instead of normal speaker to avoid triggering
//other acoustic safety mechanisms for notification
if ((device & AUDIO_DEVICE_OUT_SPEAKER) &&
(availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE;
device &= ~AUDIO_DEVICE_OUT_SPEAKER;
}STRATEGY_DTMF
沿用STRATEGY_MEDIA的策略,同时看到没有使用break,如果在通话状态就会走STRATEGY_PHONE策略。
STRATEGY_PHONE
通话策略下,判断是否强制蓝牙集成或者是外放播放播放,并且进行相关的设备选择。在没有蓝牙的情况下,依次选择的设备是headphone, wired headset, line, usb headset, usb device, earpiece。如果强制外放,就会走外放。
device = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER;STRATEGY_SONIFICATION
如果处在打电话的状态就让它走STRATEGY_PHONE策略,如果没有就走STRATEGY_ENFORCED_AUDIBLE策略。
STRATEGY_ENFORCED_AUDIBLE
强制输出,比如拍照音。
mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED如果这个判断成立就将device赋值为AUDIO_DEVICE_OUT_SPEAKER,如果连接蓝牙,则蓝牙和外放同时出声。
STRATEGY_ACCESSIBILITY
应该是针对特殊设备的。
STRATEGY_REROUTING
STRATEGY_MEDIA
这里重新定义device2是因为前面强制外放策略可能走这个媒体策略。这里的设备很多很多,不进行列举,碰到具体的问题的时候具体分析。
最后添加打印返回设备值。
另外一个非常重要的函数是输入设备的选择,类似的分析和输出设备的选择函数基本是相同的,都是通过switch case逻辑进行判断。
audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) const
{
const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices();
const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices();
const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
audio_devices_t availableDeviceTypes = availableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;
uint32_t device = AUDIO_DEVICE_NONE;
switch (inputSource) {
case AUDIO_SOURCE_VOICE_UPLINK:
if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) {
device = AUDIO_DEVICE_IN_VOICE_CALL;
break;
}
break;
case AUDIO_SOURCE_DEFAULT:
case AUDIO_SOURCE_MIC:
if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
} else if ((mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO) &&
(availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)) {
device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) {
device = AUDIO_DEVICE_IN_WIRED_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) {
device = AUDIO_DEVICE_IN_USB_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) {
device = AUDIO_DEVICE_IN_USB_DEVICE;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
device = AUDIO_DEVICE_IN_BUILTIN_MIC;
}
break;
case AUDIO_SOURCE_VOICE_COMMUNICATION:
// Allow only use of devices on primary input if in call and HAL does not support routing
// to voice call path.
if ((getPhoneState() == AUDIO_MODE_IN_CALL) &&
(availableOutputDevices.types() & AUDIO_DEVICE_OUT_TELEPHONY_TX) == 0) {
sp<AudioOutputDescriptor> primaryOutput = outputs.getPrimaryOutput();
availableDeviceTypes =
availableInputDevices.getDevicesFromHwModule(primaryOutput->getModuleHandle())
& ~AUDIO_DEVICE_BIT_IN;
}
switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) {
case AUDIO_POLICY_FORCE_BT_SCO:
// if SCO device is requested but no SCO device is available, fall back to default case
if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
break;
}
// FALL THROUGH
default: // FORCE_NONE
if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) {
device = AUDIO_DEVICE_IN_WIRED_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) {
device = AUDIO_DEVICE_IN_USB_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) {
device = AUDIO_DEVICE_IN_USB_DEVICE;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
device = AUDIO_DEVICE_IN_BUILTIN_MIC;
}
break;
case AUDIO_POLICY_FORCE_SPEAKER:
if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) {
device = AUDIO_DEVICE_IN_BACK_MIC;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
device = AUDIO_DEVICE_IN_BUILTIN_MIC;
}
break;
}
break;
case AUDIO_SOURCE_VOICE_RECOGNITION:
case AUDIO_SOURCE_UNPROCESSED:
case AUDIO_SOURCE_HOTWORD:
if (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO &&
availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) {
device = AUDIO_DEVICE_IN_WIRED_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) {
device = AUDIO_DEVICE_IN_USB_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) {
device = AUDIO_DEVICE_IN_USB_DEVICE;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
device = AUDIO_DEVICE_IN_BUILTIN_MIC;
}
break;
case AUDIO_SOURCE_CAMCORDER:
if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) {
device = AUDIO_DEVICE_IN_BACK_MIC;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) {
device = AUDIO_DEVICE_IN_BUILTIN_MIC;
}
break;
case AUDIO_SOURCE_VOICE_DOWNLINK:
case AUDIO_SOURCE_VOICE_CALL:
if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) {
device = AUDIO_DEVICE_IN_VOICE_CALL;
}
break;
case AUDIO_SOURCE_REMOTE_SUBMIX:
if (availableDeviceTypes & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
}
break;
case AUDIO_SOURCE_FM_TUNER:
if (availableDeviceTypes & AUDIO_DEVICE_IN_FM_TUNER) {
device = AUDIO_DEVICE_IN_FM_TUNER;
}
break;
default:
ALOGW("getDeviceForInputSource() invalid input source %d", inputSource);
break;
}
if (device == AUDIO_DEVICE_NONE) {
ALOGV("getDeviceForInputSource() no device found for source %d", inputSource);
if (availableDeviceTypes & AUDIO_DEVICE_IN_STUB) {
device = AUDIO_DEVICE_IN_STUB;
}
ALOGE_IF(device == AUDIO_DEVICE_NONE,
"getDeviceForInputSource() no default device defined");
}
ALOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device);
return device;
}比如录音的时候会走case AUDIO_SOURCE_MIC,按照优先级来划分的话从高到低是蓝牙,耳机,内置麦克风。
总结:
这里就已经讲完设备选择的流程,当然只是讲到它们的功能,具体的如何调用这些函数还在AudioPolicyManager中,AudioPolicyManager是个很大的架构,所以需要更多的章节去介绍。
下篇:
接下来将会从实际问题出发结合代码解决问题,问题的种类有很多,比如设备的切换错误,无声问题,杂音问题,声音外放的问题,音量大小的调节问题。
相关文章推荐
- Android源码分析--AudioPolicyEngine设备路由(01)
- Android源码分析--Audio设备文件加载(06)
- Android源码分析--Audio设备文件解析(07)
- Android摄像头相关源码分析: 设备驱动, HAL, Framework
- Android音频子系统源码分析之AudioFlinger的实现
- Android M AudioPolicy 分析
- Android源码分析:AudioFlinger中的线程
- Android摄像头相关源码分析: 设备驱动, HAL, Framework
- 从源码角度分析android蓝牙设备如何互联?
- Android源码分析--Audio声音相关问题分析(03)
- Android音频子系统源码分析之AudioTrack的使用
- Android Audio 02 - AudioTrack/AudioFlinger/AudioPolicyService 构架
- android网络编程 -- HTTP通信(02) Android HTTP 通信 [附源码分析]
- Android源码分析:AudioFlinger中的线程
- 安卓MonkeyRunner源码分析之与Android设备通讯方式
- Android源码分析:AudioEffect
- MonkeyRunner源码分析之与Android设备通讯方式
- Android蓝牙源码分析——Gatt写设备
- MonkeyRunner源码分析之与Android设备通讯方式
- android多媒体系统分析-AudioPolicyService