背景:
在学习了音量调节相关的课程后,大家对整个音量调节整个流程都应该很清楚了,但是在使用手机音量调节时候,对一些场景功能可能还是会有一些疑问,主要有以下几个部分:
1、为啥物理音量键可以准确识别当前正在播放的StreamType类型,从而调节对应的音量进度

2、物理音量按键也可以调节声音,设置界面的Seekbar也可以调节声音,请问二者是如何进行同步的呢?

物理音量调节后设置的画面的也seekbar会同步动起来。
那么本文主要来剖析一下疑问1
疑问1剖析:
为什么系统在按音量上下键时候,就可以准确识别出当前的StreamType,调节的都是当前正在播放的StreamType?
现象如下:
在设置中触发notification的声音播放时候,按下物理音量上下键,观察音量进度条是选中的notification这部分。
铃声播放时候按音量上下键发现调节也是铃声的声音进度
那么这块的具体的代码是如何实现的?具体应该如何剖析调试追踪呢?
调试追踪选中Active StreamType相关方法:
需要在AudioService中把DEBUG_VOL = true就行
然后再使用如下方式过滤log:
adb logcat | grep AudioService
按一下物理的音量上下键
日志中其实也大概可以看出StreamType是如何变化的:
刚开始stream=-2147483648,然后通过getActiveStreamType变成的默认的StreamType为3,也就是StreamType为music,后续都一直以StreamType为3进行音量条件,那么接下来主要分析一下getActiveStreamType方法。
AudioService的getActiveStreamType获取活跃状态的StreamType
private int getActiveStreamType(int suggestedStreamType) {
//省略
switch (mPlatformType) {
case AudioSystem.PLATFORM_VOICE:
//判断是否处于语音电话等模式
if (isInCommunication()
|| mAudioSystem.isStreamActive(AudioManager.STREAM_VOICE_CALL, 0)) {
if (!replaceStreamBtSco()
&& mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_SCO) {
//都会有log打印当前的StreamType情况
if (DEBUG_VOL) {
Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
}
//返回蓝牙相关StreamType
return AudioSystem.STREAM_BLUETOOTH_SCO;
} else {
if (DEBUG_VOL) {
Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
}
//返回通话相关StreamType
return AudioSystem.STREAM_VOICE_CALL;
}
}
//判断suggestedStreamType是不是值为默认,默认就是一个很小的负数
else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
//这里会判断是不是STREAM_RING处于活跃状态
if (wasStreamActiveRecently(AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
if (DEBUG_VOL)
Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING stream active");
return AudioSystem.STREAM_RING;
//这里会判断是不是STREAM_NOTIFICATION处于活跃状态
} else if (wasStreamActiveRecently(
AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) {
Log.v(
TAG,
"getActiveStreamType: Forcing STREAM_NOTIFICATION stream"
+ " active");
}
return AudioSystem.STREAM_NOTIFICATION;
} else {
//如果上面两种StreamType都是不处于Active状态,那么就会返回默认的StreamType为music
if (DEBUG_VOL) {
Log.v(TAG, "getActiveStreamType: Forcing DEFAULT_VOL_STREAM_NO_PLAYBACK("
+ DEFAULT_VOL_STREAM_NO_PLAYBACK + ") b/c default");
}
//返回默认streamtype,也就是music
return DEFAULT_VOL_STREAM_NO_PLAYBACK;
}
} else if (
wasStreamActiveRecently(AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
if (DEBUG_VOL)
Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION stream active");
return AudioSystem.STREAM_NOTIFICATION;
} else if (wasStreamActiveRecently(AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
if (DEBUG_VOL)
Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING stream active");
return AudioSystem.STREAM_RING;
}
default:
if (isInCommunication()) {
if (!replaceStreamBtSco()
&& mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_SCO) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
return AudioSystem.STREAM_BLUETOOTH_SCO;
} else {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
return AudioSystem.STREAM_VOICE_CALL;
}
} else if (mAudioSystem.isStreamActive(
AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
return AudioSystem.STREAM_NOTIFICATION;
} else if (mAudioSystem.isStreamActive(
AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
return AudioSystem.STREAM_RING;
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
if (mAudioSystem.isStreamActive(
AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
return AudioSystem.STREAM_NOTIFICATION;
}
if (mAudioSystem.isStreamActive(
AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
return AudioSystem.STREAM_RING;
}
if (DEBUG_VOL) {
Log.v(TAG, "getActiveStreamType: Forcing DEFAULT_VOL_STREAM_NO_PLAYBACK("
+ DEFAULT_VOL_STREAM_NO_PLAYBACK + ") b/c default");
}
return DEFAULT_VOL_STREAM_NO_PLAYBACK;
}
break;
}
//最后还会有一个蓝牙相关的替换
suggestedStreamType = replaceBtScoStreamWithVoiceCall(suggestedStreamType,
"getActiveStreamType");
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
+ suggestedStreamType);
return suggestedStreamType;
}
上面代码比较简单,本质上就是按照如下顺序
STREAM_BLUETOOTH_SCO --》 STREAM_VOICE_CALL --》 STREAM_NOTIFICATION --》STREAM_RING
进行判断是否有这些类型的StreamType处于活跃的,如果都没有最后就是返回DEFAULT_VOL_STREAM_NO_PLAYBACK也就是music状态。
那么看看是如何判断当前StreamType是否Active状态:
public boolean isStreamActive(int stream, int inPastMs) {
return AudioSystem.isStreamActive(stream, inPastMs);
}
frameworks/av/media/libaudioclient/AudioSystem.cpp
status_t AudioSystem::isStreamActive(audio_stream_type_t stream, bool* state, uint32_t inPastMs) {
const sp<IAudioPolicyService> aps = get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
if (state == NULL) return BAD_VALUE;
AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS(
legacy2aidl_audio_stream_type_t_AudioStreamType(stream));
//最后调用到了AudioPolicyManager中
int32_t inPastMsAidl = VALUE_OR_RETURN_STATUS(convertIntegral<int32_t>(inPastMs));
RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
aps->isStreamActive(streamAidl, inPastMsAidl, state)));
return OK;
}
下面来看看AudioPolicyManager的isStreamActive方法:
frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
{
//转换成VolumeSource其实就是根据StreamType找到对应volume_group_t
auto vs = toVolumeSource(stream, false);
//然后在mOutputs集合中寻找这个VolumeSource是不是Active状态
return vs != VOLUME_SOURCE_NONE ? mOutputs.isActive(vs, inPastMs) : false;
}
那么主要来看看mOutputs.isActive方法:
frameworks/av/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
// SwAudioOutputCollection implementation
bool SwAudioOutputCollection::isActive(VolumeSource volumeSource, uint32_t inPastMs) const
{
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < this->size(); i++) {
const sp<SwAudioOutputDescriptor> outputDesc = this->valueAt(i);
//这里稍微要注意一下inPastMs代表在过去的多少ms内,但是系统一般设置为0ms
if (outputDesc->isActive(volumeSource, inPastMs, sysTime)) {
return true;
}
}
return false;
}
这里会去寻找各个AudioOutputDescriptor,也就OutputThread看看是否有这个类型的track处于Active状态,其实也可以直接通过dumpsys media.audio_policy查看:
如果没有任何track在播放,则所有的active count都为0
比如这个时候如果正在播放铃声的StreamType为Ring的时候进行dump:
可以看到这里volume activities的对应id为11,其实也就是VolumeSource,对应就是

原文地址参考:
https://mp.weixin.qq.com/s/JEZWQYysDMEndT3IqklWfw
更多framework实战开发干货,请关注“千里马学框架”
2449

被折叠的 条评论
为什么被折叠?



