一、引言:
Android的音量控制是典型的audiopolicy和audioflinger协作的例子,博文针对音量调节进行详细的解析.音量控制主要分成两大部分,一部分是java层完成的操作,用于响应音量调节,记录音量值,更新UI等操作,另一部分是native层完成,用于计算音量并执行。需要提一句的是,音量控制是设备厂商的适配重点,通常分为软音量和硬件音量,所谓软音量,就是Android原生针对audiotrack中的数据进行设置,而硬件音量则是设置对应芯片的寄存器,两者结合为Android音量的最终体现,博文最后为Android原生音量设置的概括图,嫌代码麻烦的可先看博文最后的总结图。

二、代码分析:
1. java层分析:
我们按下音量键或者触屏音量调节之后,会由Android系统响应按键,由于不同系统和每个公司的策略做的不一样,所以,在响应上逻辑上,不尽相同,但是,最终都会调入到AudioManager.java中:

handleKeyDown@AudioManager.javapublic void handleKeyDown(KeyEvent event, int stream) {...switch (keyCode) {            case KeyEvent.KEYCODE_VOLUME_UP:            case KeyEvent.KEYCODE_VOLUME_DOWN:            ...            adjustSuggestedStreamVolume(...);            ...}}

响应的函数是adjustSuggestedStreamVolume,首先会获取java层的binder服务,然后调入到AudioService.java中:

adjustStreamVolume@AudioService.javaprivate void adjustStreamVolume(int streamType, int direction, int flags,            String callingPackage, int uid) {.../* 确定streamType对应的Alias组别 */int streamTypeAlias = mStreamVolumeAlias[streamType];.../* 获取对应的device */final int device = getDeviceForStream(streamTypeAlias);.../* java层消息机制:调节音量 */sendMsg(mAudioHandler,                        MSG_SET_DEVICE_VOLUME,                        SENDMSG_QUEUE,                        device,                        0,                        streamState,                        0);.../* UI更新相关 */int index = mStreamStates[streamType].getIndex(device);        sendVolumeUpdate(streamType, oldIndex, index, flags);}

因为不同的streamtype可能有相同的策略,所以,这里要先去匹配Alias组别,然后去获取到device,之后我们看到是使用了java中的消息机制,通知需要调节音量,代码最后跟UI更新相关,这里不去重点关注,我们主要看消息机制这里,handler发送了消息之后,处理是在handleMessage中,调用的是setDeviceVolume方法:

private void setDeviceVolume(VolumeStreamState streamState, int device) {...synchronized (VolumeStreamState.class) {streamState.applyDeviceVolume_syncVSS(device);...}}

这里可以看到继续调用的applyDeviceVolume_syncVSS,需要注意在调节了当前streamtype的音量之后,还会去调节其他的类型,因此在调试中,会看到很多类型的打印,我们只关注streamType == 3,applyDeviceVolume_syncVSS会去计算index值,这个index值指UI的刻度值,比如music的话共15个进度,不同的码流类型进度总值可能不一样,方法重点是去调用了AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
三个参数,参数一为流类型,参数二为index(因为native也需要记录这个值),参数三为输出的设备,这是个native方法,接下来,将正式进入native分析。

2.native层分析:
native层的策略是首先根据index计算出真正的音量值,然后再去调用audioflinger执行,先看AudioSystem:

status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream,                                           int index,                                           audio_devices_t device){    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();    if (aps == 0) return PERMISSION_DENIED;    return aps->setStreamVolumeIndex(stream, index, device);}

之前的博文已经分析了,这里是通过IAudioPolicyService去进一步调用,IAudioPolicyService的Bn端在AudioPolicyInterfaceImpl.cpp中:

status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream,                                                  int index,                                                  audio_devices_t device){...    return mAudioPolicyManager->setStreamVolumeIndex(stream,                                                    index,                                                    device);}

前面博文说过,AudioPolicyManager是audiopolicyservice的持有者,APS不会直接与AF交互,我们看下APM中做了什么:

status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,                                                      int index,                                                      audio_devices_t device){.../* 记录当前流类型对应的设备和index值 */mStreams[stream].mIndexCur.add(device, index);/* 获取设备策略 */    audio_devices_t strategyDevice = getDeviceForStrategy(getStrategy(stream), true /*fromCache*/);.../* 检查并准备往AF中设置了 */status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice);}

java层分析的时候说过,index值也会传入到native层,这里便是记录的地方,我们重点关注checkAndSetVolume函数,很多厂商自己的适配也是在这里做的:

status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,                                                   int index,                                                   audio_io_handle_t output,                                                   audio_devices_t device,                                                   int delayMs,                                                   bool force){...    float driverVol[6]= {0.00,0.02,0.03,0.04,0.05,0.06};    /* 计算音量值 */    float volume = computeVolume(stream, index, output, device);    /* 如果index值为前六个,则重新赋值 */    if( index < 6)    {        volume = driverVol[index];    }    ...    /* 这里最终会调入到AF中 */    mpClientInterface->setStreamVolume(stream, volume, output, delayMs);}

我所使用的平台是海思厂商,因为计算出来的音量值是绝对音量,所以如果index值太小的话,音量的变化并不会很明显,因此,这里对前六的index值进行了重新赋值,computeVolume为每个厂商自己的计算方法,不尽相同,海思的如下:

float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 )

实际上整个函数目的很简单,由index换算成真正的音量值,然后启动AF去设置,代码最后看起来跟AF没有关系,但实际上却是会调入到AF中,这里需要追一下代码:mpClientInterface对应的类型是AudioPolicyClientInterface,在AudioPolicyClientImpl.cpp中能找到到对应的实现:

status_t AudioPolicyService::AudioPolicyClient::setStreamVolume(audio_stream_type_t stream,                     float volume, audio_io_handle_t output,                     int delay_ms){    return mAudioPolicyService->setStreamVolume(stream, volume, output,                                               delay_ms);}

这里又会调入到AudioPolicyService中,回想一下前面,AudioPolicyService是通过index到下面的,现在由index确定了真正的音量值之后,又返回了回来,我们看下AudioPolicyService下一步又会怎么调用:

setStreamVolume@AudioPolicyService.cppint AudioPolicyService::setStreamVolume(audio_stream_type_t stream,                                        float volume,                                        audio_io_handle_t output,                                        int delayMs){    return (int)mAudioCommandThread->volumeCommand(stream, volume,                                                   output, delayMs);}

这段代码初看有点懵,mAudioCommandThread是什么鬼?这是一个audio的命令接收线程,那么,这个线程在什么时候创建的呢?其实就是在第一次引用AudioPolicyService的强指针时候创建的,我们看下AudioPolicyService::onFirstRef():

void AudioPolicyService::onFirstRef(){...    // start tone playback thread        mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this);        // start audio commands thread        mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);        // start output activity command thread        mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);...}

可以看到这里一共创建了三个命令线程,AudioCommandThread的构造函数并没有做什么实质性的事,联想前几篇博客的分析,我们看一下AudioCommandThread的onFirstRef函数:

void AudioPolicyService::AudioCommandThread::onFirstRef(){    run(mName.string(), ANDROID_PRIORITY_AUDIO);}

果然,当第一次引用AudioCommandThread的强指针时,线程就会开始转起来,不停地接受指令了,那么,哪个地方是第一次引用强指针的呢?搜了下代码,发现只有一个地方使用了AudioCommandThread的强指针,就是AudioPolicyClient声明了此成员:

class AudioPolicyClient : public AudioPolicyClientInterface{...sp<AudioCommandThread> mAudioCommandThread;     // audio commands thread    sp<AudioCommandThread> mTonePlaybackThread;     // tone playback thread    sp<AudioCommandThread> mOutputCommandThread;...}

基本我们就可以确认了,在第一次实例化AudioPolicyClient对象的时候,就会为成员变量分配指针空间,这里就相当于第一次引用了AudioCommandThread的强指针,接收命令的线程也就转起来了。
花了比较大的工夫分析了mAudioCommandThread线程的创建过程,我们去看下它的volumeCommand:

status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type_t stream,                                                               float volume,                                                               audio_io_handle_t output,                                                               int delayMs){    sp<AudioCommand> command = new AudioCommand();    command->mCommand = SET_VOLUME;    sp<VolumeData> data = new VolumeData();    data->mStream = stream;    data->mVolume = volume;    data->mIO = output;    command->mParam = data;    command->mWaitStatus = true;    ALOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d",            stream, volume, output);    return sendCommand(command, delayMs);}

封装数据,返回sendCommand,到这里似乎就跟不下去了,但不要忘了mAudioCommandThread是一个线程,并且已经run起来了,那么我们需要去看下它的threadloop干了什么:

bool AudioPolicyService::AudioCommandThread::threadLoop(){...while (!exitPending())    {    ...switch (command->mCommand) {        ...        case SET_VOLUME: {           VolumeData *data = (VolumeData *)command->mParam.get();           ALOGV("AudioCommandThread() processing set volume stream %d, \                   volume %f, output %d", data->mStream, data->mVolume, data->mIO);           command->mStatus = AudioSystem::setStreamVolume(data->mStream,                                                           data->mVolume,                                                           data->mIO);               ...        }}...}

一切如我们所想的,这里面的case语句指引了我们,饶了一大圈,还是到AudioSystem了,但是,看到AudioSystem就应该兴奋起来了,因为马上要到audioflinger了,看吧:

status_t AudioSystem::setStreamVolume(audio_stream_type_t stream, float value,        audio_io_handle_t output){    if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();    if (af == 0) return PERMISSION_DENIED;    af->setStreamVolume(stream, value, output);    return NO_ERROR;}

剥丝抽茧,果然还是会由audioflinger来完成,看下audioflinger又会做什么:

status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,        audio_io_handle_t output){.../* 1.根据output找到thread */PlaybackThread *thread = NULL;    if (output != AUDIO_IO_HANDLE_NONE) {        thread = checkPlaybackThread_l(output);        if (thread == NULL) {            return BAD_VALUE;        }    }    /* 2.记录当前的流类型的音量值 */    mStreamTypes[stream].volume = value;.../* 3.进入到thread中去设置音量 */thread->setStreamVolume(stream, value);/* 4.厂商定制化:设置硬件音量 */#if defined (PRODUCT_STB)    for (size_t i = 0; i < mAudioHwDevs.size(); i++) {        AudioHwDevice *dev = mAudioHwDevs.valueAt(i);        mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;        ALOGE("setStreamVolume by set_master_volume");        if(stream == 3)//music type,only this type can adjust hardware volum            dev->hwDevice()->set_master_volume(dev->hwDevice(),value);        mHardwareStatus = AUDIO_HW_IDLE;    }    ALOGV("setStreamVolume value over");#endif}

audioflinger中的setStreamVolume真是信息量巨大,首先,它会通过output找到对应的thread,因为之前分析audioflinger和audiotrack的博客时,已经知道,audioflinger会创建两个线程,一个是PlaybackThread,一个是RecordThread,而PlaybackThread的子类有MixerThread,DirectOutputThread等,所以,这里首先需要根据output确认到底是哪一个thread,之后,记录当前的流类型的音量值,这个值,后面会使用到,注释三为什么会到thread中去设置呢?我们这里需要清楚,thread掌管的是track,其实就是到track里面去设置音量,也就是说,这里设置音量实际上改变的是数据,简单地说,对数据进行幅值强弱处理达到音量调节的目的,这也正是我开篇所说的软音量调节, 因为是对数据进行处理,并没有真正地设置到硬件中去,但注释四就是设置到硬件中了,可以看到,这里是芯片厂商自己加的,通过hal层直接设置到芯片的sdk,然后设置到寄存器中,海思的硬件音量设置是在audioflinger中做的,各个厂商的实现策略可能不一样,但大家一定要区别设置软音量和硬件音量的区别。
硬件音量的设置我们就先不分析了,我们看软件音量上又是怎么走的,去到setStreamVolume:

void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value){    Mutex::Autolock _l(mLock);    mStreamTypes[stream].volume = value;    broadcast_l();}

其实这里跟前面的audioflinger中做了重复工作,多赋值一次,前面audioflinger中的赋值应该是可以去掉的(从binder机制上我觉得是可以的),言归正传,似乎路再一次的堵死了,这里面通过广播告诉thread有事情要处理,我们需要到threadloop里面去看看,以mixthread为例,我们关注下它的threadloop,threadloop“三把斧”的第一把prepareTracks_l就告诉了我们答案:

AudioFlinger::MixerThread::prepareTracks_l@Threads.cpp{...float typeVolume = mStreamTypes[track->streamType()].volume;float v = masterVolume * typeVolume;}

这里的typeVolume 就是前面赋值的音量值,下面一句话告诉了我们软音量的总值为masterVolume 与设置index计算出来的音量之积,所以,在实际问题的处理中,如果音量调节全程都很小,请注意是否是masterVolume 太小导致的,masterVolume 也是通过上层java接口来设置的。计算了总音量之后,后续就会去mixer中进行混音了,软音量的设置也完成了。
至此,音量设置的分析基本就完成了,回顾一下,音量设置首先是java层计算,存储index值,更新UI等操作,然后将流类型和index传入native层,native层通过audiopolicy进行计算之后,转交由audioflinger去设置,而原生audioflinger中是通过处理track数据来达到音量调节的目的。

三、总结:
Android原生音量调节略微复杂,这里做了一张图来比较完成的概括一下(原图较大,可自行下载保存):
Android音频系统之音量控制详解(Android 5.1)_第1张图片
1.不同的厂商按键响应策略不一样,但最终都会调入到AudioService中;
2.AudioPolicyService在音量值计算的过程中承载的工作比较大,首先是通过binder服务调用AudioPolicyManager去计算音量值,然后是通过自己创建的AudioCommandThread去接收audio音量调节的指令;
3.AudioPolicyManager如何设置到AudioFlinger略微有点绕,因为中间有一个AudioPolicyService自己创建的AudioCommandThread线程;
4.audioflinger中原生场景会去设置软件音量,不同的厂商硬件音量的策略不同,所以红色虚线圈起来的为硬件音量设置,需视平台而定,但是一定要区分软件音量与硬件音量的区别;
5.软件音量是通过改变track中数据实现的音量调节,硬件音量是通过修改寄存器值实现的调节;
6.原生中所说的二级音量设置就是prepareTracks_l中的masterVolume*typeVolume,因为软件总音量是二者之积,所以,音量值太小时请确认软件总音量值;

更多相关文章

  1. Android 驱动之旅: 第一章 在Android 内核源代码工程中编写硬件驱
  2. Android 开发艺术探索读书笔记 11 -- Android 的线程和线程池
  3. Android线程模型
  4. android开发——通过子线程更新界面UI
  5. 通杀所有系统的硬件漏洞?聊一聊Drammer,Android上的RowHammer攻击
  6. Android中AsyncTask(异步任务)和Handler(线程消息机制)的详解
  7. Android中的线程处理

随机推荐

  1. 盒模型居中及水平垂直
  2. Tinker热修复简单接入
  3. Android(安卓)属性动画 详解
  4. Android(安卓)WIFI状态监控
  5. Android(安卓)VideoView本地视频播放
  6. 查看Android中的AlarmManager事件
  7. Android中Toolbar随着ScrollView滑动透明
  8. SQLite_Android
  9. [Android] Adapter:SimpleAdapter Simple
  10. Android(安卓)HDecoration项目so库导出和