Processing of Volume Adjustment Native on Android Platform

Posted by lala on Tue, 17 Sep 2019 07:38:31 +0200

Article directory

Native's Process Processing

I just mentioned the logic in Audio Service, which is finally set to the native layer through Audio System. setStream Volume Index (mStream Type, index, device). So let's see how native handles it.

## Audio device
SetStream VolumeIndex is set according to device and stream type. In other words, the volume of each stream type of each device is separate and can be different. For example, a Bluetooth headset can have a Music volume of 15 and a Speaker volume of 5. So the question arises. There's no logic at all at the top? How did this work out? Let's first look at how to get the equipment!

* frameworks/base/services/core/java/com/android/server/audio/AudioService.java::VolumeStreamState

        public int observeDevicesForStream_syncVSS(boolean checkOthers) {
            final int devices = AudioSystem.getDevicesForStream(mStreamType);
            if (devices == mObservedDevices) {
                return devices;
            }
            final int prevDevices = mObservedDevices;
            mObservedDevices = devices;
            if (checkOthers) {
                // one stream's devices have changed, check the others
                observeDevicesForStreams(mStreamType);
            }
            // log base stream changes to the event log
            if (mStreamVolumeAlias[mStreamType] == mStreamType) {
                EventLogTags.writeStreamDevicesChanged(mStreamType, prevDevices, devices);
            }
            sendBroadcastToAll(mStreamDevicesChanged
                    .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, prevDevices)
                    .putExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, devices));
            return devices;
        }

device is obtained through AudioSystem. getDevices ForStream (mStreamType), which is a stream type, not an alias for mapping.

native's getDevices ForStream function is as follows:

audio_devices_t AudioSystem::getDevicesForStream(audio_stream_type_t stream)
{
    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
    if (aps == 0) return AUDIO_DEVICE_NONE;
    return aps->getDevicesForStream(stream);
}

The final decision is made through AudioPolicy.

* frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp

audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream)
{
    if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
        return AUDIO_DEVICE_NONE;
    }
    if (mAudioPolicyManager == NULL) {
        return AUDIO_DEVICE_NONE;
    }
    Mutex::Autolock _l(mLock);
    return mAudioPolicyManager->getDevicesForStream(stream);
}

MAudioPolicy Managers are the core of AudioPolicy. When AudioPolicy Service was established, mAudioPolicy Managers and corresponding threads were created.

frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp

void AudioPolicyService::onFirstRef()
{
    {
        Mutex::Autolock _l(mLock);

        // 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);

        mAudioPolicyClient = new AudioPolicyClient(this);
        mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);
    }
    // load audio processing modules
    sp<AudioPolicyEffects>audioPolicyEffects = new AudioPolicyEffects();
    {
        Mutex::Autolock _l(mLock);
        mAudioPolicyEffects = audioPolicyEffects;
    }
}

MAudioPolicy Manager is created through the createAudioPolicy Manager function. AOSP's createAudioPolicy Manager is as follows:

* frameworks/av/services/audiopolicy/manager/AudioPolicyFactory.cpp

extern "C" AudioPolicyInterface* createAudioPolicyManager(
        AudioPolicyClientInterface *clientInterface)
{
    return new AudioPolicyManager(clientInterface);
}

Vendor can also implement its own Audio Policy Manager, such as Qualcomm's:

* hardware/qcom/audio/policy_hal/AudioPolicyManager.cpp

extern "C" AudioPolicyInterface* createAudioPolicyManager(
         AudioPolicyClientInterface *clientInterface)
{
     return new AudioPolicyManagerCustom(clientInterface);
}

Let's move on to the getDevicesForStream function:

* frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp

audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stream) {
    // By checking the range of stream before calling getStrategy, we avoid
    // getStrategy's behavior for invalid streams.  getStrategy would do a ALOGE
    // and then return STRATEGY_MEDIA, but we want to return the empty set.
    if (stream < (audio_stream_type_t) 0 || stream >= AUDIO_STREAM_PUBLIC_CNT) {
        return AUDIO_DEVICE_NONE;
    }
    audio_devices_t devices = AUDIO_DEVICE_NONE;
    for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
        if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
            continue;
        }
        routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);
        audio_devices_t curDevices =
                getDeviceForStrategy((routing_strategy)curStrategy, false /*fromCache*/);
        SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(curDevices, mOutputs);
        for (size_t i = 0; i < outputs.size(); i++) {
            sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
            if (outputDesc->isStreamActive((audio_stream_type_t)curStream)) {
                curDevices |= outputDesc->device();
            }
        }
        devices |= curDevices;
    }

    /*Filter SPEAKER_SAFE out of results, as AudioService doesn't know about it
      and doesn't really need to.*/
    if (devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
        devices |= AUDIO_DEVICE_OUT_SPEAKER;
        devices &= ~AUDIO_DEVICE_OUT_SPEAKER_SAFE;
    }
    return devices;
}
  • Streams Match Forvolume is to determine whether two are equal.
  • getStrategy function, which converts stream to Strategy
  • GettDeviceForStrategy gets the device based on Strategy
  • getOutputsForDevice gets device-related output
  • Output-related device, output Desc - > device ()

## Mapping Audio Stream to Audio Policy
In native, streams are converted into policies, devices are selected according to policies, etc.

* frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp

routing_strategy AudioPolicyManager::getStrategy(audio_stream_type_t stream) const
{
    ALOG_ASSERT(stream != AUDIO_STREAM_PATCH,"getStrategy() called for AUDIO_STREAM_PATCH");
    return mEngine->getStrategyForStream(stream);
}

mEngine is generated in the constructor of AudioPolicy Manager.

AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
    :
... ...
{
    ... ...

    // Once policy config has been parsed, retrieve an instance of the engine and initialize it.
    audio_policy::EngineInstance *engineInstance = audio_policy::EngineInstance::getInstance();
    if (!engineInstance) {
        ALOGE("%s:  Could not get an instance of policy engine", __FUNCTION__);
        return;
    }
    // Retrieve the Policy Manager Interface
    mEngine = engineInstance->queryInterface<AudioPolicyManagerInterface>();
    if (mEngine == NULL) {
        ALOGE("%s: Failed to get Policy Engine Interface", __FUNCTION__);
        return;
    }
    mEngine->setObserver(this);
    status_t status = mEngine->initCheck();

Engine can be configured. According to the definition of USE_CONFIGURABLE_AUDIO_POLICY, we analyze Default's enginedefault here.

* frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp

routing_strategy Engine::getStrategyForStream(audio_stream_type_t stream)
{
    // stream to strategy mapping
    switch (stream) {
    case AUDIO_STREAM_VOICE_CALL:
    case AUDIO_STREAM_BLUETOOTH_SCO:
        return STRATEGY_PHONE;
    case AUDIO_STREAM_RING:
    case AUDIO_STREAM_ALARM:
        return STRATEGY_SONIFICATION;
    case AUDIO_STREAM_NOTIFICATION:
        return STRATEGY_SONIFICATION_RESPECTFUL;
    case AUDIO_STREAM_DTMF:
        return STRATEGY_DTMF;
    default:
        ALOGE("unknown stream type %d", stream);
    case AUDIO_STREAM_SYSTEM:
        // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs
        // while key clicks are played produces a poor result
    case AUDIO_STREAM_MUSIC:
        return STRATEGY_MEDIA;
    case AUDIO_STREAM_ENFORCED_AUDIBLE:
        return STRATEGY_ENFORCED_AUDIBLE;
    case AUDIO_STREAM_TTS:
        return STRATEGY_TRANSMITTED_THROUGH_SPEAKER;
    case AUDIO_STREAM_ACCESSIBILITY:
        return STRATEGY_ACCESSIBILITY;
    case AUDIO_STREAM_REROUTING:
        return STRATEGY_REROUTING;
    }
}

Definition of native Sound Flow:

* system/media/audio/include/system/audio-base.h

typedef enum {
    AUDIO_STREAM_DEFAULT = -1, // (-1)
    AUDIO_STREAM_MIN = 0,
    AUDIO_STREAM_VOICE_CALL = 0,
    AUDIO_STREAM_SYSTEM = 1,
    AUDIO_STREAM_RING = 2,
    AUDIO_STREAM_MUSIC = 3,
    AUDIO_STREAM_ALARM = 4,
    AUDIO_STREAM_NOTIFICATION = 5,
    AUDIO_STREAM_BLUETOOTH_SCO = 6,
    AUDIO_STREAM_ENFORCED_AUDIBLE = 7,
    AUDIO_STREAM_DTMF = 8,
    AUDIO_STREAM_TTS = 9,
    AUDIO_STREAM_ACCESSIBILITY = 10,
    AUDIO_STREAM_REROUTING = 11,
    AUDIO_STREAM_PATCH = 12,
    AUDIO_STREAM_PUBLIC_CNT = 11, // (ACCESSIBILITY + 1)
    AUDIO_STREAM_FOR_POLICY_CNT = 12, // PATCH
    AUDIO_STREAM_CNT = 13, // (PATCH + 1)
} audio_stream_type_t;

Policy Definition:

* frameworks/av/services/audiopolicy/common/include/RoutingStrategy.h

enum routing_strategy {
    STRATEGY_MEDIA,
    STRATEGY_PHONE,
    STRATEGY_SONIFICATION,
    STRATEGY_SONIFICATION_RESPECTFUL,
    STRATEGY_DTMF,
    STRATEGY_ENFORCED_AUDIBLE,
    STRATEGY_TRANSMITTED_THROUGH_SPEAKER,
    STRATEGY_ACCESSIBILITY,
    STRATEGY_REROUTING,
    NUM_STRATEGIES
};

The corresponding relations between them are as follows:

Flow code Flow type Strategy Mapping describe
0 STREAM_VOICE_CALL STRATEGY_PHONE Voice related audio
6 AUDIO_STREAM_BLUETOOTH_SCO STRATEGY_PHONE Bluetooth SCO Call Related Audio
2 AUDIO_STREAM_RING STRATEGY_SONIFICATION Ring tone
4 AUDIO_STREAM_ALARM STRATEGY_SONIFICATION alarm clock
5 AUDIO_STREAM_NOTIFICATION STRATEGY_SONIFICATION_RESPECTFUL Notification tone
8 AUDIO_STREAM_DTMF STRATEGY_DTMF DTMF tone
1 AUDIO_STREAM_SYSTEM STRATEGY_MEDIA System sound
3 AUDIO_STREAM_MUSIC STRATEGY_MEDIA Media voice
7 AUDIO_STREAM_ENFORCED_AUDIBLE STRATEGY_ENFORCED_AUDIBLE Force Speaker to speak
9 AUDIO_STREAM_TTS STRATEGY_TRANSMITTED_THROUGH_SPEAKER TTS broadcast
10 AUDIO_STREAM_ACCESSIBILITY STRATEGY_ACCESSIBILITY Auxiliary tone
11 AUDIO_STREAM_REROUTING STRATEGY_REROUTING Dynamic output mixing

AUDIO_STREAM_ENFORCED_AUDIBLE is a stream that does not silence users and forces Speaker to speak. For example, photographic sound, photographic sound must be Speaker voice, anti-camera.

Back to the getDevices ForStream function ~

Acquiring equipment

According to the flow type, after the policy is acquired, the related devices are acquired according to the policy.

* frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp

audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
                                                         bool fromCache)
{
    // Routing
    // see if we have an explicit route
    // scan the whole RouteMap, for each entry, convert the stream type to a strategy
    // (getStrategy(stream)).
    // if the strategy from the stream type in the RouteMap is the same as the argument above,
    // and activity count is non-zero and the device in the route descriptor is available
    // then select this device.
    for (size_t routeIndex = 0; routeIndex < mOutputRoutes.size(); routeIndex++) {
        sp<SessionRoute> route = mOutputRoutes.valueAt(routeIndex);
        routing_strategy routeStrategy = getStrategy(route->mStreamType);
        if ((routeStrategy == strategy) && route->isActiveOrChanged() &&
                (mAvailableOutputDevices.indexOf(route->mDeviceDescriptor) >= 0)) {
            return route->mDeviceDescriptor->type();
        }
    }

    if (fromCache) {
        ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x",
              strategy, mDeviceForStrategy[strategy]);
        return mDeviceForStrategy[strategy];
    }
    return mEngine->getDeviceForStrategy(strategy);
}

Finally, it is realized by mEngine. STRATEGY_TRANSMITTED_THROUGH_SPEAKER

* frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp

audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const
{
    DeviceVector availableOutputDevices = mApmObserver->getAvailableOutputDevices();
    DeviceVector availableInputDevices = mApmObserver->getAvailableInputDevices();

    const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();

    return getDeviceForStrategyInt(strategy, availableOutputDevices,
                                   availableInputDevices, outputs);
}

It is mainly implemented in getDeviceForStrategyInt function. The getDeviceForStrategyInt function is very long. There is no full code to paste here. The main decisions are as follows:

  1. Is it on the phone?
  2. What is the current flow out of Active state
  3. Is there ForceUse?

Let's look at the STRATEGY_SONIFICATION_RESPECTFUL, AUDIO_STREAM_NOTIFICATION scenario:

    case STRATEGY_SONIFICATION_RESPECTFUL:
        if (isInCall()) {
            device = getDeviceForStrategyInt(
                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
        } else if (outputs.isStreamActiveRemotely(AUDIO_STREAM_MUSIC,
                SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) {
            // while media is playing on a remote device, use the the sonification behavior.
            // Note that we test this usecase before testing if media is playing because
            //   the isStreamActive() method only informs about the activity of a stream, not
            //   if it's for local playback. Note also that we use the same delay between both tests
            device = getDeviceForStrategyInt(
                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
            //user "safe" speaker if available instead of normal speaker to avoid triggering
            //other acoustic safety mechanisms for notification
            if ((device & AUDIO_DEVICE_OUT_SPEAKER) &&
                    (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
                device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE;
                device &= ~AUDIO_DEVICE_OUT_SPEAKER;
            }
        } else if (outputs.isStreamActive(
                                AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
                    || outputs.isStreamActive(
                            AUDIO_STREAM_ACCESSIBILITY, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY))
        {
            // while media/a11y is playing (or has recently played), use the same device
            device = getDeviceForStrategyInt(
                    STRATEGY_MEDIA, availableOutputDevices, availableInputDevices, outputs);
        } else {
            // when media is not playing anymore, fall back on the sonification behavior
            device = getDeviceForStrategyInt(
                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
            //user "safe" speaker if available instead of normal speaker to avoid triggering
            //other acoustic safety mechanisms for notification
            if ((device & AUDIO_DEVICE_OUT_SPEAKER) &&
                    (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
                device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE;
                device &= ~AUDIO_DEVICE_OUT_SPEAKER;
            }
        }
        break;
  1. Convert to STRATEGY_SONIFICATION in case of a call
  2. If Remote Music Active, convert to STRATEGY_SONIFICATION, but if SPEAKER_SAFE is supported, SPEAKER_SAFE is preferred.
  3. If AUDIO_STREAM_MUSIC or AUDIO_STREAM_ACCESSIBILITY, convert to STRATEGY_MEDIA
  4. In other cases, STRATEGY_SONIFICATION is used, but if SPEAKER_SAFE is supported, SPEAKER_SAFE is preferred.

Specific circumstances, in particular, are more complex. Anyway, so far, our device has been acquired.

Native sets volume

Volume is set to native through Audio System's setStream VolumeIndex function:

        public void applyDeviceVolume_syncVSS(int device) {
            int index;
            if (mIsMuted) {
                index = 0;
            } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported) {
                index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
            } else if ((device & mFullVolumeDevices) != 0) {
                index = (mIndexMax + 5)/10;
            } else {
                index = (getIndex(device) + 5)/10;
            }
            AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
        }

The intermediate process is omitted. We implement AudioPolicy Manager directly:

* frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp

status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
                                                  int index,
                                                  audio_devices_t device)
{

    ... ...

    // Force max volume if stream cannot be muted
    if (!mVolumeCurves->canBeMuted(stream)) index = mVolumeCurves->getVolumeIndexMax(stream);

    ALOGV("setStreamVolumeIndex() stream %d, device %08x, index %d",
          stream, device, index);

    // update other private stream volumes which follow this one
    for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
        if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
            continue;
        }
        mVolumeCurves->addCurrentVolumeIndex((audio_stream_type_t)curStream, device, index);
    }

    status_t status = NO_ERROR;
    for (size_t i = 0; i < mOutputs.size(); i++) {
        sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
        audio_devices_t curDevice = Volume::getDeviceForVolume(desc->device());
        for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
            if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
                continue;
            }
            if (!(desc->isStreamActive((audio_stream_type_t)curStream) ||
                    (isInCall() && (curStream == AUDIO_STREAM_VOICE_CALL)))) {
                continue;
            }
            routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);
            audio_devices_t curStreamDevice = Volume::getDeviceForVolume(getDeviceForStrategy(
                    curStrategy, false /*fromCache*/));
            if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) &&
                    ((curStreamDevice & device) == 0)) {
                continue;
            }
            bool applyVolume;
            if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
                curStreamDevice |= device;
                applyVolume = (curDevice & curStreamDevice) != 0;
            } else {
                applyVolume = !mVolumeCurves->hasVolumeIndexForDevice(
                        stream, curStreamDevice);
            }

            if (applyVolume) {
                //FIXME: workaround for truncated touch sounds
                // delayed volume change for system stream to be removed when the problem is
                // handled by system UI
                status_t volStatus =
                        checkAndSetVolume((audio_stream_type_t)curStream, index, desc, curDevice,
                            (stream == AUDIO_STREAM_SYSTEM) ? TOUCH_SOUND_FIXED_DELAY_MS : 0);
                if (volStatus != NO_ERROR) {
                    status = volStatus;
                }
            }
        }
    }
    return status;
}
  • mVolumeCurves, the description of streams, is a collection, represented by Stream Descriptor Collection. Specific flows are described by Stream Descriptor. The previous flows are described by an int type, and now Stream Descriptor is used to describe them.
frameworks/av/services/audiopolicy/common/managerdefinitions/src/StreamDescriptor.cpp
  • The index value of Volume is saved in Stream Descriptor by addCurrent Volume Index:
* frameworks/av/services/audiopolicy/common/managerdefinitions/src/StreamDescriptor.cpp

void StreamDescriptor::addCurrentVolumeIndex(audio_devices_t device, int index)
{
    mIndexCur.add(device, index);
}

Volume is saved in mIndexCur according to device:

KeyedVector<audio_devices_t, int> mIndexCur;
  • mOutputs Current Output Devices
    Each output device is described in SwAudio Output Descriptor:
frameworks/av/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
  • Get the equipment, confirm again
Volume::getDeviceForVolume

    static audio_devices_t getDeviceForVolume(audio_devices_t device)
    {
        if (device == AUDIO_DEVICE_NONE) {
            device =  AUDIO_DEVICE_OUT_SPEAKER;
        } else if (popcount(device) > 1) {
            if (device & AUDIO_DEVICE_OUT_SPEAKER) {
                device = AUDIO_DEVICE_OUT_SPEAKER;
            } else if (device & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
                device = AUDIO_DEVICE_OUT_SPEAKER_SAFE;
            } else if (device & AUDIO_DEVICE_OUT_HDMI_ARC) {
                device = AUDIO_DEVICE_OUT_HDMI_ARC;
            } else if (device & AUDIO_DEVICE_OUT_AUX_LINE) {
                device = AUDIO_DEVICE_OUT_AUX_LINE;
            } else if (device & AUDIO_DEVICE_OUT_SPDIF) {
                device = AUDIO_DEVICE_OUT_SPDIF;
            } else {
                device = (audio_devices_t)(device & AUDIO_DEVICE_OUT_ALL_A2DP);
            }
        }

        /*SPEAKER_SAFE is an alias of SPEAKER for purposes of volume control*/
        if (device == AUDIO_DEVICE_OUT_SPEAKER_SAFE)
            device = AUDIO_DEVICE_OUT_SPEAKER;

        return device;
    }
  • According to the flow acquisition strategy, the device is acquired according to the test, and then the volume is judged according to the device whether the volume needs to be applied. The volume is validated by checkAndSetVolume.

If no stream is currently active, the volume settings are not valid, depending on the previous applyVolume. If it does not take effect at this time, AndSetVolume will also be checked at startSource later.

* frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp

status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,
                                                   int index,
                                                   const sp<AudioOutputDescriptor>& outputDesc,
                                                   audio_devices_t device,
                                                   int delayMs,
                                                   bool force)
{
    // Quiet, no volume changes
    if (outputDesc->mMuteCount[stream] != 0) {
        ALOGVV("checkAndSetVolume() stream %d muted count %d",
              stream, outputDesc->mMuteCount[stream]);
        return NO_ERROR;
    }
    audio_policy_forced_cfg_t forceUseForComm =
            mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION);
    // If it's a Bluetooth call, don't change the volume value, and vice versa
    if ((stream == AUDIO_STREAM_VOICE_CALL && forceUseForComm == AUDIO_POLICY_FORCE_BT_SCO) ||
        (stream == AUDIO_STREAM_BLUETOOTH_SCO && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO)) {
        ALOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm",
             stream, forceUseForComm);
        return INVALID_OPERATION;
    }

    if (device == AUDIO_DEVICE_NONE) {
        device = outputDesc->device();
    }

    // The key code computeVolume calculates the volume value and converts index value to Db value.
    float volumeDb = computeVolume(stream, index, device);
    if (outputDesc->isFixedVolume(device)) {
        volumeDb = 0.0f;
    }

    // set volume
    outputDesc->setVolume(volumeDb, stream, device, delayMs, force);

    // Call volume, SCO set to maximum, headset managed by itself.
    if (stream == AUDIO_STREAM_VOICE_CALL ||
        stream == AUDIO_STREAM_BLUETOOTH_SCO) {
        float voiceVolume;
        if (stream == AUDIO_STREAM_VOICE_CALL) {
            voiceVolume = (float)index/(float)mVolumeCurves->getVolumeIndexMax(stream);
        } else {
            voiceVolume = 1.0;
        }

        if (voiceVolume != mLastVoiceVolume) {
            mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
            mLastVoiceVolume = voiceVolume;
        }
    }

    return NO_ERROR;
}

Calculate volume Db value

* frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp

float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
                                        int index,
                                        audio_devices_t device)
{
    float volumeDB = mVolumeCurves->volIndexToDb(stream, Volume::getDeviceCategory(device), index);

    // Handle scenes where auxiliary functions are turned on and the bell rings
    if ((stream == AUDIO_STREAM_ACCESSIBILITY)
            && (AUDIO_MODE_RINGTONE == mEngine->getPhoneState())
            && isStreamActive(AUDIO_STREAM_RING, 0)) {
        const float ringVolumeDB = computeVolume(AUDIO_STREAM_RING, index, device);
        return ringVolumeDB - 4 > volumeDB ? ringVolumeDB - 4 : volumeDB;
    }

    // Scene in the Call
    if ((stream != AUDIO_STREAM_VOICE_CALL) && (device & AUDIO_DEVICE_OUT_EARPIECE) && isInCall()) {
        switch (stream) {
        case AUDIO_STREAM_SYSTEM:
        case AUDIO_STREAM_RING:
        case AUDIO_STREAM_MUSIC:
        case AUDIO_STREAM_ALARM:
        case AUDIO_STREAM_NOTIFICATION:
        case AUDIO_STREAM_ENFORCED_AUDIBLE:
        case AUDIO_STREAM_DTMF:
        case AUDIO_STREAM_ACCESSIBILITY: {
            const float maxVoiceVolDb = computeVolume(AUDIO_STREAM_VOICE_CALL, index, device)
                    + IN_CALL_EARPIECE_HEADROOM_DB;
            if (volumeDB > maxVoiceVolDb) {
                ALOGV("computeVolume() stream %d at vol=%f overriden by stream %d at vol=%f",
                        stream, volumeDB, AUDIO_STREAM_VOICE_CALL, maxVoiceVolDb);
                volumeDB = maxVoiceVolDb;
            }
            } break;
        default:
            break;
        }
    }

    // The scene of inserting headphones to prevent loud ringing and other sounds
    const routing_strategy stream_strategy = getStrategy(stream);
    if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
            AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
            AUDIO_DEVICE_OUT_WIRED_HEADSET |
            AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
            AUDIO_DEVICE_OUT_USB_HEADSET)) &&
        ((stream_strategy == STRATEGY_SONIFICATION)
                || (stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL)
                || (stream == AUDIO_STREAM_SYSTEM)
                || ((stream_strategy == STRATEGY_ENFORCED_AUDIBLE) &&
                    (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) &&
            mVolumeCurves->canBeMuted(stream)) {
        // when the phone is ringing we must consider that music could have been paused just before
        // by the music application and behave as if music was active if the last music track was
        // just stopped
        if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY) ||
                mLimitRingtoneVolume) {
            volumeDB += SONIFICATION_HEADSET_VOLUME_FACTOR_DB;
            audio_devices_t musicDevice = getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/);
            float musicVolDB = computeVolume(AUDIO_STREAM_MUSIC,
                                             mVolumeCurves->getVolumeIndex(AUDIO_STREAM_MUSIC,
                                                                              musicDevice),
                                             musicDevice);
            float minVolDB = (musicVolDB > SONIFICATION_HEADSET_VOLUME_MIN_DB) ?
                    musicVolDB : SONIFICATION_HEADSET_VOLUME_MIN_DB;
            if (volumeDB > minVolDB) {
                volumeDB = minVolDB;
                ALOGV("computeVolume limiting volume to %f musicVol %f", minVolDB, musicVolDB);
            }
            if (device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
                    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES)) {
                // on A2DP, also ensure notification volume is not too low compared to media when
                // intended to be played
                if ((volumeDB > -96.0f) &&
                        (musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB > volumeDB)) {
                    ALOGV("computeVolume increasing volume for stream=%d device=0x%X from %f to %f",
                            stream, device,
                            volumeDB, musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB);
                    volumeDB = musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB;
                }
            }
        } else if ((Volume::getDeviceForVolume(device) != AUDIO_DEVICE_OUT_SPEAKER) ||
                stream_strategy != STRATEGY_SONIFICATION) {
            volumeDB += SONIFICATION_HEADSET_VOLUME_FACTOR_DB;
        }
    }

    return volumeDB;
}

The Db value is converted through mVolumeCurves - > volIndexToDb, and then adjusted according to the actual scene.

Let's take a look at Volume::getDeviceCategory, Audio's side of the mess, all kinds of concepts. device_category classifies devices.

* frameworks/av/services/audiopolicy/common/include/Volume.h

    static device_category getDeviceCategory(audio_devices_t device)
    {
        switch(getDeviceForVolume(device)) {
        case AUDIO_DEVICE_OUT_EARPIECE:
            return DEVICE_CATEGORY_EARPIECE;
        case AUDIO_DEVICE_OUT_WIRED_HEADSET:
        case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
        case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
        case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
        case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
        case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
        case AUDIO_DEVICE_OUT_USB_HEADSET:
            return DEVICE_CATEGORY_HEADSET;
        case AUDIO_DEVICE_OUT_LINE:
        case AUDIO_DEVICE_OUT_AUX_DIGITAL:
        case AUDIO_DEVICE_OUT_USB_DEVICE:
            return DEVICE_CATEGORY_EXT_MEDIA;
        case AUDIO_DEVICE_OUT_SPEAKER:
        case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
        case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
        case AUDIO_DEVICE_OUT_USB_ACCESSORY:
        case AUDIO_DEVICE_OUT_REMOTE_SUBMIX:
        default:
            return DEVICE_CATEGORY_SPEAKER;
        }
    }

The volIndexToDb function is as follows:

float StreamDescriptorCollection::volIndexToDb(audio_stream_type_t stream, device_category category,
                                               int indexInUi) const
{
    const StreamDescriptor &streamDesc = valueAt(stream);
    return Gains::volIndexToDb(streamDesc.getVolumeCurvePoint(category),
                               streamDesc.getVolumeIndexMin(), streamDesc.getVolumeIndexMax(),
                               indexInUi);
}
float Gains::volIndexToDb(const VolumeCurvePoint *curve, int indexMin, int indexMax, int indexInUi)
{
    // the volume index in the UI is relative to the min and max volume indices for this stream type
    int nbSteps = 1 + curve[Volume::VOLMAX].mIndex - curve[Volume::VOLMIN].mIndex;
    int volIdx = (nbSteps * (indexInUi - indexMin)) / (indexMax - indexMin);

    // find what part of the curve this index volume belongs to, or if it's out of bounds
    int segment = 0;
    if (volIdx < curve[Volume::VOLMIN].mIndex) {         // out of bounds
        return VOLUME_MIN_DB;
    } else if (volIdx < curve[Volume::VOLKNEE1].mIndex) {
        segment = 0;
    } else if (volIdx < curve[Volume::VOLKNEE2].mIndex) {
        segment = 1;
    } else if (volIdx <= curve[Volume::VOLMAX].mIndex) {
        segment = 2;
    } else {                                                               // out of bounds
        return 0.0f;
    }

    // linear interpolation in the attenuation table in dB
    float decibels = curve[segment].mDBAttenuation +
            ((float)(volIdx - curve[segment].mIndex)) *
                ( (curve[segment+1].mDBAttenuation -
                        curve[segment].mDBAttenuation) /
                    ((float)(curve[segment+1].mIndex -
                            curve[segment].mIndex)) );

    ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]",
            curve[segment].mIndex, volIdx,
            curve[segment+1].mIndex,
            curve[segment].mDBAttenuation,
            decibels,
            curve[segment+1].mDBAttenuation);

    return decibels;
}

The Db values are all configured in audio_policy_volumes.xml. AOSP

frameworks/av/services/audiopolicy/config/audio_policy_volumes.xml

Installation streams and device classifications to distinguish, for example, the volume of a headset call:

    <volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_HEADSET">
        <point>0,-4200</point>
        <point>33,-2800</point>
        <point>66,-1400</point>
        <point>100,0</point>
    </volume>

In addition to directly defining values, you can also refer to other things, such as the media volume of headphones, DEFAULT_MEDIA_VOLUME_CURVE.

    <volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_HEADSET"
                                        ref="DEFAULT_MEDIA_VOLUME_CURVE"/>

DEFAULT_MEDIA_VOLUME_CURVE is given in another table:

* frameworks/av/services/audiopolicy/config/default_volume_tables.xml

    <reference name="DEFAULT_MEDIA_VOLUME_CURVE">
    <!-- Default Media reference Volume Curve -->
        <point>1,-5800</point>
        <point>20,-4000</point>
        <point>60,-1700</point>
        <point>100,0</point>
    </reference>

So when we adjust the volume, we change it to the corresponding DB according to the volume index, and the final effect is db. Many users feedback that the volume above is very high, but the actual voice is still low, maybe adjust the DB configuration here.

Volume effective

The calculated Db value is set to output through the setVolume function

outputDesc->setVolume(volumeDb, stream, device, delayMs, force);

The functions are as follows:

bool SwAudioOutputDescriptor::setVolume(float volume,
                                        audio_stream_type_t stream,
                                        audio_devices_t device,
                                        uint32_t delayMs,
                                        bool force)
{
    bool changed = AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force);

    if (changed) {
        // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
        // enabled
        float volume = Volume::DbToAmpl(mCurVolume[stream]);
        if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
            mClientInterface->setStreamVolume(
                    AUDIO_STREAM_VOICE_CALL, volume, mIoHandle, delayMs);
        }
        mClientInterface->setStreamVolume(stream, volume, mIoHandle, delayMs);
    }
    return changed;
}
  • Save the volume value in Descriptor first
bool AudioOutputDescriptor::setVolume(float volume,
                                      audio_stream_type_t stream,
                                      audio_devices_t device __unused,
                                      uint32_t delayMs,
                                      bool force)
{
    // We actually change the volume if:
    // - the float value returned by computeVolume() changed
    // - the force flag is set
    if (volume != mCurVolume[stream] || force) {
        ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volume, delayMs);
        mCurVolume[stream] = volume;
        return true;
    }
    return false;
}

Each output is an mCurVolume[stream] saved by the installation stream type

  • SetStream Volume Interface Through mClientInterface
* frameworks/av/services/audiopolicy/service/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);
}

Audio Policy Service is passed to Audio Manager through Audio Command Thread

* frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp

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

The corresponding command: SET_VOLUME

bool AudioPolicyService::AudioCommandThread::threadLoop()
{
    nsecs_t waitTime = -1;

    mLock.lock();
    while (!exitPending())
    {
        sp<AudioPolicyService> svc;
        while (!mAudioCommands.isEmpty() && !exitPending()) {
... ...
                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);
                    }break;

Ultimately through the Audio System interface to complete:

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's setStream Volume function is as follows:

status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
        audio_io_handle_t output)
{
    ALOGI("setStreamVolume: stream %d, value %f, output %d", stream, value, output);
    // Jurisdiction
    if (!settingsAllowed()) {
        return PERMISSION_DENIED;
    }

    // Flow type
    status_t status = checkStreamType(stream);
    if (status != NO_ERROR) {
        return status;
    }
    ALOG_ASSERT(stream != AUDIO_STREAM_PATCH, "attempt to change AUDIO_STREAM_PATCH volume");

    AutoMutex lock(mLock);
    Vector<VolumeInterface *> volumeInterfaces;
    //Get volume interface
    if (output != AUDIO_IO_HANDLE_NONE) {
        VolumeInterface *volumeInterface = getVolumeInterface_l(output);
        if (volumeInterface == NULL) {
            return BAD_VALUE;
        }
        volumeInterfaces.add(volumeInterface);
    }

    // Save volume Db value
    mStreamTypes[stream].volume = value;

    if (volumeInterfaces.size() == 0) {
        volumeInterfaces = getAllVolumeInterfaces_l();
    }
    // Setting Volume Value
    for (size_t i = 0; i < volumeInterfaces.size(); i++) {
        volumeInterfaces[i]->setStreamVolume(stream, value);
    }

    return NO_ERROR;
}

Volume Interface is mainly used to set the volume. Playback Thread and Map Playback Thread implement the specific interface of Volume Interface:

class VolumeInterface {
 public:

    virtual ~VolumeInterface() {}

    virtual void        setMasterVolume(float value) = 0;
    virtual void        setMasterMute(bool muted) = 0;
    virtual void        setStreamVolume(audio_stream_type_t stream, float value) = 0;
    virtual void        setStreamMute(audio_stream_type_t stream, bool muted) = 0;
    virtual float       streamVolume(audio_stream_type_t stream) const = 0;

};

Let's look at PlaybackThread's setStream Volume function:

void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{
    Mutex::Autolock _l(mLock);
    size_t size = mEffectChains.size();
    mStreamTypes[stream].volume = value;
    for (size_t i = 0; i < size; i++) {
        mEffectChains[i]->setStreamVolume_l(stream, value);
    }
    broadcast_l();
}

PlaybackThread saves the volume value and sets it to the sound effect.

OK, so far, it's all about setting the volume, so where does it work?

For MixerThread, it takes effect here, and the volume value is eventually mixed into the data stream.

AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
        Vector< sp<Track> > *tracksToRemove)
{
... ...
            // compute volume for this track
            uint32_t vl, vr;       // in U8.24 integer format
            float vlf, vrf, vaf;   // in [0.0, 1.0] float format
            // read original volumes with volume control
            float typeVolume = mStreamTypes[track->streamType()].volume;
            float v = masterVolume * typeVolume;

            if (track->isPausing() || mStreamTypes[track->streamType()].mute) {
                vl = vr = 0;
                vlf = vrf = vaf = 0.;
                if (track->isPausing()) {
                    track->setPaused();
                }
            } else {
                sp<AudioTrackServerProxy> proxy = track->mAudioTrackServerProxy;
                gain_minifloat_packed_t vlr = proxy->getVolumeLR();
                vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
                vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
                // track volumes come from shared memory, so can't be trusted and must be clamped
                if (vlf > GAIN_FLOAT_UNITY) {
                    ALOGV("Track left volume out of range: %.3g", vlf);
                    vlf = GAIN_FLOAT_UNITY;
                }
                if (vrf > GAIN_FLOAT_UNITY) {
                    ALOGV("Track right volume out of range: %.3g", vrf);
                    vrf = GAIN_FLOAT_UNITY;
                }
                const float vh = track->getVolumeHandler()->getVolume(
                        track->mAudioTrackServerProxy->framesReleased()).first;
                // now apply the master volume and stream type volume and shaper volume
                vlf *= v * vh;
                vrf *= v * vh;
                // assuming master volume and stream type volume each go up to 1.0,
                // then derive vl and vr as U8.24 versions for the effect chain
                const float scaleto8_24 = MAX_GAIN_INT * MAX_GAIN_INT;
                vl = (uint32_t) (scaleto8_24 * vlf);
                vr = (uint32_t) (scaleto8_24 * vrf);
                // vl and vr are now in U8.24 format
                uint16_t sendLevel = proxy->getSendLevel_U4_12();
                // send level comes from shared memory and so may be corrupt
                if (sendLevel > MAX_GAIN_INT) {
                    ALOGV("Track send level out of range: %04X", sendLevel);
                    sendLevel = MAX_GAIN_INT;
                }
                // vaf is represented as [0.0, 1.0] float by rescaling sendLevel
                vaf = v * sendLevel * (1. / MAX_GAIN_INT);
            }

We can dump the volume of each stream of thread s through the adb command:

$ adb shell dumpsys media.audio_flinger | grep "Stream volumes in dB"
  Stream volumes in dB: 0:-24, 1:-27, 2:-25, 3:-52, 4:0, 5:-27, 6:0, 7:-6, 8:-27, 9:0, 10:-47, 11:0, 12:0
  Stream volumes in dB: 0:-24, 1:-27, 2:-25, 3:-52, 4:0, 5:-27, 6:0, 7:-6, 8:-27, 9:0, 10:-47, 11:0, 12:0
  Stream volumes in dB: 0:-24, 1:-27, 2:-25, 3:-52, 4:0, 5:-27, 6:0, 7:-6, 8:-27, 9:0, 10:-47, 11:0, 12:0

Topics: xml Java Android shell