Android的音量调节分3个部分,分别是master volume(硬件音量,控制声卡),stream volume(流音量)和track volume(app音量)。
master volume:设置它等于设置所有的stream volume和track volume。它可以写到声卡里面去,控制所有声音的音量。也可以不写到声卡里面去,而是作为一个乘数因子来影响所有的音量。换句话说:master volume 可以设置所有的AudioTrack volume和stream volume。
stream volume:设置某一stream的音量,Android系统中支持10种stream。各种stream的音量也可以单独设置、互不影响。比如"音乐音量"不应该影响到"来电振铃"、"闹钟"、"通话"的音量。
stream volume alias:设置的是同一组stream音量,分组在Android源码中称之为"别名",即alias。比如在电话中,5种stream(STREAM_SYSTEM、STREAM_RING、STREAM_NOTIFICATION、STREAM_SYSTEM_ENFORCED、STREAM_DTMF)的alias都是STREAM_RING,那么对应的滑动条即可控制这5种stream的音量。
AudioTrack volume: 单个App设置音量时设置的是这个,它只影响本App的音量。
除此之外CarAudioManager通过SetGroupVolume设置组音量。
stream的种类
Android系统中有10种stream,在system/core/include/system/audio.h中定义,但把这10种stream分成组,属于同一组的stream具有相同的别名(alias)。在我们设置音量时,一个音量调节滑动条具有一个alias,具有相同alias的stream都会受到这个滑动条的影响。stream与alias的关系(参考)如下所示:
流类型 | 最大音量 | 小音量最 | 默认音量 | VOICE/DEFAULT | TELEVISION | |
---|---|---|---|---|---|---|
0 | STREAM_VOICE_CALL | 5 | 1 | 4 | STREAM_VOICE_CALL | STREAM_MUSIC |
1 | STREAM_SYSTEM | 7 | 0 | 7 | STREAM_RING | STREAM_MUSIC |
2 | STREAM_RING | 7 | 0 | 7 | STREAM_RING | STREAM_MUSIC |
3 | STREAM_MUSIC | 15 | 0 | 5 | STREAM_MUSIC | STREAM_MUSIC |
4 | STREAM_ALARM | 7 | 0 | 6 | STREAM_ALARM | STREAM_MUSIC |
5 | STREAM_NOTIFICATION | 7 | 0 | 5 | STREAM_RING | STREAM_MUSIC |
6 | STREAM_BLUETOOTH_SCO | 15 | 0 | 7 | STREAM_BLUETOOTH_SCO | STREAM_MUSIC |
7 | STREAM_SYSTEM_ENFORCED | 7 | 0 | 7 | STREAM_RING | STREAM_MUSIC |
8 | STREAM_DTMF | 15 | 0 | 5 | STREAM_RING | STREAM_MUSIC |
9 | STREAM_TTS | 15 | 0 | 5 | STREAM_MUSIC | STREAM_MUSIC |
10 | STREAM_ACCESSIBILITY | 15 | 0 | 5 | STREAM_MUSIC | STREAM_MUSIC |
声音播放的两种路径
MixerThread:APP对音量的设置不会影响到声卡的硬件音量,而只会影响APP的音频数据的幅值(变小或放大),这些音频数据最终被混合后传给声卡。
DirectOutputThread(比如HDMI,单个音频应用程序单独使用一个声卡):同一时间里只有一个APP、只有一个AudioTrack使用它,所以该AudioTrack的音量可以被DirectOutputThread直接用来设置硬件音量,这种声卡使用的不多。若配置文件中参数信息包含"flags AUDIO_OUTPUT_FLAG_DIRECT",则表示这个声卡可以被某个App独占。App就能以DirectOutputThread的形式来使用这个声卡。
混音的逻辑
app1:混音数据1 = 音频数据1 * master_volume * stream1_volume * AudioTrack1_volume
app2:混音数据2 = 音频数据2 * master_volume * stream2_volume * AudioTrack2_volume
app3:混音数据3 = 音频数据3 * master_volume * stream3_volume * AudioTrack3_volume
混合在一起: 最终混音 =混音数据1+混音数据2+混音数据3,然后把混合后的数据写给硬件。
void adjustVolume(int direction,int flags):调整最相关的数据流的音量
void adjustStreamVolume(int streamType, int direction, int flags):调整指定类型的声音
void setStreamVolume (int streamType, int index, int flags):直接设置指定类型的音量值
void setStreamMute(int streamType,booleanstate):将指定类型的声音调整为静音
int getStreamVolume(int streamType):返回特定数据流的当前音量
int getStreamMaxVolume(int streamType):返回特定流的最大音量
boolean isMasterMute():是否为主静音状态
void setMasterMute(boolean mute, int flags):设置主静音状态
boolean isStreamMute(int streamType):指定流是否为Mute状态
boolean isMasterMute():是否为主静音状态
int getStreamVolume(int streamType):返回特定数据流的当前音量
int getStreamMinVolume(int streamType):返回特定流的最晓音量
int getStreamMaxVolume(int streamType):返回特定流的最大音量
int getVolumeIndexForAttributes(in AudioAttributes aa):返回音量索引
int getMaxVolumeIndexForAttributes(in AudioAttributes aa):返回最大音量索引
int getMinVolumeIndexForAttributes(in AudioAttributes aa):返回最小音量索引
int getLastAudibleStreamVolume(int streamType):返回最后听得见流音量
List<AudioVolumeGroup> getAudioVolumeGroups():返回获取音频音量组
void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage):调整指定类型的声音
void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, String callingPackage, String caller):
void setStreamVolume(int streamType, int index, int flags, String callingPackage):直接设置指定类型的音量值
void setMasterMute(boolean mute, int flags, String callingPackage, int userId):设置主静音状态
void setVolumeIndexForAttributes(in AudioAttributes aa, int index, int flags, String callingPackage):设置音量索引
void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb):
首先看AudioManager的setStreamVolume方法:
//frameworks/base/media/java/android/media/AudioManager.java
public class AudioManager {
......
public void setStreamVolume(int streamType, int index, int flags) {
final IAudioService service = getService();
try {
//调用AudioService的setStreamVolume方法
service.setStreamVolume(streamType, index, flags, getContext().getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
......
}
在AudioManager 中调用了AudioService的setStreamVolume方法:
//frameworks/base/service/java/com/android/server/audio/AudioService.java
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
AccessibilityManager.AccessibilityServicesStateChangeListener {
......
/** The controller for the volume UI. */
private final VolumeController mVolumeController = new VolumeController();
......
public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
//验证流类型是否合法
if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
Log.w(TAG, "Trying to call setStreamVolume() for a11y without"
+ " CHANGE_ACCESSIBILITY_VOLUME callingPackage=" + callingPackage);
return;
}
if ((streamType == AudioManager.STREAM_VOICE_CALL) && (index == 0)
&& (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED)) {
Log.w(TAG, "Trying to call setStreamVolume() for STREAM_VOICE_CALL and index 0 without"
+ " MODIFY_PHONE_STATE callingPackage=" + callingPackage);
return;
}
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
index/*val1*/, flags/*val2*/, callingPackage));
setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
Binder.getCallingUid());
}
private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
String caller, int uid) {
if (DEBUG_VOL) {
Log.d(TAG, "setStreamVolume(stream=" + streamType+", index=" + index
+ ", calling=" + callingPackage + ")");
}
if (mUseFixedVolume) {
return;
}
//确认流类型
ensureValidStreamType(streamType);
//获取别名,安卓在通过流设置音量时可以通过传入一个流类型通过mStreamVolumeAlias查找隐射的流类型来达到通过一个流来设置另一个流的目的。
(此处比较绕,简单的理解就是我可以通过使用STREAM_RING 通过mStreamVolumeAlias来映射到STREAM_MUSIC这个流以达到改变STREAM_RING来控制STREAM_MUSIC的目的)。
int streamTypeAlias = mStreamVolumeAlias[streamType];
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
//获取当前流的设备(如喇叭,蓝牙,以下所设置以及保存的音量是和设备进行保存的)
final int device = getDeviceForStream(streamType);
//上一次设置的音量值
int oldIndex;
// skip a2dp absolute volume control request when the device
// is not an a2dp device
if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) == 0 &&
(flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
return;
}
// If we are being called by the system (e.g. hardware keys) check for current user
// so we handle user restrictions correctly.
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
}
if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
return;
}
if (isAndroidNPlus(callingPackage)
&& wouldToggleZenMode(getNewRingerMode(streamTypeAlias, index, flags))
&& !mNm.isNotificationPolicyAccessGrantedForPackage(callingPackage)) {
throw new SecurityException("Not allowed to change Do Not Disturb state");
}
if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
return;
}
synchronized (mSafeMediaVolumeStateLock) {
// reset any pending volume command
mPendingVolumeCommand = null;
//获取旧的音量值
oldIndex = streamState.getIndex(device);
//对音量值进行某些操作(在音量设置逻辑中对传入的值进行修改,达到对不同流对应的音量大小进行分别控制的目的,简单说大概就是单位换算的意思)
index = rescaleIndex(index * 10, streamType, streamTypeAlias);
if (streamTypeAlias == AudioSystem.STREAM_MUSIC
&& (device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
Log.d(TAG, "setStreamVolume postSetAvrcpAbsoluteVolumeIndex index=" + index
+ "stream=" + streamType);
}
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10);
}
if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0
&& streamType == getHearingAidStreamType()) {
Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index
+ " stream=" + streamType);
mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
}
if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
setSystemAudioVolume(oldIndex, index, getStreamMaxVolume(streamType), flags); // 这里是和hdmi相关,不用管
}
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
((device & mFixedVolumeDevices) != 0)) {
flags |= AudioManager.FLAG_FIXED_VOLUME;
// volume is either 0 or max allowed for fixed volume devices
if (index != 0) {
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
(device & mSafeMediaVolumeDevices) != 0) {
index = safeMediaVolumeIndex(device);
} else {
index = streamState.getMaxIndex();
}
}
}
if (!checkSafeMediaVolume(streamTypeAlias, index, device)) {
mVolumeController.postDisplaySafeVolumeWarning(flags);
mPendingVolumeCommand = new StreamVolumeCommand(
streamType, index, flags, device);
} else {
onSetStreamVolume(streamType, index, flags, device, caller); //此处时真正设置音量的地方,调用onSetStreamVolume
index = mStreamStates[streamType].getIndex(device);
}
}
synchronized (mHdmiClientLock) {
if (mHdmiManager != null &&
mHdmiAudioSystemClient != null &&
mHdmiSystemAudioSupported &&
streamTypeAlias == AudioSystem.STREAM_MUSIC &&
(oldIndex != index)) {
final long identity = Binder.clearCallingIdentity();
mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(
false, getStreamVolume(AudioSystem.STREAM_MUSIC),
getStreamMaxVolume(AudioSystem.STREAM_MUSIC),
isStreamMute(AudioSystem.STREAM_MUSIC));
Binder.restoreCallingIdentity(identity);
}
}
sendVolumeUpdate(streamType, oldIndex, index, flags, device); //发送更新音量的信息。
}
......
private void onSetStreamVolume(int streamType, int index, int flags, int device,
String caller) {
final int stream = mStreamVolumeAlias[streamType];
setStreamVolumeInt(stream, index, device, false, caller); //
// setting volume on ui sounds stream type also controls silent mode
if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
(stream == getUiSoundsStreamType())) {
setRingerMode(getNewRingerMode(stream, index, flags),
TAG + ".onSetStreamVolume", false /*external*/);
}
// setting non-zero volume for a muted stream unmutes the stream and vice versa,
// except for BT SCO stream where only explicit mute is allowed to comply to BT requirements
if (streamType != AudioSystem.STREAM_BLUETOOTH_SCO) {
mStreamStates[stream].mute(index == 0);
}
}
......
private void setStreamVolumeInt(int streamType,
int index,
int device,
boolean force,
String caller) {
if ((device & mFullVolumeDevices) != 0) {
return;
}
VolumeStreamState streamState = mStreamStates[streamType];
//先调用streamState.setIndex将音量设置到表里面保存起来
if (streamState.setIndex(index, device, caller) || force) {
// Post message to set system volume (it in turn will post a message
// to persist).
//发送消息给handler设置音量
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
}
......
}
AudioService的setStreamVolume方法中调用onSetStreamVolume->setStreamVolumeInt然后发送MSG_SET_DEVICE_VOLUME给AudioHandler,AudioHandler处理如下:
//frameworks/base/service/java/com/android/server/audio/AudioService.java
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
AccessibilityManager.AccessibilityServicesStateChangeListener {
......
/** Handles internal volume messages in separate volume thread. */
private class AudioHandler extends Handler {
......
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SET_DEVICE_VOLUME:
setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1); //调用setDeviceVolume
break;
}
}
......
}
我们接着分析setDeviceVolume的处理:
//frameworks/base/service/java/com/android/server/audio/AudioService.java
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
AccessibilityManager.AccessibilityServicesStateChangeListener {
......
void setDeviceVolume(VolumeStreamState streamState, int device) {
final boolean isAvrcpAbsVolSupported = mDeviceBroker.isAvrcpAbsoluteVolumeSupported();
synchronized (VolumeStreamState.class) {
// Apply volume
//调用streamState.applyDeviceVolume_syncVSS
streamState.applyDeviceVolume_syncVSS(device, isAvrcpAbsVolSupported);
// Apply change to all streams using this one as alias
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
if (streamType != streamState.mStreamType &&
mStreamVolumeAlias[streamType] == streamState.mStreamType) {
// Make sure volume is also maxed out on A2DP device for aliased stream
// that may have a different device selected
int streamDevice = getDeviceForStream(streamType);
if ((device != streamDevice) && isAvrcpAbsVolSupported
&& ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0)) {
mStreamStates[streamType].applyDeviceVolume_syncVSS(device,
isAvrcpAbsVolSupported);
}
mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice,
isAvrcpAbsVolSupported);
}
}
}
// Post a persist volume msg
sendMsg(mAudioHandler,
MSG_PERSIST_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
PERSIST_DELAY);
}
......
}
setDeviceVolume方法中调用内部类VolumeStreamState对象的applyDeviceVolume_syncVSS方法:
//frameworks/base/service/java/com/android/server/audio/AudioService.java
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
AccessibilityManager.AccessibilityServicesStateChangeListener {
......
private class VolumeStreamState {
// must be called while synchronized VolumeStreamState.class
void applyDeviceVolume_syncVSS(int device, boolean isAvrcpAbsVolSupported) {
int index;
if (mIsMuted) {
index = 0;
} else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && isAvrcpAbsVolSupported) {
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if ((device & mFullVolumeDevices) != 0) {
index = (mIndexMax + 5)/10;
} else if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
index = (mIndexMax + 5)/10;
} else {
index = (getIndex(device) + 5)/10;
}
setStreamVolumeIndex(index, device);
}
......
private void setStreamVolumeIndex(int index, int device) {
// Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
// This allows RX path muting by the audio HAL only when explicitly muted but not when
// index is just set to 0 to repect BT requirements
if (mStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0 && !mIsMuted) {
index = 1;
}
//进入AudioSystem,开始真正地设置音量了
AudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
}
......
}
......
}
在VolumeStreamState 的setStreamVolumeIndex调用AudioSystem的setStreamVolumeIndexAS,开始真正地设置音量:
//frameworks/base/service/java/com/android/server/audio/AudioSystem.java
public class AudioSystem{
......
/** Wrapper for native methods called from AudioService */
public static int setStreamVolumeIndexAS(int stream, int index, int device) {
if (DEBUG_VOLUME) {
Log.i(TAG, "setStreamVolumeIndex: " + STREAM_NAMES[stream]
+ " dev=" + Integer.toHexString(device) + " idx=" + index);
}
return setStreamVolumeIndex(stream, index, device);
}
......
}
在AudioSystem的setStreamVolumeIndexAS中调用setStreamVolumeIndex方法,setStreamVolumeIndex方法是一个native方法,通过JNI调用AudioSystem native的setStreamVolumeIndex函数:
// frameworks/base/core/jni/android_media_AudioSystem.cpp
static jint
android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env,
jobject thiz,
jint stream,
jint index,
jint device)
{
return (jint) check_AudioSystem_Command(
AudioSystem::setStreamVolumeIndex(static_cast <audio_stream_type_t>(stream),
index,
(audio_devices_t)device));
}
在JNI代码中调用AudioSystem的setStreamVolumeIndex函数:
//frameworks/av/media/libaudioclient/AudioSystem.cpp
status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->setStreamVolumeIndex(stream, index, device);
}
此处创建了一个AudioPolicyService对象,并调用了该对象的setStreamVolume方法。
AudioPolicyService调用了BnAudioPolicyService::onTransact后回去调用自己的setStreamVolumeIndex方法。而setStreamVolumeIndex的实现在AudioPolicyInterfaceImpl.cpp中。
//frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device)
{
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
return mAudioPolicyManager->setStreamVolumeIndex(stream,
index,
device);
}
这里调用mAudioPolicyManager->setStreamVolumeIndex(stream, index,device);,AudioPolicyManager下的setStreamVolumeIndex定义在AudioPolicyManager.cpp中:
//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device)
{
auto attributes = mEngine->getAttributesForStreamType(stream);
ALOGV("%s: stream %s attributes=%s", __func__,
toString(stream).c_str(), toString(attributes).c_str());
return setVolumeIndexForAttributes(attributes, index, device);
}
在setStreamVolumeIndex调用setVolumeIndexForAttributes函数:
//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_t &attributes,
int index,
audio_devices_t device)
{
// Get Volume group matching the Audio Attributes
auto group = mEngine->getVolumeGroupForAttributes(attributes);
if (group == VOLUME_GROUP_NONE) {
ALOGD("%s: no group matching with %s", __FUNCTION__, toString(attributes).c_str());
return BAD_VALUE;
}
ALOGV("%s: group %d matching with %s", __FUNCTION__, group, toString(attributes).c_str());
status_t status = NO_ERROR;
IVolumeCurves &curves = getVolumeCurves(attributes);
VolumeSource vs = toVolumeSource(group);
product_strategy_t strategy = mEngine->getProductStrategyForAttributes(attributes);
status = setVolumeCurveIndex(index, device, curves);
if (status != NO_ERROR) {
ALOGE("%s failed to set curve index for group %d device 0x%X", __func__, group, device);
return status;
}
audio_devices_t curSrcDevice;
auto curCurvAttrs = curves.getAttributes();
if (!curCurvAttrs.empty() && curCurvAttrs.front() != defaultAttr) {
auto attr = curCurvAttrs.front();
curSrcDevice = mEngine->getOutputDevicesForAttributes(attr, nullptr, false).types();
} else if (!curves.getStreamTypes().empty()) {
auto stream = curves.getStreamTypes().front();
curSrcDevice = mEngine->getOutputDevicesForStream(stream, false).types();
} else {
ALOGE("%s: Invalid src %d: no valid attributes nor stream",__func__, vs);
return BAD_VALUE;
}
curSrcDevice = Volume::getDeviceForVolume(curSrcDevice);
// update volume on all outputs and streams matching the following:
// - The requested stream (or a stream matching for volume control) is active on the output
// - The device (or devices) selected by the engine for this stream includes
// the requested device
// - For non default requested device, currently selected device on the output is either the
// requested device or one of the devices selected by the engine for this stream
// - For default requested device (AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME), apply volume only if
// no specific device volume value exists for currently selected device.
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
audio_devices_t curDevice = desc->devices().types();
if (curDevice & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
curDevice |= AUDIO_DEVICE_OUT_SPEAKER;
curDevice &= ~AUDIO_DEVICE_OUT_SPEAKER_SAFE;
}
// Inter / intra volume group priority management: Loop on strategies arranged by priority
// If a higher priority strategy is active, and the output is routed to a device with a
// HW Gain management, do not change the volume
bool applyVolume = false;
if (desc->useHwGain()) {
if (!(desc->isActive(toVolumeSource(group)) || isInCall())) {
continue;
}
for (const auto &productStrategy : mEngine->getOrderedProductStrategies()) {
auto activeClients = desc->clientsList(true /*activeOnly*/, productStrategy,
false /*preferredDevice*/);
if (activeClients.empty()) {
continue;
}
bool isPreempted = false;
bool isHigherPriority = productStrategy < strategy;
for (const auto &client : activeClients) {
if (isHigherPriority && (client->volumeSource() != vs)) {
ALOGV("%s: Strategy=%d (\nrequester:\n"
" group %d, volumeGroup=%d attributes=%s)\n"
" higher priority source active:\n"
" volumeGroup=%d attributes=%s) \n"
" on output %zu, bailing out", __func__, productStrategy,
group, group, toString(attributes).c_str(),
client->volumeSource(), toString(client->attributes()).c_str(), i);
applyVolume = false;
isPreempted = true;
break;
}
// However, continue for loop to ensure no higher prio clients running on output
if (client->volumeSource() == vs) {
applyVolume = true;
}
}
if (isPreempted || applyVolume) {
break;
}
}
if (!applyVolume) {
continue; // next output
}
status_t volStatus = checkAndSetVolume(curves, vs, index, desc, curDevice,
(vs == toVolumeSource(AUDIO_STREAM_SYSTEM)?
TOUCH_SOUND_FIXED_DELAY_MS : 0));
if (volStatus != NO_ERROR) {
status = volStatus;
}
continue;
}
if (!(desc->isActive(vs) || isInCall())) {
continue;
}
if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) && ((curDevice & device) == 0)) {
continue;
}
if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
curSrcDevice |= device;
applyVolume = (Volume::getDeviceForVolume(curDevice) & curSrcDevice) != 0;
} else {
applyVolume = !curves.hasVolumeIndexForDevice(curSrcDevice);
}
if (applyVolume) {
//FIXME: workaround for truncated touch sounds
// delayed volume change for system stream to be removed when the problem is
// handled by system UI
status_t volStatus = checkAndSetVolume(
curves, vs, index, desc, curDevice,
((vs == toVolumeSource(AUDIO_STREAM_SYSTEM))?
TOUCH_SOUND_FIXED_DELAY_MS : 0));
if (volStatus != NO_ERROR) {
status = volStatus;
}
}
}
mpClientInterface->onAudioVolumeGroupChanged(group, 0 /*flags*/);
return status;
}
在setVolumeIndexForAttributes中调用checkAndSetVolume:
//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
status_t AudioPolicyManager::checkAndSetVolume(IVolumeCurves &curves,
VolumeSource volumeSource,
int index,
const sp<AudioOutputDescriptor>& outputDesc,
audio_devices_t device,
int delayMs,
bool force)
{
// do not change actual attributes volume if the attributes is muted
if (outputDesc->isMuted(volumeSource)) {
ALOGVV("%s: volume source %d muted count %d active=%d", __func__, volumeSource,
outputDesc->getMuteCount(volumeSource), outputDesc->isActive(volumeSource));
return NO_ERROR;
}
VolumeSource callVolSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL);
VolumeSource btScoVolSrc = toVolumeSource(AUDIO_STREAM_BLUETOOTH_SCO);
bool isVoiceVolSrc = callVolSrc == volumeSource;
bool isBtScoVolSrc = btScoVolSrc == volumeSource;
audio_policy_forced_cfg_t forceUseForComm =
mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION);
// do not change in call volume if bluetooth is connected and vice versa
// if sco and call follow same curves, bypass forceUseForComm
if ((callVolSrc != btScoVolSrc) &&
((isVoiceVolSrc && forceUseForComm == AUDIO_POLICY_FORCE_BT_SCO) ||
(isBtScoVolSrc && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO))) {
ALOGV("%s cannot set volume group %d volume with force use = %d for comm", __func__,
volumeSource, forceUseForComm);
return INVALID_OPERATION;
}
if (device == AUDIO_DEVICE_NONE) {
device = outputDesc->devices().types();
}
float volumeDb = computeVolume(curves, volumeSource, index, device); //计算volumeDb
if (outputDesc->isFixedVolume(device) ||
// Force VoIP volume to max for bluetooth SCO
((isVoiceVolSrc || isBtScoVolSrc) && (device & AUDIO_DEVICE_OUT_ALL_SCO) != 0)) {
volumeDb = 0.0f;
}
outputDesc->setVolume(volumeDb, volumeSource, curves.getStreamTypes(), device, delayMs, force); //AudioPolicyService
if (isVoiceVolSrc || isBtScoVolSrc) {
float voiceVolume;
// Force voice volume to max or mute for Bluetooth SCO as other attenuations are managed by the headset
if (isVoiceVolSrc) {
voiceVolume = (float)index/(float)curves.getVolumeIndexMax();
} else {
voiceVolume = index == 0 ? 0.0 : 1.0;
}
if (voiceVolume != mLastVoiceVolume) {
mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
mLastVoiceVolume = voiceVolume;
}
}
return NO_ERROR;
}
AudioOutputDescriptor 在 android 中有两个实现,分别是 SwAudioOutputDescriptor 和 HwAudioOutputDescriptor,音量设置的执行来到 SwAudioOutputDescriptor::setVolume():
//frameworks/av/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
bool SwAudioOutputDescriptor::setVolume(float volumeDb,
VolumeSource vs, const StreamTypeVector &streamTypes,
audio_devices_t device,
uint32_t delayMs,
bool force)
{
StreamTypeVector streams = streamTypes;
if (!AudioOutputDescriptor::setVolume(volumeDb, vs, streamTypes, device, delayMs, force)) {
return false;
}
if (streams.empty()) {
streams.push_back(AUDIO_STREAM_MUSIC);
}
for (const auto& devicePort : devices()) {
// APM loops on all group, so filter on active group to set the port gain,
// let the other groups set the stream volume as per legacy
// TODO: Pass in the device address and check against it.
if (device == devicePort->type() &&
devicePort->hasGainController(true) && isActive(vs)) {
ALOGV("%s: device %s has gain controller", __func__, devicePort->toString().c_str());
// @todo: here we might be in trouble if the SwOutput has several active clients with
// different Volume Source (or if we allow several curves within same volume group)
//
// @todo: default stream volume to max (0) when using HW Port gain?
float volumeAmpl = Volume::DbToAmpl(0);
for (const auto &stream : streams) {
mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
}
AudioGains gains = devicePort->getGains();
int gainMinValueInMb = gains[0]->getMinValueInMb();
int gainMaxValueInMb = gains[0]->getMaxValueInMb();
int gainStepValueInMb = gains[0]->getStepValueInMb();
int gainValueMb = ((volumeDb * 100)/ gainStepValueInMb) * gainStepValueInMb;
gainValueMb = std::max(gainMinValueInMb, std::min(gainValueMb, gainMaxValueInMb));
audio_port_config config = {};
devicePort->toAudioPortConfig(&config);
config.config_mask = AUDIO_PORT_CONFIG_GAIN;
config.gain.values[0] = gainValueMb;
return mClientInterface->setAudioPortConfig(&config, 0) == NO_ERROR;
}
}
// Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is enabled
float volumeAmpl = Volume::DbToAmpl(getCurVolume(vs));
if (hasStream(streams, AUDIO_STREAM_BLUETOOTH_SCO)) {
mClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volumeAmpl, mIoHandle, delayMs);
}
for (const auto &stream : streams) {
ALOGV("%s output %d for volumeSource %d, volume %f, delay %d stream=%s", __func__,
mIoHandle, vs, volumeDb, delayMs, toString(stream).c_str());
mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
}
return true;
}
SwAudioOutputDescriptor 又通过 AudioPolicyClientInterface 设置音量;AudioPolicyClientInterface 的实现为 AudioPolicyService::AudioPolicyClient,设置音量的动作经 AudioPolicyService::AudioPolicyClient 转回 AudioPolicyService,只是这次调用的是 AudioPolicyService::setStreamVolume();
//frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp
int AudioPolicyService::setStreamVolume(audio_stream_type_t stream,float volume,
audio_io_handle_t output,int delayMs)
{
return (int)mAudioCommandThread->volumeCommand(stream, volume,output, delayMs);
}
status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type_t stream,
float volume,audio_io_handle_t output,int delayMs)
{
...(封装了一下data跟command)
return sendCommand(command, delayMs);
}
status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs) {...(一些命令队列的操作)}
// 处理函数
bool AudioPolicyService::AudioCommandThread::threadLoop()
{
...
while (!exitPending())
{
...
switch (command->mCommand) {
...
case SET_VOLUME:
...(Lock)
VolumeData *data = (VolumeData *)command->mParam.get();
command->mStatus = AudioSystem::setStreamVolume(data->mStream,
data->mVolume,data->mIO);
break;
...
}
}
AudioPolicyService异步执行这个操作,最后会转到AudioSystem的setStreamVolume:
//frameworks/av/media/libaudioclient/AudioSystem.cpp
status_t AudioSystem::setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output)
{
if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
af->setStreamVolume(stream, value, output);
return NO_ERROR;
}
在AudioSystem的setStreamVolume中会调用AudioFlinger的setStreamVolume:
//frameworks/base/media/java/android/media/audioflinger/AudioFlinger.cpp
status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output)
{
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
status_t status = checkStreamType(stream);
if (status != NO_ERROR) {
return status;
}
if (output == AUDIO_IO_HANDLE_NONE) {
return BAD_VALUE;
}
LOG_ALWAYS_FATAL_IF(stream == AUDIO_STREAM_PATCH && value != 1.0f,
"AUDIO_STREAM_PATCH must have full scale volume");
AutoMutex lock(mLock);
VolumeInterface *volumeInterface = getVolumeInterface_l(output);
if (volumeInterface == NULL) {
return BAD_VALUE;
}
volumeInterface->setStreamVolume(stream, value);
return NO_ERROR;
}
AudioFlinger会去获取output对应的PlaybackThread并设置PlaybackThread的音量,如果output == AUDIOIOHANDLE_NONE,则设置所有PlaybackThread的音量。
PlaybackThread设置mStreamTypes的volume。并唤醒PlaybackThread线程
//frameworks/base/media/java/android/media/audioflinger/Threads.cpp
void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{
Mutex::Autolock _l(mLock);
mStreamTypes[stream].volume = value;
broadcast_l();
}
整体的概要流程图如下: