本篇文章基于Android11源码分析,本篇文章的源码均在
frameworks
目录下
1. 调整音量的方式:
在学习AudioService源码服务之前,我们看一下在应用层如何调节音量的增加、减小、静音、非静音示例:
private AudioManager audioManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.checkout_main);
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
//.....
}
/**
* 增加音频流为STREAM_MUSIC的音量
*/
private void increaseVolume(){
int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
Log.d(TAG,"当前音量大小 = "+ currentVolume);
if (currentVolume < maxVolume){
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_RAISE,FLAG_SHOW_UI | FLAG_PLAY_SOUND);
}
int currentIncreaseVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
Log.d(TAG,"增加后的音量大小 = "+ currentIncreaseVolume);
}
/**
* 减少音频流为STREAM_MUSIC的音量
*/
private void decreaseVolume(){
int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
Log.d(TAG,"当前音量大小 = "+ currentVolume);
if (currentVolume > 0 ){
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_LOWER,FLAG_SHOW_UI);
}
int currentDecreaseVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
Log.d(TAG,"减小后的音量大小 = "+ currentDecreaseVolume);
}
/**
* 使频流为STREAM_MUSIC的静音
*/
private void muteVolume() {
Log.d(TAG,"muteVolume ");
previousVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_MUTE, FLAG_SHOW_UI);
}
/**
* 使频流为STREAM_MUSIC的关闭静音
*/
private void unmuteVolume() {
Log.d(TAG,"unmuteVolume ");
if (previousVolume > 0) {
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_UNMUTE, FLAG_SHOW_UI);
}
}
应用层可以通过AudioManager.adjustStreamVolume(int streamType, int direction, int flags)
和AudioManager.setStreamVolume(int streamType, int index, int flags)
其中streamType是音频流,direction是表示操作的描述如增加/减少/静音/非静音。
其中streamType类型有:
/**
* Increase the ringer volume.
*/
public static final int ADJUST_RAISE = 1;
/**
* Decrease the ringer volume.
*/
public static final int ADJUST_LOWER = -1;
/**
* Maintain the previous ringer volume. This may be useful when needing to
* show the volume toast without actually modifying the volume.
*/
public static final int ADJUST_SAME = 0;
/**
* Mute the volume. Has no effect if the stream is already muted.
*/
public static final int ADJUST_MUTE = -100;
/**
* Unmute the volume. Has no effect if the stream is not muted.
*/
public static final int ADJUST_UNMUTE = 100;
/**
* Toggle the mute state. If muted the stream will be unmuted. If not muted
* the stream will be muted.
*/
public static final int ADJUST_TOGGLE_MUTE = 101;
在Android中有两种方式来控制音量,一种是通过代码来调整音量、一种是通过音量键来控制音量。
如下我们看下通过音量键是如何调整音量的:
在按下音量键的时候,会先经过PhoneWindowManager的处理
是否拦截:
// PhoneWindowManager
@Override
public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
int policyFlags) {
//......
else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP
|| keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|| keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
if (mUseTvRouting || mHandleVolumeKeysInWM) {
// On TVs or when the configuration is enabled, volume keys never
// go to the foreground app.
// 调用此方法
dispatchDirectAudioEvent(event);
}
return -1;
}
private void dispatchDirectAudioEvent(KeyEvent event) {
//....
try {
// 调用AudioService.handleVolumeKey()方法
getAudioService().handleVolumeKey(event, mUseTvRouting,
mContext.getOpPackageName(), TAG);
} catch (Exception e) {
Log.e(TAG, "Error dispatching volume key in handleVolumeKey for event:"
+ event, e);
}
}
// AudioService
public void handleVolumeKey(@NonNull KeyEvent event, boolean isOnTv,
@NonNull String callingPackage, @NonNull String caller) {
int keyEventMode = VOL_ADJUST_NORMAL;
if (isOnTv) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
keyEventMode = VOL_ADJUST_START;
} else { // may catch more than ACTION_UP, but will end vol adjustement
// the vol key is either released (ACTION_UP), or multiple keys are pressed
// (ACTION_MULTIPLE) and we don't know what to do for volume control on CEC, end
// the repeated volume adjustement
keyEventMode = VOL_ADJUST_END;
}
} else if (event.getAction() != KeyEvent.ACTION_DOWN) {
return;
}
int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND
| AudioManager.FLAG_FROM_KEY;
// 调用adjustSuggestedStreamVolume()方法
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_VOLUME_UP:
adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,
Binder.getCallingUid(), true, keyEventMode);
break;
case KeyEvent.KEYCODE_VOLUME_DOWN:
adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,
Binder.getCallingUid(), true, keyEventMode);
break;
case KeyEvent.KEYCODE_VOLUME_MUTE:
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
adjustSuggestedStreamVolume(AudioManager.ADJUST_TOGGLE_MUTE,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,
Binder.getCallingUid(), true, VOL_ADJUST_NORMAL);
}
break;
default:
Log.e(TAG, "Invalid key code " + event.getKeyCode() + " sent by " + callingPackage);
return; // not needed but added if code gets added below this switch statement
}
}
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller) {
boolean hasModifyAudioSettings =
mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
== PackageManager.PERMISSION_GRANTED;
adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
caller, Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
}
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
int keyEventMode) {
// ....;
// 最终也是调用到adjustStreamVolume()方法
adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid,
hasModifyAudioSettings, keyEventMode);
}
音量按键在PhoneWindowManager的时候最终会调用dispatchDirectAudioEvent()
方法,此方法又调用AudioService.handleVolumeKey()
方法,在AudioService最终还是调用adjustStreamVolume()
方法进行音量的调整。
接下来将看adjustStreamVolume()
方法源码是如何控制音量的。在讲源码之前,先讲讲音频流和设备。
2. 音频流和设备:
在android11中工定义了12中音频流类型,在AudioSystem
中有相关定义:
public class AudioSystem
{
/**电话 */
public static final int STREAM_VOICE_CALL = 0;
/** 系统 */
public static final int STREAM_SYSTEM = 1;
/** 响铃和消息 */
public static final int STREAM_RING = 2;
/** 音乐 */
public static final int STREAM_MUSIC = 3;
/** 闹钟 */
public static final int STREAM_ALARM = 4;
/** 通知 */
public static final int STREAM_NOTIFICATION = 5;
/** 蓝牙 */
public static final int STREAM_BLUETOOTH_SCO = 6;
/** 强制系统声音 */
@UnsupportedAppUsage
public static final int STREAM_SYSTEM_ENFORCED = 7;
/** 双音多频 */
public static final int STREAM_DTMF = 8;
/** 语音 */
public static final int STREAM_TTS = 9;
/** 辅助功能*/
public static final int STREAM_ACCESSIBILITY = 10;
/** 助手*/
public static final int STREAM_ASSISTANT = 11;
private static final int NUM_STREAM_TYPES = 12;
}
一个或多个音频流共享一个音量:
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
AccessibilityManager.AccessibilityServicesStateChangeListener {
// 多种音频流对应一种音量,系统音频流,响铃与消息音频流,通知音频流,强制声音音频流,DTMF这五种音频流共用一个音量
private final int[] STREAM_VOLUME_ALIAS_VOICE = new int[] {
AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
AudioSystem.STREAM_RING, // STREAM_SYSTEM
AudioSystem.STREAM_RING, // STREAM_RING
AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
AudioSystem.STREAM_ALARM, // STREAM_ALARM
AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED
AudioSystem.STREAM_RING, // STREAM_DTMF
AudioSystem.STREAM_MUSIC, // STREAM_TTS
AudioSystem.STREAM_MUSIC, // STREAM_ACCESSIBILITY
AudioSystem.STREAM_MUSIC // STREAM_ASSISTANT
};
// 电视的
private final int[] STREAM_VOLUME_ALIAS_TELEVISION = new int[] {
AudioSystem.STREAM_MUSIC, // STREAM_VOICE_CALL
AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM
AudioSystem.STREAM_RING, // STREAM_RING
AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
AudioSystem.STREAM_ALARM, // STREAM_ALAR
AudioSystem.STREAM_MUSIC, // STREAM_NOTIFICATION
AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM_ENFORCED
AudioSystem.STREAM_MUSIC, // STREAM_DTMF
AudioSystem.STREAM_MUSIC, // STREAM_TTS
AudioSystem.STREAM_MUSIC, // STREAM_ACCESSIBILITY
AudioSystem.STREAM_MUSIC // STREAM_ASSISTANT
};
private final int[] STREAM_VOLUME_ALIAS_DEFAULT = new int[] {
AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
AudioSystem.STREAM_RING, // STREAM_SYSTEM
AudioSystem.STREAM_RING, // STREAM_RING
AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
AudioSystem.STREAM_ALARM, // STREAM_ALARM
AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED
AudioSystem.STREAM_RING, // STREAM_DTMF
AudioSystem.STREAM_MUSIC, // STREAM_TTS
AudioSystem.STREAM_MUSIC, // STREAM_ACCESSIBILITY
AudioSystem.STREAM_MUSIC // STREAM_ASSISTANT
};
// 存储多种音频流对应一个音量
protected static int[] mStreamVolumeAlias;
}
STREAM_VOLUME_ALIAS_VOICE
代表的是具有语音功能的设备如手机。
STREAM_VOLUME_ALIAS_TELEVISION
代表电视或机顶盒的设备。
STREAM_VOLUME_ALIAS_DEFAULT
代表其他的设备。
在STREAM_VOLUME_ALIAS_VOICE
中统音频流,响铃与消息音频流,通知音频流,强制声音音频流,DTMF音频流就和响铃与消息音频流共享音量,他们的音量是一致的,每当STREAM_RING
音量发生改变时,其他的与之共享音量的音频流也要对应的发生改变。
其中mStreamVolumeAlias
数组变量就是就是设置为对应的变量。
- 最大最小音量
每种音频流都有自己的最大最小的音量值,定义在AudioService
的两个数组变量中:
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
AccessibilityManager.AccessibilityServicesStateChangeListener {
//每种流的最小音量
protected static int[] MAX_STREAM_VOLUME = new int[] {
5, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
7, // STREAM_RING
15, // STREAM_MUSIC
7, // STREAM_ALARM
7, // STREAM_NOTIFICATION
15, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
15, // STREAM_DTMF
15, // STREAM_TTS
15, // STREAM_ACCESSIBILITY
15 // STREAM_ASSISTANT
};
/** Minimum volume index values for audio streams */
//每种流的最小音量
protected static int[] MIN_STREAM_VOLUME = new int[] {
1, // STREAM_VOICE_CALL
0, // STREAM_SYSTEM
0, // STREAM_RING
0, // STREAM_MUSIC
1, // STREAM_ALARM
0, // STREAM_NOTIFICATION
0, // STREAM_BLUETOOTH_SCO
0, // STREAM_SYSTEM_ENFORCED
0, // STREAM_DTMF
0, // STREAM_TTS
1, // STREAM_ACCESSIBILITY
0 // STREAM_ASSISTANT
};
}
- 输出设备
音频的输出设备有很多种,一种音频流有多种输出设备,而且每种设备对应的音量不一定相同,比如插入耳机时是另一种音量大小。
AUDIO_DEVICE_OUT_EARPIECE: 听筒,用于通话时将声音传递到耳朵。
AUDIO_DEVICE_OUT_WIRED_HEADSET: 有线耳机/耳麦,通过耳机插孔连接。
AUDIO_DEVICE_OUT_WIRED_HEADPHONE: 有线普通耳机,通过耳机插孔连接。
AUDIO_DEVICE_OUT_BLUETOOTH_SCO: 单声道蓝牙耳机,用于通话。
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: 蓝牙电话耳机,用于通话。
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: 蓝牙立体声耳机。
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: 蓝牙立体声耳机。
AUDIO_DEVICE_OUT_USB_HEADSET: USB 耳机/耳麦。
AUDIO_DEVICE_OUT_HEARING_AID: 助听器,用于助听设备。
AUDIO_DEVICE_OUT_LINE: 线级输出。
AUDIO_DEVICE_OUT_AUX_DIGITAL: 数字辅助输出,例如 HDMI。
AUDIO_DEVICE_OUT_USB_DEVICE: USB 设备。
AUDIO_DEVICE_OUT_SPEAKER: 扬声器,外部设备,通常是手机的内置扬声器。
AUDIO_DEVICE_OUT_SPEAKER_SAFE: 安全扬声器,与扬声器类似。
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: 车载蓝牙免提设备。
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: 蓝牙 A2DP 扬声器。
AUDIO_DEVICE_OUT_USB_ACCESSORY: USB 附件。
AUDIO_DEVICE_OUT_REMOTE_SUBMIX: 远程混音输出。
- VolumeStreamState
在AudioService中如何去管理这个复杂的音频流和输出设备间的音量大小关系呢?其通过一个内部类VolumeStreamState
,每个音频流都有一个VolumeStreamState类存储着当前音频流的相关信息:
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
AccessibilityManager.AccessibilityServicesStateChangeListener {
// 所有音频流对应的VolumeStreamState
private VolumeStreamState[] mStreamStates;
private void createStreamStates() {
// 音频流的总数
int numStreamTypes = AudioSystem.getNumStreamTypes();
// 赋值mStreamStates数组大小为音频流的总数
VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
for (int i = 0; i < numStreamTypes; i++) {
// 为每个音频流创建VolumeStreamState实例
streams[i] =
new VolumeStreamState(System.VOLUME_SETTINGS_INT[mStreamVolumeAlias[i]], i);
}
}
private class VolumeStreamState {
private final int mStreamType; // 指示哪个音频流
private int mIndexMin; // 对应的最小音量值
// min index when user doesn't have permission to change audio settings
private int mIndexMinNoPerm;
private int mIndexMax; // 对应的最大音量值
private boolean mIsMuted;
private boolean mIsMutedInternally;
private String mVolumeIndexSettingName;
private int mObservedDevices;
// 保存每种设备的音量值
private final SparseIntArray mIndexMap = new SparseIntArray(8) {
@Override
public void put(int key, int value) {
super.put(key, value);
record("put", key, value);
}
@Override
public void setValueAt(int index, int value) {
super.setValueAt(index, value);
record("setValueAt", keyAt(index), value);
}
};
}
1. adjustStreamVolume()
按键或代码设置都可以通过adjustStreamVolume()去调整音量大小:
// 设置音量API之一
public void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage) {
if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without"
+ "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
return;
}
final boolean hasModifyAudioSettings =
mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
== PackageManager.PERMISSION_GRANTED;
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
direction/*val1*/, flags/*val2*/, callingPackage));
adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
}
protected void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
int keyEventMode) {
ensureValidDirection(direction);
ensureValidStreamType(streamType);
boolean isMuteAdjust = isMuteAdjust(direction);
//....
if (adjustVolume
&& (direction != AudioManager.ADJUST_SAME) && (keyEventMode != VOL_ADJUST_END)) {
mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);
if (isMuteAdjust) {
for (int stream = 0; stream < mStreamStates.length; stream++) {
if (streamTypeAlias == mStreamVolumeAlias[stream]) {
if (!(readCameraSoundForced()
&& (mStreamStates[stream].getStreamType()
== AudioSystem.STREAM_SYSTEM_ENFORCED))) {
// 调用VolumeStreamState.mute()去设置静音/非静音
mStreamStates[stream].mute(state);
}
}
}
} else if (!isInVolumePassthrough()
&& (streamState.adjustIndex(direction * step, device, caller,
hasModifyAudioSettings)
|| streamState.mIsMuted)) {
// 去设置音量
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
int newIndex = mStreamStates[streamType].getIndex(device);
// 更新UI
sendVolumeUpdate(streamType, oldIndex, newIndex, flags, device);
}
adjustStreamVolume
方法中代码中有很多,这里挑出主要的步骤:
- 如果是传入的direction是
ADJUST_MUTE、ADJUST_UNMUTE
静音的属性,则会调用VolumeStreamState.mute()
方法,其相关执行后面在讲,大致流程都差不多。 - 通过
VolumeStreamState.adjustIndex
设置Index的大小 - 发送
AudioHandler. MSG_SET_DEVICE_VOLUME
消息去执行音量的设置 - 通过
sendVolumeUpdate()
方法回调给SystemUI中的VolumeUI去展示对应的UI通知。
VolumeStreamState.adjustIndex
// AudioService
public void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage) {
//....
// 音频流是音乐且是固定音量设备
// 计算step为音量增加或较少的单位
if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
flags |= AudioManager.FLAG_FIXED_VOLUME;
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
mSafeMediaVolumeDevices.contains(device)) {
step = safeMediaVolumeIndex(device);
} else {
step = streamState.getMaxIndex();
}
if (aliasIndex != 0) {
aliasIndex = step;
}
} else {
// 一般走这里,内部单元值,即增加或减少一个音量的单元值是多少
step = rescaleStep(10, streamType, streamTypeAlias);
}
//...
// direction 就是1,-1,100,-100,刚好*单元值后就是index要设置的选项
else if ( (streamState.adjustIndex(direction * step, device, caller, hasModifyAudioSettings))
}
private int rescaleStep(int step, int srcStream, int dstStream) {
// 源的最大值-最小值
int srcRange = getIndexRange(srcStream);
// 最大值-最小值
int dstRange = getIndexRange(dstStream);
// 错误
if (srcRange == 0) {
Log.e(TAG, "rescaleStep : index range should not be zero");
return 0;
}
// 转换后的布值
return ((step * dstRange + srcRange / 2) / srcRange);
}
}
// AudioManager
public static final int ADJUST_RAISE = 1;
public static final int ADJUST_LOWER = -1;
public static final int ADJUST_MUTE = -100;
public static final int ADJUST_UNMUTE = 100;
// VolumeStreamState
private class VolumeStreamState {
public boolean adjustIndex(int deltaIndex, int device, String caller,
boolean hasModifyAudioSettings) {
return setIndex(getIndex(device) + deltaIndex, device, caller,
hasModifyAudioSettings);
}
public boolean setIndex(int index, int device, String caller,
boolean hasModifyAudioSettings) {
boolean changed;
int oldIndex;
synchronized (mSettingsLock) {
synchronized (VolumeStreamState.class) {
oldIndex = getIndex(device);
// 检查有效值,不超过最小最大值
index = getValidIndex(index, hasModifyAudioSettings);
if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
index = mIndexMax;
}
// 将对应的设备设置到对应的新值
mIndexMap.put(device, index);
changed = oldIndex != index;
final boolean isCurrentDevice = (device == getDeviceForStream(mStreamType));
// 同时也要更新与之共享音量的其他音频流的VolumeStreamState中的对应device的新值
final int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
final VolumeStreamState aliasStreamState = mStreamStates[streamType];
if (streamType != mStreamType &&
mStreamVolumeAlias[streamType] == mStreamType &&
(changed || !aliasStreamState.hasIndexForDevice(device))) {
final int scaledIndex = rescaleIndex(index, mStreamType, streamType);
aliasStreamState.setIndex(scaledIndex, device, caller,
hasModifyAudioSettings);
if (isCurrentDevice) {
aliasStreamState.setIndex(scaledIndex,
getDeviceForStream(streamType), caller,
hasModifyAudioSettings);
}
}
}
// Mirror changes in SPEAKER ringtone volume on SCO when
if (changed && mStreamType == AudioSystem.STREAM_RING
&& device == AudioSystem.DEVICE_OUT_SPEAKER) {
for (int i = 0; i < mIndexMap.size(); i++) {
int otherDevice = mIndexMap.keyAt(i);
if (AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(otherDevice)) {
mIndexMap.put(otherDevice, index);
}
}
}
}
}
if (changed) {
// 发送广播
}
return changed;
}
}
在设置Index中主要操作是
- 通过
rescaleStep()
方法计算出单元值step - 通过
direction * step
可以得到新值的大小,在AudioManager中direction在+/-时被巧妙的设置了1/-1,然后传给VolumeStreamState.adjustIndex
-
adjustIndex
后又调用setIndex()
方法,首先对新值进行有效转换,不超过最大最小值,然后通过mIndexMap
存储到对应的设备和新值,并且也更新与之共享音量的音频流的对应的mIndexMap值,并且发送广播。
AudioHandler. MSG_SET_DEVICE_VOLUME
private class AudioHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SET_DEVICE_VOLUME:
// 执行setDeviceVolume方法
setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
break;
}
}
void setDeviceVolume(VolumeStreamState streamState, int device) {
synchronized (VolumeStreamState.class) {
//调用VolumeStreamState.applyDeviceVolume_syncVSS方法
streamState.applyDeviceVolume_syncVSS(device);
// 其他与之共享音量的音频流的也随之设置之对应设备的音量大小
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
if (streamType != streamState.mStreamType &&
mStreamVolumeAlias[streamType] == streamState.mStreamType) {
int streamDevice = getDeviceForStream(streamType);
if ((device != streamDevice) && mAvrcpAbsVolSupported
&& AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)) {
mStreamStates[streamType].applyDeviceVolume_syncVSS(device);
}
mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);
}
}
}
}
// VolumeStreamState
void applyDeviceVolume_syncVSS(int device) {
int index;
if (isFullyMuted()) {
// 如果整个音频流都被静音,index 被设置为 0
index = 0;
} else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
&& mAvrcpAbsVolSupported) {
//A2DP 设备集合并且支持绝对音量控制
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if (isFullVolumeDevice(device)) {
//如果设备是全音量设备(如扬声器),index 被设置为最大音量索引的 1/10
index = (mIndexMax + 5)/10;
} else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
//如果设备是听力辅助设备,同样将 index 设置为最大音量索引的 1/10
index = (mIndexMax + 5)/10;
} else {
//其他,当前值+5/10
index = (getIndex(device) + 5)/10;
}
setStreamVolumeIndex(index, device);
}
private void setStreamVolumeIndex(int index, int device) {
if (mStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0
&& !isFullyMuted()) {
index = 1;
}
//通过AudioSystem.setStreamVolumeIndexAS底层设置音量值
AudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
}
MSG_SET_DEVICE_VOLUME消息主要是真正的设置音量值的新值,主要操作有:
- MSG_SET_DEVICE_VOLUME消息执行
setDeviceVolume
方法,setDeviceVolume方法给音频流和与之共享的音频流调用VSS的applyDeviceVolume_syncVSS
进行音量值设置 -
applyDeviceVolume_syncVSS
将新值index进行转换,并通过setStreamVolumeIndex
方法交给AudioSystem.setStreamVolumeIndexAS
进行真正音量值设置。
sendVolumeUpdate
protected void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags, int device)
{
streamType = mStreamVolumeAlias[streamType];
if (streamType == AudioSystem.STREAM_MUSIC) {
flags = updateFlagsForTvPlatform(flags);
// The volume bar ui shows depends on whether the device is in passthrough mode.
if (isInVolumePassthrough()) {
Slog.d(TAG, "no volume bar");
flags &= ~AudioManager.FLAG_SHOW_UI;
}
}
mVolumeController.postVolumeChanged(streamType, flags);
}
public static class VolumeController {
private static final String TAG = "VolumeController";
private IVolumeController mController;
// 设置IVolumeController
public void setController(IVolumeController controller) {
mController = controller;
mVisible = false;
}
public void postVolumeChanged(int streamType, int flags) {
if (mController == null)
return;
try {
// 调用IVolumeController的volumeChanged方法
mController.volumeChanged(streamType, flags);
} catch (RemoteException e) {
Log.w(TAG, "Error calling volumeChanged", e);
}
}
}
通过IVolumeController.volumeChanged
进行回调,给SystemUi那边进行UI的更新,那么这个IVolumeController
是谁设置的呢?
在AudioService中有个setVolumeController()方法给设置VolumeController.setController
private final VolumeController mVolumeController = new VolumeController();
@Override
public void setVolumeController(final IVolumeController controller) {
//...
mVolumeController.setController(controller);
if (DEBUG_VOL) Log.d(TAG, "Volume controller: " + mVolumeController);
}
setVolumeController()
这个方法又是谁设置的呢?这就需要看一下VolumeUI
的启动流程:
public class VolumeUI extends SystemUI {
private VolumeDialogComponent mVolumeComponent;
// 启动会调用start()
@Override
public void start() {
//....
setDefaultVolumeController();
}
private void setDefaultVolumeController() {
DndTile.setVisible(mContext, true);
if (LOGD) Log.d(TAG, "Registering default volume controller");
// 调用VolumeDialogComponent.register()
mVolumeComponent.register();
}
}
// VolumeDialogComponent
public class VolumeDialogComponent implements VolumeComponent, TunerService.Tunable,
VolumeDialogControllerImpl.UserActivityListener{
private final VolumeDialogControllerImpl mController;
private VolumeDialog mDialog;
// UI显示的Dialog是VolumeDialogImpl
protected VolumeDialog createDefault() {
VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
impl.setSilentMode(false);
return impl;
}
@Override
public void register() {
// 调用VolumeDialogControllerImpl.register()
mController.register();
}
}
// VolumeDialogControllerImpl
public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
protected final VC mVolumeController = new VC();
public void register() {
// 设置VolumeController
setVolumeController();
}
protected void setVolumeController() {
try {
// 调用AudioService.setVolumeController将VC设置为VolumeController
mAudio.setVolumeController(mVolumeController);
} catch (SecurityException e) {
Log.w(TAG, "Unable to set the volume controller", e);
return;
}
}
private final class VC extends IVolumeController.Stub {
@Override
public void volumeChanged(int streamType, int flags) throws RemoteException {
if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)
+ " " + Util.audioManagerFlagsToString(flags));
if (mDestroyed) return;
// 发送VOLUME_CHANGED消息
mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
}
}
}
在VolumeUI
开始的时候会调用start()
方法,最后通过VolumeDialogControllerImpl
将VC
设置VolumeController。之后AudioService于VC通信,而VC则通过与VolumeDialogImpl
进行UI的展示。
AdjustStreamVolume小结
主要分为三大步骤:
- 通过
adjustIndex()
更新index的新值 - 发送MSG_SET_DEVICE_VOLUME消息,通过
AudioSystem
设置新的音量值 - sendVolumeUpdate发送通知UI消息
2. setStreamVolume
代码调整音量时,可以通过setStreamVolume方法进行调节,所以它也是设置音量的入口之一。
// 设置音量API之一
private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
String caller, int uid, boolean hasModifyAudioSettings) {
ensureValidStreamType(streamType); // 这里先判断一下流类型这个参数的有效性
// 对应共享音量的音频流
int streamTypeAlias = mStreamVolumeAlias[streamType];
// 对应音频流的VolumeStreamState
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
// 根据音频流确定输出的设备
final int device = getDeviceForStream(streamType);
int oldIndex;
//...权限判断
synchronized (mSafeMediaVolumeStateLock) {
// reset any pending volume command
mPendingVolumeCommand = null;
// 获取流当前的音量
oldIndex = streamState.getIndex(device);
// 将原流类型下的音量值映射到目标流类型下的音量值
// 因为不同流类型的音量值刻度不一样,所以需要进行这个转换
index = rescaleIndex(index * 10, streamType, streamTypeAlias);
//...
// 调用setStreamVolumeInt()
onSetStreamVolume(streamType, index, flags, device, caller, hasModifyAudioSettings);
// 获取设置的结果
index = mStreamStates[streamType].getIndex(device);
// 广播通知
sendVolumeUpdate(streamType, oldIndex, index, flags, device);
}
private void onSetStreamVolume(int streamType, int index, int flags, int device,
String caller, boolean hasModifyAudioSettings) {
final int stream = mStreamVolumeAlias[streamType];
// 调用setStreamVolumeInt()
setStreamVolumeInt(stream, index, device, false, caller, hasModifyAudioSettings);
//....
}
private void setStreamVolumeInt(int streamType,
int index,
int device,
boolean force,
String caller, boolean hasModifyAudioSettings) {
// 获取保存音量信息的VolumeStreamState对象
VolumeStreamState streamState = mStreamStates[streamType];
// 为什么还要判断streamState.setIndex的返回值呢?
// 因为如果音量值在setIndex之后并没有发生变化,比如说达到了最大值,就不需要继续后面的操作了
// 或者force参数为true的话
if (streamState.setIndex(index, device, caller, hasModifyAudioSettings) || force) {
// 通过MSG_SET_DEVICE_VOLUME去设置音量值
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
}
setStreamVolume
方法的执行流程跟adjustStreamVolume
方法大同小异,都是先更新index值
,然后通过MSG_SET_DEVICE_VOLUME
消息去设置音量值,最后通过sendVolumeUpdate
去回调通知Ui。
总结
本篇文章主要介绍了AudioService调整音量大小的源码,主要在java层,真正的设置是通过的AudioSystem去调用底层的native去完成。VolumeStreamState
存储着音频流的所有关系,包括最大最小值,device对应的index值。调整音量有两个API:adjustStreamVolume
和setStreamVolume
它们的执行流程都大概是先更新对应device的index值,然后设置音量值,最后通知UI更新。
参考文章:
https://juejin.cn/post/6983977417173893128
https://wizardforcel.gitbooks.io/deepin-android-vol3/content/2.html