diff options
Diffstat (limited to 'services/audioflinger/AudioPolicyManagerBase.cpp')
| -rw-r--r-- | services/audioflinger/AudioPolicyManagerBase.cpp | 2127 | 
1 files changed, 2127 insertions, 0 deletions
| diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp new file mode 100644 index 000000000000..425ca315c455 --- /dev/null +++ b/services/audioflinger/AudioPolicyManagerBase.cpp @@ -0,0 +1,2127 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AudioPolicyManagerBase" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> +#include <hardware_legacy/AudioPolicyManagerBase.h> +#include <media/mediarecorder.h> + +namespace android { + + +// ---------------------------------------------------------------------------- +// AudioPolicyInterface implementation +// ---------------------------------------------------------------------------- + + +status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_devices device, +                                                  AudioSystem::device_connection_state state, +                                                  const char *device_address) +{ + +    LOGV("setDeviceConnectionState() device: %x, state %d, address %s", device, state, device_address); + +    // connect/disconnect only 1 device at a time +    if (AudioSystem::popCount(device) != 1) return BAD_VALUE; + +    if (strlen(device_address) >= MAX_DEVICE_ADDRESS_LEN) { +        LOGE("setDeviceConnectionState() invalid address: %s", device_address); +        return BAD_VALUE; +    } + +    // handle output devices +    if (AudioSystem::isOutputDevice(device)) { + +#ifndef WITH_A2DP +        if (AudioSystem::isA2dpDevice(device)) { +            LOGE("setDeviceConnectionState() invalid device: %x", device); +            return BAD_VALUE; +        } +#endif + +        switch (state) +        { +        // handle output device connection +        case AudioSystem::DEVICE_STATE_AVAILABLE: +            if (mAvailableOutputDevices & device) { +                LOGW("setDeviceConnectionState() device already connected: %x", device); +                return INVALID_OPERATION; +            } +            LOGV("setDeviceConnectionState() connecting device %x", device); + +            // register new device as available +            mAvailableOutputDevices |= device; + +#ifdef WITH_A2DP +            // handle A2DP device connection +            if (AudioSystem::isA2dpDevice(device)) { +                status_t status = handleA2dpConnection(device, device_address); +                if (status != NO_ERROR) { +                    mAvailableOutputDevices &= ~device; +                    return status; +                } +            } else +#endif +            { +                if (AudioSystem::isBluetoothScoDevice(device)) { +                    LOGV("setDeviceConnectionState() BT SCO  device, address %s", device_address); +                    // keep track of SCO device address +                    mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN); +#ifdef WITH_A2DP +                    if (mA2dpOutput != 0 && +                        mPhoneState != AudioSystem::MODE_NORMAL) { +                        mpClientInterface->suspendOutput(mA2dpOutput); +                    } +#endif +                } +            } +            break; +        // handle output device disconnection +        case AudioSystem::DEVICE_STATE_UNAVAILABLE: { +            if (!(mAvailableOutputDevices & device)) { +                LOGW("setDeviceConnectionState() device not connected: %x", device); +                return INVALID_OPERATION; +            } + + +            LOGV("setDeviceConnectionState() disconnecting device %x", device); +            // remove device from available output devices +            mAvailableOutputDevices &= ~device; + +#ifdef WITH_A2DP +            // handle A2DP device disconnection +            if (AudioSystem::isA2dpDevice(device)) { +                status_t status = handleA2dpDisconnection(device, device_address); +                if (status != NO_ERROR) { +                    mAvailableOutputDevices |= device; +                    return status; +                } +            } else +#endif +            { +                if (AudioSystem::isBluetoothScoDevice(device)) { +                    mScoDeviceAddress = ""; +#ifdef WITH_A2DP +                    if (mA2dpOutput != 0 && +                        mPhoneState != AudioSystem::MODE_NORMAL) { +                        mpClientInterface->restoreOutput(mA2dpOutput); +                    } +#endif +                } +            } +            } break; + +        default: +            LOGE("setDeviceConnectionState() invalid state: %x", state); +            return BAD_VALUE; +        } + +        // request routing change if necessary +        uint32_t newDevice = getNewDevice(mHardwareOutput, false); +#ifdef WITH_A2DP +        checkOutputForAllStrategies(); +        // A2DP outputs must be closed after checkOutputForAllStrategies() is executed +        if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) { +            closeA2dpOutputs(); +        } +#endif +        updateDeviceForStrategy(); +        setOutputDevice(mHardwareOutput, newDevice); + +        if (device == AudioSystem::DEVICE_OUT_WIRED_HEADSET) { +            device = AudioSystem::DEVICE_IN_WIRED_HEADSET; +        } else if (device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO || +                   device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET || +                   device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT) { +            device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET; +        } else { +            return NO_ERROR; +        } +    } +    // handle input devices +    if (AudioSystem::isInputDevice(device)) { + +        switch (state) +        { +        // handle input device connection +        case AudioSystem::DEVICE_STATE_AVAILABLE: { +            if (mAvailableInputDevices & device) { +                LOGW("setDeviceConnectionState() device already connected: %d", device); +                return INVALID_OPERATION; +            } +            mAvailableInputDevices |= device; +            } +            break; + +        // handle input device disconnection +        case AudioSystem::DEVICE_STATE_UNAVAILABLE: { +            if (!(mAvailableInputDevices & device)) { +                LOGW("setDeviceConnectionState() device not connected: %d", device); +                return INVALID_OPERATION; +            } +            mAvailableInputDevices &= ~device; +            } break; + +        default: +            LOGE("setDeviceConnectionState() invalid state: %x", state); +            return BAD_VALUE; +        } + +        audio_io_handle_t activeInput = getActiveInput(); +        if (activeInput != 0) { +            AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput); +            uint32_t newDevice = getDeviceForInputSource(inputDesc->mInputSource); +            if (newDevice != inputDesc->mDevice) { +                LOGV("setDeviceConnectionState() changing device from %x to %x for input %d", +                        inputDesc->mDevice, newDevice, activeInput); +                inputDesc->mDevice = newDevice; +                AudioParameter param = AudioParameter(); +                param.addInt(String8(AudioParameter::keyRouting), (int)newDevice); +                mpClientInterface->setParameters(activeInput, param.toString()); +            } +        } + +        return NO_ERROR; +    } + +    LOGW("setDeviceConnectionState() invalid device: %x", device); +    return BAD_VALUE; +} + +AudioSystem::device_connection_state AudioPolicyManagerBase::getDeviceConnectionState(AudioSystem::audio_devices device, +                                                  const char *device_address) +{ +    AudioSystem::device_connection_state state = AudioSystem::DEVICE_STATE_UNAVAILABLE; +    String8 address = String8(device_address); +    if (AudioSystem::isOutputDevice(device)) { +        if (device & mAvailableOutputDevices) { +#ifdef WITH_A2DP +            if (AudioSystem::isA2dpDevice(device) && +                address != "" && mA2dpDeviceAddress != address) { +                return state; +            } +#endif +            if (AudioSystem::isBluetoothScoDevice(device) && +                address != "" && mScoDeviceAddress != address) { +                return state; +            } +            state = AudioSystem::DEVICE_STATE_AVAILABLE; +        } +    } else if (AudioSystem::isInputDevice(device)) { +        if (device & mAvailableInputDevices) { +            state = AudioSystem::DEVICE_STATE_AVAILABLE; +        } +    } + +    return state; +} + +void AudioPolicyManagerBase::setPhoneState(int state) +{ +    LOGV("setPhoneState() state %d", state); +    uint32_t newDevice = 0; +    if (state < 0 || state >= AudioSystem::NUM_MODES) { +        LOGW("setPhoneState() invalid state %d", state); +        return; +    } + +    if (state == mPhoneState ) { +        LOGW("setPhoneState() setting same state %d", state); +        return; +    } + +    // if leaving call state, handle special case of active streams +    // pertaining to sonification strategy see handleIncallSonification() +    if (mPhoneState == AudioSystem::MODE_IN_CALL) { +        LOGV("setPhoneState() in call state management: new state is %d", state); +        for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { +            handleIncallSonification(stream, false, true); +        } +    } + +    // store previous phone state for management of sonification strategy below +    int oldState = mPhoneState; +    mPhoneState = state; +    bool force = false; + +    // are we entering or starting a call +    if ((oldState != AudioSystem::MODE_IN_CALL) && (state == AudioSystem::MODE_IN_CALL)) { +        LOGV("  Entering call in setPhoneState()"); +        // force routing command to audio hardware when starting a call +        // even if no device change is needed +        force = true; +    } else if ((oldState == AudioSystem::MODE_IN_CALL) && (state != AudioSystem::MODE_IN_CALL)) { +        LOGV("  Exiting call in setPhoneState()"); +        // force routing command to audio hardware when exiting a call +        // even if no device change is needed +        force = true; +    } + +    // check for device and output changes triggered by new phone state +    newDevice = getNewDevice(mHardwareOutput, false); +#ifdef WITH_A2DP +    checkOutputForAllStrategies(); +    // suspend A2DP output if a SCO device is present. +    if (mA2dpOutput != 0 && mScoDeviceAddress != "") { +        if (oldState == AudioSystem::MODE_NORMAL) { +            mpClientInterface->suspendOutput(mA2dpOutput); +        } else if (state == AudioSystem::MODE_NORMAL) { +            mpClientInterface->restoreOutput(mA2dpOutput); +        } +    } +#endif +    updateDeviceForStrategy(); + +    AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput); + +    // force routing command to audio hardware when ending call +    // even if no device change is needed +    if (oldState == AudioSystem::MODE_IN_CALL && newDevice == 0) { +        newDevice = hwOutputDesc->device(); +    } + +    // when changing from ring tone to in call mode, mute the ringing tone +    // immediately and delay the route change to avoid sending the ring tone +    // tail into the earpiece or headset. +    int delayMs = 0; +    if (state == AudioSystem::MODE_IN_CALL && oldState == AudioSystem::MODE_RINGTONE) { +        // delay the device change command by twice the output latency to have some margin +        // and be sure that audio buffers not yet affected by the mute are out when +        // we actually apply the route change +        delayMs = hwOutputDesc->mLatency*2; +        setStreamMute(AudioSystem::RING, true, mHardwareOutput); +    } + +    // change routing is necessary +    setOutputDevice(mHardwareOutput, newDevice, force, delayMs); + +    // if entering in call state, handle special case of active streams +    // pertaining to sonification strategy see handleIncallSonification() +    if (state == AudioSystem::MODE_IN_CALL) { +        LOGV("setPhoneState() in call state management: new state is %d", state); +        // unmute the ringing tone after a sufficient delay if it was muted before +        // setting output device above +        if (oldState == AudioSystem::MODE_RINGTONE) { +            setStreamMute(AudioSystem::RING, false, mHardwareOutput, MUTE_TIME_MS); +        } +        for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { +            handleIncallSonification(stream, true, true); +        } +    } + +    // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE +    if (state == AudioSystem::MODE_RINGTONE && +        (hwOutputDesc->mRefCount[AudioSystem::MUSIC] || +        (systemTime() - mMusicStopTime) < seconds(SONIFICATION_HEADSET_MUSIC_DELAY))) { +        mLimitRingtoneVolume = true; +    } else { +        mLimitRingtoneVolume = false; +    } +} + +void AudioPolicyManagerBase::setRingerMode(uint32_t mode, uint32_t mask) +{ +    LOGV("setRingerMode() mode %x, mask %x", mode, mask); + +    mRingerMode = mode; +} + +void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config) +{ +    LOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState); + +    bool forceVolumeReeval = false; +    switch(usage) { +    case AudioSystem::FOR_COMMUNICATION: +        if (config != AudioSystem::FORCE_SPEAKER && config != AudioSystem::FORCE_BT_SCO && +            config != AudioSystem::FORCE_NONE) { +            LOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config); +            return; +        } +        mForceUse[usage] = config; +        break; +    case AudioSystem::FOR_MEDIA: +        if (config != AudioSystem::FORCE_HEADPHONES && config != AudioSystem::FORCE_BT_A2DP && +            config != AudioSystem::FORCE_WIRED_ACCESSORY && config != AudioSystem::FORCE_NONE) { +            LOGW("setForceUse() invalid config %d for FOR_MEDIA", config); +            return; +        } +        mForceUse[usage] = config; +        break; +    case AudioSystem::FOR_RECORD: +        if (config != AudioSystem::FORCE_BT_SCO && config != AudioSystem::FORCE_WIRED_ACCESSORY && +            config != AudioSystem::FORCE_NONE) { +            LOGW("setForceUse() invalid config %d for FOR_RECORD", config); +            return; +        } +        mForceUse[usage] = config; +        break; +    case AudioSystem::FOR_DOCK: +        if (config != AudioSystem::FORCE_NONE && config != AudioSystem::FORCE_BT_CAR_DOCK && +            config != AudioSystem::FORCE_BT_DESK_DOCK && config != AudioSystem::FORCE_WIRED_ACCESSORY) { +            LOGW("setForceUse() invalid config %d for FOR_DOCK", config); +        } +        forceVolumeReeval = true; +        mForceUse[usage] = config; +        break; +    default: +        LOGW("setForceUse() invalid usage %d", usage); +        break; +    } + +    // check for device and output changes triggered by new phone state +    uint32_t newDevice = getNewDevice(mHardwareOutput, false); +#ifdef WITH_A2DP +    checkOutputForAllStrategies(); +#endif +    updateDeviceForStrategy(); +    setOutputDevice(mHardwareOutput, newDevice); +    if (forceVolumeReeval) { +        applyStreamVolumes(mHardwareOutput, newDevice); +    } + +    audio_io_handle_t activeInput = getActiveInput(); +    if (activeInput != 0) { +        AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput); +        newDevice = getDeviceForInputSource(inputDesc->mInputSource); +        if (newDevice != inputDesc->mDevice) { +            LOGV("setForceUse() changing device from %x to %x for input %d", +                    inputDesc->mDevice, newDevice, activeInput); +            inputDesc->mDevice = newDevice; +            AudioParameter param = AudioParameter(); +            param.addInt(String8(AudioParameter::keyRouting), (int)newDevice); +            mpClientInterface->setParameters(activeInput, param.toString()); +        } +    } + +} + +AudioSystem::forced_config AudioPolicyManagerBase::getForceUse(AudioSystem::force_use usage) +{ +    return mForceUse[usage]; +} + +void AudioPolicyManagerBase::setSystemProperty(const char* property, const char* value) +{ +    LOGV("setSystemProperty() property %s, value %s", property, value); +    if (strcmp(property, "ro.camera.sound.forced") == 0) { +        if (atoi(value)) { +            LOGV("ENFORCED_AUDIBLE cannot be muted"); +            mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = false; +        } else { +            LOGV("ENFORCED_AUDIBLE can be muted"); +            mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = true; +        } +    } +} + +audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_type stream, +                                    uint32_t samplingRate, +                                    uint32_t format, +                                    uint32_t channels, +                                    AudioSystem::output_flags flags) +{ +    audio_io_handle_t output = 0; +    uint32_t latency = 0; +    routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream); +    uint32_t device = getDeviceForStrategy(strategy); +    LOGV("getOutput() stream %d, samplingRate %d, format %d, channels %x, flags %x", stream, samplingRate, format, channels, flags); + +#ifdef AUDIO_POLICY_TEST +    if (mCurOutput != 0) { +        LOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channels %x, mDirectOutput %d", +                mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput); + +        if (mTestOutputs[mCurOutput] == 0) { +            LOGV("getOutput() opening test output"); +            AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(); +            outputDesc->mDevice = mTestDevice; +            outputDesc->mSamplingRate = mTestSamplingRate; +            outputDesc->mFormat = mTestFormat; +            outputDesc->mChannels = mTestChannels; +            outputDesc->mLatency = mTestLatencyMs; +            outputDesc->mFlags = (AudioSystem::output_flags)(mDirectOutput ? AudioSystem::OUTPUT_FLAG_DIRECT : 0); +            outputDesc->mRefCount[stream] = 0; +            mTestOutputs[mCurOutput] = mpClientInterface->openOutput(&outputDesc->mDevice, +                                            &outputDesc->mSamplingRate, +                                            &outputDesc->mFormat, +                                            &outputDesc->mChannels, +                                            &outputDesc->mLatency, +                                            outputDesc->mFlags); +            if (mTestOutputs[mCurOutput]) { +                AudioParameter outputCmd = AudioParameter(); +                outputCmd.addInt(String8("set_id"),mCurOutput); +                mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString()); +                addOutput(mTestOutputs[mCurOutput], outputDesc); +            } +        } +        return mTestOutputs[mCurOutput]; +    } +#endif //AUDIO_POLICY_TEST + +    // open a direct output if required by specified parameters +    if (needsDirectOuput(stream, samplingRate, format, channels, flags, device)) { + +        LOGV("getOutput() opening direct output device %x", device); +        AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(); +        outputDesc->mDevice = device; +        outputDesc->mSamplingRate = samplingRate; +        outputDesc->mFormat = format; +        outputDesc->mChannels = channels; +        outputDesc->mLatency = 0; +        outputDesc->mFlags = (AudioSystem::output_flags)(flags | AudioSystem::OUTPUT_FLAG_DIRECT); +        outputDesc->mRefCount[stream] = 0; +        output = mpClientInterface->openOutput(&outputDesc->mDevice, +                                        &outputDesc->mSamplingRate, +                                        &outputDesc->mFormat, +                                        &outputDesc->mChannels, +                                        &outputDesc->mLatency, +                                        outputDesc->mFlags); + +        // only accept an output with the requeted parameters +        if (output == 0 || +            (samplingRate != 0 && samplingRate != outputDesc->mSamplingRate) || +            (format != 0 && format != outputDesc->mFormat) || +            (channels != 0 && channels != outputDesc->mChannels)) { +            LOGV("getOutput() failed opening direct output: samplingRate %d, format %d, channels %d", +                    samplingRate, format, channels); +            if (output != 0) { +                mpClientInterface->closeOutput(output); +            } +            delete outputDesc; +            return 0; +        } +        addOutput(output, outputDesc); +        return output; +    } + +    if (channels != 0 && channels != AudioSystem::CHANNEL_OUT_MONO && +        channels != AudioSystem::CHANNEL_OUT_STEREO) { +        return 0; +    } +    // open a non direct output + +    // get which output is suitable for the specified stream. The actual routing change will happen +    // when startOutput() will be called +    uint32_t a2dpDevice = device & AudioSystem::DEVICE_OUT_ALL_A2DP; +    if (AudioSystem::popCount((AudioSystem::audio_devices)device) == 2) { +#ifdef WITH_A2DP +        if (a2dpUsedForSonification() && a2dpDevice != 0) { +            // if playing on 2 devices among which one is A2DP, use duplicated output +            LOGV("getOutput() using duplicated output"); +            LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device in multiple %x selected but A2DP output not opened", device); +            output = mDuplicatedOutput; +        } else +#endif +        { +            // if playing on 2 devices among which none is A2DP, use hardware output +            output = mHardwareOutput; +        } +        LOGV("getOutput() using output %d for 2 devices %x", output, device); +    } else { +#ifdef WITH_A2DP +        if (a2dpDevice != 0) { +            // if playing on A2DP device, use a2dp output +            LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device %x selected but A2DP output not opened", device); +            output = mA2dpOutput; +        } else +#endif +        { +            // if playing on not A2DP device, use hardware output +            output = mHardwareOutput; +        } +    } + + +    LOGW_IF((output ==0), "getOutput() could not find output for stream %d, samplingRate %d, format %d, channels %x, flags %x", +                stream, samplingRate, format, channels, flags); + +    return output; +} + +status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output, +                                             AudioSystem::stream_type stream, +                                             int session) +{ +    LOGV("startOutput() output %d, stream %d, session %d", output, stream, session); +    ssize_t index = mOutputs.indexOfKey(output); +    if (index < 0) { +        LOGW("startOutput() unknow output %d", output); +        return BAD_VALUE; +    } + +    AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); +    routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream); + +#ifdef WITH_A2DP +    if (mA2dpOutput != 0  && !a2dpUsedForSonification() && strategy == STRATEGY_SONIFICATION) { +        setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput); +    } +#endif + +    // incremenent usage count for this stream on the requested output: +    // NOTE that the usage count is the same for duplicated output and hardware output which is +    // necassary for a correct control of hardware output routing by startOutput() and stopOutput() +    outputDesc->changeRefCount(stream, 1); + +    setOutputDevice(output, getNewDevice(output)); + +    // handle special case for sonification while in call +    if (mPhoneState == AudioSystem::MODE_IN_CALL) { +        handleIncallSonification(stream, true, false); +    } + +    // apply volume rules for current stream and device if necessary +    checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, outputDesc->device()); + +    return NO_ERROR; +} + +status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output, +                                            AudioSystem::stream_type stream, +                                            int session) +{ +    LOGV("stopOutput() output %d, stream %d, session %d", output, stream, session); +    ssize_t index = mOutputs.indexOfKey(output); +    if (index < 0) { +        LOGW("stopOutput() unknow output %d", output); +        return BAD_VALUE; +    } + +    AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); +    routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream); + +    // handle special case for sonification while in call +    if (mPhoneState == AudioSystem::MODE_IN_CALL) { +        handleIncallSonification(stream, false, false); +    } + +    if (outputDesc->mRefCount[stream] > 0) { +        // decrement usage count of this stream on the output +        outputDesc->changeRefCount(stream, -1); +        // store time at which the last music track was stopped - see computeVolume() +        if (stream == AudioSystem::MUSIC) { +            mMusicStopTime = systemTime(); +        } + +        setOutputDevice(output, getNewDevice(output)); + +#ifdef WITH_A2DP +        if (mA2dpOutput != 0 && !a2dpUsedForSonification() && +                strategy == STRATEGY_SONIFICATION) { +            setStrategyMute(STRATEGY_MEDIA, +                            false, +                            mA2dpOutput, +                            mOutputs.valueFor(mHardwareOutput)->mLatency*2); +        } +#endif +        if (output != mHardwareOutput) { +            setOutputDevice(mHardwareOutput, getNewDevice(mHardwareOutput), true); +        } +        return NO_ERROR; +    } else { +        LOGW("stopOutput() refcount is already 0 for output %d", output); +        return INVALID_OPERATION; +    } +} + +void AudioPolicyManagerBase::releaseOutput(audio_io_handle_t output) +{ +    LOGV("releaseOutput() %d", output); +    ssize_t index = mOutputs.indexOfKey(output); +    if (index < 0) { +        LOGW("releaseOutput() releasing unknown output %d", output); +        return; +    } + +#ifdef AUDIO_POLICY_TEST +    int testIndex = testOutputIndex(output); +    if (testIndex != 0) { +        AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); +        if (outputDesc->refCount() == 0) { +            mpClientInterface->closeOutput(output); +            delete mOutputs.valueAt(index); +            mOutputs.removeItem(output); +            mTestOutputs[testIndex] = 0; +        } +        return; +    } +#endif //AUDIO_POLICY_TEST + +    if (mOutputs.valueAt(index)->mFlags & AudioSystem::OUTPUT_FLAG_DIRECT) { +        mpClientInterface->closeOutput(output); +        delete mOutputs.valueAt(index); +        mOutputs.removeItem(output); +    } +} + +audio_io_handle_t AudioPolicyManagerBase::getInput(int inputSource, +                                    uint32_t samplingRate, +                                    uint32_t format, +                                    uint32_t channels, +                                    AudioSystem::audio_in_acoustics acoustics) +{ +    audio_io_handle_t input = 0; +    uint32_t device = getDeviceForInputSource(inputSource); + +    LOGV("getInput() inputSource %d, samplingRate %d, format %d, channels %x, acoustics %x", inputSource, samplingRate, format, channels, acoustics); + +    if (device == 0) { +        return 0; +    } + +    // adapt channel selection to input source +    switch(inputSource) { +    case AUDIO_SOURCE_VOICE_UPLINK: +        channels = AudioSystem::CHANNEL_IN_VOICE_UPLINK; +        break; +    case AUDIO_SOURCE_VOICE_DOWNLINK: +        channels = AudioSystem::CHANNEL_IN_VOICE_DNLINK; +        break; +    case AUDIO_SOURCE_VOICE_CALL: +        channels = (AudioSystem::CHANNEL_IN_VOICE_UPLINK | AudioSystem::CHANNEL_IN_VOICE_DNLINK); +        break; +    default: +        break; +    } + +    AudioInputDescriptor *inputDesc = new AudioInputDescriptor(); + +    inputDesc->mInputSource = inputSource; +    inputDesc->mDevice = device; +    inputDesc->mSamplingRate = samplingRate; +    inputDesc->mFormat = format; +    inputDesc->mChannels = channels; +    inputDesc->mAcoustics = acoustics; +    inputDesc->mRefCount = 0; +    input = mpClientInterface->openInput(&inputDesc->mDevice, +                                    &inputDesc->mSamplingRate, +                                    &inputDesc->mFormat, +                                    &inputDesc->mChannels, +                                    inputDesc->mAcoustics); + +    // only accept input with the exact requested set of parameters +    if (input == 0 || +        (samplingRate != inputDesc->mSamplingRate) || +        (format != inputDesc->mFormat) || +        (channels != inputDesc->mChannels)) { +        LOGV("getInput() failed opening input: samplingRate %d, format %d, channels %d", +                samplingRate, format, channels); +        if (input != 0) { +            mpClientInterface->closeInput(input); +        } +        delete inputDesc; +        return 0; +    } +    mInputs.add(input, inputDesc); +    return input; +} + +status_t AudioPolicyManagerBase::startInput(audio_io_handle_t input) +{ +    LOGV("startInput() input %d", input); +    ssize_t index = mInputs.indexOfKey(input); +    if (index < 0) { +        LOGW("startInput() unknow input %d", input); +        return BAD_VALUE; +    } +    AudioInputDescriptor *inputDesc = mInputs.valueAt(index); + +#ifdef AUDIO_POLICY_TEST +    if (mTestInput == 0) +#endif //AUDIO_POLICY_TEST +    { +        // refuse 2 active AudioRecord clients at the same time +        if (getActiveInput() != 0) { +            LOGW("startInput() input %d failed: other input already started", input); +            return INVALID_OPERATION; +        } +    } + +    AudioParameter param = AudioParameter(); +    param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice); + +    // use Voice Recognition mode or not for this input based on input source +    int vr_enabled = inputDesc->mInputSource == AUDIO_SOURCE_VOICE_RECOGNITION ? 1 : 0; +    param.addInt(String8("vr_mode"), vr_enabled); +    LOGV("AudioPolicyManager::startInput(%d), setting vr_mode to %d", inputDesc->mInputSource, vr_enabled); + +    mpClientInterface->setParameters(input, param.toString()); + +    inputDesc->mRefCount = 1; +    return NO_ERROR; +} + +status_t AudioPolicyManagerBase::stopInput(audio_io_handle_t input) +{ +    LOGV("stopInput() input %d", input); +    ssize_t index = mInputs.indexOfKey(input); +    if (index < 0) { +        LOGW("stopInput() unknow input %d", input); +        return BAD_VALUE; +    } +    AudioInputDescriptor *inputDesc = mInputs.valueAt(index); + +    if (inputDesc->mRefCount == 0) { +        LOGW("stopInput() input %d already stopped", input); +        return INVALID_OPERATION; +    } else { +        AudioParameter param = AudioParameter(); +        param.addInt(String8(AudioParameter::keyRouting), 0); +        mpClientInterface->setParameters(input, param.toString()); +        inputDesc->mRefCount = 0; +        return NO_ERROR; +    } +} + +void AudioPolicyManagerBase::releaseInput(audio_io_handle_t input) +{ +    LOGV("releaseInput() %d", input); +    ssize_t index = mInputs.indexOfKey(input); +    if (index < 0) { +        LOGW("releaseInput() releasing unknown input %d", input); +        return; +    } +    mpClientInterface->closeInput(input); +    delete mInputs.valueAt(index); +    mInputs.removeItem(input); +    LOGV("releaseInput() exit"); +} + +void AudioPolicyManagerBase::initStreamVolume(AudioSystem::stream_type stream, +                                            int indexMin, +                                            int indexMax) +{ +    LOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax); +    if (indexMin < 0 || indexMin >= indexMax) { +        LOGW("initStreamVolume() invalid index limits for stream %d, min %d, max %d", stream , indexMin, indexMax); +        return; +    } +    mStreams[stream].mIndexMin = indexMin; +    mStreams[stream].mIndexMax = indexMax; +} + +status_t AudioPolicyManagerBase::setStreamVolumeIndex(AudioSystem::stream_type stream, int index) +{ + +    if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) { +        return BAD_VALUE; +    } + +    // Force max volume if stream cannot be muted +    if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax; + +    LOGV("setStreamVolumeIndex() stream %d, index %d", stream, index); +    mStreams[stream].mIndexCur = index; + +    // compute and apply stream volume on all outputs according to connected device +    status_t status = NO_ERROR; +    for (size_t i = 0; i < mOutputs.size(); i++) { +        status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), mOutputs.valueAt(i)->device()); +        if (volStatus != NO_ERROR) { +            status = volStatus; +        } +    } +    return status; +} + +status_t AudioPolicyManagerBase::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index) +{ +    if (index == 0) { +        return BAD_VALUE; +    } +    LOGV("getStreamVolumeIndex() stream %d", stream); +    *index =  mStreams[stream].mIndexCur; +    return NO_ERROR; +} + +audio_io_handle_t AudioPolicyManagerBase::getOutputForEffect(effect_descriptor_t *desc) +{ +    LOGV("getOutputForEffect()"); +    // apply simple rule where global effects are attached to the same output as MUSIC streams +    return getOutput(AudioSystem::MUSIC); +} + +status_t AudioPolicyManagerBase::registerEffect(effect_descriptor_t *desc, +                                audio_io_handle_t output, +                                uint32_t strategy, +                                int session, +                                int id) +{ +    ssize_t index = mOutputs.indexOfKey(output); +    if (index < 0) { +        LOGW("registerEffect() unknown output %d", output); +        return INVALID_OPERATION; +    } + +    if (mTotalEffectsCpuLoad + desc->cpuLoad > getMaxEffectsCpuLoad()) { +        LOGW("registerEffect() CPU Load limit exceeded for Fx %s, CPU %f MIPS", +                desc->name, (float)desc->cpuLoad/10); +        return INVALID_OPERATION; +    } +    if (mTotalEffectsMemory + desc->memoryUsage > getMaxEffectsMemory()) { +        LOGW("registerEffect() memory limit exceeded for Fx %s, Memory %d KB", +                desc->name, desc->memoryUsage); +        return INVALID_OPERATION; +    } +    mTotalEffectsCpuLoad += desc->cpuLoad; +    mTotalEffectsMemory += desc->memoryUsage; +    LOGV("registerEffect() effect %s, output %d, strategy %d session %d id %d", +            desc->name, output, strategy, session, id); + +    LOGV("registerEffect() CPU %d, memory %d", desc->cpuLoad, desc->memoryUsage); +    LOGV("  total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory); + +    EffectDescriptor *pDesc = new EffectDescriptor(); +    memcpy (&pDesc->mDesc, desc, sizeof(effect_descriptor_t)); +    pDesc->mOutput = output; +    pDesc->mStrategy = (routing_strategy)strategy; +    pDesc->mSession = session; +    mEffects.add(id, pDesc); + +    return NO_ERROR; +} + +status_t AudioPolicyManagerBase::unregisterEffect(int id) +{ +    ssize_t index = mEffects.indexOfKey(id); +    if (index < 0) { +        LOGW("unregisterEffect() unknown effect ID %d", id); +        return INVALID_OPERATION; +    } + +    EffectDescriptor *pDesc = mEffects.valueAt(index); + +    if (mTotalEffectsCpuLoad < pDesc->mDesc.cpuLoad) { +        LOGW("unregisterEffect() CPU load %d too high for total %d", +                pDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad); +        pDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad; +    } +    mTotalEffectsCpuLoad -= pDesc->mDesc.cpuLoad; +    if (mTotalEffectsMemory < pDesc->mDesc.memoryUsage) { +        LOGW("unregisterEffect() memory %d too big for total %d", +                pDesc->mDesc.memoryUsage, mTotalEffectsMemory); +        pDesc->mDesc.memoryUsage = mTotalEffectsMemory; +    } +    mTotalEffectsMemory -= pDesc->mDesc.memoryUsage; +    LOGV("unregisterEffect() effect %s, ID %d, CPU %d, memory %d", +            pDesc->mDesc.name, id, pDesc->mDesc.cpuLoad, pDesc->mDesc.memoryUsage); +    LOGV("  total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory); + +    mEffects.removeItem(id); +    delete pDesc; + +    return NO_ERROR; +} + +status_t AudioPolicyManagerBase::dump(int fd) +{ +    const size_t SIZE = 256; +    char buffer[SIZE]; +    String8 result; + +    snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this); +    result.append(buffer); +    snprintf(buffer, SIZE, " Hardware Output: %d\n", mHardwareOutput); +    result.append(buffer); +#ifdef WITH_A2DP +    snprintf(buffer, SIZE, " A2DP Output: %d\n", mA2dpOutput); +    result.append(buffer); +    snprintf(buffer, SIZE, " Duplicated Output: %d\n", mDuplicatedOutput); +    result.append(buffer); +    snprintf(buffer, SIZE, " A2DP device address: %s\n", mA2dpDeviceAddress.string()); +    result.append(buffer); +#endif +    snprintf(buffer, SIZE, " SCO device address: %s\n", mScoDeviceAddress.string()); +    result.append(buffer); +    snprintf(buffer, SIZE, " Output devices: %08x\n", mAvailableOutputDevices); +    result.append(buffer); +    snprintf(buffer, SIZE, " Input devices: %08x\n", mAvailableInputDevices); +    result.append(buffer); +    snprintf(buffer, SIZE, " Phone state: %d\n", mPhoneState); +    result.append(buffer); +    snprintf(buffer, SIZE, " Ringer mode: %d\n", mRingerMode); +    result.append(buffer); +    snprintf(buffer, SIZE, " Force use for communications %d\n", mForceUse[AudioSystem::FOR_COMMUNICATION]); +    result.append(buffer); +    snprintf(buffer, SIZE, " Force use for media %d\n", mForceUse[AudioSystem::FOR_MEDIA]); +    result.append(buffer); +    snprintf(buffer, SIZE, " Force use for record %d\n", mForceUse[AudioSystem::FOR_RECORD]); +    result.append(buffer); +    snprintf(buffer, SIZE, " Force use for dock %d\n", mForceUse[AudioSystem::FOR_DOCK]); +    result.append(buffer); +    write(fd, result.string(), result.size()); + +    snprintf(buffer, SIZE, "\nOutputs dump:\n"); +    write(fd, buffer, strlen(buffer)); +    for (size_t i = 0; i < mOutputs.size(); i++) { +        snprintf(buffer, SIZE, "- Output %d dump:\n", mOutputs.keyAt(i)); +        write(fd, buffer, strlen(buffer)); +        mOutputs.valueAt(i)->dump(fd); +    } + +    snprintf(buffer, SIZE, "\nInputs dump:\n"); +    write(fd, buffer, strlen(buffer)); +    for (size_t i = 0; i < mInputs.size(); i++) { +        snprintf(buffer, SIZE, "- Input %d dump:\n", mInputs.keyAt(i)); +        write(fd, buffer, strlen(buffer)); +        mInputs.valueAt(i)->dump(fd); +    } + +    snprintf(buffer, SIZE, "\nStreams dump:\n"); +    write(fd, buffer, strlen(buffer)); +    snprintf(buffer, SIZE, " Stream  Index Min  Index Max  Index Cur  Can be muted\n"); +    write(fd, buffer, strlen(buffer)); +    for (size_t i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { +        snprintf(buffer, SIZE, " %02d", i); +        mStreams[i].dump(buffer + 3, SIZE); +        write(fd, buffer, strlen(buffer)); +    } + +    snprintf(buffer, SIZE, "\nTotal Effects CPU: %f MIPS, Total Effects memory: %d KB\n", +            (float)mTotalEffectsCpuLoad/10, mTotalEffectsMemory); +    write(fd, buffer, strlen(buffer)); + +    snprintf(buffer, SIZE, "Registered effects:\n"); +    write(fd, buffer, strlen(buffer)); +    for (size_t i = 0; i < mEffects.size(); i++) { +        snprintf(buffer, SIZE, "- Effect %d dump:\n", mEffects.keyAt(i)); +        write(fd, buffer, strlen(buffer)); +        mEffects.valueAt(i)->dump(fd); +    } + + +    return NO_ERROR; +} + +// ---------------------------------------------------------------------------- +// AudioPolicyManagerBase +// ---------------------------------------------------------------------------- + +AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface) +    : +#ifdef AUDIO_POLICY_TEST +    Thread(false), +#endif //AUDIO_POLICY_TEST +    mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), +    mLimitRingtoneVolume(false), mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0) +{ +    mpClientInterface = clientInterface; + +    for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) { +        mForceUse[i] = AudioSystem::FORCE_NONE; +    } + +    // devices available by default are speaker, ear piece and microphone +    mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE | +                        AudioSystem::DEVICE_OUT_SPEAKER; +    mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC; + +#ifdef WITH_A2DP +    mA2dpOutput = 0; +    mDuplicatedOutput = 0; +    mA2dpDeviceAddress = String8(""); +#endif +    mScoDeviceAddress = String8(""); + +    // open hardware output +    AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(); +    outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER; +    mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice, +                                    &outputDesc->mSamplingRate, +                                    &outputDesc->mFormat, +                                    &outputDesc->mChannels, +                                    &outputDesc->mLatency, +                                    outputDesc->mFlags); + +    if (mHardwareOutput == 0) { +        LOGE("Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d", +                outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels); +    } else { +        addOutput(mHardwareOutput, outputDesc); +        setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true); +        //TODO: configure audio effect output stage here +    } + +    updateDeviceForStrategy(); +#ifdef AUDIO_POLICY_TEST +    AudioParameter outputCmd = AudioParameter(); +    outputCmd.addInt(String8("set_id"), 0); +    mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString()); + +    mTestDevice = AudioSystem::DEVICE_OUT_SPEAKER; +    mTestSamplingRate = 44100; +    mTestFormat = AudioSystem::PCM_16_BIT; +    mTestChannels =  AudioSystem::CHANNEL_OUT_STEREO; +    mTestLatencyMs = 0; +    mCurOutput = 0; +    mDirectOutput = false; +    for (int i = 0; i < NUM_TEST_OUTPUTS; i++) { +        mTestOutputs[i] = 0; +    } + +    const size_t SIZE = 256; +    char buffer[SIZE]; +    snprintf(buffer, SIZE, "AudioPolicyManagerTest"); +    run(buffer, ANDROID_PRIORITY_AUDIO); +#endif //AUDIO_POLICY_TEST +} + +AudioPolicyManagerBase::~AudioPolicyManagerBase() +{ +#ifdef AUDIO_POLICY_TEST +    exit(); +#endif //AUDIO_POLICY_TEST +   for (size_t i = 0; i < mOutputs.size(); i++) { +        mpClientInterface->closeOutput(mOutputs.keyAt(i)); +        delete mOutputs.valueAt(i); +   } +   mOutputs.clear(); +   for (size_t i = 0; i < mInputs.size(); i++) { +        mpClientInterface->closeInput(mInputs.keyAt(i)); +        delete mInputs.valueAt(i); +   } +   mInputs.clear(); +} + +#ifdef AUDIO_POLICY_TEST +bool AudioPolicyManagerBase::threadLoop() +{ +    LOGV("entering threadLoop()"); +    while (!exitPending()) +    { +        String8 command; +        int valueInt; +        String8 value; + +        Mutex::Autolock _l(mLock); +        mWaitWorkCV.waitRelative(mLock, milliseconds(50)); + +        command = mpClientInterface->getParameters(0, String8("test_cmd_policy")); +        AudioParameter param = AudioParameter(command); + +        if (param.getInt(String8("test_cmd_policy"), valueInt) == NO_ERROR && +            valueInt != 0) { +            LOGV("Test command %s received", command.string()); +            String8 target; +            if (param.get(String8("target"), target) != NO_ERROR) { +                target = "Manager"; +            } +            if (param.getInt(String8("test_cmd_policy_output"), valueInt) == NO_ERROR) { +                param.remove(String8("test_cmd_policy_output")); +                mCurOutput = valueInt; +            } +            if (param.get(String8("test_cmd_policy_direct"), value) == NO_ERROR) { +                param.remove(String8("test_cmd_policy_direct")); +                if (value == "false") { +                    mDirectOutput = false; +                } else if (value == "true") { +                    mDirectOutput = true; +                } +            } +            if (param.getInt(String8("test_cmd_policy_input"), valueInt) == NO_ERROR) { +                param.remove(String8("test_cmd_policy_input")); +                mTestInput = valueInt; +            } + +            if (param.get(String8("test_cmd_policy_format"), value) == NO_ERROR) { +                param.remove(String8("test_cmd_policy_format")); +                int format = AudioSystem::INVALID_FORMAT; +                if (value == "PCM 16 bits") { +                    format = AudioSystem::PCM_16_BIT; +                } else if (value == "PCM 8 bits") { +                    format = AudioSystem::PCM_8_BIT; +                } else if (value == "Compressed MP3") { +                    format = AudioSystem::MP3; +                } +                if (format != AudioSystem::INVALID_FORMAT) { +                    if (target == "Manager") { +                        mTestFormat = format; +                    } else if (mTestOutputs[mCurOutput] != 0) { +                        AudioParameter outputParam = AudioParameter(); +                        outputParam.addInt(String8("format"), format); +                        mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); +                    } +                } +            } +            if (param.get(String8("test_cmd_policy_channels"), value) == NO_ERROR) { +                param.remove(String8("test_cmd_policy_channels")); +                int channels = 0; + +                if (value == "Channels Stereo") { +                    channels =  AudioSystem::CHANNEL_OUT_STEREO; +                } else if (value == "Channels Mono") { +                    channels =  AudioSystem::CHANNEL_OUT_MONO; +                } +                if (channels != 0) { +                    if (target == "Manager") { +                        mTestChannels = channels; +                    } else if (mTestOutputs[mCurOutput] != 0) { +                        AudioParameter outputParam = AudioParameter(); +                        outputParam.addInt(String8("channels"), channels); +                        mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); +                    } +                } +            } +            if (param.getInt(String8("test_cmd_policy_sampleRate"), valueInt) == NO_ERROR) { +                param.remove(String8("test_cmd_policy_sampleRate")); +                if (valueInt >= 0 && valueInt <= 96000) { +                    int samplingRate = valueInt; +                    if (target == "Manager") { +                        mTestSamplingRate = samplingRate; +                    } else if (mTestOutputs[mCurOutput] != 0) { +                        AudioParameter outputParam = AudioParameter(); +                        outputParam.addInt(String8("sampling_rate"), samplingRate); +                        mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); +                    } +                } +            } + +            if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) { +                param.remove(String8("test_cmd_policy_reopen")); + +                mpClientInterface->closeOutput(mHardwareOutput); +                delete mOutputs.valueFor(mHardwareOutput); +                mOutputs.removeItem(mHardwareOutput); + +                AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(); +                outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER; +                mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice, +                                                &outputDesc->mSamplingRate, +                                                &outputDesc->mFormat, +                                                &outputDesc->mChannels, +                                                &outputDesc->mLatency, +                                                outputDesc->mFlags); +                if (mHardwareOutput == 0) { +                    LOGE("Failed to reopen hardware output stream, samplingRate: %d, format %d, channels %d", +                            outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels); +                } else { +                    AudioParameter outputCmd = AudioParameter(); +                    outputCmd.addInt(String8("set_id"), 0); +                    mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString()); +                    addOutput(mHardwareOutput, outputDesc); +                } +            } + + +            mpClientInterface->setParameters(0, String8("test_cmd_policy=")); +        } +    } +    return false; +} + +void AudioPolicyManagerBase::exit() +{ +    { +        AutoMutex _l(mLock); +        requestExit(); +        mWaitWorkCV.signal(); +    } +    requestExitAndWait(); +} + +int AudioPolicyManagerBase::testOutputIndex(audio_io_handle_t output) +{ +    for (int i = 0; i < NUM_TEST_OUTPUTS; i++) { +        if (output == mTestOutputs[i]) return i; +    } +    return 0; +} +#endif //AUDIO_POLICY_TEST + +// --- + +void AudioPolicyManagerBase::addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc) +{ +    outputDesc->mId = id; +    mOutputs.add(id, outputDesc); +} + + +#ifdef WITH_A2DP +status_t AudioPolicyManagerBase::handleA2dpConnection(AudioSystem::audio_devices device, +                                                 const char *device_address) +{ +    // when an A2DP device is connected, open an A2DP and a duplicated output +    LOGV("opening A2DP output for device %s", device_address); +    AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(); +    outputDesc->mDevice = device; +    mA2dpOutput = mpClientInterface->openOutput(&outputDesc->mDevice, +                                            &outputDesc->mSamplingRate, +                                            &outputDesc->mFormat, +                                            &outputDesc->mChannels, +                                            &outputDesc->mLatency, +                                            outputDesc->mFlags); +    if (mA2dpOutput) { +        // add A2DP output descriptor +        addOutput(mA2dpOutput, outputDesc); + +        //TODO: configure audio effect output stage here + +        // set initial stream volume for A2DP device +        applyStreamVolumes(mA2dpOutput, device); +        if (a2dpUsedForSonification()) { +            mDuplicatedOutput = mpClientInterface->openDuplicateOutput(mA2dpOutput, mHardwareOutput); +        } +        if (mDuplicatedOutput != 0 || +            !a2dpUsedForSonification()) { +            // If both A2DP and duplicated outputs are open, send device address to A2DP hardware +            // interface +            AudioParameter param; +            param.add(String8("a2dp_sink_address"), String8(device_address)); +            mpClientInterface->setParameters(mA2dpOutput, param.toString()); +            mA2dpDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN); + +            if (a2dpUsedForSonification()) { +                // add duplicated output descriptor +                AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor(); +                dupOutputDesc->mOutput1 = mOutputs.valueFor(mHardwareOutput); +                dupOutputDesc->mOutput2 = mOutputs.valueFor(mA2dpOutput); +                dupOutputDesc->mSamplingRate = outputDesc->mSamplingRate; +                dupOutputDesc->mFormat = outputDesc->mFormat; +                dupOutputDesc->mChannels = outputDesc->mChannels; +                dupOutputDesc->mLatency = outputDesc->mLatency; +                addOutput(mDuplicatedOutput, dupOutputDesc); +                applyStreamVolumes(mDuplicatedOutput, device); +            } +        } else { +            LOGW("getOutput() could not open duplicated output for %d and %d", +                    mHardwareOutput, mA2dpOutput); +            mpClientInterface->closeOutput(mA2dpOutput); +            mOutputs.removeItem(mA2dpOutput); +            mA2dpOutput = 0; +            delete outputDesc; +            return NO_INIT; +        } +    } else { +        LOGW("setDeviceConnectionState() could not open A2DP output for device %x", device); +        delete outputDesc; +        return NO_INIT; +    } +    AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput); + +    if (mScoDeviceAddress != "") { +        // It is normal to suspend twice if we are both in call, +        // and have the hardware audio output routed to BT SCO +        if (mPhoneState != AudioSystem::MODE_NORMAL) { +            mpClientInterface->suspendOutput(mA2dpOutput); +        } +        if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)hwOutputDesc->device())) { +            mpClientInterface->suspendOutput(mA2dpOutput); +        } +    } + +    if (!a2dpUsedForSonification()) { +        // mute music on A2DP output if a notification or ringtone is playing +        uint32_t refCount = hwOutputDesc->strategyRefCount(STRATEGY_SONIFICATION); +        for (uint32_t i = 0; i < refCount; i++) { +            setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput); +        } +    } +    return NO_ERROR; +} + +status_t AudioPolicyManagerBase::handleA2dpDisconnection(AudioSystem::audio_devices device, +                                                    const char *device_address) +{ +    if (mA2dpOutput == 0) { +        LOGW("setDeviceConnectionState() disconnecting A2DP and no A2DP output!"); +        return INVALID_OPERATION; +    } + +    if (mA2dpDeviceAddress != device_address) { +        LOGW("setDeviceConnectionState() disconnecting unknow A2DP sink address %s", device_address); +        return INVALID_OPERATION; +    } + +    // mute media strategy to avoid outputting sound on hardware output while music stream +    // is switched from A2DP output and before music is paused by music application +    setStrategyMute(STRATEGY_MEDIA, true, mHardwareOutput); +    setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, MUTE_TIME_MS); + +    if (!a2dpUsedForSonification()) { +        // unmute music on A2DP output if a notification or ringtone is playing +        uint32_t refCount = mOutputs.valueFor(mHardwareOutput)->strategyRefCount(STRATEGY_SONIFICATION); +        for (uint32_t i = 0; i < refCount; i++) { +            setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput); +        } +    } +    mA2dpDeviceAddress = ""; +    return NO_ERROR; +} + +void AudioPolicyManagerBase::closeA2dpOutputs() +{ +    LOGV("setDeviceConnectionState() closing A2DP and duplicated output!"); + +    if (mDuplicatedOutput != 0) { +        AudioOutputDescriptor *dupOutputDesc = mOutputs.valueFor(mDuplicatedOutput); +        AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput); +        // As all active tracks on duplicated output will be deleted, +        // and as they were also referenced on hardware output, the reference +        // count for their stream type must be adjusted accordingly on +        // hardware output. +        for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { +            int refCount = dupOutputDesc->mRefCount[i]; +            hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount); +        } + +        mpClientInterface->closeOutput(mDuplicatedOutput); +        delete mOutputs.valueFor(mDuplicatedOutput); +        mOutputs.removeItem(mDuplicatedOutput); +        mDuplicatedOutput = 0; +    } +    if (mA2dpOutput != 0) { +        AudioParameter param; +        param.add(String8("closing"), String8("true")); +        mpClientInterface->setParameters(mA2dpOutput, param.toString()); + +        mpClientInterface->closeOutput(mA2dpOutput); +        delete mOutputs.valueFor(mA2dpOutput); +        mOutputs.removeItem(mA2dpOutput); +        mA2dpOutput = 0; +    } +} + +void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy) +{ +    uint32_t prevDevice = getDeviceForStrategy(strategy); +    uint32_t curDevice = getDeviceForStrategy(strategy, false); +    bool a2dpWasUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(prevDevice & ~AudioSystem::DEVICE_OUT_SPEAKER)); +    bool a2dpIsUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(curDevice & ~AudioSystem::DEVICE_OUT_SPEAKER)); +    audio_io_handle_t srcOutput = 0; +    audio_io_handle_t dstOutput = 0; + +    if (a2dpWasUsed && !a2dpIsUsed) { +        bool dupUsed = a2dpUsedForSonification() && a2dpWasUsed && (AudioSystem::popCount(prevDevice) == 2); +        dstOutput = mHardwareOutput; +        if (dupUsed) { +            LOGV("checkOutputForStrategy() moving strategy %d from duplicated", strategy); +            srcOutput = mDuplicatedOutput; +        } else { +            LOGV("checkOutputForStrategy() moving strategy %d from a2dp", strategy); +            srcOutput = mA2dpOutput; +        } +    } +    if (a2dpIsUsed && !a2dpWasUsed) { +        bool dupUsed = a2dpUsedForSonification() && a2dpIsUsed && (AudioSystem::popCount(curDevice) == 2); +        srcOutput = mHardwareOutput; +        if (dupUsed) { +            LOGV("checkOutputForStrategy() moving strategy %d to duplicated", strategy); +            dstOutput = mDuplicatedOutput; +        } else { +            LOGV("checkOutputForStrategy() moving strategy %d to a2dp", strategy); +            dstOutput = mA2dpOutput; +        } +    } + +    if (srcOutput != 0 && dstOutput != 0) { +        // Move effects associated to this strategy from previous output to new output +        for (size_t i = 0; i < mEffects.size(); i++) { +            EffectDescriptor *desc = mEffects.valueAt(i); +            if (desc->mSession != AudioSystem::SESSION_OUTPUT_STAGE && +                    desc->mStrategy == strategy && +                    desc->mOutput == srcOutput) { +                LOGV("checkOutputForStrategy() moving effect %d to output %d", mEffects.keyAt(i), dstOutput); +                mpClientInterface->moveEffects(desc->mSession, srcOutput, dstOutput); +                desc->mOutput = dstOutput; +            } +        } +        // Move tracks associated to this strategy from previous output to new output +        for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { +            if (getStrategy((AudioSystem::stream_type)i) == strategy) { +                mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, dstOutput); +            } +        } +    } +} + +void AudioPolicyManagerBase::checkOutputForAllStrategies() +{ +    checkOutputForStrategy(STRATEGY_PHONE); +    checkOutputForStrategy(STRATEGY_SONIFICATION); +    checkOutputForStrategy(STRATEGY_MEDIA); +    checkOutputForStrategy(STRATEGY_DTMF); +} + +#endif + +uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache) +{ +    uint32_t device = 0; + +    AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); +    // check the following by order of priority to request a routing change if necessary: +    // 1: we are in call or the strategy phone is active on the hardware output: +    //      use device for strategy phone +    // 2: the strategy sonification is active on the hardware output: +    //      use device for strategy sonification +    // 3: the strategy media is active on the hardware output: +    //      use device for strategy media +    // 4: the strategy DTMF is active on the hardware output: +    //      use device for strategy DTMF +    if (mPhoneState == AudioSystem::MODE_IN_CALL || +        outputDesc->isUsedByStrategy(STRATEGY_PHONE)) { +        device = getDeviceForStrategy(STRATEGY_PHONE, fromCache); +    } else if (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) { +        device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache); +    } else if (outputDesc->isUsedByStrategy(STRATEGY_MEDIA)) { +        device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache); +    } else if (outputDesc->isUsedByStrategy(STRATEGY_DTMF)) { +        device = getDeviceForStrategy(STRATEGY_DTMF, fromCache); +    } + +    LOGV("getNewDevice() selected device %x", device); +    return device; +} + +uint32_t AudioPolicyManagerBase::getStrategyForStream(AudioSystem::stream_type stream) { +    return (uint32_t)getStrategy(stream); +} + +AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy( +        AudioSystem::stream_type stream) { +    // stream to strategy mapping +    switch (stream) { +    case AudioSystem::VOICE_CALL: +    case AudioSystem::BLUETOOTH_SCO: +        return STRATEGY_PHONE; +    case AudioSystem::RING: +    case AudioSystem::NOTIFICATION: +    case AudioSystem::ALARM: +    case AudioSystem::ENFORCED_AUDIBLE: +        return STRATEGY_SONIFICATION; +    case AudioSystem::DTMF: +        return STRATEGY_DTMF; +    default: +        LOGE("unknown stream type"); +    case AudioSystem::SYSTEM: +        // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs +        // while key clicks are played produces a poor result +    case AudioSystem::TTS: +    case AudioSystem::MUSIC: +        return STRATEGY_MEDIA; +    } +} + +uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache) +{ +    uint32_t device = 0; + +    if (fromCache) { +        LOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]); +        return mDeviceForStrategy[strategy]; +    } + +    switch (strategy) { +    case STRATEGY_DTMF: +        if (mPhoneState != AudioSystem::MODE_IN_CALL) { +            // when off call, DTMF strategy follows the same rules as MEDIA strategy +            device = getDeviceForStrategy(STRATEGY_MEDIA, false); +            break; +        } +        // when in call, DTMF and PHONE strategies follow the same rules +        // FALL THROUGH + +    case STRATEGY_PHONE: +        // for phone strategy, we first consider the forced use and then the available devices by order +        // of priority +        switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) { +        case AudioSystem::FORCE_BT_SCO: +            if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) { +                device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT; +                if (device) break; +            } +            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET; +            if (device) break; +            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO; +            if (device) break; +            // if SCO device is requested but no SCO device is available, fall back to default case +            // FALL THROUGH + +        default:    // FORCE_NONE +            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE; +            if (device) break; +            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET; +            if (device) break; +#ifdef WITH_A2DP +            // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP +            if (mPhoneState != AudioSystem::MODE_IN_CALL) { +                device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP; +                if (device) break; +                device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; +                if (device) break; +            } +#endif +            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE; +            if (device == 0) { +                LOGE("getDeviceForStrategy() earpiece device not found"); +            } +            break; + +        case AudioSystem::FORCE_SPEAKER: +            if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) { +                device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT; +                if (device) break; +            } +#ifdef WITH_A2DP +            // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to +            // A2DP speaker when forcing to speaker output +            if (mPhoneState != AudioSystem::MODE_IN_CALL) { +                device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; +                if (device) break; +            } +#endif +            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER; +            if (device == 0) { +                LOGE("getDeviceForStrategy() speaker device not found"); +            } +            break; +        } +    break; + +    case STRATEGY_SONIFICATION: + +        // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by +        // handleIncallSonification(). +        if (mPhoneState == AudioSystem::MODE_IN_CALL) { +            device = getDeviceForStrategy(STRATEGY_PHONE, false); +            break; +        } +        device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER; +        if (device == 0) { +            LOGE("getDeviceForStrategy() speaker device not found"); +        } +        // The second device used for sonification is the same as the device used by media strategy +        // FALL THROUGH + +    case STRATEGY_MEDIA: { +        uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; +        if (device2 == 0) { +            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE; +        } +        if (device2 == 0) { +            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET; +        } +#ifdef WITH_A2DP +        if (mA2dpOutput != 0) { +            if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) { +                break; +            } +            if (device2 == 0) { +                device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP; +            } +            if (device2 == 0) { +                device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; +            } +            if (device2 == 0) { +                device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; +            } +        } +#endif +        if (device2 == 0) { +            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER; +        } + +        // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION, 0 otherwise +        device |= device2; +        if (device == 0) { +            LOGE("getDeviceForStrategy() speaker device not found"); +        } +        } break; + +    default: +        LOGW("getDeviceForStrategy() unknown strategy: %d", strategy); +        break; +    } + +    LOGV("getDeviceForStrategy() strategy %d, device %x", strategy, device); +    return device; +} + +void AudioPolicyManagerBase::updateDeviceForStrategy() +{ +    for (int i = 0; i < NUM_STRATEGIES; i++) { +        mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false); +    } +} + +void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs) +{ +    LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs); +    AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + + +    if (outputDesc->isDuplicated()) { +        setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs); +        setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs); +        return; +    } +#ifdef WITH_A2DP +    // filter devices according to output selected +    if (output == mA2dpOutput) { +        device &= AudioSystem::DEVICE_OUT_ALL_A2DP; +    } else { +        device &= ~AudioSystem::DEVICE_OUT_ALL_A2DP; +    } +#endif + +    uint32_t prevDevice = (uint32_t)outputDesc->device(); +    // Do not change the routing if: +    //  - the requestede device is 0 +    //  - the requested device is the same as current device and force is not specified. +    // Doing this check here allows the caller to call setOutputDevice() without conditions +    if ((device == 0 || device == prevDevice) && !force) { +        LOGV("setOutputDevice() setting same device %x or null device for output %d", device, output); +        return; +    } + +    outputDesc->mDevice = device; +    // mute media streams if both speaker and headset are selected +    if (output == mHardwareOutput && AudioSystem::popCount(device) == 2) { +        setStrategyMute(STRATEGY_MEDIA, true, output); +        // wait for the PCM output buffers to empty before proceeding with the rest of the command +        usleep(outputDesc->mLatency*2*1000); +    } +#ifdef WITH_A2DP +    // suspend A2DP output if SCO device is selected +    if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) { +         if (mA2dpOutput != 0) { +             mpClientInterface->suspendOutput(mA2dpOutput); +         } +    } +#endif +    // do the routing +    AudioParameter param = AudioParameter(); +    param.addInt(String8(AudioParameter::keyRouting), (int)device); +    mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs); +    // update stream volumes according to new device +    applyStreamVolumes(output, device, delayMs); + +#ifdef WITH_A2DP +    // if disconnecting SCO device, restore A2DP output +    if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)prevDevice)) { +         if (mA2dpOutput != 0) { +             LOGV("restore A2DP output"); +             mpClientInterface->restoreOutput(mA2dpOutput); +         } +    } +#endif +    // if changing from a combined headset + speaker route, unmute media streams +    if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) { +        setStrategyMute(STRATEGY_MEDIA, false, output, delayMs); +    } +} + +uint32_t AudioPolicyManagerBase::getDeviceForInputSource(int inputSource) +{ +    uint32_t device; + +    switch(inputSource) { +    case AUDIO_SOURCE_DEFAULT: +    case AUDIO_SOURCE_MIC: +    case AUDIO_SOURCE_VOICE_RECOGNITION: +        if (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO && +            mAvailableInputDevices & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) { +            device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET; +        } else if (mAvailableInputDevices & AudioSystem::DEVICE_IN_WIRED_HEADSET) { +            device = AudioSystem::DEVICE_IN_WIRED_HEADSET; +        } else { +            device = AudioSystem::DEVICE_IN_BUILTIN_MIC; +        } +        break; +    case AUDIO_SOURCE_CAMCORDER: +        if (hasBackMicrophone()) { +            device = AudioSystem::DEVICE_IN_BACK_MIC; +        } else { +            device = AudioSystem::DEVICE_IN_BUILTIN_MIC; +        } +        break; +    case AUDIO_SOURCE_VOICE_UPLINK: +    case AUDIO_SOURCE_VOICE_DOWNLINK: +    case AUDIO_SOURCE_VOICE_CALL: +        device = AudioSystem::DEVICE_IN_VOICE_CALL; +        break; +    default: +        LOGW("getInput() invalid input source %d", inputSource); +        device = 0; +        break; +    } +    LOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device); +    return device; +} + +audio_io_handle_t AudioPolicyManagerBase::getActiveInput() +{ +    for (size_t i = 0; i < mInputs.size(); i++) { +        if (mInputs.valueAt(i)->mRefCount > 0) { +            return mInputs.keyAt(i); +        } +    } +    return 0; +} + +float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device) +{ +    float volume = 1.0; +    AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); +    StreamDescriptor &streamDesc = mStreams[stream]; + +    if (device == 0) { +        device = outputDesc->device(); +    } + +    int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin); +    volume = AudioSystem::linearToLog(volInt); + +    // if a headset is connected, apply the following rules to ring tones and notifications +    // to avoid sound level bursts in user's ears: +    // - always attenuate ring tones and notifications volume by 6dB +    // - if music is playing, always limit the volume to current music volume, +    // with a minimum threshold at -36dB so that notification is always perceived. +    if ((device & +        (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP | +        AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | +        AudioSystem::DEVICE_OUT_WIRED_HEADSET | +        AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) && +        (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) && +        streamDesc.mCanBeMuted) { +        volume *= SONIFICATION_HEADSET_VOLUME_FACTOR; +        // 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 (outputDesc->mRefCount[AudioSystem::MUSIC] || mLimitRingtoneVolume) { +            float musicVol = computeVolume(AudioSystem::MUSIC, mStreams[AudioSystem::MUSIC].mIndexCur, output, device); +            float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ? musicVol : SONIFICATION_HEADSET_VOLUME_MIN; +            if (volume > minVol) { +                volume = minVol; +                LOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol); +            } +        } +    } + +    return volume; +} + +status_t AudioPolicyManagerBase::checkAndSetVolume(int stream, int index, audio_io_handle_t output, uint32_t device, int delayMs, bool force) +{ + +    // do not change actual stream volume if the stream is muted +    if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) { +        LOGV("checkAndSetVolume() stream %d muted count %d", stream, mOutputs.valueFor(output)->mMuteCount[stream]); +        return NO_ERROR; +    } + +    // do not change in call volume if bluetooth is connected and vice versa +    if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) || +        (stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) { +        LOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm", +             stream, mForceUse[AudioSystem::FOR_COMMUNICATION]); +        return INVALID_OPERATION; +    } + +    float volume = computeVolume(stream, index, output, device); +    // do not set volume if the float value did not change +    if (volume != mOutputs.valueFor(output)->mCurVolume[stream] || force) { +        mOutputs.valueFor(output)->mCurVolume[stream] = volume; +        LOGV("setStreamVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs); +        if (stream == AudioSystem::VOICE_CALL || +            stream == AudioSystem::DTMF || +            stream == AudioSystem::BLUETOOTH_SCO) { +            float voiceVolume = -1.0; +            // offset value to reflect actual hardware volume that never reaches 0 +            // 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java) +            volume = 0.01 + 0.99 * volume; +            if (stream == AudioSystem::VOICE_CALL) { +                voiceVolume = (float)index/(float)mStreams[stream].mIndexMax; +            } else if (stream == AudioSystem::BLUETOOTH_SCO) { +                voiceVolume = 1.0; +            } +            if (voiceVolume >= 0 && output == mHardwareOutput) { +                mpClientInterface->setVoiceVolume(voiceVolume, delayMs); +            } +        } +        mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs); +    } + +    return NO_ERROR; +} + +void AudioPolicyManagerBase::applyStreamVolumes(audio_io_handle_t output, uint32_t device, int delayMs) +{ +    LOGV("applyStreamVolumes() for output %d and device %x", output, device); + +    for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { +        checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, device, delayMs); +    } +} + +void AudioPolicyManagerBase::setStrategyMute(routing_strategy strategy, bool on, audio_io_handle_t output, int delayMs) +{ +    LOGV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output); +    for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { +        if (getStrategy((AudioSystem::stream_type)stream) == strategy) { +            setStreamMute(stream, on, output, delayMs); +        } +    } +} + +void AudioPolicyManagerBase::setStreamMute(int stream, bool on, audio_io_handle_t output, int delayMs) +{ +    StreamDescriptor &streamDesc = mStreams[stream]; +    AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + +    LOGV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d", stream, on, output, outputDesc->mMuteCount[stream]); + +    if (on) { +        if (outputDesc->mMuteCount[stream] == 0) { +            if (streamDesc.mCanBeMuted) { +                checkAndSetVolume(stream, 0, output, outputDesc->device(), delayMs); +            } +        } +        // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored +        outputDesc->mMuteCount[stream]++; +    } else { +        if (outputDesc->mMuteCount[stream] == 0) { +            LOGW("setStreamMute() unmuting non muted stream!"); +            return; +        } +        if (--outputDesc->mMuteCount[stream] == 0) { +            checkAndSetVolume(stream, streamDesc.mIndexCur, output, outputDesc->device(), delayMs); +        } +    } +} + +void AudioPolicyManagerBase::handleIncallSonification(int stream, bool starting, bool stateChange) +{ +    // if the stream pertains to sonification strategy and we are in call we must +    // mute the stream if it is low visibility. If it is high visibility, we must play a tone +    // in the device used for phone strategy and play the tone if the selected device does not +    // interfere with the device used for phone strategy +    // if stateChange is true, we are called from setPhoneState() and we must mute or unmute as +    // many times as there are active tracks on the output + +    if (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) { +        AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mHardwareOutput); +        LOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d", +                stream, starting, outputDesc->mDevice, stateChange); +        if (outputDesc->mRefCount[stream]) { +            int muteCount = 1; +            if (stateChange) { +                muteCount = outputDesc->mRefCount[stream]; +            } +            if (AudioSystem::isLowVisibility((AudioSystem::stream_type)stream)) { +                LOGV("handleIncallSonification() low visibility, muteCount %d", muteCount); +                for (int i = 0; i < muteCount; i++) { +                    setStreamMute(stream, starting, mHardwareOutput); +                } +            } else { +                LOGV("handleIncallSonification() high visibility"); +                if (outputDesc->device() & getDeviceForStrategy(STRATEGY_PHONE)) { +                    LOGV("handleIncallSonification() high visibility muted, muteCount %d", muteCount); +                    for (int i = 0; i < muteCount; i++) { +                        setStreamMute(stream, starting, mHardwareOutput); +                    } +                } +                if (starting) { +                    mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL); +                } else { +                    mpClientInterface->stopTone(); +                } +            } +        } +    } +} + +bool AudioPolicyManagerBase::needsDirectOuput(AudioSystem::stream_type stream, +                                    uint32_t samplingRate, +                                    uint32_t format, +                                    uint32_t channels, +                                    AudioSystem::output_flags flags, +                                    uint32_t device) +{ +   return ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) || +          (format !=0 && !AudioSystem::isLinearPCM(format))); +} + +uint32_t AudioPolicyManagerBase::getMaxEffectsCpuLoad() +{ +    return MAX_EFFECTS_CPU_LOAD; +} + +uint32_t AudioPolicyManagerBase::getMaxEffectsMemory() +{ +    return MAX_EFFECTS_MEMORY; +} + +// --- AudioOutputDescriptor class implementation + +AudioPolicyManagerBase::AudioOutputDescriptor::AudioOutputDescriptor() +    : mId(0), mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0), +    mFlags((AudioSystem::output_flags)0), mDevice(0), mOutput1(0), mOutput2(0) +{ +    // clear usage count for all stream types +    for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { +        mRefCount[i] = 0; +        mCurVolume[i] = -1.0; +        mMuteCount[i] = 0; +    } +} + +uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::device() +{ +    uint32_t device = 0; +    if (isDuplicated()) { +        device = mOutput1->mDevice | mOutput2->mDevice; +    } else { +        device = mDevice; +    } +    return device; +} + +void AudioPolicyManagerBase::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int delta) +{ +    // forward usage count change to attached outputs +    if (isDuplicated()) { +        mOutput1->changeRefCount(stream, delta); +        mOutput2->changeRefCount(stream, delta); +    } +    if ((delta + (int)mRefCount[stream]) < 0) { +        LOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]); +        mRefCount[stream] = 0; +        return; +    } +    mRefCount[stream] += delta; +    LOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]); +} + +uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::refCount() +{ +    uint32_t refcount = 0; +    for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { +        refcount += mRefCount[i]; +    } +    return refcount; +} + +uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::strategyRefCount(routing_strategy strategy) +{ +    uint32_t refCount = 0; +    for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { +        if (getStrategy((AudioSystem::stream_type)i) == strategy) { +            refCount += mRefCount[i]; +        } +    } +    return refCount; +} + + +status_t AudioPolicyManagerBase::AudioOutputDescriptor::dump(int fd) +{ +    const size_t SIZE = 256; +    char buffer[SIZE]; +    String8 result; + +    snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate); +    result.append(buffer); +    snprintf(buffer, SIZE, " Format: %d\n", mFormat); +    result.append(buffer); +    snprintf(buffer, SIZE, " Channels: %08x\n", mChannels); +    result.append(buffer); +    snprintf(buffer, SIZE, " Latency: %d\n", mLatency); +    result.append(buffer); +    snprintf(buffer, SIZE, " Flags %08x\n", mFlags); +    result.append(buffer); +    snprintf(buffer, SIZE, " Devices %08x\n", device()); +    result.append(buffer); +    snprintf(buffer, SIZE, " Stream volume refCount muteCount\n"); +    result.append(buffer); +    for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { +        snprintf(buffer, SIZE, " %02d     %.03f     %02d       %02d\n", i, mCurVolume[i], mRefCount[i], mMuteCount[i]); +        result.append(buffer); +    } +    write(fd, result.string(), result.size()); + +    return NO_ERROR; +} + +// --- AudioInputDescriptor class implementation + +AudioPolicyManagerBase::AudioInputDescriptor::AudioInputDescriptor() +    : mSamplingRate(0), mFormat(0), mChannels(0), +     mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0) +{ +} + +status_t AudioPolicyManagerBase::AudioInputDescriptor::dump(int fd) +{ +    const size_t SIZE = 256; +    char buffer[SIZE]; +    String8 result; + +    snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate); +    result.append(buffer); +    snprintf(buffer, SIZE, " Format: %d\n", mFormat); +    result.append(buffer); +    snprintf(buffer, SIZE, " Channels: %08x\n", mChannels); +    result.append(buffer); +    snprintf(buffer, SIZE, " Acoustics %08x\n", mAcoustics); +    result.append(buffer); +    snprintf(buffer, SIZE, " Devices %08x\n", mDevice); +    result.append(buffer); +    snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount); +    result.append(buffer); +    write(fd, result.string(), result.size()); + +    return NO_ERROR; +} + +// --- StreamDescriptor class implementation + +void AudioPolicyManagerBase::StreamDescriptor::dump(char* buffer, size_t size) +{ +    snprintf(buffer, size, "      %02d         %02d         %02d         %d\n", +            mIndexMin, +            mIndexMax, +            mIndexCur, +            mCanBeMuted); +} + +// --- EffectDescriptor class implementation + +status_t AudioPolicyManagerBase::EffectDescriptor::dump(int fd) +{ +    const size_t SIZE = 256; +    char buffer[SIZE]; +    String8 result; + +    snprintf(buffer, SIZE, " Output: %d\n", mOutput); +    result.append(buffer); +    snprintf(buffer, SIZE, " Strategy: %d\n", mStrategy); +    result.append(buffer); +    snprintf(buffer, SIZE, " Session: %d\n", mSession); +    result.append(buffer); +    snprintf(buffer, SIZE, " Name: %s\n",  mDesc.name); +    result.append(buffer); +    write(fd, result.string(), result.size()); + +    return NO_ERROR; +} + + + +}; // namespace android | 
