34-Android之耳机音量加大时警告提示框问题
Android4.4
展锐SC9820E平台,在进行GCF测试时,提出耳机警语问题。
我们在使用手机时,插入耳机,然后加大音量至安全音量时,会出现一个警告提示框,点击确定(或连续多次按音量+按键),才会继续增大音量。
而根据规范要求,在超过显示警告提示框的安全音量后, 如果播放音乐等超过20个小时,音量会自动回退到显示警告提示的安全音量。如果继续加大音量,又会出现警告提示框。
而GCF测试出现的问题是,超过20个小时后,音量没有回退安全音量。通过了解GCF测试时, 是通过播放FM进行测试的,通过对AudioService进行查看后,发现AudioService中展锐没有对AudioSystem.STREAM_FM(展锐添加的音频流类型)进行安全音量处理,因此需要自己添加,可以通过对比AudioSystem.STREAM_MUSIC进行添加。
frameworks/base/media/java/android/media/AudioService.java
/** @see AudioManager#adjustStreamVolume(int, int, int) */
public void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage) {
// ...省略
// 进行安全音量检查
if ((direction == AudioManager.ADJUST_RAISE) &&
!checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex);
mVolumePanel.postDisplaySafeVolumeWarning(flags); // 显示警告提示框
} else if (streamState.adjustIndex(direction * step, device)) {
// ... 省略
int index = mStreamStates[streamType].getIndex(device);
sendVolumeUpdate(streamType, oldIndex, index, flags);
}
// 检查安全音量
private boolean checkSafeMediaVolume(int streamType, int index, int device) {
synchronized (mSafeMediaVolumeState) {
if ((mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) &&
((mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) ||
// 此处添加对AudioSystem.STREAM_FM的判断
(mStreamVolumeAlias[streamType] == AudioSystem.STREAM_FM)) &&
((device & mSafeMediaVolumeDevices) != 0) &&
(index > mSafeMediaVolumeIndex)) {
return false;
}
return true;
}
}
当点击警告提示框的确定或者继续加大音量,则执行AudioService的以下方法:
private final int SAFE_MEDIA_VOLUME_NOT_CONFIGURED = 0;
private final int SAFE_MEDIA_VOLUME_DISABLED = 1;
private final int SAFE_MEDIA_VOLUME_INACTIVE = 2;
private final int SAFE_MEDIA_VOLUME_ACTIVE = 3;
private Integer mSafeMediaVolumeState;
private int mMcc = 0;
// mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property
private int mSafeMediaVolumeIndex;
// mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced,
// 安全音量提示针对的是插入耳机的情况
private final int mSafeMediaVolumeDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET |
AudioSystem.DEVICE_OUT_WIRED_HEADPHONE;
// mMusicActiveMs is the cumulative time of music activity since safe volume was disabled.
// When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled
// automatically. mMusicActiveMs is rounded to a multiple of MUSIC_ACTIVE_POLL_PERIOD_MS.
private int mMusicActiveMs; // 超过安全音量使用时长的计时器
// 此处就是超过安全音量,使用的最长时间20个小时
private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval
private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000; // 30s after boot completed
// 设置是否启用安全音量
private void setSafeMediaVolumeEnabled(boolean on) {
Log.d(TAG, "setSafeMediaVolumeEnabled on: " + on);
synchronized (mSafeMediaVolumeState) {
if ((mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_NOT_CONFIGURED) &&
(mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_DISABLED)) {
if (on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE)) {
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE; // 修改安全音量的状态, 启用
// 当超过安全音量使用超过20小时的时候, 执行此处,启用安全音量
enforceSafeMediaVolume();
} else if (!on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)) {
// 点击警告提示框的确定或者继续加大音量, 执行此处,取消安全音量
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_INACTIVE; // 修改安全音量的状态, 未启用
mMusicActiveMs = 0; // 将计时器的时间置为0
sendMsg(mAudioHandler,
MSG_CHECK_MUSIC_ACTIVE,
SENDMSG_REPLACE,
0,
0,
null,
MUSIC_ACTIVE_POLL_PERIOD_MS); // 延迟1分钟进行音乐播放检测,执行onCheckMusicActive方法
}
}
}
}
private void onCheckMusicActive() { // 检测音乐是否播放
synchronized (mSafeMediaVolumeState) {
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) {
// 因为AudioSystem.STREAM_FM, 底层实际也是用的AudioSystem.STREAM_MUSIC, 因此播放FM时, 也能获取到输出设备
int device = getDeviceForStream(AudioSystem.STREAM_MUSIC);
if ((device & mSafeMediaVolumeDevices) != 0) {
sendMsg(mAudioHandler,
MSG_CHECK_MUSIC_ACTIVE,
SENDMSG_REPLACE,
0,
0,
null,
MUSIC_ACTIVE_POLL_PERIOD_MS); // 此处延迟1分钟, 再执行该方法,重新进行校验
int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device);
// 修改的代码, 添加AudioSystem.STREAM_FM的判断,参照AudioSystem.STREAM_MUSIC
int index2 = mStreamStates[AudioSystem.STREAM_FM].getIndex(device);
if (
(AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) && (index > mSafeMediaVolumeIndex))
||
(AudioSystem.isStreamActive(AudioSystem.STREAM_FM, 0) && (index2 > mSafeMediaVolumeIndex))
) {
// 将计时器加上MUSIC_ACTIVE_POLL_PERIOD_MS(1分钟), 因为上面是延迟1分钟再次执行该方法
// 这里其实有问题,就是上面执行延迟1分钟执行该方法,延迟的时间可能不精确, 导致计时器的时间可能小于实际播放时长,
// 这个在下篇文章MTK的Android10中解决。 因为该版本是Android4.4, 目前还没有出现这中情况
mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS;
Log.d(TAG, "onCheckMusicActive mMusicActiveMs: " + mMusicActiveMs);
// 当超过20小时的时候, 启动安全音量
if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX) {
setSafeMediaVolumeEnabled(true); // 接下来看enforceSafeMediaVolume方法
mMusicActiveMs = 0; // 将计时器的置为0
}
}
}
}
}
}
private void enforceSafeMediaVolume() { // 将AudioSystem.STREAM_MUSIC强制设置为安全音量
VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
int devices = mSafeMediaVolumeDevices;
int i = 0;
while (devices != 0) {
final int device = 1 << i++;
if ((device & devices) == 0) {
continue;
}
int index = streamState.getIndex(device);
if (index > mSafeMediaVolumeIndex) {
streamState.setIndex(mSafeMediaVolumeIndex, device);
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
// 此处是添加的代码,参考上面的AudioSystem.STREAM_MUSIC
VolumeStreamState state = mStreamStates[AudioSystem.STREAM_FM];
final int fmIndex = state.getIndex(device);
if (fmIndex > mSafeMediaVolumeIndex) {
state.setIndex(mSafeMediaVolumeIndex, device);
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
state,
0);
// 注意此处,AudioSystem.STREAM_MUSIC在执行MSG_SET_DEVICE_VOLUME消息后, 可以正常的将音量设置为安全音量, 但是AudioSystem.STREAM_FM不行,具体什么原因,还没有去跟代码, 只知道执行下面的代码后, AudioSystem.STREAM_FM可以被修改到安全音量
mAudioHandler.postDelayed(new Runnable() {
@Override
public void run() {
int index = mStreamStates[AudioSystem.STREAM_FM].getIndex(device);
sendVolumeUpdate(AudioSystem.STREAM_FM, fmIndex, index, 0);
}
}, 200);
}
devices &= ~device;
}
}
此外,关于安全音量(mSafeMediaVolumeIndex)可以在AudioService的构造方法中看到赋值:
public AudioService(Context context) {
// ...
// The default safe volume index read here will be replaced by the actual value when
// the mcc is read by onConfigureSafeVolume()
mSafeMediaVolumeIndex = mContext.getResources().getInteger(
com.android.internal.R.integer.config_safe_media_volume_index) * 10;
}