在android\frameworks\base\media\java\android\media\AudioManager.java中
/**
* @hide
*/
public void handleKeyDown(KeyEvent event, int stream) {
int keyCode = event.getKeyCode();
Log.d("zmq","AudioManager handleKeyDown keyCode = "+keyCode);
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
/*
* Adjust the volume in on key down since it is more
* responsive to the user. 在按键按下去的时候处理,给用户响应更快。
*/
int flags = FLAG_SHOW_UI | FLAG_VIBRATE; //1|16得到的值为17
Log.d("zmq","AudioManager handleKeyDown flags = "+flags);
Log.d("zmq","AudioManager handleKeyDown mUseMasterVolume = "+mUseMasterVolume);
if (mUseMasterVolume) { //使用主音量,在config中定义,为true
adjustMasterVolume(
keyCode == KeyEvent.KEYCODE_VOLUME_UP
? ADJUST_RAISE
: ADJUST_LOWER,
flags);
} else {
adjustSuggestedStreamVolume(
keyCode == KeyEvent.KEYCODE_VOLUME_UP
? ADJUST_RAISE
: ADJUST_LOWER,
stream,
flags);
}
break;
case KeyEvent.KEYCODE_VOLUME_MUTE:
if (event.getRepeatCount() == 0) {
if (mUseMasterVolume) {
setMasterMute(!isMasterMute());
} else {
// TODO: Actually handle MUTE.
}
}
break;
}
}
/**
* Show a toast containing the current volume.
*
* @see #adjustStreamVolume(int, int, int)
* @see #adjustVolume(int, int)
* @see #setStreamVolume(int, int, int)
* @see #setRingerMode(int)
*/
public static final int FLAG_SHOW_UI = 1 << 0; //其值为1
/**
* Whether to vibrate if going into the vibrate ringer mode.
*/
public static final int FLAG_VIBRATE = 1 << 4; //其值为16
private final boolean mUseMasterVolume; //在AudioManager的构造函数中进行初始化
/**
* @hide
*/
public AudioManager(Context context) {
mContext = context;
mUseMasterVolume = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useMasterVolume); //在config.xml中定义初始值
mUseVolumeKeySounds = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useVolumeKeySounds);
}
变量config_useMasterVolume在android\device\mstar\mango\overlay\frameworks\base\core\res\res\values\config.xml中定义
true
/**
* Adjusts the master volume for the device's audio amplifier.
* @param steps The number of volume steps to adjust. A positive
* value will raise the volume.
* @param flags One or more flags.
* @hide
*/
public void adjustMasterVolume(int steps, int flags) {
Log.d("zmq","AudioManager adjustMasterVolume ");
IAudioService service = getService();
try {
service.adjustMasterVolume(steps, flags, mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustMasterVolume", e);
}
}
调用到android\frameworks\base\media\java\android\media\AudioService.java中,真正有实现功能的地方
/** @see AudioManager#adjustMasterVolume(int, int) */
public void adjustMasterVolume(int steps, int flags, String callingPackage) {
Log.d("zmq","AudioService adjustMasterVolume");
if (mUseFixedVolume) {
return;
}
ensureValidSteps(steps); //确保steps的绝对值不超过4
Log.d("zmq","AudioService adjustMasterVolume AudioSystem.getMasterVolume() = "
+AudioSystem.getMasterVolume());
int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME); //需要*100,从AudioSystem中得到的是0.0~1.0
Log.d("zmq","AudioService adjustMasterVolume volume1 = "+volume);
int delta = 0; //需要增加的音量坡度
int numSteps = Math.abs(steps);
Log.d("zmq","AudioService adjustMasterVolume numSteps = "+numSteps);
int direction = steps > 0 ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER;
Log.d("zmq", "AudioService adjustMasterVolume direction = "+direction);
for (int i = 0; i < numSteps; ++i) {
delta = findVolumeDelta(direction, volume);
Log.d("zmq","AudioService adjustMasterVolume delta = "+delta);
volume += delta;
Log.d("zmq", "AudioService adjustMasterVolume volume2 = "+volume);
}
//Log.d(TAG, "adjustMasterVolume volume: " + volume + " steps: " + steps);
setMasterVolume(volume, flags, callingPackage);
}
private final boolean mUseFixedVolume;
mUseFixedVolume = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume);
变量在config_useFixedVolume在android\frameworks\base\core\res\res\values\config.xml中
false
private void ensureValidSteps(int steps) {
if (Math.abs(steps) > MAX_BATCH_VOLUME_ADJUST_STEPS) {
throw new IllegalArgumentException("Bad volume adjust steps " + steps);
}
}
// Maximum volume adjust steps allowed in a single batch call.
private static final int MAX_BATCH_VOLUME_ADJUST_STEPS = 4;
调用到android\frameworks\base\media\java\android\media\AudioSystem.java中,会调用本地层,先不做追踪。
public static native float getMasterVolume();
// Internally master volume is a float in the 0.0 - 1.0 range,
// but to support integer based AudioManager API we translate it to 0 - 100
private static final int MAX_MASTER_VOLUME = 100;
private int findVolumeDelta(int direction, int volume) {
Log.d("zmq"," AudioService findVolumeDelta");
int delta = 0;
if (direction == AudioManager.ADJUST_RAISE) {
if (volume == MAX_MASTER_VOLUME) {
return 0;
}
// This is the default value if we make it to the end
delta = mMasterVolumeRamp[1]; //为1
Log.d("zmq", "AudioService findVolumeDelta delta = "+delta);
// If we're raising the volume move down the ramp array until we
// find the volume we're above and use that groups delta.
for (int i = mMasterVolumeRamp.length - 1; i > 1; i -= 2) { // 没有执行
Log.d("zmq","AudioService findVolumeDelta for ");
if (volume >= mMasterVolumeRamp[i - 1]) {
delta = mMasterVolumeRamp[i];
break;
}
}
} else if (direction == AudioManager.ADJUST_LOWER){
if (volume == 0) {
return 0;
}
int length = mMasterVolumeRamp.length;
Log.d("zmq","AudioService findVolumeDelta length = "+length);
// This is the default value if we make it to the end
delta = -mMasterVolumeRamp[length - 1];
Log.d("zmq","AudioService findVolumeDelta delta = "+delta);
// If we're lowering the volume move up the ramp array until we
// find the volume we're below and use the group below it's delta
for (int i = 2; i < length; i += 2) { //没有执行
Log.d("zmq","AudioService findVolumeDelta for ");
if (volume <= mMasterVolumeRamp[i]) {
delta = -mMasterVolumeRamp[i - 1];
break;
}
}
}
return delta;
}
private final int[] mMasterVolumeRamp;
mMasterVolumeRamp = context.getResources().getIntArray(
com.android.internal.R.array.config_masterVolumeRamp);
变量config_masterVolumeRamp在android\frameworks\base\core\res\res\values\config.xml中
- 0
- 5
一般可以在android\device\mstar\mango\overlay\frameworks\base\core\res\res\values\config.xml中覆盖
- 0
- 1
public void setMasterVolume(int volume, int flags, String callingPackage) {
Log.d("zmq","AudioService setMasterVolume");
if (mUseFixedVolume) {
return;
}
if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, Binder.getCallingUid(),
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return;
}
if (volume < 0) {
volume = 0;
} else if (volume > MAX_MASTER_VOLUME) {
volume = MAX_MASTER_VOLUME;
}
doSetMasterVolume((float)volume / MAX_MASTER_VOLUME, flags);
}
doSetMasterVolume经过了原厂的修改
private void doSetMasterVolume(float volume, int flags) {
boolean bSetA2dpVolume = false;
if ((isPipOn() == false) && isBluetoothA2dpOn()) {
bSetA2dpVolume = true;
} else {
bSetA2dpVolume = false;
}
if (isMasterMute() ) {
if(bSetA2dpVolume){
AudioSystem.setA2dpMute(false);
} else {
//disappear mute
AudioSystem.setMasterMute(false);
// Post a persist master volume msg
sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, false ? 1
: 0, 0, null, PERSIST_DELAY);
}
sendMasterMuteUpdate(false, flags);
}
int oldVolume = getMasterVolume();
AudioSystem.setMasterVolume(volume);
int newVolume = getMasterVolume();
if (newVolume != oldVolume) {
// Post a persist master volume msg
if(bSetA2dpVolume){
sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE,
Math.round(volume * (float)1000.0), 1, null, PERSIST_DELAY);
} else {
sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE,
Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY);
}
}
// Send the volume update regardless whether there was a change.
sendMasterVolumeUpdate(flags, oldVolume, newVolume);
}
// UI update and Broadcast Intent
private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) {
mVolumePanel.postMasterVolumeChanged(flags);
Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume);
sendBroadcastToAll(intent);
}
public void postMasterVolumeChanged(int flags) {
postVolumeChanged(STREAM_MASTER, flags);
}
public void postVolumeChanged(int streamType, int flags) {
if (hasMessages(MSG_VOLUME_CHANGED)) return;
synchronized (this) {
if (mStreamControls == null) {
createSliders();
}
}
removeMessages(MSG_FREE_RESOURCES);
obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
}
private void createSliders() {
LayoutInflater inflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mStreamControls = new HashMap(STREAMS.length);
Resources res = mContext.getResources();
for (int i = 0; i < STREAMS.length; i++) { //STREAMS.length=8
StreamResources streamRes = STREAMS[i];
int streamType = streamRes.streamType;
if (mVoiceCapable && streamRes == StreamResources.NotificationStream) {
streamRes = StreamResources.RingerStream;
}
StreamControl sc = new StreamControl();
sc.streamType = streamType;
sc.group = (ViewGroup) inflater.inflate(R.layout.volume_adjust_item, null);
sc.group.setTag(sc);
sc.icon = (ImageView) sc.group.findViewById(R.id.stream_icon);
sc.icon.setTag(sc);
sc.icon.setContentDescription(res.getString(streamRes.descRes));
sc.iconRes = streamRes.iconRes;
sc.iconMuteRes = streamRes.iconMuteRes;
sc.icon.setImageResource(sc.iconRes);
sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar);
int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
sc.seekbarView.setProgress(getStreamVolume(sc.streamType));
sc.seekbarView.setOnSeekBarChangeListener(this);
sc.seekbarView.setTag(sc);
sc.seekBarValue = (TextView) sc.group.findViewById(R.id.seekbarvalue);
sc.seekBarValue.setText(String.valueOf(getStreamVolume(sc.streamType)));
mStreamControls.put(streamType, sc);
}
}
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_VOLUME_CHANGED: {
onVolumeChanged(msg.arg1, msg.arg2);
break;
}
case MSG_MUTE_CHANGED: {
onMuteChanged(msg.arg1, msg.arg2);
break;
}
case MSG_FREE_RESOURCES: {
onFreeResources();
break;
}
case MSG_STOP_SOUNDS: {
onStopSounds();
break;
}
case MSG_PLAY_SOUND: {
onPlaySound(msg.arg1, msg.arg2);
break;
}
case MSG_VIBRATE: {
onVibrate();
break;
}
case MSG_TIMEOUT: {
if (mDialog.isShowing()) {
// MStar Android Patch Begin
// Don't dismiss mute panel.
if ((mActiveStreamType != -1) && (!isMuted(mActiveStreamType))) {
mDialog.dismiss();
mActiveStreamType = -1;
}
// MStar Android Patch End
}
synchronized (sConfirmSafeVolumeLock) {
if (sConfirmSafeVolumeDialog != null) {
sConfirmSafeVolumeDialog.dismiss();
}
}
break;
}
case MSG_RINGER_MODE_CHANGED: {
if (mDialog.isShowing()) {
updateStates();
}
break;
}
case MSG_REMOTE_VOLUME_CHANGED: {
onRemoteVolumeChanged(msg.arg1, msg.arg2);
break;
}
case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
onRemoteVolumeUpdateIfShown();
break;
case MSG_SLIDER_VISIBILITY_CHANGED:
onSliderVisibilityChanged(msg.arg1, msg.arg2);
break;
case MSG_DISPLAY_SAFE_VOLUME_WARNING:
onDisplaySafeVolumeWarning(msg.arg1);
break;
}
}
/**
* Override this if you have other work to do when the volume changes (for
* example, vibrating, playing a sound, etc.). Make sure to call through to
* the superclass implementation.
*/
protected void onVolumeChanged(int streamType, int flags) {
if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
synchronized (this) {
if (mActiveStreamType != streamType) {
reorderSliders(streamType);
}
onShowVolumeChanged(streamType, flags);
}
}
if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
removeMessages(MSG_PLAY_SOUND);
sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
}
if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
removeMessages(MSG_PLAY_SOUND);
removeMessages(MSG_VIBRATE);
onStopSounds();
}
removeMessages(MSG_FREE_RESOURCES);
sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
resetTimeout();
}
private void reorderSliders(int activeStreamType) {
mSliderGroup.removeAllViews();
StreamControl active = mStreamControls.get(activeStreamType);
if (active == null) {
Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
mActiveStreamType = -1;
} else {
mSliderGroup.addView(active.group);
mActiveStreamType = activeStreamType;
active.group.setVisibility(View.VISIBLE);
updateSlider(active);
}
addOtherVolumes();
}
/** Update the mute and progress state of a slider */
private void updateSlider(StreamControl sc) {
sc.seekbarView.setProgress(getStreamVolume(sc.streamType));
final boolean muted = isMuted(sc.streamType);
// Force reloading the image resource
sc.icon.setImageDrawable(null);
sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
if (((sc.streamType == AudioManager.STREAM_RING) ||
(sc.streamType == AudioManager.STREAM_NOTIFICATION)) &&
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate);
}
if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
// never disable touch interactions for remote playback, the muting is not tied to
// the state of the phone.
sc.seekbarView.setEnabled(true);
} else if ((sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
(sConfirmSafeVolumeDialog != null)) {
sc.seekbarView.setEnabled(false);
} else {
sc.seekbarView.setEnabled(true);
}
}
private void addOtherVolumes() {
if (!mShowCombinedVolumes) return;
for (int i = 0; i < STREAMS.length; i++) {
// Skip the phone specific ones and the active one
final int streamType = STREAMS[i].streamType;
if (!STREAMS[i].show || streamType == mActiveStreamType) {
continue;
}
StreamControl sc = mStreamControls.get(streamType);
mSliderGroup.addView(sc.group);
updateSlider(sc);
}
}
protected void onShowVolumeChanged(int streamType, int flags) {
int index = getStreamVolume(streamType);
mRingIsSilent = false;
if (LOGD) {
Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType
+ ", flags: " + flags + "), index: " + index);
}
// get max volume for progress bar
int max = getStreamMaxVolume(streamType);
switch (streamType) {
case AudioManager.STREAM_RING: {
// setRingerIcon();
Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
mContext, RingtoneManager.TYPE_RINGTONE);
if (ringuri == null) {
mRingIsSilent = true;
}
break;
}
case AudioManager.STREAM_MUSIC: {
// Special case for when Bluetooth is active for music
if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
(AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
} else {
setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
}
break;
}
case AudioManager.STREAM_VOICE_CALL: {
/*
* For in-call voice call volume, there is no inaudible volume.
* Rescale the UI control so the progress bar doesn't go all
* the way to zero and don't show the mute icon.
*/
index++;
max++;
break;
}
case AudioManager.STREAM_ALARM: {
break;
}
case AudioManager.STREAM_NOTIFICATION: {
Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
mContext, RingtoneManager.TYPE_NOTIFICATION);
if (ringuri == null) {
mRingIsSilent = true;
}
break;
}
case AudioManager.STREAM_BLUETOOTH_SCO: {
/*
* For in-call voice call volume, there is no inaudible volume.
* Rescale the UI control so the progress bar doesn't go all
* the way to zero and don't show the mute icon.
*/
index++;
max++;
break;
}
case AudioService.STREAM_REMOTE_MUSIC: {
if (LOGD) { Log.d(TAG, "showing remote volume "+index+" over "+ max); }
break;
}
}
StreamControl sc = mStreamControls.get(streamType);
if (sc != null) {
if (sc.seekbarView.getMax() != max) {
sc.seekbarView.setMax(max);
}
sc.seekbarView.setProgress(index);
if (((flags & AudioManager.FLAG_FIXED_VOLUME) != 0) ||
(streamType != mAudioManager.getMasterStreamType() &&
streamType != AudioService.STREAM_REMOTE_MUSIC &&
isMuted(streamType)) ||
sConfirmSafeVolumeDialog != null) {
sc.seekbarView.setEnabled(false);
} else {
sc.seekbarView.setEnabled(true);
}
}
if (!mDialog.isShowing()) {
int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
// when the stream is for remote playback, use -1 to reset the stream type evaluation
mAudioManager.forceVolumeControlStream(stream);
mDialog.setContentView(mView);
// Showing dialog - use collapsed state
if (mShowCombinedVolumes) {
collapse();
}
mDialog.show();
}
// Do a little vibrate if applicable (only when going into vibrate mode)
if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
((flags & AudioManager.FLAG_VIBRATE) != 0) &&
mAudioService.isStreamAffectedByRingerMode(streamType) &&
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
}
}
protected void onPlaySound(int streamType, int flags) {
if (hasMessages(MSG_STOP_SOUNDS)) {
removeMessages(MSG_STOP_SOUNDS);
// Force stop right now
onStopSounds();
}
synchronized (this) {
ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
if (toneGen != null) {
toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
}
}
}
protected void onStopSounds() {
synchronized (this) {
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int i = numStreamTypes - 1; i >= 0; i--) {
ToneGenerator toneGen = mToneGenerators[i];
if (toneGen != null) {
toneGen.stopTone();
}
}
}
}
protected void onFreeResources() {
synchronized (this) {
for (int i = mToneGenerators.length - 1; i >= 0; i--) {
if (mToneGenerators[i] != null) {
mToneGenerators[i].release();
}
mToneGenerators[i] = null;
}
}
}
framework层调用,不涉及JNI层。