本篇文章基于Android11源碼分析后雷,本篇文章的源碼均在
frameworks
目錄下
1. 調(diào)整音量的方式:
在學(xué)習(xí)AudioService源碼服務(wù)之前吧史,我們看一下在應(yīng)用層如何調(diào)節(jié)音量的增加、減小蕴轨、靜音港谊、非靜音示例:
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,"當(dāng)前音量大小 = "+ 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,"當(dāng)前音量大小 = "+ 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的關(guān)閉靜音
*/
private void unmuteVolume() {
Log.d(TAG,"unmuteVolume ");
if (previousVolume > 0) {
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_UNMUTE, FLAG_SHOW_UI);
}
}
應(yīng)用層可以通過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中有兩種方式來控制音量歧寺,一種是通過代碼來調(diào)整音量、一種是通過音量鍵來控制音量棘脐。
如下我們看下通過音量鍵是如何調(diào)整音量的:
在按下音量鍵的時候斜筐,會先經(jīng)過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.
// 調(diào)用此方法
dispatchDirectAudioEvent(event);
}
return -1;
}
private void dispatchDirectAudioEvent(KeyEvent event) {
//....
try {
// 調(diào)用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;
// 調(diào)用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) {
// ....;
// 最終也是調(diào)用到adjustStreamVolume()方法
adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid,
hasModifyAudioSettings, keyEventMode);
}
音量按鍵在PhoneWindowManager的時候最終會調(diào)用dispatchDirectAudioEvent()
方法,此方法又調(diào)用AudioService.handleVolumeKey()
方法蛀缝,在AudioService最終還是調(diào)用adjustStreamVolume()
方法進行音量的調(diào)整顷链。
接下來將看adjustStreamVolume()
方法源碼是如何控制音量的。在講源碼之前屈梁,先講講音頻流和設(shè)備嗤练。
2. 音頻流和設(shè)備:
在android11中工定義了12中音頻流類型,在AudioSystem
中有相關(guān)定義:
public class AudioSystem
{
/**電話 */
public static final int STREAM_VOICE_CALL = 0;
/** 系統(tǒng) */
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;
/** 強制系統(tǒng)聲音 */
@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 {
// 多種音頻流對應(yīng)一種音量,系統(tǒng)音頻流在讶,響鈴與消息音頻流煞抬,通知音頻流,強制聲音音頻流构哺,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
};
// 存儲多種音頻流對應(yīng)一個音量
protected static int[] mStreamVolumeAlias;
}
STREAM_VOLUME_ALIAS_VOICE
代表的是具有語音功能的設(shè)備如手機革答。
STREAM_VOLUME_ALIAS_TELEVISION
代表電視或機頂盒的設(shè)備。
STREAM_VOLUME_ALIAS_DEFAULT
代表其他的設(shè)備曙强。
在STREAM_VOLUME_ALIAS_VOICE
中統(tǒng)音頻流残拐,響鈴與消息音頻流,通知音頻流旗扑,強制聲音音頻流蹦骑,DTMF音頻流就和響鈴與消息音頻流共享音量,他們的音量是一致的臀防,每當(dāng)STREAM_RING
音量發(fā)生改變時眠菇,其他的與之共享音量的音頻流也要對應(yīng)的發(fā)生改變边败。
其中mStreamVolumeAlias
數(shù)組變量就是就是設(shè)置為對應(yīng)的變量。
- 最大最小音量
每種音頻流都有自己的最大最小的音量值捎废,定義在AudioService
的兩個數(shù)組變量中:
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
};
}
- 輸出設(shè)備
音頻的輸出設(shè)備有很多種笑窜,一種音頻流有多種輸出設(shè)備,而且每種設(shè)備對應(yīng)的音量不一定相同登疗,比如插入耳機時是另一種音量大小排截。
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: 助聽器矾克,用于助聽設(shè)備页慷。
AUDIO_DEVICE_OUT_LINE: 線級輸出。
AUDIO_DEVICE_OUT_AUX_DIGITAL: 數(shù)字輔助輸出聂渊,例如 HDMI差购。
AUDIO_DEVICE_OUT_USB_DEVICE: USB 設(shè)備。
AUDIO_DEVICE_OUT_SPEAKER: 揚聲器汉嗽,外部設(shè)備,通常是手機的內(nèi)置揚聲器找蜜。
AUDIO_DEVICE_OUT_SPEAKER_SAFE: 安全揚聲器饼暑,與揚聲器類似。
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: 車載藍牙免提設(shè)備洗做。
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: 藍牙 A2DP 揚聲器弓叛。
AUDIO_DEVICE_OUT_USB_ACCESSORY: USB 附件。
AUDIO_DEVICE_OUT_REMOTE_SUBMIX: 遠程混音輸出诚纸。
- VolumeStreamState
在AudioService中如何去管理這個復(fù)雜的音頻流和輸出設(shè)備間的音量大小關(guān)系呢撰筷?其通過一個內(nèi)部類VolumeStreamState
,每個音頻流都有一個VolumeStreamState類存儲著當(dāng)前音頻流的相關(guān)信息:
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
AccessibilityManager.AccessibilityServicesStateChangeListener {
// 所有音頻流對應(yīng)的VolumeStreamState
private VolumeStreamState[] mStreamStates;
private void createStreamStates() {
// 音頻流的總數(shù)
int numStreamTypes = AudioSystem.getNumStreamTypes();
// 賦值mStreamStates數(shù)組大小為音頻流的總數(shù)
VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
for (int i = 0; i < numStreamTypes; i++) {
// 為每個音頻流創(chuàng)建VolumeStreamState實例
streams[i] =
new VolumeStreamState(System.VOLUME_SETTINGS_INT[mStreamVolumeAlias[i]], i);
}
}
private class VolumeStreamState {
private final int mStreamType; // 指示哪個音頻流
private int mIndexMin; // 對應(yīng)的最小音量值
// min index when user doesn't have permission to change audio settings
private int mIndexMinNoPerm;
private int mIndexMax; // 對應(yīng)的最大音量值
private boolean mIsMuted;
private boolean mIsMutedInternally;
private String mVolumeIndexSettingName;
private int mObservedDevices;
// 保存每種設(shè)備的音量值
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()
按鍵或代碼設(shè)置都可以通過adjustStreamVolume()去調(diào)整音量大小:
// 設(shè)置音量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))) {
// 調(diào)用VolumeStreamState.mute()去設(shè)置靜音/非靜音
mStreamStates[stream].mute(state);
}
}
}
} else if (!isInVolumePassthrough()
&& (streamState.adjustIndex(direction * step, device, caller,
hasModifyAudioSettings)
|| streamState.mIsMuted)) {
// 去設(shè)置音量
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
靜音的屬性抬闯,則會調(diào)用VolumeStreamState.mute()
方法,其相關(guān)執(zhí)行后面在講关筒,大致流程都差不多溶握。 - 通過
VolumeStreamState.adjustIndex
設(shè)置Index的大小 - 發(fā)送
AudioHandler. MSG_SET_DEVICE_VOLUME
消息去執(zhí)行音量的設(shè)置 - 通過
sendVolumeUpdate()
方法回調(diào)給SystemUI中的VolumeUI去展示對應(yīng)的UI通知。
VolumeStreamState.adjustIndex
// AudioService
public void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage) {
//....
// 音頻流是音樂且是固定音量設(shè)備
// 計算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 {
// 一般走這里蒸播,內(nèi)部單元值睡榆,即增加或減少一個音量的單元值是多少
step = rescaleStep(10, streamType, streamTypeAlias);
}
//...
// direction 就是1,-1袍榆,100胀屿,-100,剛好*單元值后就是index要設(shè)置的選項
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;
}
// 轉(zhuǎn)換后的布值
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;
}
// 將對應(yīng)的設(shè)備設(shè)置到對應(yīng)的新值
mIndexMap.put(device, index);
changed = oldIndex != index;
final boolean isCurrentDevice = (device == getDeviceForStream(mStreamType));
// 同時也要更新與之共享音量的其他音頻流的VolumeStreamState中的對應(yīng)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) {
// 發(fā)送廣播
}
return changed;
}
}
在設(shè)置Index中主要操作是
- 通過
rescaleStep()
方法計算出單元值step - 通過
direction * step
可以得到新值的大小碉纳,在AudioManager中direction在+/-時被巧妙的設(shè)置了1/-1,然后傳給VolumeStreamState.adjustIndex
-
adjustIndex
后又調(diào)用setIndex()
方法,首先對新值進行有效轉(zhuǎn)換馏艾,不超過最大最小值劳曹,然后通過mIndexMap
存儲到對應(yīng)的設(shè)備和新值,并且也更新與之共享音量的音頻流的對應(yīng)的mIndexMap值琅摩,并且發(fā)送廣播铁孵。
AudioHandler. MSG_SET_DEVICE_VOLUME
private class AudioHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SET_DEVICE_VOLUME:
// 執(zhí)行setDeviceVolume方法
setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
break;
}
}
void setDeviceVolume(VolumeStreamState streamState, int device) {
synchronized (VolumeStreamState.class) {
//調(diào)用VolumeStreamState.applyDeviceVolume_syncVSS方法
streamState.applyDeviceVolume_syncVSS(device);
// 其他與之共享音量的音頻流的也隨之設(shè)置之對應(yīng)設(shè)備的音量大小
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 被設(shè)置為 0
index = 0;
} else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
&& mAvrcpAbsVolSupported) {
//A2DP 設(shè)備集合并且支持絕對音量控制
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if (isFullVolumeDevice(device)) {
//如果設(shè)備是全音量設(shè)備(如揚聲器)房资,index 被設(shè)置為最大音量索引的 1/10
index = (mIndexMax + 5)/10;
} else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
//如果設(shè)備是聽力輔助設(shè)備蜕劝,同樣將 index 設(shè)置為最大音量索引的 1/10
index = (mIndexMax + 5)/10;
} else {
//其他,當(dāng)前值+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底層設(shè)置音量值
AudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
}
MSG_SET_DEVICE_VOLUME消息主要是真正的設(shè)置音量值的新值轰异,主要操作有:
- MSG_SET_DEVICE_VOLUME消息執(zhí)行
setDeviceVolume
方法岖沛,setDeviceVolume方法給音頻流和與之共享的音頻流調(diào)用VSS的applyDeviceVolume_syncVSS
進行音量值設(shè)置 -
applyDeviceVolume_syncVSS
將新值index進行轉(zhuǎn)換,并通過setStreamVolumeIndex
方法交給AudioSystem.setStreamVolumeIndexAS
進行真正音量值設(shè)置搭独。
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;
// 設(shè)置IVolumeController
public void setController(IVolumeController controller) {
mController = controller;
mVisible = false;
}
public void postVolumeChanged(int streamType, int flags) {
if (mController == null)
return;
try {
// 調(diào)用IVolumeController的volumeChanged方法
mController.volumeChanged(streamType, flags);
} catch (RemoteException e) {
Log.w(TAG, "Error calling volumeChanged", e);
}
}
}
通過IVolumeController.volumeChanged
進行回調(diào)婴削,給SystemUi那邊進行UI的更新,那么這個IVolumeController
是誰設(shè)置的呢牙肝?
在AudioService中有個setVolumeController()方法給設(shè)置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()
這個方法又是誰設(shè)置的呢唉俗?這就需要看一下VolumeUI
的啟動流程:
public class VolumeUI extends SystemUI {
private VolumeDialogComponent mVolumeComponent;
// 啟動會調(diào)用start()
@Override
public void start() {
//....
setDefaultVolumeController();
}
private void setDefaultVolumeController() {
DndTile.setVisible(mContext, true);
if (LOGD) Log.d(TAG, "Registering default volume controller");
// 調(diào)用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() {
// 調(diào)用VolumeDialogControllerImpl.register()
mController.register();
}
}
// VolumeDialogControllerImpl
public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
protected final VC mVolumeController = new VC();
public void register() {
// 設(shè)置VolumeController
setVolumeController();
}
protected void setVolumeController() {
try {
// 調(diào)用AudioService.setVolumeController將VC設(shè)置為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;
// 發(fā)送VOLUME_CHANGED消息
mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
}
}
}
在VolumeUI
開始的時候會調(diào)用start()
方法,最后通過VolumeDialogControllerImpl
將VC
設(shè)置VolumeController配椭。之后AudioService于VC通信虫溜,而VC則通過與VolumeDialogImpl
進行UI的展示。
AdjustStreamVolume小結(jié)
主要分為三大步驟:
- 通過
adjustIndex()
更新index的新值 - 發(fā)送MSG_SET_DEVICE_VOLUME消息股缸,通過
AudioSystem
設(shè)置新的音量值 - sendVolumeUpdate發(fā)送通知UI消息
2. setStreamVolume
代碼調(diào)整音量時衡楞,可以通過setStreamVolume方法進行調(diào)節(jié),所以它也是設(shè)置音量的入口之一敦姻。
// 設(shè)置音量API之一
private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
String caller, int uid, boolean hasModifyAudioSettings) {
ensureValidStreamType(streamType); // 這里先判斷一下流類型這個參數(shù)的有效性
// 對應(yīng)共享音量的音頻流
int streamTypeAlias = mStreamVolumeAlias[streamType];
// 對應(yīng)音頻流的VolumeStreamState
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
// 根據(jù)音頻流確定輸出的設(shè)備
final int device = getDeviceForStream(streamType);
int oldIndex;
//...權(quán)限判斷
synchronized (mSafeMediaVolumeStateLock) {
// reset any pending volume command
mPendingVolumeCommand = null;
// 獲取流當(dāng)前的音量
oldIndex = streamState.getIndex(device);
// 將原流類型下的音量值映射到目標流類型下的音量值
// 因為不同流類型的音量值刻度不一樣瘾境,所以需要進行這個轉(zhuǎn)換
index = rescaleIndex(index * 10, streamType, streamTypeAlias);
//...
// 調(diào)用setStreamVolumeInt()
onSetStreamVolume(streamType, index, flags, device, caller, hasModifyAudioSettings);
// 獲取設(shè)置的結(jié)果
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];
// 調(diào)用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之后并沒有發(fā)生變化歧杏,比如說達到了最大值,就不需要繼續(xù)后面的操作了
// 或者force參數(shù)為true的話
if (streamState.setIndex(index, device, caller, hasModifyAudioSettings) || force) {
// 通過MSG_SET_DEVICE_VOLUME去設(shè)置音量值
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
}
setStreamVolume
方法的執(zhí)行流程跟adjustStreamVolume
方法大同小異寄雀,都是先更新index值
得滤,然后通過MSG_SET_DEVICE_VOLUME
消息去設(shè)置音量值,最后通過sendVolumeUpdate
去回調(diào)通知Ui盒犹。
總結(jié)
本篇文章主要介紹了AudioService調(diào)整音量大小的源碼懂更,主要在java層,真正的設(shè)置是通過的AudioSystem去調(diào)用底層的native去完成急膀。VolumeStreamState
存儲著音頻流的所有關(guān)系沮协,包括最大最小值,device對應(yīng)的index值卓嫂。調(diào)整音量有兩個API:adjustStreamVolume
和setStreamVolume
它們的執(zhí)行流程都大概是先更新對應(yīng)device的index值慷暂,然后設(shè)置音量值,最后通知UI更新晨雳。
參考文章:
https://juejin.cn/post/6983977417173893128
https://wizardforcel.gitbooks.io/deepin-android-vol3/content/2.html