产品需求
- 通过设定最大音量限制大屏声音输出,设置完后需要立即生效且需要记忆;
- 以10%为递增状态,设置最大音量后,无论最大音量调节至多少百分比,音量条始终显示为100%(比如最大音量设置为80%,即侧边音量条依然可以显示充满,但是音量只有原来的80%)。
从技术角度上理解为:Framework层需要提供一个动态设置系统音量最大值的开放接口供应用层调用,应用apk通过设置最大音量百分比后系统声音效果需要立即生效,同时原生音量条的最大值范围也要变化。
需要涉及3个地方的修改:
- 添加Framework层接口方法
- 应用层apk调用新增的接口
- SystemUI音量条监听最大值变化
Framework层接口
1、以Android 13为例,如果修改系统默认的STREAM_MUSIC音量值,通常是修改AudioService.java下MAX_STREAM_VOLUME数组对应的值15。但我们需要动态修改这个值且不需要重启服务立即生效。
java
/** Maximum volume index values for audio streams */
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
......
};
2、代码实现
/frameworks/base/media/java/android/media/AudioManager.java
/frameworks/base/media/java/android/media/IAudioService.aidl
/frameworks/base/services/core/java/com/android/server/audio/AudioService.java
IAudioService.aidl主要是声明一个新的系统服务方法,然后通过AudioManager.java来调用服务的具体实现
java
public void setStreamMaxVolume(int streamType, int maxVolume) {
final IAudioService service = getService();
try {
service.setStreamMaxVolume(streamType, maxVolume);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
AudioService.java具体实现setStreamMaxVolume方法
java
//动态变更STREAM_MUSIC最大音量值接口
public void setStreamMaxVolume(int streamType, int maxVolume) {
ensureValidStreamType(streamType);
if (streamType == AudioSystem.STREAM_MUSIC) {
if (maxVolume <= 15 && maxVolume >= 0) {
int preMaxVolume = MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
//更新MAX_STREAM_VOLUME数组值同时更新mIndexMax
MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] = maxVolume;
mStreamStates[streamType].updateMaxIndex(maxVolume);
//发送一个MAX_VOLUME_CHANGED_ACTION广播用于通知最大音量值变化,该系统广播需要声明在frameworks/base/core/res/AndroidManifest.xml里
Intent intent = new Intent("com.ist.media.MAX_VOLUME_CHANGED_ACTION");
intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
intent.putExtra("preMaxVolume", preMaxVolume);
intent.putExtra("maxVolume", maxVolume);
sendBroadcastToAll(intent);
}
}
}
//关键:该方法加在内部class VolumeStreamState中,VolumeStreamState保存了运行时的音量信息,这里有个流音量index的换算关系,更新MAX_STREAM_VOLUME数组的同时也需要更新mIndexMax
//mIndexMax = MAX_STREAM_VOLUME[streamType] * 10;
public void updateMaxIndex(int index) {
mIndexMax = index * 10;
}
设置后需要有记忆性,即重启后AudioService服务构造初始化时设置最大音量值
java
int maxMusicVolume = SystemProperties.getInt("ro.config.media_vol_steps", -1);
if (maxMusicVolume != -1) {
MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] = maxMusicVolume;
} else {
//add by XXX 初始化STREAM_MUSIC最大音量值
String ptsStr = mSettings.getGlobalString(mContentResolver, "persist.sys.max_volume_percent");//上层通过该Settings值来保存音量设置的百分比
if (TextUtils.isEmpty(ptsStr)) {
ptsStr = "10";
}
int maxPercent = Integer.parseInt(ptsStr);
maxMusicVolume = Math.round((float) maxPercent / 10 * 15);
MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] = maxMusicVolume;
Log.i(TAG, "init maxMusicVolume = " + maxMusicVolume);
//end by XXX
}
应用层apk调用
java
/**
* 设置最大音量百分比
* @param percent 0~10,10%递进
*/
public void setMaxVolumePercent(int percent){
Log.i(TAG, "setMaxVolumePercent() percent = "+ percent);
//保存最大音量百分比设置
Settings.Global.putString(context.getContentResolver(), "persist.sys.max_volume_percent", String.valueOf(percent));
//最大音量值设置后需要立即生效
if (mAudioManager != null) {
int currentMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);//获取系统最大音量值
int currentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);//当前音量值
int maxMusicVolume = Math.round((float) percent / 10 * 15);
//调用新增的系统接口方法
mAudioManager.setStreamMaxVolume(AudioManager.STREAM_MUSIC, maxMusicVolume);
Log.i(TAG, "setStreamMaxVolume = " + maxMusicVolume);
if (currentVolume > maxMusicVolume) { //若当前音量值大于最大音量值则取较小值
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, maxMusicVolume, 0);
}
}
}
SystemUI音量条监听最大值变化
/frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
java
final String action = intent.getAction();
boolean changed = false;
if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
final int level = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
final int oldLevel = intent
.getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream
+ " level=" + level + " oldLevel=" + oldLevel);
changed = updateStreamLevelW(stream, level);
}
......
//add by XXX 侧边音量条最大值即时更新
else if (action.equals("com.ist.media.MAX_VOLUME_CHANGED_ACTION")) {
Log.d(TAG, "onReceive com.ist.media.MAX_VOLUME_CHANGED_ACTION");
final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
final int preMaxVolume = intent.getIntExtra("preMaxVolume", -1);
final int maxVolume = intent.getIntExtra("maxVolume", -1);
Log.i(TAG, "onReceive preMaxVolume = " + preMaxVolume + ", maxVolume = " + maxVolume);
if (preMaxVolume != maxVolume) {
changed = true;
getState();
}
}
//end by XXX
if (changed) {
mCallbacks.onStateChanged(mState);
}
dumpsys audio调试
通过adb shell dumpsys audio
命令查看音频系统的状态信息。
Stream volumes (device: index):查看各类型流的音量值。其中Muted为是否静音,Min为最小值,Max为最大值,Current为各输出设备的当前音量,Devices为当前输出设备。
调试时,我们关注STREAM_MUSIC的Max值有没有按预期的变化即可。
shell
- STREAM_MUSIC:
Muted: false
Muted Internally: false
Min: 0
Max: 15
streamVolume:8
Current: 2 (speaker): 8, 400 (hdmi): 8, 80000 (spdif): 8, 100000 (fm_transmitter): 8, 4000000 (usb_headset): 2, 10000000 (echo_canceller): 8, 40000000 (default): 3