现在主要根据物理按键调节Audio音量的流程进行分析:
PhoneWindow.java#onKeyUp()/onKeyDown()
-->MediaSessionLegacyHelper.sendVolumeKeyEvent()
-->MediaSessionManager.dispatchVolumeKeyEventP{
-->MediaSessionService.dispatchVolumeKeyEvent()
-->MediaSessionService.dispatchVolumeKeyEventLocked()
-->MediaSessionService.dispatchAdjustVolumeLocked()
-->AudioService.adjustSuggestedStreamVolumeForUid()
-->AudioService.adjustSuggestedStreamVolume()
当物理按键被按下后,如果当前应用没有拦截并处理这个事件,PhoneWindow的onKeyDown()或onKeyUp()方法会处理此事件,在PhoneWindow的onKeyDoen() 有对当前有无会话流的判断,
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
// If we have a session send it the volume command, otherwise
// use the suggested stream.
if (mMediaController != null) {
int direction = 0;
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
direction = AudioManager.ADJUST_RAISE;
break;
case KeyEvent.KEYCODE_VOLUME_DOWN:
direction = AudioManager.ADJUST_LOWER;
break;
case KeyEvent.KEYCODE_VOLUME_MUTE:
direction = AudioManager.ADJUST_TOGGLE_MUTE;
break;
}
mMediaController.adjustVolume(direction, AudioManager.FLAG_SHOW_UI);
} else {
MediaSessionLegacyHelper.getHelper(getContext()).sendVolumeKeyEvent(
event, mVolumeControlStreamType, false);
}
return true;
}
若mMediaController != null,既当前无会话流,则使用suggesed stream.接下来调用MediaSessionLegacyHelperd的sendVolumeKeyEvent()方法,将按键事件往下传递:
public void sendVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
if (keyEvent == null) {
Log.w(TAG, "Tried to send a null key event. Ignoring.");
return;
}
mSessionManager.dispatchVolumeKeyEvent(keyEvent, stream, musicOnly);
}
sendVolumeKeyEvent()方法调用MediaSessionManager的dispatchVolumeKeyEvent()方法,顺着代码看可以看到:最终调到AudioService的adjustSuggestedStreamVolume()方法.
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller, int uid) {
mVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_SUGG_VOL, suggestedStreamType,
direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage)
.append("/").append(caller).append(" uid:").append(uid).toString()));
final int streamType;
synchronized (mForceControlStreamLock) {
if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream=" + suggestedStreamType
+ ", flags=" + flags + ", caller=" + caller
+ ", volControlStream=" + mVolumeControlStream
+ ", userSelect=" + mUserSelectedVolumeControlStream);
if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
streamType = mVolumeControlStream;
} else {
// 判断suggestedStream 是否是当前活动的音频流.
final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
final boolean activeForReal;
if (maybeActiveStreamType == AudioSystem.STREAM_MUSIC) {
activeForReal = isAfMusicActiveRecently(0);
} else {
activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
}
// 在这里获取当前活动的音频流,进而控制当前活动的音频流.
if (activeForReal || mVolumeControlStream == -1) {
streamType = maybeActiveStreamType;
} else {
streamType = mVolumeControlStream;
}
}
}
// 判断是否是MUTE
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
if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)) {
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);
}
所以在adjustSuggestedStreamVolume()方法中确定要调节的流类型,并然后调用adjustStreamVolume()方法.
private void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage, String caller, int uid) {
if (mUseFixedVolume) {
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];
final int device = getDeviceForStream(streamTypeAlias);
..........
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))) {
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去设置音量
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
// Check if volume update should be sent to Hdmi system audio.
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);
// 通知修改UI的音量条变化
sendVolumeUpdate(streamType, oldIndex, index, flags);
}
(这里面的调用比较多,贴源码比较麻烦,还是参看/frameworks/base/services/core/java/com/android/server/audio/AudioService.java
)
在adjustStreamVolume里调用VolumeStreamState对象的adjustIndex()更改这个对象中保存的音量值;
VolumeStreamState类保存了一个流类型所有音量相关的信息,AudioService为每一种流类型都分配了一个VolumeStreamState对象,并以流类型的值为索引,保存在一个名为数组mStreamStates中;
在adjustStreamVolume方法中调用了VolumeStreamState对象的adjustIndex()方法,于是就改变了这个对象中存储的音量值,并通过sendBroadcastToAll方法发送广播通知外界音量发生变化。
通过sendMsg()发送消息MSG_SET_DEVICE_VOLUME到mAudioHandler 发送消息后会调用到mAudioHandler中负责处理MSG_SET_DEVICE_VOLUME消息的setDeviceVolume方法,此方法最后会调用AudioSystem.setStreamVolumeIndex方法将音量设置到底层的AudioFlinger,并调用AudioHandler的persistVolume方法将音量的设置信息存储到SettingsProvider中
调用sendVolumeUpdate()方法,显示音量调节通知框 此方法通过AIDL最终调用SystemUI中VolumeDialogController的onVolumeChangedW方法去显示音量调节通知框.