Android动态设置系统音量最大值

产品需求

  • 通过设定最大音量限制大屏声音输出,设置完后需要立即生效且需要记忆;
  • 以10%为递增状态,设置最大音量后,无论最大音量调节至多少百分比,音量条始终显示为100%(比如最大音量设置为80%,即侧边音量条依然可以显示充满,但是音量只有原来的80%)。

从技术角度上理解为:Framework层需要提供一个动态设置系统音量最大值的开放接口供应用层调用,应用apk通过设置最大音量百分比后系统声音效果需要立即生效,同时原生音量条的最大值范围也要变化。

需要涉及3个地方的修改:

  • 添加Framework层接口方法
  • 应用层apk调用新增的接口
  • SystemUI音量条监听最大值变化

Framework层接口

1、以Android 13为例,如果修改系统默认的STREAM_MUSIC音量值,通常是修改AudioService.java下MAX_STREAM_VOLUME数组对应的值15。但我们需要动态修改这个值且不需要重启服务立即生效。

/** 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来调用服务的具体实现

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方法

//动态变更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服务构造初始化时设置最大音量值

	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调用

 /**
  * 设置最大音量百分比
  * @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

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值有没有按预期的变化即可。

- 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
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值