AndroidQ按音量键,默认会调出meida的音量,但是客户要求将默认调出ringer。改为默认ringer后,发现点击音量条上方的ringer_icon,设置为静音模式后,在按音量上键,不能调节音量大小了。
关于音量这块,framework中是与AudioService、AudioManager、AudioSystem相关,音量弹框的界面处理是在SystemUI下,有个volumn的文件夹。
要解此问题,很显然要从AudioService入手,因为音量的加减和AudioProfile模式都是在AudioServie处理好之后,将消息发给SystemUI,SystemUI去做界面上的更新。
AudioService中处理音量的关键方法adjustSuggestedStreamVolume–>adjustStreamVolume
adjustSuggestedStreamVolume根据名称可以猜到是Suggested,建议的音量调整。该方法主要是将传递进来的suggestedStreamType,通过etActiveStreamType方法计算出来真正要调整的音量类型。前面说的客户要求将默认的音量类型由media改为ringer关键就是在这个方法中处理的。
adjustSuggestedStreamVolume计算出来真正需要调整的音量类型后就调用adjustStreamVolume,流程转到adjustStreamVolume中。
这里先说一个AudioService中的内部类VolumeStreamState,每种音量类型(STREAM_VOICE_CALL、STREAM_RING、STREAM_MUSIC。。。)都会实例化一个VolumeStreamState对象,其中int mStreamType就是音量类型,还有一个SparseIntArray mIndexMap,这里有个device的概念,比如说STREAM_MUSIC类型,music可以通过麦克、耳机(有线、蓝牙耳机)等很多设备播放,这个SparseIntArray就是保存的该种音量类型在不同设备上的音量大小。
流程转到adjustStreamVolume中,首先该方法有一些条件判断,不满足的直接return。通过重重判断后来到第一个关键的地方:
计算步长,就是本次音量调整调多少,log看到是以10为步长的。比如15级的音量,通过音量键调大小是70、80、90等等这样的大小,step是以10来增加的。
再下来就进入了RingerMode的相关处理了
跟踪getUiSoundsStreamType的实现,就是STREAM_RING
/** @see AudioManager#getUiSoundsStreamType() */
public int getUiSoundsStreamType() {
return mStreamVolumeAlias[AudioSystem.STREAM_SYSTEM];
}
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
};
来看下checkForRingerModeChange的逻辑,该方法是case不同的mode,我们重点关注RINGER_MODE_SILENT。mIsSingleVolume为false,那就进到else if中
!mVolumePolicy.volumeUpToExitSilent =====》添加一个hint标志位
else ====》切换到振动模式(振动模式下增加音量就会切到normal模式,然后就可以正常调整音量了)
现在暂时将mVolumePolicy.volumeUpToExitSilent的来源忽略,继续跟踪adjustStreamVolume后面的逻辑。
checkForRingerModeChange的计算结果会影响adjustVolume的boolean值。
调整音量值的是在我们上面提到的内部类VolumeStreamState中,
adjustStreamVolume方法的最后一行是调用sendVolumeUpdate
// UI update and Broadcast Intent
protected void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags, int device)
{
streamType = mStreamVolumeAlias[streamType];
if (streamType == AudioSystem.STREAM_MUSIC) {
flags = updateFlagsForTvPlatform(flags);
if ((device & mFullVolumeDevices) != 0) {
flags &= ~AudioManager.FLAG_SHOW_UI;
}
}
mVolumeController.postVolumeChanged(streamType, flags);
}
public void postVolumeChanged(int streamType, int flags) {
if (mController == null)
return;
try {
mController.volumeChanged(streamType, flags);
} catch (RemoteException e) {
Log.w(TAG, "Error calling volumeChanged", e);
}
}
这里的volumeChanged就是向SystemUI去通知音量变化了,SystemUI收到该通知就会读取系统音量:
frameworks\base\packages\SystemUI\src\com\android\systemui\volume\VolumeDialogControllerImpl.java代码:
这里的getAudioManagerStreamVolume,最后就是调用AudioService的getLastAudibleStreamVolume,即上面提到的VolumeStreamType中put的值
/** Get last audible volume before stream was muted. */
public int getLastAudibleStreamVolume(int streamType) {
ensureValidStreamType(streamType);
int device = getDeviceForStream(streamType);
return (mStreamStates[streamType].getIndex(device) + 5) / 10;
}
到此是音量调整从AudioService到SystemUI中的逻辑。
对于我们这个问题,静音模式下不能调节音量的问题,我们回到前面忽略的关键判断:checkForRingerModeChange方法中的mVolumePolicy.volumeUpToExitSilent,从名称上就好理解,音量上键退出静音模式,追踪mVolumePolicy的赋值
@Override
public void setVolumePolicy(VolumePolicy policy) {
enforceVolumeController("set volume policy");
if (policy != null && !policy.equals(mVolumePolicy)) {
mVolumePolicy = policy;
if (DEBUG_VOL) Log.d(TAG, "Volume policy changed: " + mVolumePolicy);
}
}
public方法,显然是被外界调用的,framework/base下搜索,竟然是在SystemUI下的VolumeDialogComponent.java中调用
看下该类是如果实例化VolumePolicy对象的:
果然DEFAULT_VOLUME_UP_TO_EXIT_SILENT配置的是false,改为true就可以在静音模式下按音量上面,先切换到振动模式,再按音量上键,进入normal模式,就可以正常调节音量了。