84cf7d9e88
Part 1 of the fix: when the user doesn't elect to use the car dock for music and media, the APM was not aware of the device being docked. This is fixed by dissociating the notification for the APM of the docking to the dock from the sink state change of the A2DP device. Also missing was forcing the volumes to be reevaluated whenever the device is docked or undocked, as volumes for docks may differ, even when the same output device is being used. Change-Id: If5314e27821a71adbd6df6fdf887c45208241d96
1946 lines
76 KiB
C++
1946 lines
76 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 <media/mediarecorder.h>
|
|
|
|
namespace android {
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// AudioPolicyInterface implementation
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_devices device,
|
|
AudioSystem::device_connection_state state,
|
|
const char *device_address)
|
|
{
|
|
|
|
LOGV("setDeviceConnectionState() device: %x, state %d, address %s", device, state, device_address);
|
|
|
|
// connect/disconnect only 1 device at a time
|
|
if (AudioSystem::popCount(device) != 1) return BAD_VALUE;
|
|
|
|
if (strlen(device_address) >= MAX_DEVICE_ADDRESS_LEN) {
|
|
LOGE("setDeviceConnectionState() invalid address: %s", device_address);
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
// handle output devices
|
|
if (AudioSystem::isOutputDevice(device)) {
|
|
|
|
#ifndef WITH_A2DP
|
|
if (AudioSystem::isA2dpDevice(device)) {
|
|
LOGE("setDeviceConnectionState() invalid device: %x", device);
|
|
return BAD_VALUE;
|
|
}
|
|
#endif
|
|
|
|
switch (state)
|
|
{
|
|
// handle output device connection
|
|
case AudioSystem::DEVICE_STATE_AVAILABLE:
|
|
if (mAvailableOutputDevices & device) {
|
|
LOGW("setDeviceConnectionState() device already connected: %x", device);
|
|
return INVALID_OPERATION;
|
|
}
|
|
LOGV("setDeviceConnectionState() connecting device %x", device);
|
|
|
|
// register new device as available
|
|
mAvailableOutputDevices |= device;
|
|
|
|
#ifdef WITH_A2DP
|
|
// handle A2DP device connection
|
|
if (AudioSystem::isA2dpDevice(device)) {
|
|
status_t status = handleA2dpConnection(device, device_address);
|
|
if (status != NO_ERROR) {
|
|
mAvailableOutputDevices &= ~device;
|
|
return status;
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
if (AudioSystem::isBluetoothScoDevice(device)) {
|
|
LOGV("setDeviceConnectionState() BT SCO device, address %s", device_address);
|
|
// keep track of SCO device address
|
|
mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
|
|
#ifdef WITH_A2DP
|
|
if (mA2dpOutput != 0 &&
|
|
mPhoneState != AudioSystem::MODE_NORMAL) {
|
|
mpClientInterface->suspendOutput(mA2dpOutput);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
// handle output device disconnection
|
|
case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
|
|
if (!(mAvailableOutputDevices & device)) {
|
|
LOGW("setDeviceConnectionState() device not connected: %x", device);
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
|
|
LOGV("setDeviceConnectionState() disconnecting device %x", device);
|
|
// remove device from available output devices
|
|
mAvailableOutputDevices &= ~device;
|
|
|
|
#ifdef WITH_A2DP
|
|
// handle A2DP device disconnection
|
|
if (AudioSystem::isA2dpDevice(device)) {
|
|
status_t status = handleA2dpDisconnection(device, device_address);
|
|
if (status != NO_ERROR) {
|
|
mAvailableOutputDevices |= device;
|
|
return status;
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
if (AudioSystem::isBluetoothScoDevice(device)) {
|
|
mScoDeviceAddress = "";
|
|
#ifdef WITH_A2DP
|
|
if (mA2dpOutput != 0 &&
|
|
mPhoneState != AudioSystem::MODE_NORMAL) {
|
|
mpClientInterface->restoreOutput(mA2dpOutput);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
LOGE("setDeviceConnectionState() invalid state: %x", state);
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
// request routing change if necessary
|
|
uint32_t newDevice = getNewDevice(mHardwareOutput, false);
|
|
#ifdef WITH_A2DP
|
|
checkOutputForAllStrategies(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 a SCO device is present.
|
|
if (mA2dpOutput != 0 && mScoDeviceAddress != "") {
|
|
if (oldState == AudioSystem::MODE_NORMAL) {
|
|
mpClientInterface->suspendOutput(mA2dpOutput);
|
|
} else if (state == AudioSystem::MODE_NORMAL) {
|
|
mpClientInterface->restoreOutput(mA2dpOutput);
|
|
}
|
|
}
|
|
#endif
|
|
updateDeviceForStrategy();
|
|
|
|
AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
|
|
|
|
// force routing command to audio hardware when ending call
|
|
// even if no device change is needed
|
|
if (oldState == AudioSystem::MODE_IN_CALL && newDevice == 0) {
|
|
newDevice = hwOutputDesc->device();
|
|
}
|
|
|
|
// when changing from ring tone to in call mode, mute the ringing tone
|
|
// immediately and delay the route change to avoid sending the ring tone
|
|
// tail into the earpiece or headset.
|
|
int delayMs = 0;
|
|
if (state == AudioSystem::MODE_IN_CALL && oldState == AudioSystem::MODE_RINGTONE) {
|
|
// delay the device change command by twice the output latency to have some margin
|
|
// and be sure that audio buffers not yet affected by the mute are out when
|
|
// we actually apply the route change
|
|
delayMs = hwOutputDesc->mLatency*2;
|
|
setStreamMute(AudioSystem::RING, true, mHardwareOutput);
|
|
}
|
|
|
|
// change routing is necessary
|
|
setOutputDevice(mHardwareOutput, newDevice, force, delayMs);
|
|
|
|
// if entering in call state, handle special case of active streams
|
|
// pertaining to sonification strategy see handleIncallSonification()
|
|
if (state == AudioSystem::MODE_IN_CALL) {
|
|
LOGV("setPhoneState() in call state management: new state is %d", state);
|
|
// unmute the ringing tone after a sufficient delay if it was muted before
|
|
// setting output device above
|
|
if (oldState == AudioSystem::MODE_RINGTONE) {
|
|
setStreamMute(AudioSystem::RING, false, mHardwareOutput, MUTE_TIME_MS);
|
|
}
|
|
for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
|
|
handleIncallSonification(stream, true, true);
|
|
}
|
|
}
|
|
|
|
// Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE
|
|
if (state == AudioSystem::MODE_RINGTONE &&
|
|
(hwOutputDesc->mRefCount[AudioSystem::MUSIC] ||
|
|
(systemTime() - mMusicStopTime) < seconds(SONIFICATION_HEADSET_MUSIC_DELAY))) {
|
|
mLimitRingtoneVolume = true;
|
|
} else {
|
|
mLimitRingtoneVolume = false;
|
|
}
|
|
}
|
|
|
|
void AudioPolicyManagerBase::setRingerMode(uint32_t mode, uint32_t mask)
|
|
{
|
|
LOGV("setRingerMode() mode %x, mask %x", mode, mask);
|
|
|
|
mRingerMode = mode;
|
|
}
|
|
|
|
void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
|
|
{
|
|
LOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState);
|
|
|
|
bool forceVolumeReeval = false;
|
|
switch(usage) {
|
|
case AudioSystem::FOR_COMMUNICATION:
|
|
if (config != AudioSystem::FORCE_SPEAKER && config != AudioSystem::FORCE_BT_SCO &&
|
|
config != AudioSystem::FORCE_NONE) {
|
|
LOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config);
|
|
return;
|
|
}
|
|
mForceUse[usage] = config;
|
|
break;
|
|
case AudioSystem::FOR_MEDIA:
|
|
if (config != AudioSystem::FORCE_HEADPHONES && config != AudioSystem::FORCE_BT_A2DP &&
|
|
config != AudioSystem::FORCE_WIRED_ACCESSORY && config != AudioSystem::FORCE_NONE) {
|
|
LOGW("setForceUse() invalid config %d for FOR_MEDIA", config);
|
|
return;
|
|
}
|
|
mForceUse[usage] = config;
|
|
break;
|
|
case AudioSystem::FOR_RECORD:
|
|
if (config != AudioSystem::FORCE_BT_SCO && config != AudioSystem::FORCE_WIRED_ACCESSORY &&
|
|
config != AudioSystem::FORCE_NONE) {
|
|
LOGW("setForceUse() invalid config %d for FOR_RECORD", config);
|
|
return;
|
|
}
|
|
mForceUse[usage] = config;
|
|
break;
|
|
case AudioSystem::FOR_DOCK:
|
|
if (config != AudioSystem::FORCE_NONE && config != AudioSystem::FORCE_BT_CAR_DOCK &&
|
|
config != AudioSystem::FORCE_BT_DESK_DOCK && config != AudioSystem::FORCE_WIRED_ACCESSORY) {
|
|
LOGW("setForceUse() invalid config %d for FOR_DOCK", config);
|
|
}
|
|
forceVolumeReeval = true;
|
|
mForceUse[usage] = config;
|
|
break;
|
|
default:
|
|
LOGW("setForceUse() invalid usage %d", usage);
|
|
break;
|
|
}
|
|
|
|
// check for device and output changes triggered by new phone state
|
|
uint32_t newDevice = getNewDevice(mHardwareOutput, false);
|
|
#ifdef WITH_A2DP
|
|
checkOutputForAllStrategies(newDevice);
|
|
#endif
|
|
updateDeviceForStrategy();
|
|
setOutputDevice(mHardwareOutput, newDevice);
|
|
if (forceVolumeReeval) {
|
|
applyStreamVolumes(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 (mScoDeviceAddress != "") {
|
|
// It is normal to suspend twice if we are both in call,
|
|
// and have the hardware audio output routed to BT SCO
|
|
if (mPhoneState != AudioSystem::MODE_NORMAL) {
|
|
mpClientInterface->suspendOutput(mA2dpOutput);
|
|
}
|
|
if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)hwOutputDesc->device())) {
|
|
mpClientInterface->suspendOutput(mA2dpOutput);
|
|
}
|
|
}
|
|
|
|
if (!a2dpUsedForSonification()) {
|
|
// mute music on A2DP output if a notification or ringtone is playing
|
|
uint32_t refCount = hwOutputDesc->strategyRefCount(STRATEGY_SONIFICATION);
|
|
for (uint32_t i = 0; i < refCount; i++) {
|
|
setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
|
|
}
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t AudioPolicyManagerBase::handleA2dpDisconnection(AudioSystem::audio_devices device,
|
|
const char *device_address)
|
|
{
|
|
if (mA2dpOutput == 0) {
|
|
LOGW("setDeviceConnectionState() disconnecting A2DP and no A2DP output!");
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
if (mA2dpDeviceAddress != device_address) {
|
|
LOGW("setDeviceConnectionState() disconnecting unknow A2DP sink address %s", device_address);
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
// mute media strategy to avoid outputting sound on hardware output while music stream
|
|
// is switched from A2DP output and before music is paused by music application
|
|
setStrategyMute(STRATEGY_MEDIA, true, mHardwareOutput);
|
|
setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, MUTE_TIME_MS);
|
|
|
|
if (!a2dpUsedForSonification()) {
|
|
// unmute music on A2DP output if a notification or ringtone is playing
|
|
uint32_t refCount = mOutputs.valueFor(mHardwareOutput)->strategyRefCount(STRATEGY_SONIFICATION);
|
|
for (uint32_t i = 0; i < refCount; i++) {
|
|
setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput);
|
|
}
|
|
}
|
|
mA2dpDeviceAddress = "";
|
|
return NO_ERROR;
|
|
}
|
|
|
|
void AudioPolicyManagerBase::closeA2dpOutputs()
|
|
{
|
|
LOGV("setDeviceConnectionState() closing A2DP and duplicated output!");
|
|
|
|
if (mDuplicatedOutput != 0) {
|
|
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 if 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;
|
|
if (device2 == 0) {
|
|
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
|
|
}
|
|
if (device2 == 0) {
|
|
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
|
|
}
|
|
#ifdef WITH_A2DP
|
|
if (mA2dpOutput != 0) {
|
|
if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) {
|
|
break;
|
|
}
|
|
if (device2 == 0) {
|
|
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
|
|
}
|
|
if (device2 == 0) {
|
|
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
|
|
}
|
|
if (device2 == 0) {
|
|
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
|
|
}
|
|
}
|
|
#endif
|
|
if (device2 == 0) {
|
|
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
|
|
}
|
|
|
|
// device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION, 0 otherwise
|
|
device |= device2;
|
|
if (device == 0) {
|
|
LOGE("getDeviceForStrategy() speaker device not found");
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
LOGW("getDeviceForStrategy() unknown strategy: %d", strategy);
|
|
break;
|
|
}
|
|
|
|
LOGV("getDeviceForStrategy() strategy %d, device %x", strategy, device);
|
|
return device;
|
|
}
|
|
|
|
void AudioPolicyManagerBase::updateDeviceForStrategy()
|
|
{
|
|
for (int i = 0; i < NUM_STRATEGIES; i++) {
|
|
mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);
|
|
}
|
|
}
|
|
|
|
void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)
|
|
{
|
|
LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs);
|
|
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
|
|
|
|
|
|
if (outputDesc->isDuplicated()) {
|
|
setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);
|
|
setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);
|
|
return;
|
|
}
|
|
#ifdef WITH_A2DP
|
|
// filter devices according to output selected
|
|
if (output == 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 A2DP output if SCO device is selected
|
|
if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) {
|
|
if (mA2dpOutput != 0) {
|
|
mpClientInterface->suspendOutput(mA2dpOutput);
|
|
}
|
|
}
|
|
#endif
|
|
// do the routing
|
|
AudioParameter param = AudioParameter();
|
|
param.addInt(String8(AudioParameter::keyRouting), (int)device);
|
|
mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs);
|
|
// update stream volumes according to new device
|
|
applyStreamVolumes(output, device, delayMs);
|
|
|
|
#ifdef WITH_A2DP
|
|
// if disconnecting SCO device, restore A2DP output
|
|
if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)prevDevice)) {
|
|
if (mA2dpOutput != 0) {
|
|
LOGV("restore A2DP output");
|
|
mpClientInterface->restoreOutput(mA2dpOutput);
|
|
}
|
|
}
|
|
#endif
|
|
// if changing from a combined headset + speaker route, unmute media streams
|
|
if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) {
|
|
setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);
|
|
}
|
|
}
|
|
|
|
uint32_t AudioPolicyManagerBase::getDeviceForInputSource(int inputSource)
|
|
{
|
|
uint32_t device;
|
|
|
|
switch(inputSource) {
|
|
case AUDIO_SOURCE_DEFAULT:
|
|
case AUDIO_SOURCE_MIC:
|
|
case AUDIO_SOURCE_VOICE_RECOGNITION:
|
|
if (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO &&
|
|
mAvailableInputDevices & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
|
|
device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
|
|
} else if (mAvailableInputDevices & AudioSystem::DEVICE_IN_WIRED_HEADSET) {
|
|
device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
|
|
} else {
|
|
device = AudioSystem::DEVICE_IN_BUILTIN_MIC;
|
|
}
|
|
break;
|
|
case AUDIO_SOURCE_CAMCORDER:
|
|
if (hasBackMicrophone()) {
|
|
device = AudioSystem::DEVICE_IN_BACK_MIC;
|
|
} else {
|
|
device = AudioSystem::DEVICE_IN_BUILTIN_MIC;
|
|
}
|
|
break;
|
|
case AUDIO_SOURCE_VOICE_UPLINK:
|
|
case AUDIO_SOURCE_VOICE_DOWNLINK:
|
|
case AUDIO_SOURCE_VOICE_CALL:
|
|
device = AudioSystem::DEVICE_IN_VOICE_CALL;
|
|
break;
|
|
default:
|
|
LOGW("getInput() invalid input source %d", inputSource);
|
|
device = 0;
|
|
break;
|
|
}
|
|
LOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device);
|
|
return device;
|
|
}
|
|
|
|
audio_io_handle_t AudioPolicyManagerBase::getActiveInput()
|
|
{
|
|
for (size_t i = 0; i < mInputs.size(); i++) {
|
|
if (mInputs.valueAt(i)->mRefCount > 0) {
|
|
return mInputs.keyAt(i);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device)
|
|
{
|
|
float volume = 1.0;
|
|
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
|
|
StreamDescriptor &streamDesc = mStreams[stream];
|
|
|
|
if (device == 0) {
|
|
device = outputDesc->device();
|
|
}
|
|
|
|
int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin);
|
|
volume = AudioSystem::linearToLog(volInt);
|
|
|
|
// if a 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
|