流程框图
初始化
开机的时候,系统会从数据库中更新当前的音量值给各个音频流
// 根据数据库的配置创建流的状态
private void createStreamStates() {
int numStreamTypes = AudioSystem.getNumStreamTypes();
VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
for (int i = 0; i < numStreamTypes; i++) {
streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[mStreamVolumeAlias[i]], i); // VolumeStreamState 构造
}
checkAllAliasStreamVolumes(); // 更新音量到设备中
}
// 在数据库中读取每个设备的流音量
private VolumeStreamState(String settingName, int streamType) {
mVolumeIndexSettingName = settingName;
mStreamType = streamType;
mIndexMax = MAX_STREAM_VOLUME[streamType];
AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
mIndexMax *= 10;
// mDeathHandlers must be created before calling readSettings()
mDeathHandlers = new ArrayList<VolumeDeathHandler>();
readSettings(); // 从数据库中读取数据,每个设备有自己独立的音量
}
// 数据库中键值的拼接方法,exp: volume_music_speaker
public String getSettingNameForDevice(int device) {
String name = mVolumeIndexSettingName;
String suffix = AudioSystem.getDeviceName(device);
if (suffix.isEmpty()) {
return name;
}
return name + "_" + suffix;
}
// 更新音量到设备中
private void checkAllAliasStreamVolumes() {
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = 0; streamType < numStreamTypes; streamType++) {
if (streamType != mStreamVolumeAlias[streamType]) {
mStreamStates[streamType].
setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]]);
}
// apply stream volume
if (!mStreamStates[streamType].isMuted()) {
mStreamStates[streamType].applyAllVolumes(); // 更新设备音量
}
}
}
音量调整
上层通过java层接口设置音量,该流程主要分析 AudioService.java
提供的 setStreamVolume
接口
// AudioService.java 设置音量入口
public void setStreamVolume(int streamType, int index, int flags) {
IAudioService service = getService();
try {
if (mUseMasterVolume) {
service.setMasterVolume(index, flags, mContext.getOpPackageName());
} else {
service.setStreamVolume(streamType, index, flags, mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setStreamVolume", e);
}
}
// AudioService.java 设置音量
public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
....
ensureValidStreamType(streamType);
int streamTypeAlias = mStreamVolumeAlias[streamType];
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
final int device = getDeviceForStream(streamType);
int oldIndex;
...
onSetStreamVolume(streamType, index, flags, device); // 发送 MSG_SET_DEVICE_VOLUME
index = mStreamStates[streamType].getIndex(device);
sendVolumeUpdate(streamType, oldIndex, index, flags); // 发送广播
}
// onSetStreamVolume -> setStreamVolumeInt
private void setStreamVolumeInt(int streamType,
int index,
int device,
boolean force) {
VolumeStreamState streamState = mStreamStates[streamType];
if (streamState.setIndex(index, device) || force) { // 更新到 streamState 的映射表中
// Post message to set system volume (it in turn will post a message
// to persist).
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME, // 更新音量到设备
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
}
// 处理消息:MSG_SET_DEVICE_VOLUME
private void setDeviceVolume(VolumeStreamState streamState, int device) {
// Apply volume
streamState.applyDeviceVolume(device);
....
// Post a persist volume msg
sendMsg(mAudioHandler,
MSG_PERSIST_VOLUME, // 保存数据库
SENDMSG_QUEUE,
device,
0,
streamState,
PERSIST_DELAY);
}
// 通过message:MSG_PERSIST_VOLUME 把音量同步到数据库
private void persistVolume(VolumeStreamState streamState, int device) {
if (mUseFixedVolume) {
return;
}
System.putIntForUser(mContentResolver,
streamState.getSettingNameForDevice(device),
(streamState.getIndex(device) + 5)/ 10,
UserHandle.USER_CURRENT);
}
// 通过binder通信调用到ap中的设置音量接口
AudioPolicyManagerBase::setStreamVolumeIndex
// 最后在Mixer里准备播放时,音量生效
case VOLUME:
switch (param) {
case VOLUME0:
case VOLUME1:
if (track.volume[param-VOLUME0] != valueInt) {
ALOGV("setParameter(VOLUME, VOLUME0/1: %04x)", valueInt);
track.prevVolume[param-VOLUME0] = track.volume[param-VOLUME0] << 16;
track.volume[param-VOLUME0] = valueInt;
if (target == VOLUME) {
track.prevVolume[param-VOLUME0] = valueInt << 16;
track.volumeInc[param-VOLUME0] = 0;
} else {
int32_t d = (valueInt<<16) - track.prevVolume[param-VOLUME0];
int32_t volInc = d / int32_t(mState.frameCount);
track.volumeInc[param-VOLUME0] = volInc;
if (volInc == 0) {
track.prevVolume[param-VOLUME0] = valueInt << 16;
}
}
invalidateState(1 << name);
}
break;
参考链接:https://blog.csdn.net/xlnaan/article/details/80527902
常用调试手段
系统音量查询
DumpSys
我们可以利用android的dumpsys机制,来查询系统的流音量
root@rk3188:/data # dumpsys media.audio_policy
Outputs dump:
- Output 2 dump: // outputs index
Sampling rate: 44100
Format: 00000001
Channels: 00000003
Latency: 92
Flags 00000002
Devices 00000002 // 设备号
Stream volume refCount muteCount // 这边声音流的相关信息
00 0.444 00 00
01 0.501 00 00
02 0.135 00 00
03 1.000 00 00
04 0.135 00 00
05 0.135 00 00
06 -1.000 00 00
07 0.501 00 00
08 0.501 00 00
09 1.000 00 00
// Android系统现有流的定义
typedef enum {
AUDIO_STREAM_DEFAULT = -1,
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, /* Sounds that cannot be muted by user and must be routed to speaker */
AUDIO_STREAM_DTMF = 8,
AUDIO_STREAM_TTS = 9,
AUDIO_STREAM_CNT,
AUDIO_STREAM_MAX = AUDIO_STREAM_CNT - 1,
} audio_stream_type_t;
数据库(sqlite3)
// 首先进入数据库的存储目录
root@rk3188:/ # cd /data/data/com.android.providers.settings/databases
root@rk3188:/data/data/com.android.providers.settings/databases # ls
settings.db
settings.db-journal
ngs.db
// 音量数据保存在 settings.db 中的 system 表里,可以使用sqlite命令进行查询
root@rk3188:/data/data/com.android.providers.settings/databases # sqlite3 settings.db
SQLite version 3.7.11 2012-03-20 11:35:50
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .table // 查看数据库里包含的表
android_metadata bookmarks secure
bluetooth_devices global system
sqlite> select * from system; // 选中system表dump出数据,我们可以看到volume相关的一些设置
2|volume_ring|5
3|volume_system|7
4|volume_voice|4
5|volume_alarm|6
6|volume_notification|5
7|volume_bluetooth_sco|7
34|volume_music_headset|10
35|volume_music_headphone|10
298|volume_master|1.0
1035|volume_ring_speaker|5
1504|volume_ring_analog_dock|5
1505|volume_alarm_speaker|7
1649|volume_music|20
1885|volume_music_analog_dock|99
1893|volume_voice_analog_dock|100
1895|volume_music_speaker|70
1896|volume_voice_speaker|70
Audio模块Log开关
默认的发行版本中可见的log非常少,我们需要修改代码的log定义,然后替换对应so文件把用于调试的log全部打开。
具体的修改方法如下:
// 以 AudioPolicyManagerBase.cpp 文件举例,我们只需要把文件前面的log注释打开,代码中的调试Log就可以打印出来
diff --git a/hardware/rk29/audio/AudioPolicyManagerBase.cpp b/hardware/rk29/audio/AudioPolicyManagerBase.cpp
old mode 100644
new mode 100755
index cba5eb4..d18b348
--- a/hardware/rk29/audio/AudioPolicyManagerBase.cpp
+++ b/hardware/rk29/audio/AudioPolicyManagerBase.cpp
@@ -15,9 +15,9 @@
*/
#define LOG_TAG "AudioPolicyManagerBase"
-//#define LOG_NDEBUG 0
+#define LOG_NDEBUG 0
-//#define VERY_VERBOSE_LOGGING
+#define VERY_VERBOSE_LOGGING
#ifdef VERY_VERBOSE_LOGGING
#define ALOGVV ALOGV
#else
Mixer设备查询
Mixer的一些设备状态对于定位Audio问题非常有帮助,这些设备里包括控制通路开关、音量、增益等;这些配置对于Audio是能够正常使用,起着至关重要的作用。
Android系统内置了一些查询和设置命令,使用方法如下:
cmd: amix
// 查询
root@rk3188:/ # amix -c 0
Card:0
id iface dev sub idx num perms type name
1 MIXER 0 0 0 2 rw BOOL HP Playback Switch: OFF OFF { OFF=0, ON=1 }
2 MIXER 0 0 0 2 rw R INT32 HP Playback Volume: 31 31 { 0-31 }
3 MIXER 0 0 0 2 rw BOOL OUT Playback Switch: OFF OFF { OFF=0, ON=1 }
4 MIXER 0 0 0 2 rw BOOL OUT Channel Switch: OFF OFF { OFF=0, ON=1 }
5 MIXER 0 0 0 2 rw R INT32 OUT Playback Volume: 31 31 { 0-39 }
6 MIXER 0 0 0 2 rw R INT32 DAC1 Playback Volume: 175 175 { 0-175 }
7 MIXER 0 0 0 1 rw ENUM IN1 Mode Control: (0 Single ended) { Single ended=0, Differential=1 }
8 MIXER 0 0 0 1 rw R INT32 IN1 Boost: 0 { 0-8 }
9 MIXER 0 0 0 1 rw ENUM IN2 Mode Control: (0 Single ended) { Single ended=0, Differential=1 }
10 MIXER 0 0 0 1 rw R INT32 IN2 Boost: 1 { 0-8 }
11 MIXER 0 0 0 2 rw R INT32 IN Capture Volume: 23 23 { 0-31 }
// 设置
root@rk3188:/ # amix -c 0 1 1
Card:0
HP Playback Switch: ON ON { OFF=0, ON=1 }
// 重新查询之后发现修改的配置已经生效
root@rk3188:/ # amix -c 0
Card:0
id iface dev sub idx num perms type name
1 MIXER 0 0 0 2 rw BOOL HP Playback Switch: ON ON { OFF=0, ON=1 } // 从 OFF -> ON
2 MIXER 0 0 0 2 rw R INT32 HP Playback Volume: 31 31 { 0-31 }
cmd: alsa_amixer
// 和amix的使用方法类似,只是展现的格式有些差异
root@rk3188:/ # alsa_amixer -c 0 contents
numid=14,iface=MIXER,name='ADC Boost Gain'
; type=INTEGER,access=rw---R--,values=2,min=0,max=3,step=0
: values=0,0
| dBscale-min=0.00dB,step=12.00dB,mute=0
numid=12,iface=MIXER,name='ADC Capture Switch'
; type=BOOLEAN,access=rw------,values=2
: values=off,off
numid=13,iface=MIXER,name='ADC Capture Volume'
; type=INTEGER,access=rw---R--,values=2,min=0,max=127,step=0
: values=47,47
| dBscale-min=-176.25dB,step=3.75dB,mute=0
numid=38,iface=MIXER,name='DAC MIXL INF1 Switch'