文章目录
- 前言
- UI 处理流程
- 音量键的响应流程
- AudioService设置音量流程
- AAOS音量条选项
- 总结
前言
AAOS音量设置内容可以分为几个部分
- 音量的ui响应流程
- legacy模式
- dynamic模式
- 通过按键进行设置
AAOS 音量设置比通用的Android系统复杂,表现在两个方面。首先是UI响应上面的区别,AAOS 虽然有复用部分AOSP的UI逻辑,但有关音量设置的界面和音量变化的回调响应都是独立实现。这一部分还不是很完善,部分的功能实现不全。 其次 AAOS动态路由模式下的音量设置,这一模式下的音量设置跟正常的流程完全不一样,极大的依赖于hal层的实现。
本文的目标
- 理解CarService中UI的响应流程
- 理解AudioService中音量设置流程
- CarAudioService 中legacy和dynamic音量的处理
- 按键调整音量的流程
- 相关问题的处理
UI 处理流程
- system volume ui的处理流程
要理解car volume ui的处理首先要理解system volume ui的处理。
system volume ui代码位置:
frameworks\base\packages\SystemUI\src\com\android\systemui\volume\VolumeDialogImpl.java
frameworks\base\packages\SystemUI\src\com\android\systemui\volume\VolumeDialogControllerImpl.java
frameworks\base\packages\SystemUI\src\com\android\systemui\volume\VolumeDialogComponent.java
volume UI通过将 volumeUI的IVolumeController设置到audioService中,audioService音量设置完成后调用volumeControl的volumeChanged将音量变化通知到volumeUI。volumeUI根据传递的参数来确认是不是要弹出UI,播放提示音等等操作。
具体来说
- VolumeController首先设置audioManager 中,audioManager 在设置到audioService中。
- 上层的处理逻辑主要在VolumeDialogControllerImpl 和 VolumeDialogImpl这里面。
- 在VolumeDialogImpl通过addCallback将ui这边的callback和handler传递给VolumeDialogControllerImpl。
- VolumeDialogControllerImpl这里面同样有一个handler,所有audioService的调用都会转换为消息通过handler进行发送。
- 在对应的消息处理函数中进行flags和streamType的判断处理,需要显示的ui就调用之前通过addCallback设置下来的回调函数和handler进行处理。
- 在VolumeDialogImpl就是ui的各种处理比如音量设置的时候的弹出UI等等。
关键的代码:
注册到audioSerivice
VolumeDialogControllerImpl.java
public void register() {
setVolumeController();
}
protected void setVolumeController()
mAudio.setVolumeController(mVolumeController);
}
audioService回调
AudioService.java
protected void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags, int device) {
mVolumeController.postVolumeChanged(streamType, flags);
}
VolumeDialogControllerImpl.java
public void volumeChanged(int streamType, int flags) throws RemoteException {
mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
}
VolumeDialogController的回调和处理
public void init(int windowType, Callback callback) {
mController.addCallback(mControllerCallbackH, mHandler);
}
boolean onVolumeChangedW(int stream, int flags) {
final boolean showUI = shouldShowUI(flags);
final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
boolean changed = true;
if (showUI) {
mCallbacks.onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
}
return changed;
}
- car volume ui的处理流程
car volume ui跟system volume ui的区别是
- 复用了VolumeDialogControllerImpl 这一部分,但是VolumeDialogImpl这一部分独立实现 为CarVolumeDialogImpl。
- volume UI的实现也跟system volume ui也不一样。在上层CarVolumeDialogImpl没有设置callback的回调 ,但是有设置control到audioService。
- audioSeriver调用volumeControl部分的作用就不是很大。没有回调函数,VolumeDialogControllerImpl对于callback的调用是空的。
-
legacy模式
- car volume ui 中如果是legacy的模式. CarVolumeDialogImpl会注册CarVolumeCallback到CarAudioService。
- CarAudioService会监听VolumeChange的Intent。
- audioService这边如果有volumechange的intent广播出来之后。会回调到 CarVolumeDialogImpl进行ui相关的处理。
- intent中只包含了stramType 而对于之前的flag参数(是showUI play sound等等标志的或)是没有的。所以这边有个问题在需要显示ui或者播放声音的时候,处理不了。
-
dynamic 模式
dynamic模式跟legacy模式又完全不一样,分为carAudioService 和 audioService-
carAudioService利用audioService 的setAudioPolicyVolumeCallback 将自己的调节音量的回调注册到audioSerivice。
-
audioService 中实际是判断mIsVolumeController来处理的。有回调的时候,外部注册AudioPolicyProxy是将mIsVolumeController置为1.
-
audioService:在进行调节之前 会判断外部的control 是不是存在的,存在的话 会notifyExternalVolumeController 发送 MSG_NOTIFY_VOL_EVENT
-
这个会调用到CarAudioService 的mVolCb.onVolumeAdjustment(msg.arg1);利用carAudioService 的 CarAudioPolicyVolumeCallback 然后调用setGroupVolume。
-
setGroupVolume会调用AudioManger的AudioManager.setAudioPortGain。其实现就直接调用到hal层了。
-
同时在设置外部的controller的时候,会判断下面这个值,需要在overlay进行默认值覆盖。否则设置的无效。
mContext.getResources().getBoolean(com.android.internal.R.bool.config_handleVolumeKeysInWindowManager)
-
对于ui的处理在CarAudioPolicyVolumeCallback中调用setGroupVolume时将flag设置为了showUI 和from key这样在callbackGroupVolumeChange中就会根据这个flag来显示音量变化时进行ui的显示了。
-
音量键的响应流程
简单的一个流程:
- phonwinowsManager 是第一个截获按键的地方,这边会派发给MediaSessionService。
- MediaSessionService调用mAudioManager.adjustSuggestedStreamVolumeForUid。
- 这里面调用了AudioPolicy的音量设置函数。 是通过音量曲线,将index 转换为增益,最后增益是设置到audioPloicy 设置到hal起作用的。 音频曲线有一个xml来定义,在xml 中是定义了一些坐标表示从1到100 对应的音频增益是多少。
- 设置完成之后会发送brodcast来通知外部 同时通过回调通知外部。外部的ui接收到信息后 会做出弹出ui,播放声音等响应。
AudioService设置音量流程
不管是通过按键还是进度条进行音量的调节,最后都会调用到AudioService。 但两种方式所调用的函数不一样。
需要注意的地方
-
streamalias
audioService中定义的流类型在不同的平台映射是不一样的,有些平台如tv上music类型的音量是和ring alarm一样的,也就是说改变这些类型的音量会同时改变streamAlias对应的音量。同时base下面的的config.xml 中的config_single_volume 也会影响映射矩阵。
假如config_single_volume 这个在overlay被重载为true,那么isSingleVolume就为true,所以对应映射矩阵是 PLATFORM_TELEVISION。而对于TVLEVISION 而言所有的streamType基本都是music。
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
-
suppressAdjustment 处理
这个只有在按键进行调节的时候 会进行这样的处理, 目的是在第一次设置不会真正的去设置音量 会先回调到UI这边 让UI显示出来,UI显示出来之后会通知到AudioService 这边 设置mVisible为true。
之后在设置音量的话就可以显示了。(这样处理的目的是什么? 可能是为了在第一次调试的时候 让用户可以知道当前正在调节的音量是哪一个)。
AAOS音量条选项
-
动态路由情况
进度条选项跟car_audio_configuration 中定义的volumegroup对应。xml定义的group 每个group有多个usage的时候, 用的是第一个的。这边会有一个context转usage的过程。例子: 目前定义了4个 这边就会有4个选项。
-
legacy 模式
carAudioservice 中如果不是动态路由的话定义的是下面的这三种。AudioManager.STREAM_MUSIC, AudioManager.STREAM_ALARM, AudioManager.STREAM_RING。这个是固定在代码中。AAOS音频这边主要还是实现动态路由的场景。
总结
AAOS 音频音量的设置从ui上面看不太完善,与原生的Android有较大的差距,主要是为了支持传统的音频路由和动态路由,两者对于音量响应不一样,一个是依赖audioService intent的广播,一个是依赖于carAudioSerivice这边的回调。 从设置流程上看传统模式跟原生Android类似,而动态路由是可以之间操作到hal进行处理。