c5eb8b4a5d
The change for volume per device introduced a problem where if a headset is plugged in while in silent mode, the sound will not be restored at the first press on volume up key but only at the second press. This is because when exiting silent mode, AudioService reapplies the last known volume for all devices in any order and the last volume applied prevails, even if it is for a device other than currently selected by audio policy manager. The fix consists in applying a new volume only if the device indicated matches currently selected device on an output. Change-Id: I778b90a9ec5a8b4a15e0333bd045f2dd327f388c
2515 lines
97 KiB
C++
2515 lines
97 KiB
C++
/*
|
|
* 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 <hardware/audio_effect.h>
|
|
#include <math.h>
|
|
|
|
namespace android_audio_legacy {
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// AudioPolicyInterface implementation
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_devices device,
|
|
AudioSystem::device_connection_state state,
|
|
const char *device_address)
|
|
{
|
|
|
|
ALOGV("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) {
|
|
ALOGE("setDeviceConnectionState() invalid address: %s", device_address);
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
// handle output devices
|
|
if (AudioSystem::isOutputDevice(device)) {
|
|
|
|
#ifndef WITH_A2DP
|
|
if (AudioSystem::isA2dpDevice(device)) {
|
|
ALOGE("setDeviceConnectionState() invalid device: %x", device);
|
|
return BAD_VALUE;
|
|
}
|
|
#endif
|
|
|
|
switch (state)
|
|
{
|
|
// handle output device connection
|
|
case AudioSystem::DEVICE_STATE_AVAILABLE:
|
|
if (mAvailableOutputDevices & device) {
|
|
ALOGW("setDeviceConnectionState() device already connected: %x", device);
|
|
return INVALID_OPERATION;
|
|
}
|
|
ALOGV("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)) {
|
|
ALOGV("setDeviceConnectionState() BT SCO device, address %s", device_address);
|
|
// keep track of SCO device address
|
|
mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
|
|
}
|
|
}
|
|
break;
|
|
// handle output device disconnection
|
|
case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
|
|
if (!(mAvailableOutputDevices & device)) {
|
|
ALOGW("setDeviceConnectionState() device not connected: %x", device);
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
|
|
ALOGV("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 = "";
|
|
}
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
ALOGE("setDeviceConnectionState() invalid state: %x", state);
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
// request routing change if necessary
|
|
uint32_t newDevice = getNewDevice(mHardwareOutput, false);
|
|
#ifdef WITH_A2DP
|
|
checkA2dpSuspend();
|
|
checkOutputForAllStrategies();
|
|
// A2DP outputs must be closed after checkOutputForAllStrategies() is executed
|
|
if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) {
|
|
closeA2dpOutputs();
|
|
}
|
|
#endif
|
|
updateDeviceForStrategy();
|
|
setOutputDevice(mHardwareOutput, newDevice);
|
|
|
|
if (device == AudioSystem::DEVICE_OUT_WIRED_HEADSET) {
|
|
device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
|
|
} else if (device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO ||
|
|
device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET ||
|
|
device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
|
|
device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
|
|
} else {
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
// handle input devices
|
|
if (AudioSystem::isInputDevice(device)) {
|
|
|
|
switch (state)
|
|
{
|
|
// handle input device connection
|
|
case AudioSystem::DEVICE_STATE_AVAILABLE: {
|
|
if (mAvailableInputDevices & device) {
|
|
ALOGW("setDeviceConnectionState() device already connected: %d", device);
|
|
return INVALID_OPERATION;
|
|
}
|
|
mAvailableInputDevices |= device;
|
|
}
|
|
break;
|
|
|
|
// handle input device disconnection
|
|
case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
|
|
if (!(mAvailableInputDevices & device)) {
|
|
ALOGW("setDeviceConnectionState() device not connected: %d", device);
|
|
return INVALID_OPERATION;
|
|
}
|
|
mAvailableInputDevices &= ~device;
|
|
} break;
|
|
|
|
default:
|
|
ALOGE("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) {
|
|
ALOGV("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;
|
|
}
|
|
|
|
ALOGW("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)
|
|
{
|
|
ALOGV("setPhoneState() state %d", state);
|
|
uint32_t newDevice = 0;
|
|
if (state < 0 || state >= AudioSystem::NUM_MODES) {
|
|
ALOGW("setPhoneState() invalid state %d", state);
|
|
return;
|
|
}
|
|
|
|
if (state == mPhoneState ) {
|
|
ALOGW("setPhoneState() setting same state %d", state);
|
|
return;
|
|
}
|
|
|
|
// if leaving call state, handle special case of active streams
|
|
// pertaining to sonification strategy see handleIncallSonification()
|
|
if (isInCall()) {
|
|
ALOGV("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 (!isStateInCall(oldState) && isStateInCall(state)) {
|
|
ALOGV(" 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 (isStateInCall(oldState) && !isStateInCall(state)) {
|
|
ALOGV(" Exiting call in setPhoneState()");
|
|
// force routing command to audio hardware when exiting a call
|
|
// even if no device change is needed
|
|
force = true;
|
|
} else if (isStateInCall(state) && (state != oldState)) {
|
|
ALOGV(" Switching between telephony and VoIP in setPhoneState()");
|
|
// force routing command to audio hardware when switching between telephony and VoIP
|
|
// 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
|
|
checkA2dpSuspend();
|
|
checkOutputForAllStrategies();
|
|
#endif
|
|
updateDeviceForStrategy();
|
|
|
|
AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
|
|
|
|
// force routing command to audio hardware when ending call
|
|
// even if no device change is needed
|
|
if (isStateInCall(oldState) && newDevice == 0) {
|
|
newDevice = hwOutputDesc->device();
|
|
}
|
|
|
|
// when changing from ring tone to in call mode, mute the ringing tone
|
|
// immediately and delay the route change to avoid sending the ring tone
|
|
// tail into the earpiece or headset.
|
|
int delayMs = 0;
|
|
if (isStateInCall(state) && oldState == AudioSystem::MODE_RINGTONE) {
|
|
// delay the device change command by twice the output latency to have some margin
|
|
// and be sure that audio buffers not yet affected by the mute are out when
|
|
// we actually apply the route change
|
|
delayMs = hwOutputDesc->mLatency*2;
|
|
setStreamMute(AudioSystem::RING, true, mHardwareOutput);
|
|
}
|
|
|
|
// change routing is necessary
|
|
setOutputDevice(mHardwareOutput, newDevice, force, delayMs);
|
|
|
|
// if entering in call state, handle special case of active streams
|
|
// pertaining to sonification strategy see handleIncallSonification()
|
|
if (isStateInCall(state)) {
|
|
ALOGV("setPhoneState() in call state management: new state is %d", state);
|
|
// unmute the ringing tone after a sufficient delay if it was muted before
|
|
// setting output device above
|
|
if (oldState == AudioSystem::MODE_RINGTONE) {
|
|
setStreamMute(AudioSystem::RING, false, mHardwareOutput, MUTE_TIME_MS);
|
|
}
|
|
for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
|
|
handleIncallSonification(stream, true, true);
|
|
}
|
|
}
|
|
|
|
// Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE
|
|
if (state == AudioSystem::MODE_RINGTONE &&
|
|
isStreamActive(AudioSystem::MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY)) {
|
|
mLimitRingtoneVolume = true;
|
|
} else {
|
|
mLimitRingtoneVolume = false;
|
|
}
|
|
}
|
|
|
|
void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
|
|
{
|
|
ALOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState);
|
|
|
|
bool forceVolumeReeval = false;
|
|
switch(usage) {
|
|
case AudioSystem::FOR_COMMUNICATION:
|
|
if (config != AudioSystem::FORCE_SPEAKER && config != AudioSystem::FORCE_BT_SCO &&
|
|
config != AudioSystem::FORCE_NONE) {
|
|
ALOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config);
|
|
return;
|
|
}
|
|
forceVolumeReeval = true;
|
|
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_ANALOG_DOCK &&
|
|
config != AudioSystem::FORCE_DIGITAL_DOCK && config != AudioSystem::FORCE_NONE) {
|
|
ALOGW("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) {
|
|
ALOGW("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 &&
|
|
config != AudioSystem::FORCE_ANALOG_DOCK &&
|
|
config != AudioSystem::FORCE_DIGITAL_DOCK) {
|
|
ALOGW("setForceUse() invalid config %d for FOR_DOCK", config);
|
|
}
|
|
forceVolumeReeval = true;
|
|
mForceUse[usage] = config;
|
|
break;
|
|
default:
|
|
ALOGW("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
|
|
checkA2dpSuspend();
|
|
checkOutputForAllStrategies();
|
|
#endif
|
|
updateDeviceForStrategy();
|
|
setOutputDevice(mHardwareOutput, newDevice);
|
|
if (forceVolumeReeval) {
|
|
applyStreamVolumes(mHardwareOutput, newDevice, 0, true);
|
|
}
|
|
|
|
audio_io_handle_t activeInput = getActiveInput();
|
|
if (activeInput != 0) {
|
|
AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput);
|
|
newDevice = getDeviceForInputSource(inputDesc->mInputSource);
|
|
if (newDevice != inputDesc->mDevice) {
|
|
ALOGV("setForceUse() changing device from %x to %x for input %d",
|
|
inputDesc->mDevice, newDevice, activeInput);
|
|
inputDesc->mDevice = newDevice;
|
|
AudioParameter param = AudioParameter();
|
|
param.addInt(String8(AudioParameter::keyRouting), (int)newDevice);
|
|
mpClientInterface->setParameters(activeInput, param.toString());
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
AudioSystem::forced_config AudioPolicyManagerBase::getForceUse(AudioSystem::force_use usage)
|
|
{
|
|
return mForceUse[usage];
|
|
}
|
|
|
|
void AudioPolicyManagerBase::setSystemProperty(const char* property, const char* value)
|
|
{
|
|
ALOGV("setSystemProperty() property %s, value %s", property, value);
|
|
if (strcmp(property, "ro.camera.sound.forced") == 0) {
|
|
if (atoi(value)) {
|
|
ALOGV("ENFORCED_AUDIBLE cannot be muted");
|
|
mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = false;
|
|
} else {
|
|
ALOGV("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);
|
|
ALOGV("getOutput() stream %d, samplingRate %d, format %d, channels %x, flags %x", stream, samplingRate, format, channels, flags);
|
|
|
|
#ifdef AUDIO_POLICY_TEST
|
|
if (mCurOutput != 0) {
|
|
ALOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channels %x, mDirectOutput %d",
|
|
mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput);
|
|
|
|
if (mTestOutputs[mCurOutput] == 0) {
|
|
ALOGV("getOutput() opening test output");
|
|
AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
|
|
outputDesc->mDevice = mTestDevice;
|
|
outputDesc->mSamplingRate = mTestSamplingRate;
|
|
outputDesc->mFormat = mTestFormat;
|
|
outputDesc->mChannels = mTestChannels;
|
|
outputDesc->mLatency = mTestLatencyMs;
|
|
outputDesc->mFlags = (AudioSystem::output_flags)(mDirectOutput ? AudioSystem::OUTPUT_FLAG_DIRECT : 0);
|
|
outputDesc->mRefCount[stream] = 0;
|
|
mTestOutputs[mCurOutput] = mpClientInterface->openOutput(&outputDesc->mDevice,
|
|
&outputDesc->mSamplingRate,
|
|
&outputDesc->mFormat,
|
|
&outputDesc->mChannels,
|
|
&outputDesc->mLatency,
|
|
outputDesc->mFlags);
|
|
if (mTestOutputs[mCurOutput]) {
|
|
AudioParameter outputCmd = AudioParameter();
|
|
outputCmd.addInt(String8("set_id"),mCurOutput);
|
|
mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString());
|
|
addOutput(mTestOutputs[mCurOutput], outputDesc);
|
|
}
|
|
}
|
|
return mTestOutputs[mCurOutput];
|
|
}
|
|
#endif //AUDIO_POLICY_TEST
|
|
|
|
// open a direct output if required by specified parameters
|
|
if (needsDirectOuput(stream, samplingRate, format, channels, flags, device)) {
|
|
|
|
ALOGV("getOutput() opening direct output device %x", device);
|
|
AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
|
|
outputDesc->mDevice = device;
|
|
outputDesc->mSamplingRate = samplingRate;
|
|
outputDesc->mFormat = format;
|
|
outputDesc->mChannels = channels;
|
|
outputDesc->mLatency = 0;
|
|
outputDesc->mFlags = (AudioSystem::output_flags)(flags | AudioSystem::OUTPUT_FLAG_DIRECT);
|
|
outputDesc->mRefCount[stream] = 0;
|
|
outputDesc->mStopTime[stream] = 0;
|
|
output = mpClientInterface->openOutput(&outputDesc->mDevice,
|
|
&outputDesc->mSamplingRate,
|
|
&outputDesc->mFormat,
|
|
&outputDesc->mChannels,
|
|
&outputDesc->mLatency,
|
|
outputDesc->mFlags);
|
|
|
|
// only accept an output with the requeted parameters
|
|
if (output == 0 ||
|
|
(samplingRate != 0 && samplingRate != outputDesc->mSamplingRate) ||
|
|
(format != 0 && format != outputDesc->mFormat) ||
|
|
(channels != 0 && channels != outputDesc->mChannels)) {
|
|
ALOGV("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
|
|
ALOGV("getOutput() using duplicated output");
|
|
ALOGW_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;
|
|
}
|
|
ALOGV("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
|
|
ALOGW_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;
|
|
}
|
|
}
|
|
|
|
|
|
ALOGW_IF((output ==0), "getOutput() could not find output for stream %d, samplingRate %d, format %d, channels %x, flags %x",
|
|
stream, samplingRate, format, channels, flags);
|
|
|
|
return output;
|
|
}
|
|
|
|
status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output,
|
|
AudioSystem::stream_type stream,
|
|
int session)
|
|
{
|
|
ALOGV("startOutput() output %d, stream %d, session %d", output, stream, session);
|
|
ssize_t index = mOutputs.indexOfKey(output);
|
|
if (index < 0) {
|
|
ALOGW("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 || strategy == STRATEGY_ENFORCED_AUDIBLE)) {
|
|
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);
|
|
|
|
uint32_t prevDevice = outputDesc->device();
|
|
setOutputDevice(output, getNewDevice(output));
|
|
uint32_t newDevice = outputDesc->device();
|
|
|
|
// handle special case for sonification while in call
|
|
if (isInCall()) {
|
|
handleIncallSonification(stream, true, false);
|
|
}
|
|
|
|
// apply volume rules for current stream and device if necessary
|
|
checkAndSetVolume(stream,
|
|
mStreams[stream].getVolumeIndex((audio_devices_t)newDevice),
|
|
output,
|
|
newDevice);
|
|
|
|
// FIXME: need a delay to make sure that audio path switches to speaker before sound
|
|
// starts. Should be platform specific?
|
|
if (stream == AudioSystem::ENFORCED_AUDIBLE &&
|
|
prevDevice != newDevice) {
|
|
usleep(outputDesc->mLatency*4*1000);
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output,
|
|
AudioSystem::stream_type stream,
|
|
int session)
|
|
{
|
|
ALOGV("stopOutput() output %d, stream %d, session %d", output, stream, session);
|
|
ssize_t index = mOutputs.indexOfKey(output);
|
|
if (index < 0) {
|
|
ALOGW("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 (isInCall()) {
|
|
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 stream was stopped - see isStreamActive()
|
|
outputDesc->mStopTime[stream] = systemTime();
|
|
|
|
setOutputDevice(output, getNewDevice(output), false, outputDesc->mLatency*2);
|
|
|
|
#ifdef WITH_A2DP
|
|
if (mA2dpOutput != 0 && !a2dpUsedForSonification() &&
|
|
(strategy == STRATEGY_SONIFICATION || strategy == STRATEGY_ENFORCED_AUDIBLE)) {
|
|
setStrategyMute(STRATEGY_MEDIA,
|
|
false,
|
|
mA2dpOutput,
|
|
mOutputs.valueFor(mHardwareOutput)->mLatency*2);
|
|
}
|
|
#endif
|
|
if (output != mHardwareOutput) {
|
|
setOutputDevice(mHardwareOutput, getNewDevice(mHardwareOutput), true);
|
|
}
|
|
return NO_ERROR;
|
|
} else {
|
|
ALOGW("stopOutput() refcount is already 0 for output %d", output);
|
|
return INVALID_OPERATION;
|
|
}
|
|
}
|
|
|
|
void AudioPolicyManagerBase::releaseOutput(audio_io_handle_t output)
|
|
{
|
|
ALOGV("releaseOutput() %d", output);
|
|
ssize_t index = mOutputs.indexOfKey(output);
|
|
if (index < 0) {
|
|
ALOGW("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);
|
|
|
|
ALOGV("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,
|
|
(audio_in_acoustics_t) inputDesc->mAcoustics);
|
|
|
|
// only accept input with the exact requested set of parameters
|
|
if (input == 0 ||
|
|
(samplingRate != inputDesc->mSamplingRate) ||
|
|
(format != inputDesc->mFormat) ||
|
|
(channels != inputDesc->mChannels)) {
|
|
ALOGV("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)
|
|
{
|
|
ALOGV("startInput() input %d", input);
|
|
ssize_t index = mInputs.indexOfKey(input);
|
|
if (index < 0) {
|
|
ALOGW("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) {
|
|
ALOGW("startInput() input %d failed: other input already started", input);
|
|
return INVALID_OPERATION;
|
|
}
|
|
}
|
|
|
|
AudioParameter param = AudioParameter();
|
|
param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice);
|
|
|
|
param.addInt(String8(AudioParameter::keyInputSource), (int)inputDesc->mInputSource);
|
|
ALOGV("AudioPolicyManager::startInput() input source = %d", inputDesc->mInputSource);
|
|
|
|
mpClientInterface->setParameters(input, param.toString());
|
|
|
|
inputDesc->mRefCount = 1;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t AudioPolicyManagerBase::stopInput(audio_io_handle_t input)
|
|
{
|
|
ALOGV("stopInput() input %d", input);
|
|
ssize_t index = mInputs.indexOfKey(input);
|
|
if (index < 0) {
|
|
ALOGW("stopInput() unknow input %d", input);
|
|
return BAD_VALUE;
|
|
}
|
|
AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
|
|
|
|
if (inputDesc->mRefCount == 0) {
|
|
ALOGW("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)
|
|
{
|
|
ALOGV("releaseInput() %d", input);
|
|
ssize_t index = mInputs.indexOfKey(input);
|
|
if (index < 0) {
|
|
ALOGW("releaseInput() releasing unknown input %d", input);
|
|
return;
|
|
}
|
|
mpClientInterface->closeInput(input);
|
|
delete mInputs.valueAt(index);
|
|
mInputs.removeItem(input);
|
|
ALOGV("releaseInput() exit");
|
|
}
|
|
|
|
void AudioPolicyManagerBase::initStreamVolume(AudioSystem::stream_type stream,
|
|
int indexMin,
|
|
int indexMax)
|
|
{
|
|
ALOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax);
|
|
if (indexMin < 0 || indexMin >= indexMax) {
|
|
ALOGW("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,
|
|
audio_devices_t device)
|
|
{
|
|
|
|
if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) {
|
|
return BAD_VALUE;
|
|
}
|
|
if (!audio_is_output_device(device)) {
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
// Force max volume if stream cannot be muted
|
|
if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax;
|
|
|
|
ALOGV("setStreamVolumeIndex() stream %d, device %08x, index %d",
|
|
stream, device, index);
|
|
|
|
// if device is AUDIO_DEVICE_OUT_DEFAULT set default value and
|
|
// clear all device specific values
|
|
if (device == AUDIO_DEVICE_OUT_DEFAULT) {
|
|
mStreams[stream].mIndexCur.clear();
|
|
}
|
|
mStreams[stream].mIndexCur.add(device, 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++) {
|
|
audio_devices_t curDevice =
|
|
getDeviceForVolume((audio_devices_t)mOutputs.valueAt(i)->device());
|
|
if (device == curDevice) {
|
|
status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice);
|
|
if (volStatus != NO_ERROR) {
|
|
status = volStatus;
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
status_t AudioPolicyManagerBase::getStreamVolumeIndex(AudioSystem::stream_type stream,
|
|
int *index,
|
|
audio_devices_t device)
|
|
{
|
|
if (index == NULL) {
|
|
return BAD_VALUE;
|
|
}
|
|
if (!audio_is_output_device(device)) {
|
|
return BAD_VALUE;
|
|
}
|
|
// if device is AUDIO_DEVICE_OUT_DEFAULT, return volume for device corresponding to
|
|
// the strategy the stream belongs to.
|
|
if (device == AUDIO_DEVICE_OUT_DEFAULT) {
|
|
device = (audio_devices_t)getDeviceForStrategy(getStrategy(stream), true);
|
|
}
|
|
device = getDeviceForVolume(device);
|
|
|
|
*index = mStreams[stream].getVolumeIndex(device);
|
|
ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
audio_io_handle_t AudioPolicyManagerBase::getOutputForEffect(effect_descriptor_t *desc)
|
|
{
|
|
ALOGV("getOutputForEffect()");
|
|
// apply simple rule where global effects are attached to the same output as MUSIC streams
|
|
return getOutput(AudioSystem::MUSIC);
|
|
}
|
|
|
|
status_t AudioPolicyManagerBase::registerEffect(effect_descriptor_t *desc,
|
|
audio_io_handle_t io,
|
|
uint32_t strategy,
|
|
int session,
|
|
int id)
|
|
{
|
|
ssize_t index = mOutputs.indexOfKey(io);
|
|
if (index < 0) {
|
|
index = mInputs.indexOfKey(io);
|
|
if (index < 0) {
|
|
ALOGW("registerEffect() unknown io %d", io);
|
|
return INVALID_OPERATION;
|
|
}
|
|
}
|
|
|
|
if (mTotalEffectsMemory + desc->memoryUsage > getMaxEffectsMemory()) {
|
|
ALOGW("registerEffect() memory limit exceeded for Fx %s, Memory %d KB",
|
|
desc->name, desc->memoryUsage);
|
|
return INVALID_OPERATION;
|
|
}
|
|
mTotalEffectsMemory += desc->memoryUsage;
|
|
ALOGV("registerEffect() effect %s, io %d, strategy %d session %d id %d",
|
|
desc->name, io, strategy, session, id);
|
|
ALOGV("registerEffect() memory %d, total memory %d", desc->memoryUsage, mTotalEffectsMemory);
|
|
|
|
EffectDescriptor *pDesc = new EffectDescriptor();
|
|
memcpy (&pDesc->mDesc, desc, sizeof(effect_descriptor_t));
|
|
pDesc->mIo = io;
|
|
pDesc->mStrategy = (routing_strategy)strategy;
|
|
pDesc->mSession = session;
|
|
pDesc->mEnabled = false;
|
|
|
|
mEffects.add(id, pDesc);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t AudioPolicyManagerBase::unregisterEffect(int id)
|
|
{
|
|
ssize_t index = mEffects.indexOfKey(id);
|
|
if (index < 0) {
|
|
ALOGW("unregisterEffect() unknown effect ID %d", id);
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
EffectDescriptor *pDesc = mEffects.valueAt(index);
|
|
|
|
setEffectEnabled(pDesc, false);
|
|
|
|
if (mTotalEffectsMemory < pDesc->mDesc.memoryUsage) {
|
|
ALOGW("unregisterEffect() memory %d too big for total %d",
|
|
pDesc->mDesc.memoryUsage, mTotalEffectsMemory);
|
|
pDesc->mDesc.memoryUsage = mTotalEffectsMemory;
|
|
}
|
|
mTotalEffectsMemory -= pDesc->mDesc.memoryUsage;
|
|
ALOGV("unregisterEffect() effect %s, ID %d, memory %d total memory %d",
|
|
pDesc->mDesc.name, id, pDesc->mDesc.memoryUsage, mTotalEffectsMemory);
|
|
|
|
mEffects.removeItem(id);
|
|
delete pDesc;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t AudioPolicyManagerBase::setEffectEnabled(int id, bool enabled)
|
|
{
|
|
ssize_t index = mEffects.indexOfKey(id);
|
|
if (index < 0) {
|
|
ALOGW("unregisterEffect() unknown effect ID %d", id);
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
return setEffectEnabled(mEffects.valueAt(index), enabled);
|
|
}
|
|
|
|
status_t AudioPolicyManagerBase::setEffectEnabled(EffectDescriptor *pDesc, bool enabled)
|
|
{
|
|
if (enabled == pDesc->mEnabled) {
|
|
ALOGV("setEffectEnabled(%s) effect already %s",
|
|
enabled?"true":"false", enabled?"enabled":"disabled");
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
if (enabled) {
|
|
if (mTotalEffectsCpuLoad + pDesc->mDesc.cpuLoad > getMaxEffectsCpuLoad()) {
|
|
ALOGW("setEffectEnabled(true) CPU Load limit exceeded for Fx %s, CPU %f MIPS",
|
|
pDesc->mDesc.name, (float)pDesc->mDesc.cpuLoad/10);
|
|
return INVALID_OPERATION;
|
|
}
|
|
mTotalEffectsCpuLoad += pDesc->mDesc.cpuLoad;
|
|
ALOGV("setEffectEnabled(true) total CPU %d", mTotalEffectsCpuLoad);
|
|
} else {
|
|
if (mTotalEffectsCpuLoad < pDesc->mDesc.cpuLoad) {
|
|
ALOGW("setEffectEnabled(false) CPU load %d too high for total %d",
|
|
pDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad);
|
|
pDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad;
|
|
}
|
|
mTotalEffectsCpuLoad -= pDesc->mDesc.cpuLoad;
|
|
ALOGV("setEffectEnabled(false) total CPU %d", mTotalEffectsCpuLoad);
|
|
}
|
|
pDesc->mEnabled = enabled;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
bool AudioPolicyManagerBase::isStreamActive(int stream, uint32_t inPastMs) const
|
|
{
|
|
nsecs_t sysTime = systemTime();
|
|
for (size_t i = 0; i < mOutputs.size(); i++) {
|
|
if (mOutputs.valueAt(i)->mRefCount[stream] != 0 ||
|
|
ns2ms(sysTime - mOutputs.valueAt(i)->mStopTime[stream]) < inPastMs) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
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, " 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 Can be muted Index Min Index Max Index Cur [device : index]...\n");
|
|
write(fd, buffer, strlen(buffer));
|
|
for (size_t i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
|
|
snprintf(buffer, SIZE, " %02d ", i);
|
|
write(fd, buffer, strlen(buffer));
|
|
mStreams[i].dump(fd);
|
|
}
|
|
|
|
snprintf(buffer, SIZE, "\nTotal Effects CPU: %f MIPS, Total Effects memory: %d KB\n",
|
|
(float)mTotalEffectsCpuLoad/10, mTotalEffectsMemory);
|
|
write(fd, buffer, strlen(buffer));
|
|
|
|
snprintf(buffer, SIZE, "Registered effects:\n");
|
|
write(fd, buffer, strlen(buffer));
|
|
for (size_t i = 0; i < mEffects.size(); i++) {
|
|
snprintf(buffer, SIZE, "- Effect %d dump:\n", mEffects.keyAt(i));
|
|
write(fd, buffer, strlen(buffer));
|
|
mEffects.valueAt(i)->dump(fd);
|
|
}
|
|
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// AudioPolicyManagerBase
|
|
// ----------------------------------------------------------------------------
|
|
|
|
AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)
|
|
:
|
|
#ifdef AUDIO_POLICY_TEST
|
|
Thread(false),
|
|
#endif //AUDIO_POLICY_TEST
|
|
mPhoneState(AudioSystem::MODE_NORMAL),
|
|
mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
|
|
mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
|
|
mA2dpSuspended(false)
|
|
{
|
|
mpClientInterface = clientInterface;
|
|
|
|
for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
|
|
mForceUse[i] = AudioSystem::FORCE_NONE;
|
|
}
|
|
|
|
initializeVolumeCurves();
|
|
|
|
// 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) {
|
|
ALOGE("Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d",
|
|
outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
|
|
} else {
|
|
addOutput(mHardwareOutput, outputDesc);
|
|
setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
|
|
//TODO: configure audio effect output stage here
|
|
}
|
|
|
|
updateDeviceForStrategy();
|
|
#ifdef AUDIO_POLICY_TEST
|
|
if (mHardwareOutput != 0) {
|
|
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();
|
|
}
|
|
|
|
status_t AudioPolicyManagerBase::initCheck()
|
|
{
|
|
return (mHardwareOutput == 0) ? NO_INIT : NO_ERROR;
|
|
}
|
|
|
|
#ifdef AUDIO_POLICY_TEST
|
|
bool AudioPolicyManagerBase::threadLoop()
|
|
{
|
|
ALOGV("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) {
|
|
ALOGV("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) {
|
|
ALOGE("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
|
|
ALOGV("opening A2DP output for device %s", device_address);
|
|
AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
|
|
outputDesc->mDevice = device;
|
|
mA2dpOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
|
|
&outputDesc->mSamplingRate,
|
|
&outputDesc->mFormat,
|
|
&outputDesc->mChannels,
|
|
&outputDesc->mLatency,
|
|
outputDesc->mFlags);
|
|
if (mA2dpOutput) {
|
|
// add A2DP output descriptor
|
|
addOutput(mA2dpOutput, outputDesc);
|
|
|
|
//TODO: configure audio effect output stage here
|
|
|
|
// set initial stream volume for A2DP device
|
|
applyStreamVolumes(mA2dpOutput, device);
|
|
if (a2dpUsedForSonification()) {
|
|
mDuplicatedOutput = mpClientInterface->openDuplicateOutput(mA2dpOutput, mHardwareOutput);
|
|
}
|
|
if (mDuplicatedOutput != 0 ||
|
|
!a2dpUsedForSonification()) {
|
|
// If both A2DP and duplicated outputs are open, send device address to A2DP hardware
|
|
// interface
|
|
AudioParameter param;
|
|
param.add(String8("a2dp_sink_address"), String8(device_address));
|
|
mpClientInterface->setParameters(mA2dpOutput, param.toString());
|
|
mA2dpDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
|
|
|
|
if (a2dpUsedForSonification()) {
|
|
// add duplicated output descriptor
|
|
AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor();
|
|
dupOutputDesc->mOutput1 = mOutputs.valueFor(mHardwareOutput);
|
|
dupOutputDesc->mOutput2 = mOutputs.valueFor(mA2dpOutput);
|
|
dupOutputDesc->mSamplingRate = outputDesc->mSamplingRate;
|
|
dupOutputDesc->mFormat = outputDesc->mFormat;
|
|
dupOutputDesc->mChannels = outputDesc->mChannels;
|
|
dupOutputDesc->mLatency = outputDesc->mLatency;
|
|
addOutput(mDuplicatedOutput, dupOutputDesc);
|
|
applyStreamVolumes(mDuplicatedOutput, device);
|
|
}
|
|
} else {
|
|
ALOGW("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 {
|
|
ALOGW("setDeviceConnectionState() could not open A2DP output for device %x", device);
|
|
delete outputDesc;
|
|
return NO_INIT;
|
|
}
|
|
AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
|
|
|
|
if (!a2dpUsedForSonification()) {
|
|
// mute music on A2DP output if a notification or ringtone is playing
|
|
uint32_t refCount = hwOutputDesc->strategyRefCount(STRATEGY_SONIFICATION);
|
|
refCount += hwOutputDesc->strategyRefCount(STRATEGY_ENFORCED_AUDIBLE);
|
|
for (uint32_t i = 0; i < refCount; i++) {
|
|
setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
|
|
}
|
|
}
|
|
|
|
mA2dpSuspended = false;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t AudioPolicyManagerBase::handleA2dpDisconnection(AudioSystem::audio_devices device,
|
|
const char *device_address)
|
|
{
|
|
if (mA2dpOutput == 0) {
|
|
ALOGW("setDeviceConnectionState() disconnecting A2DP and no A2DP output!");
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
if (mA2dpDeviceAddress != device_address) {
|
|
ALOGW("setDeviceConnectionState() disconnecting unknow A2DP sink address %s", device_address);
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
// mute media strategy to avoid outputting sound on hardware output while music stream
|
|
// is switched from A2DP output and before music is paused by music application
|
|
setStrategyMute(STRATEGY_MEDIA, true, mHardwareOutput);
|
|
setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, MUTE_TIME_MS);
|
|
|
|
if (!a2dpUsedForSonification()) {
|
|
// unmute music on A2DP output if a notification or ringtone is playing
|
|
uint32_t refCount = mOutputs.valueFor(mHardwareOutput)->strategyRefCount(STRATEGY_SONIFICATION);
|
|
refCount += mOutputs.valueFor(mHardwareOutput)->strategyRefCount(STRATEGY_ENFORCED_AUDIBLE);
|
|
for (uint32_t i = 0; i < refCount; i++) {
|
|
setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput);
|
|
}
|
|
}
|
|
mA2dpDeviceAddress = "";
|
|
mA2dpSuspended = false;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
void AudioPolicyManagerBase::closeA2dpOutputs()
|
|
{
|
|
|
|
ALOGV("setDeviceConnectionState() closing A2DP and duplicated output!");
|
|
|
|
if (mDuplicatedOutput != 0) {
|
|
AudioOutputDescriptor *dupOutputDesc = mOutputs.valueFor(mDuplicatedOutput);
|
|
AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
|
|
// As all active tracks on duplicated output will be deleted,
|
|
// and as they were also referenced on hardware output, the reference
|
|
// count for their stream type must be adjusted accordingly on
|
|
// hardware output.
|
|
for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
|
|
int refCount = dupOutputDesc->mRefCount[i];
|
|
hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount);
|
|
}
|
|
|
|
mpClientInterface->closeOutput(mDuplicatedOutput);
|
|
delete mOutputs.valueFor(mDuplicatedOutput);
|
|
mOutputs.removeItem(mDuplicatedOutput);
|
|
mDuplicatedOutput = 0;
|
|
}
|
|
if (mA2dpOutput != 0) {
|
|
AudioParameter param;
|
|
param.add(String8("closing"), String8("true"));
|
|
mpClientInterface->setParameters(mA2dpOutput, param.toString());
|
|
|
|
mpClientInterface->closeOutput(mA2dpOutput);
|
|
delete mOutputs.valueFor(mA2dpOutput);
|
|
mOutputs.removeItem(mA2dpOutput);
|
|
mA2dpOutput = 0;
|
|
}
|
|
}
|
|
|
|
void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy)
|
|
{
|
|
uint32_t prevDevice = getDeviceForStrategy(strategy);
|
|
uint32_t curDevice = getDeviceForStrategy(strategy, false);
|
|
bool a2dpWasUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(prevDevice & ~AudioSystem::DEVICE_OUT_SPEAKER));
|
|
bool a2dpIsUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(curDevice & ~AudioSystem::DEVICE_OUT_SPEAKER));
|
|
audio_io_handle_t srcOutput = 0;
|
|
audio_io_handle_t dstOutput = 0;
|
|
|
|
if (a2dpWasUsed && !a2dpIsUsed) {
|
|
bool dupUsed = a2dpUsedForSonification() && a2dpWasUsed && (AudioSystem::popCount(prevDevice) == 2);
|
|
dstOutput = mHardwareOutput;
|
|
if (dupUsed) {
|
|
ALOGV("checkOutputForStrategy() moving strategy %d from duplicated", strategy);
|
|
srcOutput = mDuplicatedOutput;
|
|
} else {
|
|
ALOGV("checkOutputForStrategy() moving strategy %d from a2dp", strategy);
|
|
srcOutput = mA2dpOutput;
|
|
}
|
|
}
|
|
if (a2dpIsUsed && !a2dpWasUsed) {
|
|
bool dupUsed = a2dpUsedForSonification() && a2dpIsUsed && (AudioSystem::popCount(curDevice) == 2);
|
|
srcOutput = mHardwareOutput;
|
|
if (dupUsed) {
|
|
ALOGV("checkOutputForStrategy() moving strategy %d to duplicated", strategy);
|
|
dstOutput = mDuplicatedOutput;
|
|
} else {
|
|
ALOGV("checkOutputForStrategy() moving strategy %d to a2dp", strategy);
|
|
dstOutput = mA2dpOutput;
|
|
}
|
|
}
|
|
|
|
if (srcOutput != 0 && dstOutput != 0) {
|
|
// Move effects associated to this strategy from previous output to new output
|
|
for (size_t i = 0; i < mEffects.size(); i++) {
|
|
EffectDescriptor *desc = mEffects.valueAt(i);
|
|
if (desc->mSession != AudioSystem::SESSION_OUTPUT_STAGE &&
|
|
desc->mStrategy == strategy &&
|
|
desc->mIo == srcOutput) {
|
|
ALOGV("checkOutputForStrategy() moving effect %d to output %d", mEffects.keyAt(i), dstOutput);
|
|
mpClientInterface->moveEffects(desc->mSession, srcOutput, dstOutput);
|
|
desc->mIo = dstOutput;
|
|
}
|
|
}
|
|
// Move tracks associated to this strategy from previous output to new output
|
|
for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
|
|
if (getStrategy((AudioSystem::stream_type)i) == strategy) {
|
|
mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, dstOutput);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AudioPolicyManagerBase::checkOutputForAllStrategies()
|
|
{
|
|
checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE);
|
|
checkOutputForStrategy(STRATEGY_PHONE);
|
|
checkOutputForStrategy(STRATEGY_SONIFICATION);
|
|
checkOutputForStrategy(STRATEGY_MEDIA);
|
|
checkOutputForStrategy(STRATEGY_DTMF);
|
|
}
|
|
|
|
void AudioPolicyManagerBase::checkA2dpSuspend()
|
|
{
|
|
// suspend A2DP output if:
|
|
// (NOT already suspended) &&
|
|
// ((SCO device is connected &&
|
|
// (forced usage for communication || for record is SCO))) ||
|
|
// (phone state is ringing || in call)
|
|
//
|
|
// restore A2DP output if:
|
|
// (Already suspended) &&
|
|
// ((SCO device is NOT connected ||
|
|
// (forced usage NOT for communication && NOT for record is SCO))) &&
|
|
// (phone state is NOT ringing && NOT in call)
|
|
//
|
|
if (mA2dpOutput == 0) {
|
|
return;
|
|
}
|
|
|
|
if (mA2dpSuspended) {
|
|
if (((mScoDeviceAddress == "") ||
|
|
((mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO) &&
|
|
(mForceUse[AudioSystem::FOR_RECORD] != AudioSystem::FORCE_BT_SCO))) &&
|
|
((mPhoneState != AudioSystem::MODE_IN_CALL) &&
|
|
(mPhoneState != AudioSystem::MODE_RINGTONE))) {
|
|
|
|
mpClientInterface->restoreOutput(mA2dpOutput);
|
|
mA2dpSuspended = false;
|
|
}
|
|
} else {
|
|
if (((mScoDeviceAddress != "") &&
|
|
((mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
|
|
(mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO))) ||
|
|
((mPhoneState == AudioSystem::MODE_IN_CALL) ||
|
|
(mPhoneState == AudioSystem::MODE_RINGTONE))) {
|
|
|
|
mpClientInterface->suspendOutput(mA2dpOutput);
|
|
mA2dpSuspended = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#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: the strategy enforced audible is active on the output:
|
|
// use device for strategy enforced audible
|
|
// 2: we are in call or the strategy phone is active on the output:
|
|
// use device for strategy phone
|
|
// 3: the strategy sonification is active on the output:
|
|
// use device for strategy sonification
|
|
// 4: the strategy media is active on the output:
|
|
// use device for strategy media
|
|
// 5: the strategy DTMF is active on the output:
|
|
// use device for strategy DTMF
|
|
if (outputDesc->isUsedByStrategy(STRATEGY_ENFORCED_AUDIBLE)) {
|
|
device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache);
|
|
} else if (isInCall() ||
|
|
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);
|
|
}
|
|
|
|
ALOGV("getNewDevice() selected device %x", device);
|
|
return device;
|
|
}
|
|
|
|
uint32_t AudioPolicyManagerBase::getStrategyForStream(AudioSystem::stream_type stream) {
|
|
return (uint32_t)getStrategy(stream);
|
|
}
|
|
|
|
uint32_t AudioPolicyManagerBase::getDevicesForStream(AudioSystem::stream_type stream) {
|
|
uint32_t devices;
|
|
// By checking the range of stream before calling getStrategy, we avoid
|
|
// getStrategy's behavior for invalid streams. getStrategy would do a ALOGE
|
|
// and then return STRATEGY_MEDIA, but we want to return the empty set.
|
|
if (stream < (AudioSystem::stream_type) 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
|
|
devices = 0;
|
|
} else {
|
|
AudioPolicyManagerBase::routing_strategy strategy = getStrategy(stream);
|
|
devices = getDeviceForStrategy(strategy, true);
|
|
}
|
|
return devices;
|
|
}
|
|
|
|
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:
|
|
return STRATEGY_SONIFICATION;
|
|
case AudioSystem::DTMF:
|
|
return STRATEGY_DTMF;
|
|
default:
|
|
ALOGE("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;
|
|
case AudioSystem::ENFORCED_AUDIBLE:
|
|
return STRATEGY_ENFORCED_AUDIBLE;
|
|
}
|
|
}
|
|
|
|
uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache)
|
|
{
|
|
uint32_t device = 0;
|
|
|
|
if (fromCache) {
|
|
ALOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]);
|
|
return mDeviceForStrategy[strategy];
|
|
}
|
|
|
|
switch (strategy) {
|
|
case STRATEGY_DTMF:
|
|
if (!isInCall()) {
|
|
// 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 (!isInCall() || strategy != STRATEGY_DTMF) {
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
|
|
if (device) break;
|
|
}
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
|
|
if (device) break;
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO;
|
|
if (device) break;
|
|
// if SCO device is requested but no SCO device is available, fall back to default case
|
|
// FALL THROUGH
|
|
|
|
default: // FORCE_NONE
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
|
|
if (device) break;
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
|
|
if (device) break;
|
|
#ifdef WITH_A2DP
|
|
// when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
|
|
if (!isInCall() && !mA2dpSuspended) {
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
|
|
if (device) break;
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
|
|
if (device) break;
|
|
}
|
|
#endif
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET;
|
|
if (device) break;
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
|
|
if (device) break;
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
|
|
if (device) break;
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE;
|
|
if (device == 0) {
|
|
ALOGE("getDeviceForStrategy() earpiece device not found");
|
|
}
|
|
break;
|
|
|
|
case AudioSystem::FORCE_SPEAKER:
|
|
#ifdef WITH_A2DP
|
|
// when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
|
|
// A2DP speaker when forcing to speaker output
|
|
if (!isInCall() && !mA2dpSuspended) {
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
|
|
if (device) break;
|
|
}
|
|
#endif
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET;
|
|
if (device) break;
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
|
|
if (device) break;
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
|
|
if (device) break;
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
|
|
if (device == 0) {
|
|
ALOGE("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 (isInCall()) {
|
|
device = getDeviceForStrategy(STRATEGY_PHONE, false);
|
|
break;
|
|
}
|
|
// FALL THROUGH
|
|
|
|
case STRATEGY_ENFORCED_AUDIBLE:
|
|
// strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION
|
|
// except when in call where it doesn't default to STRATEGY_PHONE behavior
|
|
|
|
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
|
|
if (device == 0) {
|
|
ALOGE("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_WIRED_HEADPHONE;
|
|
if (device2 == 0) {
|
|
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
|
|
}
|
|
#ifdef WITH_A2DP
|
|
if ((mA2dpOutput != 0) && !mA2dpSuspended &&
|
|
(strategy == STRATEGY_MEDIA || a2dpUsedForSonification())) {
|
|
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_DGTL_DOCK_HEADSET;
|
|
}
|
|
if (device2 == 0) {
|
|
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
|
|
}
|
|
if (device2 == 0) {
|
|
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
|
|
}
|
|
if (device2 == 0) {
|
|
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
|
|
}
|
|
|
|
// device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or
|
|
// STRATEGY_ENFORCED_AUDIBLE, 0 otherwise
|
|
device |= device2;
|
|
if (device == 0) {
|
|
ALOGE("getDeviceForStrategy() speaker device not found");
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
ALOGW("getDeviceForStrategy() unknown strategy: %d", strategy);
|
|
break;
|
|
}
|
|
|
|
ALOGV("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)
|
|
{
|
|
ALOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs);
|
|
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
|
|
|
|
|
|
if (outputDesc->isDuplicated()) {
|
|
setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);
|
|
setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);
|
|
return;
|
|
}
|
|
#ifdef WITH_A2DP
|
|
// filter devices according to output selected
|
|
if (output == mA2dpOutput) {
|
|
device &= AudioSystem::DEVICE_OUT_ALL_A2DP;
|
|
} else {
|
|
device &= ~AudioSystem::DEVICE_OUT_ALL_A2DP;
|
|
}
|
|
#endif
|
|
|
|
uint32_t prevDevice = (uint32_t)outputDesc->device();
|
|
// Do not change the routing if:
|
|
// - the requestede device is 0
|
|
// - the requested device is the same as current device and force is not specified.
|
|
// Doing this check here allows the caller to call setOutputDevice() without conditions
|
|
if ((device == 0 || device == prevDevice) && !force) {
|
|
ALOGV("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
|
|
// FIXME: increased delay due to larger buffers used for low power audio mode.
|
|
// remove when low power audio is controlled by policy manager.
|
|
usleep(outputDesc->mLatency*8*1000);
|
|
}
|
|
|
|
// 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);
|
|
|
|
// 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:
|
|
case AUDIO_SOURCE_VOICE_COMMUNICATION:
|
|
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:
|
|
ALOGW("getDeviceForInputSource() invalid input source %d", inputSource);
|
|
device = 0;
|
|
break;
|
|
}
|
|
ALOGV("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;
|
|
}
|
|
|
|
|
|
audio_devices_t AudioPolicyManagerBase::getDeviceForVolume(audio_devices_t device)
|
|
{
|
|
if (device == 0) {
|
|
// this happens when forcing a route update and no track is active on an output.
|
|
// In this case the returned category is not important.
|
|
device = AUDIO_DEVICE_OUT_SPEAKER;
|
|
} else if (AudioSystem::popCount(device) > 1) {
|
|
// Multiple device selection is either:
|
|
// - speaker + one other device: give priority to speaker in this case.
|
|
// - one A2DP device + another device: happens with duplicated output. In this case
|
|
// retain the device on the A2DP output as the other must not correspond to an active
|
|
// selection if not the speaker.
|
|
if (device & AUDIO_DEVICE_OUT_SPEAKER) {
|
|
device = AUDIO_DEVICE_OUT_SPEAKER;
|
|
} else {
|
|
device = (audio_devices_t)(device & AUDIO_DEVICE_OUT_ALL_A2DP);
|
|
}
|
|
}
|
|
|
|
ALOGW_IF(AudioSystem::popCount(device) != 1,
|
|
"getDeviceForVolume() invalid device combination: %08x",
|
|
device);
|
|
|
|
return device;
|
|
}
|
|
|
|
AudioPolicyManagerBase::device_category AudioPolicyManagerBase::getDeviceCategory(uint32_t device)
|
|
{
|
|
switch(getDeviceForVolume((audio_devices_t)device)) {
|
|
case AUDIO_DEVICE_OUT_EARPIECE:
|
|
return DEVICE_CATEGORY_EARPIECE;
|
|
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
|
|
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
|
|
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
|
|
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
|
|
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
|
|
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
|
|
return DEVICE_CATEGORY_HEADSET;
|
|
case AUDIO_DEVICE_OUT_SPEAKER:
|
|
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
|
|
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
|
|
case AUDIO_DEVICE_OUT_AUX_DIGITAL:
|
|
default:
|
|
return DEVICE_CATEGORY_SPEAKER;
|
|
}
|
|
}
|
|
|
|
float AudioPolicyManagerBase::volIndexToAmpl(uint32_t device, const StreamDescriptor& streamDesc,
|
|
int indexInUi)
|
|
{
|
|
device_category deviceCategory = getDeviceCategory(device);
|
|
const VolumeCurvePoint *curve = streamDesc.mVolumeCurve[deviceCategory];
|
|
|
|
// the volume index in the UI is relative to the min and max volume indices for this stream type
|
|
int nbSteps = 1 + curve[VOLMAX].mIndex -
|
|
curve[VOLMIN].mIndex;
|
|
int volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) /
|
|
(streamDesc.mIndexMax - streamDesc.mIndexMin);
|
|
|
|
// find what part of the curve this index volume belongs to, or if it's out of bounds
|
|
int segment = 0;
|
|
if (volIdx < curve[VOLMIN].mIndex) { // out of bounds
|
|
return 0.0f;
|
|
} else if (volIdx < curve[VOLKNEE1].mIndex) {
|
|
segment = 0;
|
|
} else if (volIdx < curve[VOLKNEE2].mIndex) {
|
|
segment = 1;
|
|
} else if (volIdx <= curve[VOLMAX].mIndex) {
|
|
segment = 2;
|
|
} else { // out of bounds
|
|
return 1.0f;
|
|
}
|
|
|
|
// linear interpolation in the attenuation table in dB
|
|
float decibels = curve[segment].mDBAttenuation +
|
|
((float)(volIdx - curve[segment].mIndex)) *
|
|
( (curve[segment+1].mDBAttenuation -
|
|
curve[segment].mDBAttenuation) /
|
|
((float)(curve[segment+1].mIndex -
|
|
curve[segment].mIndex)) );
|
|
|
|
float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 )
|
|
|
|
ALOGV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f",
|
|
curve[segment].mIndex, volIdx,
|
|
curve[segment+1].mIndex,
|
|
curve[segment].mDBAttenuation,
|
|
decibels,
|
|
curve[segment+1].mDBAttenuation,
|
|
amplification);
|
|
|
|
return amplification;
|
|
}
|
|
|
|
const AudioPolicyManagerBase::VolumeCurvePoint
|
|
AudioPolicyManagerBase::sDefaultVolumeCurve[AudioPolicyManagerBase::VOLCNT] = {
|
|
{1, -49.5f}, {33, -33.5f}, {66, -17.0f}, {100, 0.0f}
|
|
};
|
|
|
|
const AudioPolicyManagerBase::VolumeCurvePoint
|
|
AudioPolicyManagerBase::sDefaultMediaVolumeCurve[AudioPolicyManagerBase::VOLCNT] = {
|
|
{1, -58.0f}, {20, -40.0f}, {60, -17.0f}, {100, 0.0f}
|
|
};
|
|
|
|
const AudioPolicyManagerBase::VolumeCurvePoint
|
|
AudioPolicyManagerBase::sSpeakerMediaVolumeCurve[AudioPolicyManagerBase::VOLCNT] = {
|
|
{1, -56.0f}, {20, -34.0f}, {60, -11.0f}, {100, 0.0f}
|
|
};
|
|
|
|
const AudioPolicyManagerBase::VolumeCurvePoint
|
|
AudioPolicyManagerBase::sSpeakerSonificationVolumeCurve[AudioPolicyManagerBase::VOLCNT] = {
|
|
{1, -29.7f}, {33, -20.1f}, {66, -10.2f}, {100, 0.0f}
|
|
};
|
|
|
|
|
|
const AudioPolicyManagerBase::VolumeCurvePoint
|
|
*AudioPolicyManagerBase::sVolumeProfiles[AudioPolicyManagerBase::NUM_STRATEGIES]
|
|
[AudioPolicyManagerBase::DEVICE_CATEGORY_CNT] = {
|
|
{ // STRATEGY_MEDIA
|
|
sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET
|
|
sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER
|
|
sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EARPIECE
|
|
},
|
|
{ // STRATEGY_PHONE
|
|
sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET
|
|
sDefaultVolumeCurve, // DEVICE_CATEGORY_SPEAKER
|
|
sDefaultVolumeCurve // DEVICE_CATEGORY_EARPIECE
|
|
},
|
|
{ // STRATEGY_SONIFICATION
|
|
sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET
|
|
sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER
|
|
sDefaultVolumeCurve // DEVICE_CATEGORY_EARPIECE
|
|
},
|
|
{ // STRATEGY_DTMF
|
|
sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET
|
|
sDefaultVolumeCurve, // DEVICE_CATEGORY_SPEAKER
|
|
sDefaultVolumeCurve // DEVICE_CATEGORY_EARPIECE
|
|
},
|
|
{ // STRATEGY_ENFORCED_AUDIBLE
|
|
sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET
|
|
sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER
|
|
sDefaultVolumeCurve // DEVICE_CATEGORY_EARPIECE
|
|
},
|
|
};
|
|
|
|
void AudioPolicyManagerBase::initializeVolumeCurves()
|
|
{
|
|
for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
|
|
for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) {
|
|
mStreams[i].mVolumeCurve[j] =
|
|
sVolumeProfiles[getStrategy((AudioSystem::stream_type)i)][j];
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
// if volume is not 0 (not muted), force media volume to max on digital output
|
|
if (stream == AudioSystem::MUSIC &&
|
|
index != mStreams[stream].mIndexMin &&
|
|
(device == AudioSystem::DEVICE_OUT_AUX_DIGITAL ||
|
|
device == AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET)) {
|
|
return 1.0;
|
|
}
|
|
|
|
volume = volIndexToAmpl(device, streamDesc, index);
|
|
|
|
// if a headset is connected, apply the following rules to ring tones and notifications
|
|
// to avoid sound level bursts in user's ears:
|
|
// - always attenuate ring tones and notifications volume by 6dB
|
|
// - if music is playing, always limit the volume to current music volume,
|
|
// with a minimum threshold at -36dB so that notification is always perceived.
|
|
if ((device &
|
|
(AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP |
|
|
AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
|
|
AudioSystem::DEVICE_OUT_WIRED_HEADSET |
|
|
AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) &&
|
|
((getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) ||
|
|
(stream == AudioSystem::SYSTEM)) &&
|
|
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].getVolumeIndex((audio_devices_t)device),
|
|
output,
|
|
(uint32_t)device);
|
|
float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ?
|
|
musicVol : SONIFICATION_HEADSET_VOLUME_MIN;
|
|
if (volume > minVol) {
|
|
volume = minVol;
|
|
ALOGV("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) {
|
|
ALOGV("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)) {
|
|
ALOGV("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);
|
|
// We actually change the volume if:
|
|
// - the float value returned by computeVolume() changed
|
|
// - the force flag is set
|
|
if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
|
|
force) {
|
|
mOutputs.valueFor(output)->mCurVolume[stream] = volume;
|
|
ALOGV("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) {
|
|
// 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;
|
|
// Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
|
|
// enabled
|
|
if (stream == AudioSystem::BLUETOOTH_SCO) {
|
|
mpClientInterface->setStreamVolume(AudioSystem::VOICE_CALL, volume, output, delayMs);
|
|
}
|
|
}
|
|
|
|
mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);
|
|
}
|
|
|
|
if (stream == AudioSystem::VOICE_CALL ||
|
|
stream == AudioSystem::BLUETOOTH_SCO) {
|
|
float voiceVolume;
|
|
// Force voice volume to max for bluetooth SCO as volume is managed by the headset
|
|
if (stream == AudioSystem::VOICE_CALL) {
|
|
voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
|
|
} else {
|
|
voiceVolume = 1.0;
|
|
}
|
|
|
|
if (voiceVolume != mLastVoiceVolume && output == mHardwareOutput) {
|
|
mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
|
|
mLastVoiceVolume = voiceVolume;
|
|
}
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
void AudioPolicyManagerBase::applyStreamVolumes(audio_io_handle_t output,
|
|
uint32_t device,
|
|
int delayMs,
|
|
bool force)
|
|
{
|
|
ALOGV("applyStreamVolumes() for output %d and device %x", output, device);
|
|
|
|
for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
|
|
checkAndSetVolume(stream,
|
|
mStreams[stream].getVolumeIndex((audio_devices_t)device),
|
|
output,
|
|
device,
|
|
delayMs,
|
|
force);
|
|
}
|
|
}
|
|
|
|
void AudioPolicyManagerBase::setStrategyMute(routing_strategy strategy, bool on, audio_io_handle_t output, int delayMs)
|
|
{
|
|
ALOGV("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);
|
|
uint32_t device = outputDesc->device();
|
|
|
|
ALOGV("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, device, delayMs);
|
|
}
|
|
}
|
|
// increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
|
|
outputDesc->mMuteCount[stream]++;
|
|
} else {
|
|
if (outputDesc->mMuteCount[stream] == 0) {
|
|
ALOGW("setStreamMute() unmuting non muted stream!");
|
|
return;
|
|
}
|
|
if (--outputDesc->mMuteCount[stream] == 0) {
|
|
checkAndSetVolume(stream,
|
|
streamDesc.getVolumeIndex((audio_devices_t)device),
|
|
output,
|
|
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);
|
|
ALOGV("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)) {
|
|
ALOGV("handleIncallSonification() low visibility, muteCount %d", muteCount);
|
|
for (int i = 0; i < muteCount; i++) {
|
|
setStreamMute(stream, starting, mHardwareOutput);
|
|
}
|
|
} else {
|
|
ALOGV("handleIncallSonification() high visibility");
|
|
if (outputDesc->device() & getDeviceForStrategy(STRATEGY_PHONE)) {
|
|
ALOGV("handleIncallSonification() high visibility muted, muteCount %d", muteCount);
|
|
for (int i = 0; i < muteCount; i++) {
|
|
setStreamMute(stream, starting, mHardwareOutput);
|
|
}
|
|
}
|
|
if (starting) {
|
|
mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL);
|
|
} else {
|
|
mpClientInterface->stopTone();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AudioPolicyManagerBase::isInCall()
|
|
{
|
|
return isStateInCall(mPhoneState);
|
|
}
|
|
|
|
bool AudioPolicyManagerBase::isStateInCall(int state) {
|
|
return ((state == AudioSystem::MODE_IN_CALL) ||
|
|
(state == AudioSystem::MODE_IN_COMMUNICATION));
|
|
}
|
|
|
|
bool AudioPolicyManagerBase::needsDirectOuput(AudioSystem::stream_type stream,
|
|
uint32_t samplingRate,
|
|
uint32_t format,
|
|
uint32_t channels,
|
|
AudioSystem::output_flags flags,
|
|
uint32_t device)
|
|
{
|
|
return ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
|
|
(format !=0 && !AudioSystem::isLinearPCM(format)));
|
|
}
|
|
|
|
uint32_t AudioPolicyManagerBase::getMaxEffectsCpuLoad()
|
|
{
|
|
return MAX_EFFECTS_CPU_LOAD;
|
|
}
|
|
|
|
uint32_t AudioPolicyManagerBase::getMaxEffectsMemory()
|
|
{
|
|
return MAX_EFFECTS_MEMORY;
|
|
}
|
|
|
|
// --- AudioOutputDescriptor class implementation
|
|
|
|
AudioPolicyManagerBase::AudioOutputDescriptor::AudioOutputDescriptor()
|
|
: mId(0), mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0),
|
|
mFlags((AudioSystem::output_flags)0), mDevice(0), mOutput1(0), mOutput2(0)
|
|
{
|
|
// clear usage count for all stream types
|
|
for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
|
|
mRefCount[i] = 0;
|
|
mCurVolume[i] = -1.0;
|
|
mMuteCount[i] = 0;
|
|
mStopTime[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) {
|
|
ALOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]);
|
|
mRefCount[stream] = 0;
|
|
return;
|
|
}
|
|
mRefCount[stream] += delta;
|
|
ALOGV("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),
|
|
mInputSource(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
|
|
|
|
AudioPolicyManagerBase::StreamDescriptor::StreamDescriptor()
|
|
: mIndexMin(0), mIndexMax(1), mCanBeMuted(true)
|
|
{
|
|
mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT, 0);
|
|
}
|
|
|
|
int AudioPolicyManagerBase::StreamDescriptor::getVolumeIndex(audio_devices_t device)
|
|
{
|
|
device = AudioPolicyManagerBase::getDeviceForVolume(device);
|
|
// there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT
|
|
if (mIndexCur.indexOfKey(device) < 0) {
|
|
device = AUDIO_DEVICE_OUT_DEFAULT;
|
|
}
|
|
return mIndexCur.valueFor(device);
|
|
}
|
|
|
|
void AudioPolicyManagerBase::StreamDescriptor::dump(int fd)
|
|
{
|
|
const size_t SIZE = 256;
|
|
char buffer[SIZE];
|
|
String8 result;
|
|
|
|
snprintf(buffer, SIZE, "%s %02d %02d ",
|
|
mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax);
|
|
result.append(buffer);
|
|
for (size_t i = 0; i < mIndexCur.size(); i++) {
|
|
snprintf(buffer, SIZE, "%04x : %02d, ",
|
|
mIndexCur.keyAt(i),
|
|
mIndexCur.valueAt(i));
|
|
result.append(buffer);
|
|
}
|
|
result.append("\n");
|
|
|
|
write(fd, result.string(), result.size());
|
|
}
|
|
|
|
// --- EffectDescriptor class implementation
|
|
|
|
status_t AudioPolicyManagerBase::EffectDescriptor::dump(int fd)
|
|
{
|
|
const size_t SIZE = 256;
|
|
char buffer[SIZE];
|
|
String8 result;
|
|
|
|
snprintf(buffer, SIZE, " I/O: %d\n", mIo);
|
|
result.append(buffer);
|
|
snprintf(buffer, SIZE, " Strategy: %d\n", mStrategy);
|
|
result.append(buffer);
|
|
snprintf(buffer, SIZE, " Session: %d\n", mSession);
|
|
result.append(buffer);
|
|
snprintf(buffer, SIZE, " Name: %s\n", mDesc.name);
|
|
result.append(buffer);
|
|
snprintf(buffer, SIZE, " %s\n", mEnabled ? "Enabled" : "Disabled");
|
|
result.append(buffer);
|
|
write(fd, result.string(), result.size());
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
}; // namespace android
|