整个流程分析是基于Android 8.0来进行讲解的
在Android系统中,音量都是分开控制的,比如媒体音量,铃声音量,闹钟音量,蓝牙音量,通话音量都是分开控制的。各种音量都是通过音频流来区别不同音量类型的。在Android 8.0里面,一共定义了11种流的类型,每种流类型都定义了最大音量(MAX_STREAM_VOLUME),最小音量(MIN_STREAM_VOLUME),以及默认音量(DEFAULT_STREAM_VOLUME),细心的童鞋可能会发现,我们能调节的音量类型,也就5种。所以还存在一个流的映射关系,然后这个流就可以使用另外一个流的音量了。
-frameworks/base/services/core/java/com/android/server/audio/AudioService.java
/** Maximum volume index values for audio streams */
private 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
};
-frameworks/base/services/core/java/com/android/server/audio/AudioService.java
/** Minimum volume index values for audio streams */
private static int[] MIN_STREAM_VOLUME = new int[] {
1, // STREAM_VOICE_CALL
0, // STREAM_SYSTEM
0, // STREAM_RING
0, // STREAM_MUSIC
0, // STREAM_ALARM
0, // STREAM_NOTIFICATION
0, // STREAM_BLUETOOTH_SCO
0, // STREAM_SYSTEM_ENFORCED
0, // STREAM_DTMF
0, // STREAM_TTS
0 // STREAM_ACCESSIBILITY
};
-frameworks/base/media/java/android/media/AudioSystem.java
public static int[] DEFAULT_STREAM_VOLUME = new int[] {
4, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
5, // STREAM_RING
5, // STREAM_MUSIC
6, // STREAM_ALARM
5, // STREAM_NOTIFICATION
7, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
5, // STREAM_DTMF
5, // STREAM_TTS
5 // STREAM_ACCESSIBILITY
};
音频流映射
在Android系统中,不同设备的映射不尽相同,一共定义了三种设备的音频流的映射,分别是VOICE(STREAM_VOLUME_ALIAS_VOICE),TELEVISION(STREAM_VOLUME_ALIAS_TELEVISION),DEFAULT(STREAM_VOLUME_ALIAS_DEFAULT)。
-frameworks/base/services/core/java/com/android/server/audio/AudioService.java
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
};
-frameworks/base/services/core/java/com/android/server/audio/AudioService.java
private final int[] STREAM_VOLUME_ALIAS_TELEVISION = new int[] {
AudioSystem.STREAM_MUSIC, // STREAM_VOICE_CALL
AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM
AudioSystem.STREAM_MUSIC, // STREAM_RING
AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
AudioSystem.STREAM_MUSIC, // STREAM_ALARM
AudioSystem.STREAM_MUSIC, // STREAM_NOTIFICATION
AudioSystem.STREAM_MUSIC, // STREAM_BLUETOOTH_SCO
AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM_ENFORCED
AudioSystem.STREAM_MUSIC, // STREAM_DTMF
AudioSystem.STREAM_MUSIC, // STREAM_TTS
AudioSystem.STREAM_MUSIC // STREAM_ACCESSIBILITY
};
-frameworks/base/services/core/java/com/android/server/audio/AudioService.java
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
};
流序号 | 流类型 | 最大音量 | 最小音量 | 默认音量 | 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 | 5 | 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 |
通过上表,可以很明显的看到,在手机和平板上面,我们实际上能调节的也就是上面所讲述的5个音量,在TV和机顶盒之类的设备上,我们能调节的也就一种音量。整理一下上表可直接调节的流类型,如下表所示:
流类型 | 最大音量 | 最小音量 | 默认音量 | 含义 |
---|---|---|---|---|
STREAM_VOICE_CALL | 5 | 1 | 4 | 通话音量 |
STREAM_RING | 7 | 0 | 5 | 铃声,通知音量等 |
STREAM_MUSIC | 15 | 0 | 5 | 多媒体音量 |
STREAM_ALARM | 7 | 0 | 6 | 闹钟音量 |
STREAM_BLUETOOTH_SCO | 15 | 0 | 7 | 蓝牙音量 |
-frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
private void dispatchDirectAudioEvent(KeyEvent event) {
if (event.getAction() != KeyEvent.ACTION_DOWN) {
return;
}
int keyCode = event.getKeyCode();
int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND
| AudioManager.FLAG_FROM_KEY;
String pkgName = mContext.getOpPackageName();
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
try {
getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
} catch (Exception e) {
Log.e(TAG, "Error dispatching volume up in dispatchTvAudioEvent.", e);
}
break;
case KeyEvent.KEYCODE_VOLUME_DOWN:
try {
getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
} catch (Exception e) {
Log.e(TAG, "Error dispatching volume down in dispatchTvAudioEvent.", e);
}
break;
case KeyEvent.KEYCODE_VOLUME_MUTE:
try {
if (event.getRepeatCount() == 0) {
getAudioService().adjustSuggestedStreamVolume(
AudioManager.ADJUST_TOGGLE_MUTE,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
}
} catch (Exception e) {
Log.e(TAG, "Error dispatching mute in dispatchTvAudioEvent.", e);
}
break;
}
}
......
// 这里通过AIDL获取IAudioService的实例,不深究,有需要了解AIDL的请找度娘哈
static IAudioService getAudioService() {
IAudioService audioService = IAudioService.Stub.asInterface(
ServiceManager.checkService(Context.AUDIO_SERVICE));
if (audioService == null) {
Log.w(TAG, "Unable to find IAudioService interface.");
}
return audioService;
}
这里是直接执行了音频键的操作,通过Binder获取到了AudioService的实例,去调用了adjustSuggestedStreamVolume方法,参数含义如下:
按键类型 | Audio Service操作类型 | 含义 |
---|---|---|
KEYCODE_VOLUME_UP | AudioManager.ADJUST_RAISE | 音量减 |
KEYCODE_VOLUME_DOWN | AudioManager.ADJUST_LOWER | 音量加 |
KEYCODE_VOLUME_MUTE | AudioManager.ADJUST_TOGGLE_MUTE | 改变静音状态 |
在按键的处理过程中,并没有将相应的code传递给AudioService,而是使用了相关的定义,将KEYCODE_VOLUME_UP等操作转化为了ADJUST_RAISE等。而flag存储了一些对音量的要求或者信息吧,这个也很重要。
-frameworks/base/media/java/android/media/AudioManager.java
/**
* 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;
接下来就到了AudioService的adjustSuggestedStreamVolume方法里面了
-frameworks/base/services/core/java/com/android/server/audio/AudioService.java
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller) {
adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
caller, Binder.getCallingUid());
}
调用重载的adjustSuggestedStreamVolume方法
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller, int uid) {
final int streamType;
if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
streamType = mVolumeControlStream;
} else {
// 这里获取到,可能是活动状态的音频流,但是不确定,还有待进一步确认
final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
final boolean activeForReal;
if (maybeActiveStreamType == AudioSystem.STREAM_MUSIC) {
// 这里如果STREAM_MUSIC现在在AudioFlinger处理的流中或在最后的0ms中处于活动状态,则为true。
activeForReal = isAfMusicActiveRecently(0);
} else {
// 会调用native方法,不深究
activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
}
// 如果activeForReal为true或者mVolumeControlStream为-1
// 那么确定要操作修改的流类型为maybeActiveStreamType对应的流类型
if (activeForReal || mVolumeControlStream == -1) {
streamType = maybeActiveStreamType;
} else {
// activeForReal为false并且mVolumeControlStream不为-1
// 表示用户点击了音量进度条,这时候要操作修改的流类型为mVolumeControlStream对应的流类型
streamType = mVolumeControlStream;
}
}
final boolean isMute = isMuteAdjust(direction);
// 确保我们获取到的流类型是有效的
ensureValidStreamType(streamType);
// 将我们获取到的流,进行流映射,拿到最终需要操作的流类型
final int resolvedStream = mStreamVolumeAlias[streamType];
// Play sounds on STREAM_RING only.
if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
resolvedStream != AudioSystem.STREAM_RING) {
flags &= ~AudioManager.FLAG_PLAY_SOUND;
}
// For notifications/ring, show the ui before making any adjustments
// Don't suppress mute/unmute requests
// 通知和响铃,调整音量之前先显示UI。
if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)) {
// 将操作置为ADJUST_SAME(ADJUST_SAME = 0);
direction = 0;
flags &= ~AudioManager.FLAG_PLAY_SOUND;
flags &= ~AudioManager.FLAG_VIBRATE;
if (DEBUG_VOL) Log.d(TAG, "Volume controller suppressed adjustment");
}
// 这里设置音量
adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid);
}
这里需要注意的有以下几点:
进入到了adjustStreamVolume函数,该函数比较长,分开讲,最后再来总体分析一下
private void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage, String caller, int uid) {
if (mUseFixedVolume) {
return;
}
ensureValidDirection(direction);
ensureValidStreamType(streamType);
boolean isMuteAdjust = isMuteAdjust(direction);
if (isMuteAdjust && !isStreamAffectedByMute(streamType)) {
return;
}
// use stream type alias here so that streams with same alias have the same behavior,
// including with regard to silent mode control (e.g the use of STREAM_RING below and in
// checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
int streamTypeAlias = mStreamVolumeAlias[streamType];
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
......
}
private void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage, String caller, int uid) {
......
final int device = getDeviceForStream(streamTypeAlias);
int aliasIndex = streamState.getIndex(device);
boolean adjustVolume = true;
int step;
// 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.
// uid判断,方便去做用户权限处理
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;
}
.......
}
private int getDeviceForStream(int stream) {
//这里最终会调用VolumeStreamState的observeDevicesForStream_syncVSS方法
int device = getDevicesForStream(stream);
if ((device & (device - 1)) != 0) {
// Multiple device selection is either:
// - speaker + one other device: give priority to speaker in this case.
// - one A2DP device + another device: happens with duplicated output. In this case
// retain the device on the A2DP output as the other must not correspond to an active
// selection if not the speaker.
// - HDMI-CEC system audio mode only output: give priority to available item in order.
if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
device = AudioSystem.DEVICE_OUT_SPEAKER;
} else if ((device & AudioSystem.DEVICE_OUT_HDMI_ARC) != 0) {
device = AudioSystem.DEVICE_OUT_HDMI_ARC;
} else if ((device & AudioSystem.DEVICE_OUT_SPDIF) != 0) {
device = AudioSystem.DEVICE_OUT_SPDIF;
} else if ((device & AudioSystem.DEVICE_OUT_AUX_LINE) != 0) {
device = AudioSystem.DEVICE_OUT_AUX_LINE;
} else {
device &= AudioSystem.DEVICE_OUT_ALL_A2DP;
}
}
return device;
}
public int observeDevicesForStream_syncVSS(boolean checkOthers) {
// 调用到了AudioSystem里面,里面是一个native方法,这里就先不分析了
final int devices = AudioSystem.getDevicesForStream(mStreamType);
if (devices == mObservedDevices) {
return devices;
}
final int prevDevices = mObservedDevices;
mObservedDevices = devices;
if (checkOthers) {
// one stream's devices have changed, check the others
observeDevicesForStreams(mStreamType);
}
// log base stream changes to the event log
if (mStreamVolumeAlias[mStreamType] == mStreamType) {
EventLogTags.writeStreamDevicesChanged(mStreamType, prevDevices, devices);
}
sendBroadcastToAll(mStreamDevicesChanged
.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, prevDevices)
.putExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, devices));
return devices;
}
可以看到获取设备类型的代码最终是在native层做的,上层只是做了一些判断。并且Android系统对Audio的设备也有一系列的定义,这个我们就不讨论这个了,有需要的可以去网上找一找。
我们接着adjustStreamVolume方法往下看。
private void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage, String caller, int uid) {
......
// 清除掉任何待处理的音量命令
synchronized (mSafeMediaVolumeState) {
mPendingVolumeCommand = null;
}
// 表示不是固定音量
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
// 如果是多媒体音量,并且是使用固定音量的设备
if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
((device & mFixedVolumeDevices) != 0)) {
// 加上表示固定音量的flag
flags |= AudioManager.FLAG_FIXED_VOLUME;
// Always toggle between max safe volume and 0 for fixed volume devices where safe
// volume is enforced, and max and 0 for the others.
// This is simulated by stepping by the full allowed volume range
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
(device & mSafeMediaVolumeDevices) != 0) {
step = safeMediaVolumeIndex(device);
} else {
step = streamState.getMaxIndex();
}
if (aliasIndex != 0) {
aliasIndex = step;
}
} else {
// convert one UI step (+/-1) into a number of internal units on the stream alias
// 如果不是多媒体音量,或者是多媒体音量但是不是固定音量的设备时
// 将音量值的步进量从源流类型变换到目标流类型下,由于不同的流类型的音量调节范围不同,所以这个转换是必需的
step = rescaleIndex(10, streamType, streamTypeAlias);
}
// 情景模式的处理
// If either the client forces allowing ringer modes for this adjustment,
// or the stream type is one that is affected by ringer modes
if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
(streamTypeAlias == getUiSoundsStreamType())) {
int ringerMode = getRingerModeInternal();
// do not vibrate if already in vibrate mode
// 如果已经是震动模式,则不进行震动
if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
flags &= ~AudioManager.FLAG_VIBRATE;
}
// Check if the ringer mode handles this adjustment. If it does we don't
// need to adjust the volume further.
// 根据我们的操作来检查是否需要切换情景模式
final int result = checkForRingerModeChange(aliasIndex, direction, step,
streamState.mIsMuted, callingPackage, flags);
adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;
// If suppressing a volume adjustment in silent mode, display the UI hint
if ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
flags |= AudioManager.FLAG_SHOW_SILENT_HINT;
}
// If suppressing a volume down adjustment in vibrate mode, display the UI hint
if ((result & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {
flags |= AudioManager.FLAG_SHOW_VIBRATE_HINT;
}
}
// If the ringermode is suppressing media, prevent changes
// 勿扰模式
if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
adjustVolume = false;
}
// 获取旧的音量大小
int oldIndex = mStreamStates[streamType].getIndex(device);
......
}
private void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage, String caller, int uid) {
......
// 如果是调整音量大小
if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {
mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);
// Check if volume update should be send to AVRCP
if (streamTypeAlias == AudioSystem.STREAM_MUSIC &&
(device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
(flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
synchronized (mA2dpAvrcpLock) {
if (mA2dp != null && mAvrcpAbsVolSupported) {
mA2dp.adjustAvrcpAbsoluteVolume(direction);
}
}
}
// 先处理静音调整
if (isMuteAdjust) {
boolean state;
if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {
state = !streamState.mIsMuted;
} else {
state = direction == AudioManager.ADJUST_MUTE;
}
if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
setSystemAudioMute(state);
}
for (int stream = 0; stream < mStreamStates.length; stream++) {
if (streamTypeAlias == mStreamVolumeAlias[stream]) {
if (!(readCameraSoundForced()
&& (mStreamStates[stream].getStreamType()
== AudioSystem.STREAM_SYSTEM_ENFORCED))) {
// 这里获取当前流对应的VolumeStreamState实例,然后去调用mute方法
// 最终也会到AudioSystem去调用native方法
mStreamStates[stream].mute(state);
}
}
}
} else if ((direction == AudioManager.ADJUST_RAISE) &&
!checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
// 安全音量提示,音量增加的时候才会去检测
Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);
mVolumeController.postDisplaySafeVolumeWarning(flags);
} else if (streamState.adjustIndex(direction * step, device, caller)
|| streamState.mIsMuted) {
// Post message to set system volume (it in turn will post a
// message to persist).
if (streamState.mIsMuted) {
// Unmute the stream if it was previously muted
if (direction == AudioManager.ADJUST_RAISE) {
// unmute immediately for volume up
streamState.mute(false);
} else if (direction == AudioManager.ADJUST_LOWER) {
if (mIsSingleVolume) {
sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE,
streamTypeAlias, flags, null, UNMUTE_STREAM_DELAY);
}
}
}
// 设置音量到底层
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
// Check if volume update should be sent to Hdmi system audio.
// hdmi音量更新,这个不过多讲解
int newIndex = mStreamStates[streamType].getIndex(device);
if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
}
if (mHdmiManager != null) {
synchronized (mHdmiManager) {
// mHdmiCecSink true => mHdmiPlaybackClient != null
if (mHdmiCecSink &&
streamTypeAlias == AudioSystem.STREAM_MUSIC &&
oldIndex != newIndex) {
synchronized (mHdmiPlaybackClient) {
int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
KeyEvent.KEYCODE_VOLUME_UP;
final long ident = Binder.clearCallingIdentity();
try {
mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
}
}
}
int index = mStreamStates[streamType].getIndex(device);
// 通知外界音量发生变化
sendVolumeUpdate(streamType, oldIndex, index, flags);
......
}
这一段,如果是调整音量的大小,才会走这个if语句。先处理静音调整,然后处理安全音量提示,最后再处理音量的调整。到这里,整个adjustStreamVolume方法就讲完了,不过里面还有一些方法没有讲完,我们接着说说。
音量指数的调整是在adjustIndex这个方法里面去处理的,接下来我们看看这个方法
public boolean adjustIndex(int deltaIndex, int device, String caller) {
return setIndex(getIndex(device) + deltaIndex, device, caller);
}
public boolean setIndex(int index, int device, String caller) {
boolean changed = false;
int oldIndex;
synchronized (VolumeStreamState.class) {
// 获取到旧音量大小
oldIndex = getIndex(device);
index = getValidIndex(index);
synchronized (mCameraSoundForced) {
if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
index = mIndexMax;
}
}
mIndexMap.put(device, index);
// 如果需要调整的大小不等于旧音量大小,表示音量大小发生了改变
changed = oldIndex != index;
// Apply change to all streams using this one as alias if:
// - the index actually changed OR
// - there is no volume index stored for this device on alias stream.
// If changing volume of current device, also change volume of current
// device on aliased stream
final boolean currentDevice = (device == getDeviceForStream(mStreamType));
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);
if (currentDevice) {
aliasStreamState.setIndex(scaledIndex,
getDeviceForStream(streamType), caller);
}
}
}
}
if (changed) {
// 因为存储的指数的大小是正常的10倍,为了得到的指数是10的倍数,这是进行了四舍五入的转化
oldIndex = (oldIndex + 5) / 10;
index = (index + 5) / 10;
// log base stream changes to the event log
if (mStreamVolumeAlias[mStreamType] == mStreamType) {
if (caller == null) {
Log.w(TAG, "No caller for volume_changed event", new Throwable());
}
EventLogTags.writeVolumeChanged(mStreamType, oldIndex, index, mIndexMax / 10,
caller);
}
// fire changed intents for all streams
// 发送广播 通知音量已经更改
mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
mVolumeChanged.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE_ALIAS,
mStreamVolumeAlias[mStreamType]);
sendBroadcastToAll(mVolumeChanged);
}
return changed;
}
好了,音量键的处理暂时就写到这里吧,这篇文章主要就是讲了AudioService对音量键的一些处理。
计划第二篇文章会写音量条UI那边,如何去接受音量改变的消息,怎么去更新UI。
计划第三篇会写native方法那边,底层对音量的一些处理。
以上如有错误欢迎指正