Android平臺(tái)音量調(diào)節(jié)
本文基于Android 8.0講述Android平臺(tái)原生音量控制功能。
流的定義
Android中绽左,音量都是分開控制甲雅,各種流定義各種流的音量。在Android8.0中萎馅,定義了11種流類型双戳。對(duì)每種流類型都定義了最大音量(MAX_STREAM_VOLUME),默認(rèn)音量(DEFAULT_STREAM_VOLUME)和最小音量(MIN_STREAM_VOLUME)糜芳。一個(gè)流也可以用另外一個(gè)流的音量設(shè)置飒货,所以需要一個(gè)別名映射。
- 最大音量
* frameworks/base/services/core/java/com/android/server/audio/AudioService.java
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
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
};
- 默認(rèn)音量
* 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
11, // STREAM_MUSIC
6, // STREAM_ALARM
5, // STREAM_NOTIFICATION
7, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
11, // STREAM_DTMF
11, // STREAM_TTS
11, // STREAM_ACCESSIBILITY
};
- 別名映射
Android中各種設(shè)備的映射不盡相同峭竣,Android中定義了3中設(shè)備DEFAULT塘辅,VOICE,TELEVISION對(duì)應(yīng)的別名映射皆撩。(STREAM_VOLUME_ALIAS_VOICE)扣墩。
STREAM_VOLUME_ALIAS_VOICE對(duì)應(yīng)能處理能處理Voice的設(shè)備,比如電話扛吞,請(qǐng)映射如下:
* 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
};
STREAM_VOLUME_ALIAS_TELEVISION對(duì)應(yīng)電視呻惕,機(jī)頂盒等。
* 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
};
STREAM_VOLUME_ALIAS_DEFAULT其實(shí)和Voice映射是一樣的滥比,對(duì)應(yīng)其他的設(shè)備亚脆,比如平板等。
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
};
映射關(guān)系如下:
流代號(hào) | 流類型 | 默認(rèn)音量 | 最大音量 | 最小音量 | DEFAULT/Voice映射 | TELEVISION映射 |
---|---|---|---|---|---|---|
0 | STREAM_VOICE_CALL | 4 | 5 | 1 | STREAM_VOICE_CALL | STREAM_MUSIC |
1 | STREAM_SYSTEM | 5 | 7 | 0 | STREAM_RING | STREAM_MUSIC |
2 | STREAM_RING | 5 | 7 | 0 | STREAM_RING | STREAM_MUSIC |
5 | STREAM_NOTIFICATION | 5 | 7 | 0 | STREAM_RING | STREAM_MUSIC |
7 | STREAM_SYSTEM_ENFORCED | 7 | 7 | 0 | STREAM_RING | STREAM_MUSIC |
8 | STREAM_DTMF | 11 | 15 | 0 | STREAM_RING | STREAM_MUSIC |
3 | STREAM_MUSIC | 11 | 15 | 0 | STREAM_MUSIC | STREAM_MUSIC |
9 | STREAM_TTS | 11 | 15 | 0 | STREAM_MUSIC | STREAM_MUSIC |
10 | STREAM_ACCESSIBILITY | 11 | 15 | 0 | STREAM_MUSIC | STREAM_MUSIC |
4 | STREAM_ALARM | 6 | 7 | 0 | STREAM_ALARM | STREAM_MUSIC |
6 | STREAM_BLUETOOTH_SCO | 7 | 15 | 0 | STREAM_BLUETOOTH_SCO | STREAM_MUSIC |
所以在手機(jī)設(shè)備中盲泛,從上表來(lái)看,我們能調(diào)節(jié)的其實(shí)就5個(gè)音量。當(dāng)你想調(diào)節(jié)STREAM_SYSTEM漾根,STREAM_NOTIFICATION等流類型的音量時(shí)址遇,實(shí)際上是調(diào)節(jié)了STREAM_RING的音量。當(dāng)前可控的流類型可以通過下表更直觀地顯示:
流類型 | 默認(rèn)音量 | 最大音量 | 意義 |
---|---|---|---|
STREAM_VOICE_CALL | 4 | 5 | 通話音量 |
STREAM_RING | 5 | 7 | 響鈴,通知,系統(tǒng)默認(rèn)音等 |
STREAM_MUSIC | 11 | 15 | 多媒體音量 |
STREAM_ALARM | 6 | 7 | 鬧鐘音量 |
STREAM_BLUETOOTH_SCO | 7 | 15 | 藍(lán)牙音量 |
音量鍵的處理流程
音量鍵是常用的調(diào)節(jié)音量的方式,調(diào)節(jié)音量時(shí)棚亩,是調(diào)節(jié)音量節(jié),比如媒體音STREAM_MUSIC的最大音量為15虏杰,其實(shí)是15節(jié)讥蟆,你也可以理解為16節(jié),因?yàn)檫€有0.
音量鍵的處理
Android設(shè)置了3個(gè)音量處理鍵纺阔,音量加瘸彤,音量減和靜音。3個(gè)音量鍵通過PhoneWindowManager進(jìn)行處理笛钝。
* 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;
}
}
音量的加減都是一節(jié)一節(jié)的調(diào)節(jié)的质况,比如最大音量為 7,表示有7節(jié)玻靡,加上0结榄,就是8節(jié)。
3音量按鍵對(duì)應(yīng)如下:
按鍵 | AudioService 對(duì)應(yīng)的操作 | 實(shí)際意義 |
---|---|---|
KEYCODE_VOLUME_UP | ADJUST_RAISE | 音量加 |
KEYCODE_VOLUME_DOWN | ADJUST_LOWER | 音量減 |
KEYCODE_VOLUME_MUTE | ADJUST_TOGGLE_MUTE | 靜音按鈕 |
ADJUST_TOGGLE_MUTE是靜音按鈕囤捻,如果現(xiàn)在是靜音的臼朗,那么將切為非靜音;如果是非靜音的蝎土,那么將設(shè)置為靜音视哑。相關(guān)的還有ADJUST_MUTE和ADJUST_UNMUTE。
此外誊涯,AudioService相關(guān)的還有ADJUST_SAME挡毅,這個(gè)只是彈出調(diào)節(jié)音量的框,不去修改音量暴构。
adjustSuggestedStreamVolume通過Binder調(diào)到AudioService中跪呈。
* 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調(diào)同名函數(shù)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;
... ...
}
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;
}
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);
}
這里主要做了以下幾件事:
- mUserSelectedVolumeControlStream 表示強(qiáng)制控制某一種流類型,這里應(yīng)該很少走
- getActiveStreamType獲取要控制的流類型丹壕,從Binder那邊傳過來(lái)的 值為USE_DEFAULT_STREAM_TYPE庆械,getActiveStreamType大概流程如下:
- 獲取映射別名 resolvedStream
- 對(duì)于通知音或鈴聲STREAM_RING薇溃,先顯示調(diào)節(jié)音量的對(duì)話框菌赖,這個(gè)在AudioService的內(nèi)部類VolumeController中處理。
- 最后沐序,通過adjustStreamVolume函數(shù)設(shè)置音量
adjustStreamVolume函數(shù)
adjustStreamVolume函數(shù)比較長(zhǎng)琉用,我們分段來(lái)看
* frameworks/base/services/core/java/com/android/server/audio/AudioService.java
private void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage, String caller, int uid) {
... ...
ensureValidDirection(direction);
ensureValidStreamType(streamType);
boolean isMuteAdjust = isMuteAdjust(direction);
if (isMuteAdjust && !isStreamAffectedByMute(streamType)) {
return;
}
int streamTypeAlias = mStreamVolumeAlias[streamType];
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
- 獲取需要設(shè)置音量類型的映射別名streamTypeAlias
- 獲取音量類型對(duì)應(yīng)的音量信息狀態(tài) streamState堕绩,VolumeStreamState 音量控制的核心,映射后的每種音量都有各自的VolumeStreamState邑时,它保存了一個(gè)流類型所有的音量信息奴紧。
* frameworks/base/services/core/java/com/android/server/audio/AudioService.java
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;
- 獲取流類型對(duì)應(yīng)的設(shè)備
流各自的VolumeStreamState通過observeDevicesForStream_syncVSS去獲取對(duì)應(yīng)的device,observeDevicesForStream_syncVSS的獲取到的是一個(gè)device列表晶丘,最終是通過AudioSystem去native獲取的黍氮。
再?gòu)墨@取的device列表中進(jìn)行二次選擇device:
private int getDeviceForStream(int stream) {
int device = getDevicesForStream(stream);
if ((device & (device - 1)) != 0) {
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;
}
繼續(xù)看adjustStreamVolume,這部分我們以注釋的形式加在代碼中
* frameworks/base/services/core/java/com/android/server/audio/AudioService.java
private void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage, String caller, int uid) {
... ...
// 絕對(duì)音量控制
if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) == 0 &&
(flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
return;
}
// user處理
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
}
// 權(quán)限處理
if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
return;
}
// 清掉待處理的音量處理命令
synchronized (mSafeMediaVolumeState) {
mPendingVolumeCommand = null;
}
// 處理是否混音浅浮,獲取step
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
((device & mFixedVolumeDevices) != 0)) {
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 = mSafeMediaVolumeIndex;
} 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 (((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;
}
}
// 如果不設(shè)置音量adjustVolume
if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
adjustVolume = false;
}
int oldIndex = mStreamStates[streamType].getIndex(device);
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);
}
}
}
// 靜音設(shè)置
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)) {
//安全模式沫浆,提示音量過高
mVolumeController.postDisplaySafeVolumeWarning(flags);
} else if (streamState.adjustIndex(direction * step, device, caller)
|| streamState.mIsMuted) {
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);
}
}
} // 設(shè)置到底層
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
// 是否需要設(shè)置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);
}
在上面這段代碼中,需要注意下面幾個(gè)關(guān)鍵的流程:
- 計(jì)算step
如果不是音樂類型滚秩,且固定音量专执,那么音量調(diào)節(jié)將轉(zhuǎn)換,轉(zhuǎn)換的算法為:
private int rescaleIndex(int index, int srcStream, int dstStream) {
return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
}
index為10郁油,所以STREAM_NOTIFICATION的step為10本股,STREAM_DTMF的step為(10x15 + 15/2) / 15 = 5
- 處理RingMode
Android定義了如下的模式:
public static final int RINGER_MODE_SILENT = 0;
public static final int RINGER_MODE_VIBRATE = 1;
public static final int RINGER_MODE_NORMAL = 2;
public static final int RINGER_MODE_MAX = RINGER_MODE_NORMAL;
- 設(shè)置音量
首先處理靜音,處理安全音量桐腌,最后再處理音量拄显。
音量首先保存在每個(gè)流類型的VolumeStreamState中:
public boolean adjustIndex(int deltaIndex, int device, String caller) {
return setIndex(getIndex(device) + deltaIndex, device, caller);
}
在通過 MSG_SET_DEVICE_VOLUME 消息設(shè)置到底層。AudioHandler通過setDeviceVolume處理MSG_SET_DEVICE_VOLUME消息哩掺。最后是通過AudioSystem的native接口設(shè)置到native凿叠。
- 通知音量更改
最后,通過sendVolumeUpdate廣播整個(gè)系統(tǒng)嚼吞,通知整個(gè)系統(tǒng)音量修改盒件。
看完代碼,我們來(lái)看看Java層的時(shí)序吧~