08.音频系统:第006课_音频系统HAL分析:003_音量键和Setting界面调节音量流程

在前面两个小节小节中,已经讲解了音量调节的最重要部分,该小节讲解一下使用音量键,或者在Setting界面调节滑动条调节音量时,其处理流程是怎么样的。

使用音量键控制音量会涉及两个系统,一个是输入系统,一个是音频系统,我们需要从源头开始分析,我们先来看看输入系统,在以前我们讲解输入系统时,我们曾经说过,对于输入事件,我们会分为很多个stage进行处理:
在这里插入图片描述
分为输入法之前的处理以及输入法之后的处理,我们打开ViewRootImpl.java文件,对于音量调节键,我们关心的是在输入法之后的处理:

final class ViewPostImeInputStage extends InputStage {

处理过程,他会调用该类中的onProcess函数,在这之前我们先来梳理一下,他的处理流程:输入系统会把输入事件发送给位于最前面的APP(activity),activity收到输入事件之后,会把他发送给自己wind,wind发送给deocview,deocview再把他发送给输入焦点,所谓的输入焦点就是一个控件,这个控件位于最前面,他接受用户的输入:
在这里插入图片描述
如果其上的wind,deocview,以及输入焦点都不能处理这个事件,我们还有一个兜底工作,对于音量键,就是由兜底工作完成的,当然,我们也可以在输入焦点上重写输入函数,对音量键进行处理,这样的话,如果重写的APP位于最前面,我们按下音量键,接不能操控声音了,具体行为按照编写的程序为准。一般来说,我们不会重新音量键程序。也就是说,在兜底的工作中,实现对音量键对音量的控制。下面是调用的时序图:
在这里插入图片描述
现在我们查看onProces函数:

protected int onProcess(QueuedInputEvent q) {
	/*处理案件事件*/
	processKeyEvent(q);
		/*该就是我们前面提到的处理流程*/
        // Deliver the key to the view hierarchy.
        mView.dispatchKeyEvent(event)
            // Apply the fallback event policy.
            if (mFallbackEventHandler.dispatchKeyEvent(event)) {		

其上dispatchKeyEvent就是我们的音量处理相关函数,其实现在PhoneFallbackEventHandler.cpp中实现:


public boolean dispatchKeyEvent(KeyEvent event) {
	/*如果按下音量键*/
    if (action == KeyEvent.ACTION_DOWN) {
        return onKeyDown(keyCode, event);
    } else {//如果松开音量键
        return onKeyUp(keyCode, event);
    }

其处理流程如下:
在这里插入图片描述
这里就不一步一步对源码进行追踪了,下面是一些文字的总结:
a. 音量键处理流程
音量键:
如果APP没有重写它的处理函数,
音量键的处理将交给PhoneFallbackEventHandler来处理,
它会调用AudioService.adjustSuggestedStreamVolume( 调整"推荐的流"的音量):

根据时序,我们打开AudioService.java中的adjustSuggestedStreamVolume函数:

/*
direction:增加还是减少音量
suggestedStreamType:推荐的StreamType(最后的选择)
*/
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,String callingPackage, String caller, int uid) {
	/*获得当前活跃的StreamType,如果没有活跃的,则使用推荐值*/
	streamType = getActiveStreamType(suggestedStreamType);
		/*手机*/
		case AudioSystem.PLATFORM_VOICE:
			/*如果正在通话中*/
			if (isInCommunication()) {
				/*并且使用蓝牙设备*/
				if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) == AudioSystem.FORCE_BT_SCO) {
					/*返回蓝牙的StreamType类型*/
	                return AudioSystem.STREAM_BLUETOOTH_SCO;
				} else {//没有使用蓝牙设备
                    return AudioSystem.STREAM_VOICE_CALL;
                }
            /*如果推荐之后为USE_DEFAULT_STREAM_TYPE*/
            } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
		/*电视,如机顶盒*/
				/*如果最近听过音乐*/
				if (isAfMusicActiveRecently(StreamOverride.sDelayMs)) {
					/*返回音乐STREAM_MUSIC*/
					return AudioSystem.STREAM_MUSIC;
		case AudioSystem.PLATFORM_TELEVISION:
			/*永远方返回音乐*/		
			return AudioSystem.STREAM_MUSIC;
		default://平板之类的
			//和电话处理类似

这样我们就解答了前面的问题,如何获得推荐的StreamType, stream = getActiveStreamType(…)。

为了减少用户的设置难度,我们把Stream归为几类:
在这里插入图片描述
我们设置StreamType时,他会导致好几种音量被设置,这就是分组,或者别名alias。我们来看看这别名分组是怎么起作用单的?其调用流程如下:
在这里插入图片描述
根据前面getActiveStreamType函数,已经获得了活跃的StreamType,在根据流程,执行到:
在这里插入图片描述
打开:

private class AudioHandler extends Handler {
	private void setDeviceVolume(VolumeStreamState streamState, int device) {
        synchronized (VolumeStreamState.class) {
            // Apply volume
            streamState.applyDeviceVolume_syncVSS(device);

            // Apply change to all streams using this one as alias
            int numStreamTypes = AudioSystem.getNumStreamTypes();
            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
                if (streamType != streamState.mStreamType &&
                        mStreamVolumeAlias[streamType] == streamState.mStreamType) {
                    // Make sure volume is also maxed out on A2DP device for aliased stream
                    // that may have a different device selected
                    int streamDevice = getDeviceForStream(streamType);
                    if ((device != streamDevice) && mAvrcpAbsVolSupported &&
                            ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0)) {
                        mStreamStates[streamType].applyDeviceVolume_syncVSS(device);
                    }
                    mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);
                }
            }
        }
        // Post a persist volume msg
        sendMsg(mAudioHandler,
                MSG_PERSIST_VOLUME,
                SENDMSG_QUEUE,
                device,
                0,
                streamState,
                PERSIST_DELAY);

    }
	

注释比较详细,就不进行讲解了,小结如下:


a.2 音量设置的"alias"如何起作用: 
     set volume for stream; // 设置"推荐的流"的音量
	 
	 if (mStreamVolumeAlias[other stream] == stream)
	     set volume for other stream;  // 设置同属一个alias的其他流的音量
		 
	 对于不同的设备(电话、TV、平板), mStreamVolumeAlias指向不同的数组

下面我们看看怎么去设置stream的音量,根据时序图,其最终版会调用到:
在这里插入图片描述
我们打开AudioFlinger找到找到:

status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,audio_io_handle_t output)
	/*设置mStreamTypes的值*/
	mStreamTypes[stream].volume = value;
	/*设置其他线程的StreamVolume*/
	if (thread == NULL) {
        for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
            mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
        }
    } else {
        thread->setStreamVolume(stream, value);
    }

小节如下:

扫描二维码关注公众号,回复: 6122710 查看本文章
a.3 怎么设置流的音量:
    设置audioflinger中的数组:   mStreamTypes[stream].volume = value;
	设置PlaybackThread中的数组: mStreamTypes[stream].volume = value;

下面我们分析音量滑动条的流程。

音量滑动条

音量滑动条是一个seekBar控件,打开seekBar.java,

@Override
void onProgressRefresh(float scale, boolean fromUser, int progress) {
        super.onProgressRefresh(scale, fromUser, progress);

当我们滑动滑动条的时候onProgressRefresh函数会被调用,我们怎么去设置音量呢?有两种方法:
1.重写onProgressRefresh函数,在里面去设置音量。
2.添加Listener。

为了大家方便,文字描述第二种方法:

b. 音量滑动条处理流程
b.1 通过下面文件定义音量滑动条:
packages/apps/Settings/res/xml/notification_settings.xml
   该文件定义了多个VolumeSeekBarPreference
   每个VolumeSeekBarPreference要跟一个SeekBar绑定

b.2 在VolumeSeekBarPreference的绑定函数onBindView中,
   设置了对应的SeekBar的SeekBarChangeListener (一个SeekBarVolumizer对象)

b.3 当SeekBar被滑动时, 它的onProgressRefresh被调用
   该函数会调用 mOnSeekBarChangeListener.onProgressChanged

b.4 mOnSeekBarChangeListener.onProgressChanged去设置音量

b.5 每一个SeekBar对应一个stream,
    滑动SeekBar时会设置该stream的音量,
	也会去设置同属一个alias(同一分组)的其他stream的音量

(后两分钟无记录)

猜你喜欢

转载自blog.csdn.net/weixin_43013761/article/details/89759122