diff options
author | Eric Laurent <elaurent@google.com> | 2010-01-20 10:03:36 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-01-20 10:03:36 -0800 |
commit | 243bf50e1e582d280829bc0e16bfb6d43c70c32f (patch) | |
tree | 0ee121149b0daf1ab7cd5d0b791361e614899be7 | |
parent | d738a4d3942ebf56a34d37772228014ec5c2fbdd (diff) | |
parent | cef3cd79489fa7897ffbacbc4e435651fb04f10d (diff) | |
download | frameworks_base-243bf50e1e582d280829bc0e16bfb6d43c70c32f.zip frameworks_base-243bf50e1e582d280829bc0e16bfb6d43c70c32f.tar.gz frameworks_base-243bf50e1e582d280829bc0e16bfb6d43c70c32f.tar.bz2 |
Merge "Create base class for audio policy manager."
-rw-r--r-- | include/media/AudioSystem.h | 2 | ||||
-rw-r--r-- | libs/audioflinger/Android.mk | 11 | ||||
-rw-r--r-- | libs/audioflinger/AudioPolicyManagerBase.cpp | 1925 | ||||
-rw-r--r-- | libs/audioflinger/AudioPolicyManagerGeneric.cpp | 945 | ||||
-rw-r--r-- | libs/audioflinger/AudioPolicyManagerGeneric.h | 196 | ||||
-rw-r--r-- | libs/audioflinger/AudioPolicyService.cpp | 45 | ||||
-rw-r--r-- | libs/audioflinger/AudioPolicyService.h | 5 |
7 files changed, 1964 insertions, 1165 deletions
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index c87007c..f935bb9 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -244,6 +244,8 @@ public: DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET | DEVICE_OUT_BLUETOOTH_SCO_CARKIT | DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | DEVICE_OUT_DEFAULT), + DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), // input devices DEVICE_IN_COMMUNICATION = 0x10000, diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk index f5c03bb..b68bfc1 100644 --- a/libs/audioflinger/Android.mk +++ b/libs/audioflinger/Android.mk @@ -47,7 +47,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - AudioPolicyManagerGeneric.cpp + AudioPolicyManagerBase.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ @@ -60,7 +60,7 @@ else LOCAL_SHARED_LIBRARIES += libdl endif -LOCAL_MODULE:= libaudiopolicygeneric +LOCAL_MODULE:= libaudiopolicybase ifeq ($(BOARD_HAVE_BLUETOOTH),true) LOCAL_CFLAGS += -DWITH_A2DP @@ -70,7 +70,7 @@ ifeq ($(AUDIO_POLICY_TEST),true) LOCAL_CFLAGS += -DAUDIO_POLICY_TEST endif -include $(BUILD_SHARED_LIBRARY) +include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) @@ -87,11 +87,10 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libbinder \ libmedia \ - libhardware_legacy \ - libaudiopolicygeneric + libhardware_legacy ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true) - LOCAL_STATIC_LIBRARIES += libaudiointerface + LOCAL_STATIC_LIBRARIES += libaudiointerface libaudiopolicybase LOCAL_CFLAGS += -DGENERIC_AUDIO else LOCAL_SHARED_LIBRARIES += libaudio libaudiopolicy diff --git a/libs/audioflinger/AudioPolicyManagerBase.cpp b/libs/audioflinger/AudioPolicyManagerBase.cpp new file mode 100644 index 0000000..055dbca --- /dev/null +++ b/libs/audioflinger/AudioPolicyManagerBase.cpp @@ -0,0 +1,1925 @@ +/* + * 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 ((mA2dpDeviceAddress == mScoDeviceAddress) && + (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 ((mA2dpDeviceAddress == mScoDeviceAddress) && + (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(newDevice); + // 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(newDevice); + // suspend A2DP output if SCO device address is the same as A2DP device address. + // no need to check that a SCO device is actually connected as mScoDeviceAddress == "" + // if none is connected and the test below will fail. + if (mA2dpDeviceAddress == 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(); + } + // change routing is necessary + setOutputDevice(mHardwareOutput, newDevice, force); + + // 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); + 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); + + 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); + } + 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(newDevice); +#endif + updateDeviceForStrategy(); + setOutputDevice(mHardwareOutput, newDevice); +} + +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: + // 1 a direct output is explicitely requested + // 2 the audio format is compressed + if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) || + (format !=0 && !AudioSystem::isLinearPCM(format))) { + + 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] = 1; + 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) +{ + LOGV("startOutput() output %d, stream %d", output, stream); + 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) +{ + LOGV("stopOutput() output %d, stream %d", output, stream); + 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 + 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; +} + +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)); + } + + 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) +{ + 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); + } + + 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); + // 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 (mA2dpDeviceAddress == 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 during 2 seconds to avoid outputing 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, 2000); + + 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) { + 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 &newDevice) +{ + 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)); + AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput); + AudioOutputDescriptor *a2dpOutputDesc; + + if (a2dpWasUsed && !a2dpIsUsed) { + bool dupUsed = a2dpUsedForSonification() && a2dpWasUsed && (AudioSystem::popCount(prevDevice) == 2); + + if (dupUsed) { + LOGV("checkOutputForStrategy() moving strategy %d to duplicated", strategy); + a2dpOutputDesc = mOutputs.valueFor(mDuplicatedOutput); + } else { + LOGV("checkOutputForStrategy() moving strategy %d to a2dp", strategy); + a2dpOutputDesc = mOutputs.valueFor(mA2dpOutput); + } + + for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { + if (getStrategy((AudioSystem::stream_type)i) == strategy) { + mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mHardwareOutput); + int refCount = a2dpOutputDesc->mRefCount[i]; + // in the case of duplicated output, the ref count is first incremented + // and then decremented on hardware output tus keeping its value + hwOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount); + a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount); + } + } + // do not change newDevice is it was already set before this call by a previous call to + // getNewDevice() or checkOutputForStrategy() for a strategy with higher priority + if (newDevice == 0 && hwOutputDesc->isUsedByStrategy(strategy)) { + newDevice = getDeviceForStrategy(strategy, false); + } + } + if (a2dpIsUsed && !a2dpWasUsed) { + bool dupUsed = a2dpUsedForSonification() && a2dpIsUsed && (AudioSystem::popCount(curDevice) == 2); + audio_io_handle_t a2dpOutput; + + if (dupUsed) { + LOGV("checkOutputForStrategy() moving strategy %d from duplicated", strategy); + a2dpOutputDesc = mOutputs.valueFor(mDuplicatedOutput); + a2dpOutput = mDuplicatedOutput; + } else { + LOGV("checkOutputForStrategy() moving strategy %d from a2dp", strategy); + a2dpOutputDesc = mOutputs.valueFor(mA2dpOutput); + a2dpOutput = mA2dpOutput; + } + + for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { + if (getStrategy((AudioSystem::stream_type)i) == strategy) { + mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, a2dpOutput); + int refCount = hwOutputDesc->mRefCount[i]; + // in the case of duplicated output, the ref count is first incremented + // and then decremented on hardware output tus keeping its value + a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount); + hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount); + } + } + } +} + +void AudioPolicyManagerBase::checkOutputForAllStrategies(uint32_t &newDevice) +{ + // Check strategies in order of priority so that once newDevice is set + // for a given strategy it is not modified by subsequent calls to + // checkOutputForStrategy() + checkOutputForStrategy(STRATEGY_PHONE, newDevice); + checkOutputForStrategy(STRATEGY_SONIFICATION, newDevice); + checkOutputForStrategy(STRATEGY_MEDIA, newDevice); + checkOutputForStrategy(STRATEGY_DTMF, newDevice); +} + +#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; +} + +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; + 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; + } + 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; +#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_WIRED_HEADPHONE; + } + if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET; + } + 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 == mHardwareOutput) { + 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 A2D output if SCO device is selected + if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) { + if (mA2dpOutput && mScoDeviceAddress == mA2dpDeviceAddress) { + 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 && mScoDeviceAddress == mA2dpDeviceAddress) { + 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 heaset 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(); + } + } + } + } +} + +// --- 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); +} + + +}; // namespace android diff --git a/libs/audioflinger/AudioPolicyManagerGeneric.cpp b/libs/audioflinger/AudioPolicyManagerGeneric.cpp deleted file mode 100644 index 8cfc204..0000000 --- a/libs/audioflinger/AudioPolicyManagerGeneric.cpp +++ /dev/null @@ -1,945 +0,0 @@ -/* - * 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 "AudioPolicyManagerGeneric" -//#define LOG_NDEBUG 0 -#include <utils/Log.h> -#include "AudioPolicyManagerGeneric.h" -#include <media/mediarecorder.h> - -namespace android { - - -// ---------------------------------------------------------------------------- -// AudioPolicyInterface implementation -// ---------------------------------------------------------------------------- - - -status_t AudioPolicyManagerGeneric::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)) { - 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; - 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; - break; - - default: - LOGE("setDeviceConnectionState() invalid state: %x", state); - return BAD_VALUE; - } - 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; - } - return NO_ERROR; - } - - LOGW("setDeviceConnectionState() invalid device: %x", device); - return BAD_VALUE; -} - -AudioSystem::device_connection_state AudioPolicyManagerGeneric::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) { - state = AudioSystem::DEVICE_STATE_AVAILABLE; - } - } else if (AudioSystem::isInputDevice(device)) { - if (device & mAvailableInputDevices) { - state = AudioSystem::DEVICE_STATE_AVAILABLE; - } - } - - return state; -} - -void AudioPolicyManagerGeneric::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; - } - // store previous phone state for management of sonification strategy below - int oldState = mPhoneState; - mPhoneState = state; - - // if leaving or entering in call state, handle special case of active streams - // pertaining to sonification strategy see handleIncallSonification() - if (state == AudioSystem::MODE_IN_CALL || - oldState == AudioSystem::MODE_IN_CALL) { - bool starting = (state == AudioSystem::MODE_IN_CALL) ? true : false; - LOGV("setPhoneState() in call state management: new state is %d", state); - for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { - handleIncallSonification(stream, starting); - } - } -} - -void AudioPolicyManagerGeneric::setRingerMode(uint32_t mode, uint32_t mask) -{ - LOGV("setRingerMode() mode %x, mask %x", mode, mask); - - mRingerMode = mode; -} - -void AudioPolicyManagerGeneric::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config) -{ - LOGV("setForceUse) usage %d, config %d, mPhoneState %d", usage, config, mPhoneState); - mForceUse[usage] = config; -} - -AudioSystem::forced_config AudioPolicyManagerGeneric::getForceUse(AudioSystem::force_use usage) -{ - return mForceUse[usage]; -} - -void AudioPolicyManagerGeneric::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 AudioPolicyManagerGeneric::getOutput(AudioSystem::stream_type stream, - uint32_t samplingRate, - uint32_t format, - uint32_t channels, - AudioSystem::output_flags flags) -{ - 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()); - mOutputs.add(mTestOutputs[mCurOutput], outputDesc); - } - } - return mTestOutputs[mCurOutput]; - } -#endif //AUDIO_POLICY_TEST - if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) || - (format != 0 && !AudioSystem::isLinearPCM(format)) || - (channels != 0 && channels != AudioSystem::CHANNEL_OUT_MONO && channels != AudioSystem::CHANNEL_OUT_STEREO)) { - return 0; - } - - return mHardwareOutput; -} - -status_t AudioPolicyManagerGeneric::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream) -{ - LOGV("startOutput() output %d, stream %d", output, stream); - ssize_t index = mOutputs.indexOfKey(output); - if (index < 0) { - LOGW("startOutput() unknow output %d", output); - return BAD_VALUE; - } - - AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); - - // handle special case for sonification while in call - if (mPhoneState == AudioSystem::MODE_IN_CALL) { - handleIncallSonification(stream, true); - } - - // incremenent usage count for this stream on the requested output: - outputDesc->changeRefCount(stream, 1); - return NO_ERROR; -} - -status_t AudioPolicyManagerGeneric::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream) -{ - LOGV("stopOutput() output %d, stream %d", output, stream); - ssize_t index = mOutputs.indexOfKey(output); - if (index < 0) { - LOGW("stopOutput() unknow output %d", output); - return BAD_VALUE; - } - - AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); - - // handle special case for sonification while in call - if (mPhoneState == AudioSystem::MODE_IN_CALL) { - handleIncallSonification(stream, false); - } - - if (outputDesc->isUsedByStream(stream)) { - // decrement usage count of this stream on the output - outputDesc->changeRefCount(stream, -1); - return NO_ERROR; - } else { - LOGW("stopOutput() refcount is already 0 for output %d", output); - return INVALID_OPERATION; - } -} - -void AudioPolicyManagerGeneric::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; - } - } -#endif //AUDIO_POLICY_TEST -} - -audio_io_handle_t AudioPolicyManagerGeneric::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; - - LOGV("getInput() inputSource %d, samplingRate %d, format %d, channels %x, acoustics %x", inputSource, samplingRate, format, channels, acoustics); - - AudioInputDescriptor *inputDesc = new AudioInputDescriptor(); - inputDesc->mDevice = AudioSystem::DEVICE_IN_BUILTIN_MIC; - 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 ((samplingRate != inputDesc->mSamplingRate) || - (format != inputDesc->mFormat) || - (channels != inputDesc->mChannels)) { - LOGV("getOutput() failed opening input: samplingRate %d, format %d, channels %d", - samplingRate, format, channels); - mpClientInterface->closeInput(input); - delete inputDesc; - return 0; - } - mInputs.add(input, inputDesc); - return input; -} - -status_t AudioPolicyManagerGeneric::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 - for (size_t i = 0; i < mInputs.size(); i++) { - if (mInputs.valueAt(i)->mRefCount > 0) { - LOGW("startInput() input %d, other input %d already started", input, mInputs.keyAt(i)); - return INVALID_OPERATION; - } - } - } - - inputDesc->mRefCount = 1; - return NO_ERROR; -} - -status_t AudioPolicyManagerGeneric::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 { - inputDesc->mRefCount = 0; - return NO_ERROR; - } -} - -void AudioPolicyManagerGeneric::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); -} - - - -void AudioPolicyManagerGeneric::initStreamVolume(AudioSystem::stream_type stream, - int indexMin, - int indexMax) -{ - LOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax); - mStreams[stream].mIndexMin = indexMin; - mStreams[stream].mIndexMax = indexMax; -} - -status_t AudioPolicyManagerGeneric::setStreamVolumeIndex(AudioSystem::stream_type stream, int index) -{ - - if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) { - return BAD_VALUE; - } - - LOGV("setStreamVolumeIndex() stream %d, index %d", stream, index); - mStreams[stream].mIndexCur = index; - - // do not change actual stream volume if the stream is muted - if (mStreams[stream].mMuteCount != 0) { - return NO_ERROR; - } - - // Do not changed 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("setStreamVolumeIndex() cannot set stream %d volume with force use = %d for comm", - stream, mForceUse[AudioSystem::FOR_COMMUNICATION]); - return INVALID_OPERATION; - } - - // compute and apply stream volume on all outputs according to connected device - for (size_t i = 0; i < mOutputs.size(); i++) { - AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i); - uint32_t device = outputDesc->device(); - - float volume = computeVolume((int)stream, index, device); - - LOGV("setStreamVolume() for output %d stream %d, volume %f", mOutputs.keyAt(i), stream, volume); - mpClientInterface->setStreamVolume(stream, volume, mOutputs.keyAt(i)); - } - return NO_ERROR; -} - -status_t AudioPolicyManagerGeneric::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; -} - -status_t AudioPolicyManagerGeneric::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); - 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); - 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 Mute Count 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)); - } - - return NO_ERROR; -} - -// ---------------------------------------------------------------------------- -// AudioPolicyManagerGeneric -// ---------------------------------------------------------------------------- - -// --- class factory - -AudioPolicyManagerGeneric::AudioPolicyManagerGeneric(AudioPolicyClientInterface *clientInterface) - : -#ifdef AUDIO_POLICY_TEST - Thread(false), -#endif //AUDIO_POLICY_TEST - mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(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_SPEAKER; - mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC; - - // 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 { - mOutputs.add(mHardwareOutput, outputDesc); - } - -#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 -} - -AudioPolicyManagerGeneric::~AudioPolicyManagerGeneric() -{ -#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 AudioPolicyManagerGeneric::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()); - mOutputs.add(mHardwareOutput, outputDesc); - } - } - - - mpClientInterface->setParameters(0, String8("test_cmd_policy=")); - } - } - return false; -} - -void AudioPolicyManagerGeneric::exit() -{ - { - AutoMutex _l(mLock); - requestExit(); - mWaitWorkCV.signal(); - } - requestExitAndWait(); -} - -int AudioPolicyManagerGeneric::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 - -// --- - -AudioPolicyManagerGeneric::routing_strategy AudioPolicyManagerGeneric::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; - } -} - - -float AudioPolicyManagerGeneric::computeVolume(int stream, int index, uint32_t device) -{ - float volume = 1.0; - - StreamDescriptor &streamDesc = mStreams[stream]; - - // Force max volume if stream cannot be muted - if (!streamDesc.mCanBeMuted) index = streamDesc.mIndexMax; - - int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin); - volume = AudioSystem::linearToLog(volInt); - - return volume; -} - -void AudioPolicyManagerGeneric::setStreamMute(int stream, bool on, audio_io_handle_t output) -{ - LOGV("setStreamMute() stream %d, mute %d, output %d", stream, on, output); - - StreamDescriptor &streamDesc = mStreams[stream]; - - if (on) { - if (streamDesc.mMuteCount++ == 0) { - if (streamDesc.mCanBeMuted) { - mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, 0, output); - } - } - } else { - if (streamDesc.mMuteCount == 0) { - LOGW("setStreamMute() unmuting non muted stream!"); - return; - } - if (--streamDesc.mMuteCount == 0) { - uint32_t device = mOutputs.valueFor(output)->mDevice; - float volume = computeVolume(stream, streamDesc.mIndexCur, device); - mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output); - } - } -} - -void AudioPolicyManagerGeneric::handleIncallSonification(int stream, bool starting) -{ - // 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 (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) { - AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mHardwareOutput); - LOGV("handleIncallSonification() stream %d starting %d device %x", stream, starting, outputDesc->mDevice); - if (outputDesc->isUsedByStream((AudioSystem::stream_type)stream)) { - if (AudioSystem::isLowVisibility((AudioSystem::stream_type)stream)) { - LOGV("handleIncallSonification() low visibility"); - setStreamMute(stream, starting, mHardwareOutput); - } else { - if (starting) { - mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL); - } else { - mpClientInterface->stopTone(); - } - } - } - } -} - - -// --- AudioOutputDescriptor class implementation - -AudioPolicyManagerGeneric::AudioOutputDescriptor::AudioOutputDescriptor() - : mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0), - mFlags((AudioSystem::output_flags)0), mDevice(0) -{ - // clear usage count for all stream types - for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { - mRefCount[i] = 0; - } -} - -uint32_t AudioPolicyManagerGeneric::AudioOutputDescriptor::device() -{ - return mDevice; -} - -void AudioPolicyManagerGeneric::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int 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 AudioPolicyManagerGeneric::AudioOutputDescriptor::refCount() -{ - uint32_t refcount = 0; - for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { - refcount += mRefCount[i]; - } - return refcount; -} - -status_t AudioPolicyManagerGeneric::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", mDevice); - result.append(buffer); - snprintf(buffer, SIZE, " Stream refCount\n"); - result.append(buffer); - for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { - snprintf(buffer, SIZE, " %02d %d\n", i, mRefCount[i]); - result.append(buffer); - } - write(fd, result.string(), result.size()); - - return NO_ERROR; -} - -// --- AudioInputDescriptor class implementation - -AudioPolicyManagerGeneric::AudioInputDescriptor::AudioInputDescriptor() - : mSamplingRate(0), mFormat(0), mChannels(0), - mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0) -{ -} - -status_t AudioPolicyManagerGeneric::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 AudioPolicyManagerGeneric::StreamDescriptor::dump(char* buffer, size_t size) -{ - snprintf(buffer, size, " %02d %02d %02d %02d %d\n", - mIndexMin, - mIndexMax, - mIndexCur, - mMuteCount, - mCanBeMuted); -} - -}; // namespace android diff --git a/libs/audioflinger/AudioPolicyManagerGeneric.h b/libs/audioflinger/AudioPolicyManagerGeneric.h deleted file mode 100644 index 4997cdf..0000000 --- a/libs/audioflinger/AudioPolicyManagerGeneric.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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. - */ - - -#include <stdint.h> -#include <sys/types.h> - -#include <utils/Errors.h> -#include <utils/KeyedVector.h> -#include <hardware_legacy/AudioPolicyInterface.h> -#include <utils/threads.h> - - -namespace android { - -// ---------------------------------------------------------------------------- - -#define MAX_DEVICE_ADDRESS_LEN 20 -#define NUM_TEST_OUTPUTS 5 - -class AudioPolicyManagerGeneric: public AudioPolicyInterface -#ifdef AUDIO_POLICY_TEST - , public Thread -#endif //AUDIO_POLICY_TEST -{ - -public: - AudioPolicyManagerGeneric(AudioPolicyClientInterface *clientInterface); - virtual ~AudioPolicyManagerGeneric(); - - // AudioPolicyInterface - virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device, - AudioSystem::device_connection_state state, - const char *device_address); - virtual AudioSystem::device_connection_state getDeviceConnectionState(AudioSystem::audio_devices device, - const char *device_address); - virtual void setPhoneState(int state); - virtual void setRingerMode(uint32_t mode, uint32_t mask); - virtual void setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config); - virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage); - virtual void setSystemProperty(const char* property, const char* value); - virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream, - uint32_t samplingRate, - uint32_t format, - uint32_t channels, - AudioSystem::output_flags flags); - virtual status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream); - virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream); - virtual void releaseOutput(audio_io_handle_t output); - virtual audio_io_handle_t getInput(int inputSource, - uint32_t samplingRate, - uint32_t format, - uint32_t channels, - AudioSystem::audio_in_acoustics acoustics); - // indicates to the audio policy manager that the input starts being used. - virtual status_t startInput(audio_io_handle_t input); - // indicates to the audio policy manager that the input stops being used. - virtual status_t stopInput(audio_io_handle_t input); - virtual void releaseInput(audio_io_handle_t input); - virtual void initStreamVolume(AudioSystem::stream_type stream, - int indexMin, - int indexMax); - virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index); - virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index); - - virtual status_t dump(int fd); - -private: - - enum routing_strategy { - STRATEGY_MEDIA, - STRATEGY_PHONE, - STRATEGY_SONIFICATION, - STRATEGY_DTMF, - NUM_STRATEGIES - }; - - // descriptor for audio outputs. Used to maintain current configuration of each opened audio output - // and keep track of the usage of this output by each audio stream type. - class AudioOutputDescriptor - { - public: - AudioOutputDescriptor(); - - status_t dump(int fd); - - uint32_t device(); - void changeRefCount(AudioSystem::stream_type, int delta); - bool isUsedByStream(AudioSystem::stream_type stream) { return mRefCount[stream] > 0 ? true : false; } - uint32_t refCount(); - - uint32_t mSamplingRate; // - uint32_t mFormat; // - uint32_t mChannels; // output configuration - uint32_t mLatency; // - AudioSystem::output_flags mFlags; // - uint32_t mDevice; // current device this output is routed to - uint32_t mRefCount[AudioSystem::NUM_STREAM_TYPES]; // number of streams of each type using this output - }; - - // descriptor for audio inputs. Used to maintain current configuration of each opened audio input - // and keep track of the usage of this input. - class AudioInputDescriptor - { - public: - AudioInputDescriptor(); - - status_t dump(int fd); - - uint32_t mSamplingRate; // - uint32_t mFormat; // input configuration - uint32_t mChannels; // - AudioSystem::audio_in_acoustics mAcoustics; // - uint32_t mDevice; // current device this input is routed to - uint32_t mRefCount; // number of AudioRecord clients using this output - }; - - // stream descriptor used for volume control - class StreamDescriptor - { - public: - StreamDescriptor() - : mIndexMin(0), mIndexMax(1), mIndexCur(1), mMuteCount(0), mCanBeMuted(true) {} - - void dump(char* buffer, size_t size); - - int mIndexMin; // min volume index - int mIndexMax; // max volume index - int mIndexCur; // current volume index - int mMuteCount; // mute request counter - bool mCanBeMuted; // true is the stream can be muted - }; - - // return the strategy corresponding to a given stream type - static routing_strategy getStrategy(AudioSystem::stream_type stream); - // return the output handle of an output routed to the specified device, 0 if no output - // is routed to the device - float computeVolume(int stream, int index, uint32_t device); - // Mute or unmute the stream on the specified output - void setStreamMute(int stream, bool on, audio_io_handle_t output); - // handle special cases for sonification strategy while in call: mute streams or replace by - // a special tone in the device used for communication - void handleIncallSonification(int stream, bool starting); - - -#ifdef AUDIO_POLICY_TEST - virtual bool threadLoop(); - void exit(); - int testOutputIndex(audio_io_handle_t output); -#endif //AUDIO_POLICY_TEST - - - AudioPolicyClientInterface *mpClientInterface; // audio policy client interface - audio_io_handle_t mHardwareOutput; // hardware output handler - - KeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs; // list ot output descritors - KeyedVector<audio_io_handle_t, AudioInputDescriptor *> mInputs; // list of input descriptors - uint32_t mAvailableOutputDevices; // bit field of all available output devices - uint32_t mAvailableInputDevices; // bit field of all available input devices - int mPhoneState; // current phone state - uint32_t mRingerMode; // current ringer mode - AudioSystem::forced_config mForceUse[AudioSystem::NUM_FORCE_USE]; // current forced use configuration - - StreamDescriptor mStreams[AudioSystem::NUM_STREAM_TYPES]; // stream descriptors for volume control - -#ifdef AUDIO_POLICY_TEST - Mutex mLock; - Condition mWaitWorkCV; - - int mCurOutput; - bool mDirectOutput; - audio_io_handle_t mTestOutputs[NUM_TEST_OUTPUTS]; - int mTestInput; - uint32_t mTestDevice; - uint32_t mTestSamplingRate; - uint32_t mTestFormat; - uint32_t mTestChannels; - uint32_t mTestLatencyMs; -#endif //AUDIO_POLICY_TEST - -}; - -}; diff --git a/libs/audioflinger/AudioPolicyService.cpp b/libs/audioflinger/AudioPolicyService.cpp index aa48019..bb3905c 100644 --- a/libs/audioflinger/AudioPolicyService.cpp +++ b/libs/audioflinger/AudioPolicyService.cpp @@ -30,9 +30,10 @@ #include <utils/String16.h> #include <utils/threads.h> #include "AudioPolicyService.h" -#include "AudioPolicyManagerGeneric.h" +#include <hardware_legacy/AudioPolicyManagerBase.h> #include <cutils/properties.h> #include <dlfcn.h> +#include <hardware_legacy/power.h> // ---------------------------------------------------------------------------- // the sim build doesn't have gettid @@ -43,8 +44,9 @@ namespace android { -static const char* kDeadlockedString = "AudioPolicyService may be deadlocked\n"; -static const char* kCmdDeadlockedString = "AudioPolicyService command thread may be deadlocked\n"; + +static const char *kDeadlockedString = "AudioPolicyService may be deadlocked\n"; +static const char *kCmdDeadlockedString = "AudioPolicyService command thread may be deadlocked\n"; static const int kDumpLockRetries = 50; static const int kDumpLockSleep = 20000; @@ -67,18 +69,18 @@ AudioPolicyService::AudioPolicyService() char value[PROPERTY_VALUE_MAX]; // start tone playback thread - mTonePlaybackThread = new AudioCommandThread(); + mTonePlaybackThread = new AudioCommandThread(String8("")); // start audio commands thread - mAudioCommandThread = new AudioCommandThread(); + mAudioCommandThread = new AudioCommandThread(String8("ApmCommandThread")); #if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST) - mpPolicyManager = new AudioPolicyManagerGeneric(this); + mpPolicyManager = new AudioPolicyManagerBase(this); LOGV("build for GENERIC_AUDIO - using generic audio policy"); #else // if running in emulation - use the emulator driver if (property_get("ro.kernel.qemu", value, 0)) { LOGV("Running in emulation - using generic audio policy"); - mpPolicyManager = new AudioPolicyManagerGeneric(this); + mpPolicyManager = new AudioPolicyManagerBase(this); } else { LOGV("Using hardware specific audio policy"); @@ -556,8 +558,8 @@ status_t AudioPolicyService::setVoiceVolume(float volume, int delayMs) // ----------- AudioPolicyService::AudioCommandThread implementation ---------- -AudioPolicyService::AudioCommandThread::AudioCommandThread() - : Thread(false) +AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name) + : Thread(false), mName(name) { mpToneGenerator = NULL; } @@ -565,18 +567,20 @@ AudioPolicyService::AudioCommandThread::AudioCommandThread() AudioPolicyService::AudioCommandThread::~AudioCommandThread() { + if (mName != "" && !mAudioCommands.isEmpty()) { + release_wake_lock(mName.string()); + } mAudioCommands.clear(); if (mpToneGenerator != NULL) delete mpToneGenerator; } void AudioPolicyService::AudioCommandThread::onFirstRef() { - const size_t SIZE = 256; - char buffer[SIZE]; - - snprintf(buffer, SIZE, "AudioCommandThread"); - - run(buffer, ANDROID_PRIORITY_AUDIO); + if (mName != "") { + run(mName.string(), ANDROID_PRIORITY_AUDIO); + } else { + run("AudioCommandThread", ANDROID_PRIORITY_AUDIO); + } } bool AudioPolicyService::AudioCommandThread::threadLoop() @@ -657,6 +661,10 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() break; } } + // release delayed commands wake lock + if (mName != "" && mAudioCommands.isEmpty()) { + release_wake_lock(mName.string()); + } LOGV("AudioCommandThread() going to sleep"); mWaitWorkCV.waitRelative(mLock, waitTime); LOGV("AudioCommandThread() waking up"); @@ -815,6 +823,11 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma command->mTime = systemTime() + milliseconds(delayMs); + // acquire wake lock to make sure delayed commands are processed + if (mName != "" && mAudioCommands.isEmpty()) { + acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string()); + } + // check same pending commands with later time stamps and eliminate them for (i = mAudioCommands.size()-1; i >= 0; i--) { AudioCommand *command2 = mAudioCommands[i]; @@ -883,7 +896,7 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma removedCommands.clear(); // insert command at the right place according to its time stamp - LOGV("inserting command: %d at index %ld, num commands %d", command->mCommand, i+1, mAudioCommands.size()); + LOGV("inserting command: %d at index %d, num commands %d", command->mCommand, (int)i+1, mAudioCommands.size()); mAudioCommands.insertAt(command, i + 1); } diff --git a/libs/audioflinger/AudioPolicyService.h b/libs/audioflinger/AudioPolicyService.h index b9234ec..a13d0bd 100644 --- a/libs/audioflinger/AudioPolicyService.h +++ b/libs/audioflinger/AudioPolicyService.h @@ -132,7 +132,7 @@ private: SET_VOICE_VOLUME }; - AudioCommandThread (); + AudioCommandThread (String8 name); virtual ~AudioCommandThread(); status_t dump(int fd); @@ -195,7 +195,8 @@ private: Condition mWaitWorkCV; Vector <AudioCommand *> mAudioCommands; // list of pending commands ToneGenerator *mpToneGenerator; // the tone generator - AudioCommand mLastCommand; + AudioCommand mLastCommand; // last processed command (used by dump) + String8 mName; // string used by wake lock fo delayed commands }; // Internal dump utilities. |