/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
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;
}
......................................................................
private void dispatchDirectAudioEvent(KeyEvent event) {
// When System Audio Mode is off, volume keys received by AVR can be either consumed by AVR
// or forwarded to the TV. It's up to Amplifier manufacturer’s implementation.
HdmiControlManager hdmiControlManager = getHdmiControlManager();
if (null != hdmiControlManager
&& !hdmiControlManager.getSystemAudioMode()
&& shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff()) {
HdmiAudioSystemClient audioSystemClient = hdmiControlManager.getAudioSystemClient();
if (audioSystemClient != null) {
audioSystemClient.sendKeyEvent(
event.getKeyCode(), event.getAction() == KeyEvent.ACTION_DOWN);
return;
}
}
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;
}
}
按键的响应都是在PhoneWindowManager.java类中进行处理的。根据不同的按键事件调节音量大小。
KeyEvent.KEYCODE_VOLUME_UP | 音量+ |
KeyEvent.KEYCODE_VOLUME_DOWN | 音量- |
KeyEvent.KEYCODE_VOLUME_MUTE | 静音 |
在dispatchDirectAudioEvent方法中通过switch判断调节音量的类型。
/frameworks/base/services/core/java/com/android/server/audio/AudioService.java
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller) {
final IAudioPolicyCallback extVolCtlr;
synchronized (mExtVolumeControllerLock) {
extVolCtlr = mExtVolumeController;
}
if (extVolCtlr != null) {
sendMsg(mAudioHandler, MSG_NOTIFY_VOL_EVENT, SENDMSG_QUEUE,
direction, 0 /*ignored*/,
extVolCtlr, 0 /*delay*/);
} else {
adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
caller, Binder.getCallingUid());
}
}
通过AudioService的代理调用AudioService的adjustSuggestedStreamVolume。如果extVolCtlr == null就会执行adjustSuggestedStreamVolume方法。
protected 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);
if (isMuteAdjust) {
.....................................................................
} else if ((direction == AudioManager.ADJUST_RAISE) &&
!checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);
mVolumeController.postDisplaySafeVolumeWarning(flags);
} else if (((device & mFullVolumeDevices) == 0)
&& (streamState.adjustIndex(direction * step, device, caller)
|| streamState.mIsMuted)) {
.....................................................................
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
.....................................................................
}
}
由streamState.adjustIndex(direction * step, device, caller)方法会通知UI音量大小的变化,
通过sendMsg发送消息MSG_SET_DEVICE_VOLUME设置底层音量大小
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;
int oldIndex;
synchronized (mSettingsLock) {
synchronized (VolumeStreamState.class) {
oldIndex = getIndex(device);
index = getValidIndex(index);
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 isCurrentDevice = (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 (isCurrentDevice) {
aliasStreamState.setIndex(scaledIndex,
getDeviceForStream(streamType), caller);
}
}
}
// 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 ((otherDevice & AudioSystem.DEVICE_OUT_ALL_SCO) != 0) {
mIndexMap.put(otherDevice, index);
}
}
}
}
}
if (changed) {
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;
}
................................................................................
private void sendBroadcastToAll(Intent intent) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
final long ident = Binder.clearCallingIdentity();
try {
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
调用sendBroadcastToAll方法发送广播,通知UI声音大小的变化。
/** Handles internal volume messages in separate volume thread. */
private class AudioHandler extends Handler {
.......................................................................
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SET_DEVICE_VOLUME:
setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
break;
.......................................................................
}
}
}
在AudioHandler中调用setDeviceVolume调节底层声音的大小
/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
final boolean isAvrcpAbsVolSupported = mDeviceBroker.isAvrcpAbsoluteVolumeSupported();
synchronized (VolumeStreamState.class) {
// Apply volume
streamState.applyDeviceVolume_syncVSS(device, isAvrcpAbsVolSupported);
// Apply change to all streams using this one as alias
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
if (streamType != streamState.mStreamType &&
mStreamVolumeAlias[streamType] == streamState.mStreamType) {
// Make sure volume is also maxed out on A2DP device for aliased stream
// that may have a different device selected
int streamDevice = getDeviceForStream(streamType);
if ((device != streamDevice) && isAvrcpAbsVolSupported
&& ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0)) {
mStreamStates[streamType].applyDeviceVolume_syncVSS(device,
isAvrcpAbsVolSupported);
}
mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice,
isAvrcpAbsVolSupported);
}
}
}
// Post a persist volume msg
sendMsg(mAudioHandler,
MSG_PERSIST_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
PERSIST_DELAY);
}
调用applyDeviceVolume_syncVSS设置声音的大小
// must be called while synchronized VolumeStreamState.class
/*package*/ void applyDeviceVolume_syncVSS(int device, boolean isAvrcpAbsVolSupported) {
int index;
if (mIsMuted) {
index = 0;
} else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && isAvrcpAbsVolSupported) {
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if ((device & mFullVolumeDevices) != 0) {
index = (mIndexMax + 5)/10;
} else if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
index = (mIndexMax + 5)/10;
} else {
index = (getIndex(device) + 5)/10;
}
setStreamVolumeIndex(index, device);
}
在setStreamVolumeIndex方法中调用AudioSystem的setStreamVolumeIndexAS方法设置声音大小
private void setStreamVolumeIndex(int index, int device) {
// Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
// This allows RX path muting by the audio HAL only when explicitly muted but not when
// index is just set to 0 to repect BT requirements
if (mStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0 && !mIsMuted) {
index = 1;
}
AudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
}
/frameworks/base/media/java/android/media/AudioSystem.java
/** Wrapper for native methods called from AudioService */
public static int setStreamVolumeIndexAS(int stream, int index, int device) {
if (DEBUG_VOLUME) {
Log.i(TAG, "setStreamVolumeIndex: " + STREAM_NAMES[stream]
+ " dev=" + Integer.toHexString(device) + " idx=" + index);
}
return setStreamVolumeIndex(stream, index, device);
}
..............................................................................
private static native int setStreamVolumeIndex(int stream, int index, int device);
调用到setStreamVolumeIndex之后就是native底层的方法
/frameworks/av/media/libaudioclient/AudioSystem.cpp
status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device)
{
const sp& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->setStreamVolumeIndex(stream, index, device);
}