blob: 60790b77a9d5782c08180af762cead979b79cef5 [file] [log] [blame]
/*
* 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
//#define VERY_VERBOSE_LOGGING
#ifdef VERY_VERBOSE_LOGGING
#define ALOGVV ALOGV
#else
#define ALOGVV(a...) do { } while(0)
#endif
// A device mask for all audio input devices that are considered "virtual" when evaluating
// active inputs in getActiveInput()
#define APM_AUDIO_IN_DEVICE_VIRTUAL_ALL AUDIO_DEVICE_IN_REMOTE_SUBMIX
// A device mask for all audio output devices that are considered "remote" when evaluating
// active output devices in isStreamActiveRemotely()
#define APM_AUDIO_OUT_DEVICE_REMOTE_ALL AUDIO_DEVICE_OUT_REMOTE_SUBMIX
#include <inttypes.h>
#include <math.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/Timers.h>
#include <hardware/audio.h>
#include <hardware/audio_effect.h>
#include <hardware_legacy/audio_policy_conf.h>
#include <hardware_legacy/AudioPolicyManagerBase.h>
namespace android_audio_legacy {
// ----------------------------------------------------------------------------
// AudioPolicyInterface implementation
// ----------------------------------------------------------------------------
status_t AudioPolicyManagerBase::setDeviceConnectionState(audio_devices_t device,
AudioSystem::device_connection_state state,
const char *device_address)
{
// device_address can be NULL and should be handled as an empty string in this case,
// and it is not checked by AudioPolicyInterfaceImpl.cpp
if (device_address == NULL) {
device_address = "";
}
ALOGV("setDeviceConnectionState() device: 0x%X, state %d, address %s", device, state, device_address);
// connect/disconnect only 1 device at a time
if (!audio_is_output_device(device) && !audio_is_input_device(device)) 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 (audio_is_output_device(device)) {
SortedVector <audio_io_handle_t> outputs;
if (!mHasA2dp && audio_is_a2dp_out_device(device)) {
ALOGE("setDeviceConnectionState() invalid A2DP device: %x", device);
return BAD_VALUE;
}
if (!mHasUsb && audio_is_usb_out_device(device)) {
ALOGE("setDeviceConnectionState() invalid USB audio device: %x", device);
return BAD_VALUE;
}
if (!mHasRemoteSubmix && audio_is_remote_submix_device((audio_devices_t)device)) {
ALOGE("setDeviceConnectionState() invalid remote submix audio device: %x", device);
return BAD_VALUE;
}
// save a copy of the opened output descriptors before any output is opened or closed
// by checkOutputsForDevice(). This will be needed by checkOutputForAllStrategies()
mPreviousOutputs = mOutputs;
String8 paramStr;
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);
if (mHasA2dp && audio_is_a2dp_out_device(device)) {
// handle A2DP device connection
AudioParameter param;
param.add(String8(AUDIO_PARAMETER_A2DP_SINK_ADDRESS), String8(device_address));
paramStr = param.toString();
} else if (mHasUsb && audio_is_usb_out_device(device)) {
// handle USB device connection
paramStr = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
}
if (checkOutputsForDevice(device, state, outputs, paramStr) != NO_ERROR) {
return INVALID_OPERATION;
}
ALOGV("setDeviceConnectionState() checkOutputsForDevice() returned %zu outputs",
outputs.size());
// register new device as available
mAvailableOutputDevices = (audio_devices_t)(mAvailableOutputDevices | device);
if (mHasA2dp && audio_is_a2dp_out_device(device)) {
// handle A2DP device connection
mA2dpDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
mA2dpSuspended = false;
} else if (audio_is_bluetooth_sco_device(device)) {
// handle SCO device connection
mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
} else if (mHasUsb && audio_is_usb_out_device(device)) {
// handle USB device connection
mUsbOutCardAndDevice = 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 = (audio_devices_t)(mAvailableOutputDevices & ~device);
checkOutputsForDevice(device, state, outputs, paramStr);
if (mHasA2dp && audio_is_a2dp_out_device(device)) {
// handle A2DP device disconnection
mA2dpDeviceAddress = "";
mA2dpSuspended = false;
} else if (audio_is_bluetooth_sco_device(device)) {
// handle SCO device disconnection
mScoDeviceAddress = "";
} else if (mHasUsb && audio_is_usb_out_device(device)) {
// handle USB device disconnection
mUsbOutCardAndDevice = "";
}
// not currently handling multiple simultaneous submixes: ignoring remote submix
// case and address
} break;
default:
ALOGE("setDeviceConnectionState() invalid state: %x", state);
return BAD_VALUE;
}
checkA2dpSuspend();
checkOutputForAllStrategies();
// outputs must be closed after checkOutputForAllStrategies() is executed
if (!outputs.isEmpty()) {
for (size_t i = 0; i < outputs.size(); i++) {
AudioOutputDescriptor *desc = mOutputs.valueFor(outputs[i]);
// close unused outputs after device disconnection or direct outputs that have been
// opened by checkOutputsForDevice() to query dynamic parameters
if ((state == AudioSystem::DEVICE_STATE_UNAVAILABLE) ||
(((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) &&
(desc->mDirectOpenCount == 0))) {
closeOutput(outputs[i]);
}
}
}
updateDevicesAndOutputs();
for (size_t i = 0; i < mOutputs.size(); i++) {
// do not force device change on duplicated output because if device is 0, it will
// also force a device 0 for the two outputs it is duplicated to which may override
// a valid device selection on those outputs.
setOutputDevice(mOutputs.keyAt(i),
getNewDevice(mOutputs.keyAt(i), true /*fromCache*/),
!mOutputs.valueAt(i)->isDuplicated(),
0);
}
return NO_ERROR;
} // end if is output device
// handle input devices
if (audio_is_input_device(device)) {
SortedVector <audio_io_handle_t> inputs;
String8 paramStr;
switch (state)
{
// handle input device connection
case AudioSystem::DEVICE_STATE_AVAILABLE: {
if (mAvailableInputDevices & device) {
ALOGW("setDeviceConnectionState() device already connected: %d", device);
return INVALID_OPERATION;
}
if (mHasUsb && audio_is_usb_in_device(device)) {
// handle USB device connection
paramStr = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
} else if (mHasA2dp && audio_is_a2dp_in_device(device)) {
// handle A2DP device connection
AudioParameter param;
param.add(String8(AUDIO_PARAMETER_A2DP_SOURCE_ADDRESS), String8(device_address));
paramStr = param.toString();
}
if (checkInputsForDevice(device, state, inputs, paramStr) != NO_ERROR) {
return INVALID_OPERATION;
}
mAvailableInputDevices = mAvailableInputDevices | (device & ~AUDIO_DEVICE_BIT_IN);
}
break;
// handle input device disconnection
case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
if (!(mAvailableInputDevices & device)) {
ALOGW("setDeviceConnectionState() device not connected: %d", device);
return INVALID_OPERATION;
}
checkInputsForDevice(device, state, inputs, paramStr);
mAvailableInputDevices = (audio_devices_t) (mAvailableInputDevices & ~device);
} break;
default:
ALOGE("setDeviceConnectionState() invalid state: %x", state);
return BAD_VALUE;
}
closeAllInputs();
return NO_ERROR;
} // end if is input device
ALOGW("setDeviceConnectionState() invalid device: %x", device);
return BAD_VALUE;
}
AudioSystem::device_connection_state AudioPolicyManagerBase::getDeviceConnectionState(audio_devices_t device,
const char *device_address)
{
// similar to setDeviceConnectionState
if (device_address == NULL) {
device_address = "";
}
AudioSystem::device_connection_state state = AudioSystem::DEVICE_STATE_UNAVAILABLE;
String8 address = String8(device_address);
if (audio_is_output_device(device)) {
if (device & mAvailableOutputDevices) {
if (audio_is_a2dp_out_device(device) &&
(!mHasA2dp || (address != "" && mA2dpDeviceAddress != address))) {
return state;
}
if (audio_is_bluetooth_sco_device(device) &&
address != "" && mScoDeviceAddress != address) {
return state;
}
if (audio_is_usb_out_device(device) &&
(!mHasUsb || (address != "" && mUsbOutCardAndDevice != address))) {
ALOGE("getDeviceConnectionState() invalid device: %x", device);
return state;
}
if (audio_is_remote_submix_device((audio_devices_t)device) && !mHasRemoteSubmix) {
return state;
}
state = AudioSystem::DEVICE_STATE_AVAILABLE;
}
} else if (audio_is_input_device(device)) {
if (device & mAvailableInputDevices) {
state = AudioSystem::DEVICE_STATE_AVAILABLE;
}
}
return state;
}
void AudioPolicyManagerBase::setPhoneState(int state)
{
ALOGV("setPhoneState() state %d", state);
audio_devices_t newDevice = AUDIO_DEVICE_NONE;
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;
for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) {
mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] =
sVolumeProfiles[AUDIO_STREAM_VOICE_CALL][j];
}
} 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;
for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) {
mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] =
sVolumeProfiles[AUDIO_STREAM_DTMF][j];
}
} 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(mPrimaryOutput, false /*fromCache*/);
checkA2dpSuspend();
checkOutputForAllStrategies();
updateDevicesAndOutputs();
AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mPrimaryOutput);
// force routing command to audio hardware when ending call
// even if no device change is needed
if (isStateInCall(oldState) && newDevice == AUDIO_DEVICE_NONE) {
newDevice = hwOutputDesc->device();
}
int delayMs = 0;
if (isStateInCall(state)) {
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < mOutputs.size(); i++) {
AudioOutputDescriptor *desc = mOutputs.valueAt(i);
// mute media and sonification strategies and delay device switch by the largest
// latency of any output where either strategy is active.
// This avoid sending the ring tone or music tail into the earpiece or headset.
if ((desc->isStrategyActive(STRATEGY_MEDIA,
SONIFICATION_HEADSET_MUSIC_DELAY,
sysTime) ||
desc->isStrategyActive(STRATEGY_SONIFICATION,
SONIFICATION_HEADSET_MUSIC_DELAY,
sysTime)) &&
(delayMs < (int)desc->mLatency*2)) {
delayMs = desc->mLatency*2;
}
setStrategyMute(STRATEGY_MEDIA, true, mOutputs.keyAt(i));
setStrategyMute(STRATEGY_MEDIA, false, mOutputs.keyAt(i), MUTE_TIME_MS,
getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/));
setStrategyMute(STRATEGY_SONIFICATION, true, mOutputs.keyAt(i));
setStrategyMute(STRATEGY_SONIFICATION, false, mOutputs.keyAt(i), MUTE_TIME_MS,
getDeviceForStrategy(STRATEGY_SONIFICATION, true /*fromCache*/));
}
}
// change routing is necessary
setOutputDevice(mPrimaryOutput, 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);
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 &&
config != AudioSystem::FORCE_NO_BT_A2DP) {
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;
case AudioSystem::FOR_SYSTEM:
if (config != AudioSystem::FORCE_NONE &&
config != AudioSystem::FORCE_SYSTEM_ENFORCED) {
ALOGW("setForceUse() invalid config %d for FOR_SYSTEM", config);
}
forceVolumeReeval = true;
mForceUse[usage] = config;
break;
default:
ALOGW("setForceUse() invalid usage %d", usage);
break;
}
// check for device and output changes triggered by new force usage
checkA2dpSuspend();
checkOutputForAllStrategies();
updateDevicesAndOutputs();
for (size_t i = 0; i < mOutputs.size(); i++) {
audio_io_handle_t output = mOutputs.keyAt(i);
audio_devices_t newDevice = getNewDevice(output, true /*fromCache*/);
setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE));
if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) {
applyStreamVolumes(output, newDevice, 0, true);
}
}
audio_io_handle_t activeInput = getActiveInput();
if (activeInput != 0) {
AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput);
audio_devices_t newDevice = getDeviceForInputSource(inputDesc->mInputSource);
if ((newDevice != AUDIO_DEVICE_NONE) && (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);
}
// Find a direct output profile compatible with the parameters passed, even if the input flags do
// not explicitly request a direct output
AudioPolicyManagerBase::IOProfile *AudioPolicyManagerBase::getProfileForDirectOutput(
audio_devices_t device,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags)
{
for (size_t i = 0; i < mHwModules.size(); i++) {
if (mHwModules[i]->mHandle == 0) {
continue;
}
for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) {
IOProfile *profile = mHwModules[i]->mOutputProfiles[j];
if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
if (profile->isCompatibleProfile(device, samplingRate, format,
channelMask,
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) {
if (mAvailableOutputDevices & profile->mSupportedDevices) {
return mHwModules[i]->mOutputProfiles[j];
}
}
} else {
if (profile->isCompatibleProfile(device, samplingRate, format,
channelMask,
AUDIO_OUTPUT_FLAG_DIRECT)) {
if (mAvailableOutputDevices & profile->mSupportedDevices) {
return mHwModules[i]->mOutputProfiles[j];
}
}
}
}
}
return 0;
}
audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_type stream,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
AudioSystem::output_flags flags,
const audio_offload_info_t *offloadInfo)
{
audio_io_handle_t output = 0;
uint32_t latency = 0;
routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
ALOGV("getOutput() device %d, stream %d, samplingRate %d, format %x, channelMask %x, flags %x",
device, stream, samplingRate, format, channelMask, flags);
#ifdef AUDIO_POLICY_TEST
if (mCurOutput != 0) {
ALOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channelMask %x, mDirectOutput %d",
mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput);
if (mTestOutputs[mCurOutput] == 0) {
ALOGV("getOutput() opening test output");
AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(NULL);
outputDesc->mDevice = mTestDevice;
outputDesc->mSamplingRate = mTestSamplingRate;
outputDesc->mFormat = mTestFormat;
outputDesc->mChannelMask = mTestChannels;
outputDesc->mLatency = mTestLatencyMs;
outputDesc->mFlags = (audio_output_flags_t)(mDirectOutput ? AudioSystem::OUTPUT_FLAG_DIRECT : 0);
outputDesc->mRefCount[stream] = 0;
mTestOutputs[mCurOutput] = mpClientInterface->openOutput(0, &outputDesc->mDevice,
&outputDesc->mSamplingRate,
&outputDesc->mFormat,
&outputDesc->mChannelMask,
&outputDesc->mLatency,
outputDesc->mFlags,
offloadInfo);
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
//force direct flag if offload flag is set: offloading implies a direct output stream
// and all common behaviors are driven by checking only the direct flag
// this should normally be set appropriately in the policy configuration file
if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
flags = (AudioSystem::output_flags)(flags | AUDIO_OUTPUT_FLAG_DIRECT);
}
// Do not allow offloading if one non offloadable effect is enabled. This prevents from
// creating an offloaded track and tearing it down immediately after start when audioflinger
// detects there is an active non offloadable effect.
// FIXME: We should check the audio session here but we do not have it in this context.
// This may prevent offloading in rare situations where effects are left active by apps
// in the background.
IOProfile *profile = NULL;
if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) ||
!isNonOffloadableEffectEnabled()) {
profile = getProfileForDirectOutput(device,
samplingRate,
format,
channelMask,
(audio_output_flags_t)flags);
}
if (profile != NULL) {
AudioOutputDescriptor *outputDesc = NULL;
for (size_t i = 0; i < mOutputs.size(); i++) {
AudioOutputDescriptor *desc = mOutputs.valueAt(i);
if (!desc->isDuplicated() && (profile == desc->mProfile)) {
outputDesc = desc;
// reuse direct output if currently open and configured with same parameters
if ((samplingRate == outputDesc->mSamplingRate) &&
(format == outputDesc->mFormat) &&
(channelMask == outputDesc->mChannelMask)) {
outputDesc->mDirectOpenCount++;
ALOGV("getOutput() reusing direct output %d", mOutputs.keyAt(i));
return mOutputs.keyAt(i);
}
}
}
// close direct output if currently open and configured with different parameters
if (outputDesc != NULL) {
closeOutput(outputDesc->mId);
}
outputDesc = new AudioOutputDescriptor(profile);
outputDesc->mDevice = device;
outputDesc->mSamplingRate = samplingRate;
outputDesc->mFormat = format;
outputDesc->mChannelMask = channelMask;
outputDesc->mLatency = 0;
outputDesc->mFlags =(audio_output_flags_t) (outputDesc->mFlags | flags);
outputDesc->mRefCount[stream] = 0;
outputDesc->mStopTime[stream] = 0;
outputDesc->mDirectOpenCount = 1;
output = mpClientInterface->openOutput(profile->mModule->mHandle,
&outputDesc->mDevice,
&outputDesc->mSamplingRate,
&outputDesc->mFormat,
&outputDesc->mChannelMask,
&outputDesc->mLatency,
outputDesc->mFlags,
offloadInfo);
// only accept an output with the requested parameters
if (output == 0 ||
(samplingRate != 0 && samplingRate != outputDesc->mSamplingRate) ||
(format != AUDIO_FORMAT_DEFAULT && format != outputDesc->mFormat) ||
(channelMask != 0 && channelMask != outputDesc->mChannelMask)) {
ALOGV("getOutput() failed opening direct output: output %d samplingRate %d %d,"
"format %d %d, channelMask %04x %04x", output, samplingRate,
outputDesc->mSamplingRate, format, outputDesc->mFormat, channelMask,
outputDesc->mChannelMask);
if (output != 0) {
mpClientInterface->closeOutput(output);
}
delete outputDesc;
return 0;
}
audio_io_handle_t srcOutput = getOutputForEffect();
addOutput(output, outputDesc);
audio_io_handle_t dstOutput = getOutputForEffect();
if (dstOutput == output) {
mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, srcOutput, dstOutput);
}
mPreviousOutputs = mOutputs;
ALOGV("getOutput() returns new direct output %d", output);
return output;
}
// ignoring channel mask due to downmix capability in mixer
// open a non direct output
// for non direct outputs, only PCM is supported
if (audio_is_linear_pcm(format)) {
// get which output is suitable for the specified stream. The actual
// routing change will happen when startOutput() will be called
SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs);
output = selectOutput(outputs, flags);
}
ALOGW_IF((output == 0), "getOutput() could not find output for stream %d, samplingRate %d,"
"format %d, channels %x, flags %x", stream, samplingRate, format, channelMask, flags);
ALOGV("getOutput() returns output %d", output);
return output;
}
audio_io_handle_t AudioPolicyManagerBase::selectOutput(const SortedVector<audio_io_handle_t>& outputs,
AudioSystem::output_flags flags)
{
// select one output among several that provide a path to a particular device or set of
// devices (the list was previously build by getOutputsForDevice()).
// The priority is as follows:
// 1: the output with the highest number of requested policy flags
// 2: the primary output
// 3: the first output in the list
if (outputs.size() == 0) {
return 0;
}
if (outputs.size() == 1) {
return outputs[0];
}
int maxCommonFlags = 0;
audio_io_handle_t outputFlags = 0;
audio_io_handle_t outputPrimary = 0;
for (size_t i = 0; i < outputs.size(); i++) {
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(outputs[i]);
if (!outputDesc->isDuplicated()) {
int commonFlags = (int)AudioSystem::popCount(outputDesc->mProfile->mFlags & flags);
if (commonFlags > maxCommonFlags) {
outputFlags = outputs[i];
maxCommonFlags = commonFlags;
ALOGV("selectOutput() commonFlags for output %d, %04x", outputs[i], commonFlags);
}
if (outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
outputPrimary = outputs[i];
}
}
}
if (outputFlags != 0) {
return outputFlags;
}
if (outputPrimary != 0) {
return outputPrimary;
}
return outputs[0];
}
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() unknown output %d", output);
return BAD_VALUE;
}
AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
// increment 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
// necessary for a correct control of hardware output routing by startOutput() and stopOutput()
outputDesc->changeRefCount(stream, 1);
if (outputDesc->mRefCount[stream] == 1) {
audio_devices_t newDevice = getNewDevice(output, false /*fromCache*/);
routing_strategy strategy = getStrategy(stream);
bool shouldWait = (strategy == STRATEGY_SONIFICATION) ||
(strategy == STRATEGY_SONIFICATION_RESPECTFUL);
uint32_t waitMs = 0;
bool force = false;
for (size_t i = 0; i < mOutputs.size(); i++) {
AudioOutputDescriptor *desc = mOutputs.valueAt(i);
if (desc != outputDesc) {
// force a device change if any other output is managed by the same hw
// module and has a current device selection that differs from selected device.
// In this case, the audio HAL must receive the new device selection so that it can
// change the device currently selected by the other active output.
if (outputDesc->sharesHwModuleWith(desc) &&
desc->device() != newDevice) {
force = true;
}
// wait for audio on other active outputs to be presented when starting
// a notification so that audio focus effect can propagate.
uint32_t latency = desc->latency();
if (shouldWait && desc->isActive(latency * 2) && (waitMs < latency)) {
waitMs = latency;
}
}
}
uint32_t muteWaitMs = setOutputDevice(output, newDevice, force);
// 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(newDevice),
output,
newDevice);
// update the outputs if starting an output with a stream that can affect notification
// routing
handleNotificationRoutingForStream(stream);
if (waitMs > muteWaitMs) {
usleep((waitMs - muteWaitMs) * 2 * 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() unknown output %d", output);
return BAD_VALUE;
}
AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
// 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()
if (outputDesc->mRefCount[stream] == 0) {
outputDesc->mStopTime[stream] = systemTime();
audio_devices_t newDevice = getNewDevice(output, false /*fromCache*/);
// delay the device switch by twice the latency because stopOutput() is executed when
// the track stop() command is received and at that time the audio track buffer can
// still contain data that needs to be drained. The latency only covers the audio HAL
// and kernel buffers. Also the latency does not always include additional delay in the
// audio path (audio DSP, CODEC ...)
setOutputDevice(output, newDevice, false, outputDesc->mLatency*2);
// force restoring the device selection on other active outputs if it differs from the
// one being selected for this output
for (size_t i = 0; i < mOutputs.size(); i++) {
audio_io_handle_t curOutput = mOutputs.keyAt(i);
AudioOutputDescriptor *desc = mOutputs.valueAt(i);
if (curOutput != output &&
desc->isActive() &&
outputDesc->sharesHwModuleWith(desc) &&
(newDevice != desc->device())) {
setOutputDevice(curOutput,
getNewDevice(curOutput, false /*fromCache*/),
true,
outputDesc->mLatency*2);
}
}
// update the outputs if stopping one with a stream that can affect notification routing
handleNotificationRoutingForStream(stream);
}
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->isActive()) {
mpClientInterface->closeOutput(output);
delete mOutputs.valueAt(index);
mOutputs.removeItem(output);
mTestOutputs[testIndex] = 0;
}
return;
}
#endif //AUDIO_POLICY_TEST
AudioOutputDescriptor *desc = mOutputs.valueAt(index);
if (desc->mFlags & AudioSystem::OUTPUT_FLAG_DIRECT) {
if (desc->mDirectOpenCount <= 0) {
ALOGW("releaseOutput() invalid open count %d for output %d",
desc->mDirectOpenCount, output);
return;
}
if (--desc->mDirectOpenCount == 0) {
closeOutput(output);
// If effects where present on the output, audioflinger moved them to the primary
// output by default: move them back to the appropriate output.
audio_io_handle_t dstOutput = getOutputForEffect();
if (dstOutput != mPrimaryOutput) {
mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput);
}
}
}
}
audio_io_handle_t AudioPolicyManagerBase::getInput(int inputSource,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
AudioSystem::audio_in_acoustics acoustics)
{
audio_io_handle_t input = 0;
audio_devices_t device = getDeviceForInputSource(inputSource);
ALOGV("getInput() inputSource %d, samplingRate %d, format %d, channelMask %x, acoustics %x",
inputSource, samplingRate, format, channelMask, acoustics);
if (device == AUDIO_DEVICE_NONE) {
ALOGW("getInput() could not find device for inputSource %d", inputSource);
return 0;
}
// adapt channel selection to input source
switch(inputSource) {
case AUDIO_SOURCE_VOICE_UPLINK:
channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK;
break;
case AUDIO_SOURCE_VOICE_DOWNLINK:
channelMask = AUDIO_CHANNEL_IN_VOICE_DNLINK;
break;
case AUDIO_SOURCE_VOICE_CALL:
channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK;
break;
default:
break;
}
IOProfile *profile = getInputProfile(device,
samplingRate,
format,
channelMask);
if (profile == NULL) {
ALOGW("getInput() could not find profile for device 0x%X, samplingRate %d, format %d, "
"channelMask 0x%X",
device, samplingRate, format, channelMask);
return 0;
}
if (profile->mModule->mHandle == 0) {
ALOGE("getInput(): HW module %s not opened", profile->mModule->mName);
return 0;
}
AudioInputDescriptor *inputDesc = new AudioInputDescriptor(profile);
inputDesc->mInputSource = inputSource;
inputDesc->mDevice = device;
inputDesc->mSamplingRate = samplingRate;
inputDesc->mFormat = format;
inputDesc->mChannelMask = channelMask;
inputDesc->mRefCount = 0;
input = mpClientInterface->openInput(profile->mModule->mHandle,
&inputDesc->mDevice,
&inputDesc->mSamplingRate,
&inputDesc->mFormat,
&inputDesc->mChannelMask);
// only accept input with the exact requested set of parameters
if (input == 0 ||
(samplingRate != inputDesc->mSamplingRate) ||
(format != inputDesc->mFormat) ||
(channelMask != inputDesc->mChannelMask)) {
ALOGI("getInput() failed opening input: samplingRate %d, format %d, channelMask 0x%X",
samplingRate, format, channelMask);
if (input != 0) {
mpClientInterface->closeInput(input);
}
delete inputDesc;
return 0;
}
addInput(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() unknown 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 except if the active input
// uses AUDIO_SOURCE_HOTWORD in which case it is closed.
audio_io_handle_t activeInput = getActiveInput();
if (!isVirtualInputDevice(inputDesc->mDevice) && activeInput != 0) {
AudioInputDescriptor *activeDesc = mInputs.valueFor(activeInput);
if (activeDesc->mInputSource == AUDIO_SOURCE_HOTWORD) {
ALOGW("startInput() preempting already started low-priority input %d", activeInput);
stopInput(activeInput);
releaseInput(activeInput);
} else {
ALOGW("startInput() input %d failed: other input already started", input);
return INVALID_OPERATION;
}
}
}
audio_devices_t newDevice = getDeviceForInputSource(inputDesc->mInputSource);
if ((newDevice != AUDIO_DEVICE_NONE) && (newDevice != inputDesc->mDevice)) {
inputDesc->mDevice = newDevice;
}
// automatically enable the remote submix output when input is started
if (audio_is_remote_submix_device(inputDesc->mDevice)) {
setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
AudioSystem::DEVICE_STATE_AVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS);
}
AudioParameter param = AudioParameter();
param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice);
int aliasSource = (inputDesc->mInputSource == AUDIO_SOURCE_HOTWORD) ?
AUDIO_SOURCE_VOICE_RECOGNITION : inputDesc->mInputSource;
param.addInt(String8(AudioParameter::keyInputSource), aliasSource);
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() unknown 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 {
// automatically disable the remote submix output when input is stopped
if (audio_is_remote_submix_device(inputDesc->mDevice)) {
setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
AudioSystem::DEVICE_STATE_UNAVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS);
}
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::closeAllInputs() {
for(size_t input_index = 0; input_index < mInputs.size(); input_index++) {
mpClientInterface->closeInput(mInputs.keyAt(input_index));
}
mInputs.clear();
}
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 %04x, 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(mOutputs.valueAt(i)->device());
if ((device == AUDIO_DEVICE_OUT_DEFAULT) || (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 = getDeviceForStrategy(getStrategy(stream), true /*fromCache*/);
}
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::selectOutputForEffects(
const SortedVector<audio_io_handle_t>& outputs)
{
// select one output among several suitable for global effects.
// The priority is as follows:
// 1: An offloaded output. If the effect ends up not being offloadable,
// AudioFlinger will invalidate the track and the offloaded output
// will be closed causing the effect to be moved to a PCM output.
// 2: A deep buffer output
// 3: the first output in the list
if (outputs.size() == 0) {
return 0;
}
audio_io_handle_t outputOffloaded = 0;
audio_io_handle_t outputDeepBuffer = 0;
for (size_t i = 0; i < outputs.size(); i++) {
AudioOutputDescriptor *desc = mOutputs.valueFor(outputs[i]);
ALOGV("selectOutputForEffects outputs[%zu] flags %x", i, desc->mFlags);
if ((desc->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
outputOffloaded = outputs[i];
}
if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) != 0) {
outputDeepBuffer = outputs[i];
}
}
ALOGV("selectOutputForEffects outputOffloaded %d outputDeepBuffer %d",
outputOffloaded, outputDeepBuffer);
if (outputOffloaded != 0) {
return outputOffloaded;
}
if (outputDeepBuffer != 0) {
return outputDeepBuffer;
}
return outputs[0];
}
audio_io_handle_t AudioPolicyManagerBase::getOutputForEffect(const effect_descriptor_t *desc)
{
// apply simple rule where global effects are attached to the same output as MUSIC streams
routing_strategy strategy = getStrategy(AudioSystem::MUSIC);
audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(device, mOutputs);
audio_io_handle_t output = selectOutputForEffects(dstOutputs);
ALOGV("getOutputForEffect() got output %d for fx %s flags %x",
output, (desc == NULL) ? "unspecified" : desc->name, (desc == NULL) ? 0 : desc->flags);
return output;
}
status_t AudioPolicyManagerBase::registerEffect(const 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::isNonOffloadableEffectEnabled()
{
for (size_t i = 0; i < mEffects.size(); i++) {
const EffectDescriptor * const pDesc = mEffects.valueAt(i);
if (pDesc->mEnabled && (pDesc->mStrategy == STRATEGY_MEDIA) &&
((pDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) {
ALOGV("isNonOffloadableEffectEnabled() non offloadable effect %s enabled on session %d",
pDesc->mDesc.name, pDesc->mSession);
return true;
}
}
return false;
}
bool AudioPolicyManagerBase::isStreamActive(int stream, uint32_t inPastMs) const
{
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < mOutputs.size(); i++) {
const AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i);
if (outputDesc->isStreamActive((AudioSystem::stream_type)stream, inPastMs, sysTime)) {
return true;
}
}
return false;
}
bool AudioPolicyManagerBase::isStreamActiveRemotely(int stream, uint32_t inPastMs) const
{
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < mOutputs.size(); i++) {
const AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i);
if (((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) &&
outputDesc->isStreamActive((AudioSystem::stream_type)stream, inPastMs, sysTime)) {
return true;
}
}
return false;
}
bool AudioPolicyManagerBase::isSourceActive(audio_source_t source) const
{
for (size_t i = 0; i < mInputs.size(); i++) {
const AudioInputDescriptor * inputDescriptor = mInputs.valueAt(i);
if ((inputDescriptor->mInputSource == (int)source ||
(source == (audio_source_t)AUDIO_SOURCE_VOICE_RECOGNITION &&
inputDescriptor->mInputSource == AUDIO_SOURCE_HOTWORD))
&& (inputDescriptor->mRefCount > 0)) {
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, " Primary Output: %d\n", mPrimaryOutput);
result.append(buffer);
snprintf(buffer, SIZE, " A2DP device address: %s\n", mA2dpDeviceAddress.string());
result.append(buffer);
snprintf(buffer, SIZE, " SCO device address: %s\n", mScoDeviceAddress.string());
result.append(buffer);
snprintf(buffer, SIZE, " USB audio ALSA %s\n", mUsbOutCardAndDevice.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);
snprintf(buffer, SIZE, " Force use for system %d\n", mForceUse[AudioSystem::FOR_SYSTEM]);
result.append(buffer);
write(fd, result.string(), result.size());
snprintf(buffer, SIZE, "\nHW Modules dump:\n");
write(fd, buffer, strlen(buffer));
for (size_t i = 0; i < mHwModules.size(); i++) {
snprintf(buffer, SIZE, "- HW Module %zu:\n", i + 1);
write(fd, buffer, strlen(buffer));
mHwModules[i]->dump(fd);
}
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, " %02zu ", 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;
}
// This function checks for the parameters which can be offloaded.
// This can be enhanced depending on the capability of the DSP and policy
// of the system.
bool AudioPolicyManagerBase::isOffloadSupported(const audio_offload_info_t& offloadInfo)
{
ALOGV("isOffloadSupported: SR=%u, CM=0x%x, Format=0x%x, StreamType=%d,"
" BitRate=%u, duration=%" PRId64 " us, has_video=%d",
offloadInfo.sample_rate, offloadInfo.channel_mask,
offloadInfo.format,
offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us,
offloadInfo.has_video);
// Check if offload has been disabled
char propValue[PROPERTY_VALUE_MAX];
if (property_get("audio.offload.disable", propValue, "0")) {
if (atoi(propValue) != 0) {
ALOGV("offload disabled by audio.offload.disable=%s", propValue );
return false;
}
}
// Check if stream type is music, then only allow offload as of now.
if (offloadInfo.stream_type != AUDIO_STREAM_MUSIC)
{
ALOGV("isOffloadSupported: stream_type != MUSIC, returning false");
return false;
}
//TODO: enable audio offloading with video when ready
if (offloadInfo.has_video)
{
ALOGV("isOffloadSupported: has_video == true, returning false");
return false;
}
//If duration is less than minimum value defined in property, return false
if (property_get("audio.offload.min.duration.secs", propValue, NULL)) {
if (offloadInfo.duration_us < (atoi(propValue) * 1000000 )) {
ALOGV("Offload denied by duration < audio.offload.min.duration.secs(=%s)", propValue);
return false;
}
} else if (offloadInfo.duration_us < OFFLOAD_DEFAULT_MIN_DURATION_SECS * 1000000) {
ALOGV("Offload denied by duration < default min(=%u)", OFFLOAD_DEFAULT_MIN_DURATION_SECS);
return false;
}
// Do not allow offloading if one non offloadable effect is enabled. This prevents from
// creating an offloaded track and tearing it down immediately after start when audioflinger
// detects there is an active non offloadable effect.
// FIXME: We should check the audio session here but we do not have it in this context.
// This may prevent offloading in rare situations where effects are left active by apps
// in the background.
if (isNonOffloadableEffectEnabled()) {
return false;
}
// See if there is a profile to support this.
// AUDIO_DEVICE_NONE
IOProfile *profile = getProfileForDirectOutput(AUDIO_DEVICE_NONE /*ignore device */,
offloadInfo.sample_rate,
offloadInfo.format,
offloadInfo.channel_mask,
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
ALOGV("isOffloadSupported() profile %sfound", profile != NULL ? "" : "NOT ");
return (profile != NULL);
}
// ----------------------------------------------------------------------------
// AudioPolicyManagerBase
// ----------------------------------------------------------------------------
AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)
:
#ifdef AUDIO_POLICY_TEST
Thread(false),
#endif //AUDIO_POLICY_TEST
mPrimaryOutput((audio_io_handle_t)0),
mAvailableOutputDevices(AUDIO_DEVICE_NONE),
mPhoneState(AudioSystem::MODE_NORMAL),
mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
mA2dpSuspended(false), mHasA2dp(false), mHasUsb(false), mHasRemoteSubmix(false),
mSpeakerDrcEnabled(false)
{
mpClientInterface = clientInterface;
for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
mForceUse[i] = AudioSystem::FORCE_NONE;
}
mA2dpDeviceAddress = String8("");
mScoDeviceAddress = String8("");
mUsbOutCardAndDevice = String8("");
if (loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE) != NO_ERROR) {
if (loadAudioPolicyConfig(AUDIO_POLICY_CONFIG_FILE) != NO_ERROR) {
ALOGE("could not load audio policy configuration file, setting defaults");
defaultAudioPolicyConfig();
}
}
// must be done after reading the policy
initializeVolumeCurves();
// open all output streams needed to access attached devices
for (size_t i = 0; i < mHwModules.size(); i++) {
mHwModules[i]->mHandle = mpClientInterface->loadHwModule(mHwModules[i]->mName);
if (mHwModules[i]->mHandle == 0) {
ALOGW("could not open HW module %s", mHwModules[i]->mName);
continue;
}
// open all output streams needed to access attached devices
// except for direct output streams that are only opened when they are actually
// required by an app.
for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
{
const IOProfile *outProfile = mHwModules[i]->mOutputProfiles[j];
if ((outProfile->mSupportedDevices & mAttachedOutputDevices) &&
((outProfile->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) {
AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(outProfile);
outputDesc->mDevice = (audio_devices_t)(mDefaultOutputDevice &
outProfile->mSupportedDevices);
audio_io_handle_t output = mpClientInterface->openOutput(
outProfile->mModule->mHandle,
&outputDesc->mDevice,
&outputDesc->mSamplingRate,
&outputDesc->mFormat,
&outputDesc->mChannelMask,
&outputDesc->mLatency,
outputDesc->mFlags);
if (output == 0) {
delete outputDesc;
} else {
mAvailableOutputDevices = (audio_devices_t)(mAvailableOutputDevices |
(outProfile->mSupportedDevices & mAttachedOutputDevices));
if (mPrimaryOutput == 0 &&
outProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
mPrimaryOutput = output;
}
addOutput(output, outputDesc);
setOutputDevice(output,
(audio_devices_t)(mDefaultOutputDevice &
outProfile->mSupportedDevices),
true);
}
}
}
}
ALOGE_IF((mAttachedOutputDevices & ~mAvailableOutputDevices),
"Not output found for attached devices %08x",
(mAttachedOutputDevices & ~mAvailableOutputDevices));
ALOGE_IF((mPrimaryOutput == 0), "Failed to open primary output");
updateDevicesAndOutputs();
#ifdef AUDIO_POLICY_TEST
if (mPrimaryOutput != 0) {
AudioParameter outputCmd = AudioParameter();
outputCmd.addInt(String8("set_id"), 0);
mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString());
mTestDevice = AUDIO_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);
}
for (size_t i = 0; i < mInputs.size(); i++) {
mpClientInterface->closeInput(mInputs.keyAt(i));
delete mInputs.valueAt(i);
}
for (size_t i = 0; i < mHwModules.size(); i++) {
delete mHwModules[i];
}
}
status_t AudioPolicyManagerBase::initCheck()
{
return (mPrimaryOutput == 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"));
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mPrimaryOutput);
mpClientInterface->closeOutput(mPrimaryOutput);
audio_module_handle_t moduleHandle = outputDesc->mModule->mHandle;
delete mOutputs.valueFor(mPrimaryOutput);
mOutputs.removeItem(mPrimaryOutput);
AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(NULL);
outputDesc->mDevice = AUDIO_DEVICE_OUT_SPEAKER;
mPrimaryOutput = mpClientInterface->openOutput(moduleHandle,
&outputDesc->mDevice,
&outputDesc->mSamplingRate,
&outputDesc->mFormat,
&outputDesc->mChannelMask,
&outputDesc->mLatency,
outputDesc->mFlags);
if (mPrimaryOutput == 0) {
ALOGE("Failed to reopen hardware output stream, samplingRate: %d, format %d, channels %d",
outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannelMask);
} else {
AudioParameter outputCmd = AudioParameter();
outputCmd.addInt(String8("set_id"), 0);
mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString());
addOutput(mPrimaryOutput, 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);
}
void AudioPolicyManagerBase::addInput(audio_io_handle_t id, AudioInputDescriptor *inputDesc)
{
inputDesc->mId = id;
mInputs.add(id, inputDesc);
}
status_t AudioPolicyManagerBase::checkOutputsForDevice(audio_devices_t device,
AudioSystem::device_connection_state state,
SortedVector<audio_io_handle_t>& outputs,
const String8 paramStr)
{
AudioOutputDescriptor *desc;
if (state == AudioSystem::DEVICE_STATE_AVAILABLE) {
// first list already open outputs that can be routed to this device
for (size_t i = 0; i < mOutputs.size(); i++) {
desc = mOutputs.valueAt(i);
if (!desc->isDuplicated() && (desc->mProfile->mSupportedDevices & device)) {
ALOGV("checkOutputsForDevice(): adding opened output %d", mOutputs.keyAt(i));
outputs.add(mOutputs.keyAt(i));
}
}
// then look for output profiles that can be routed to this device
SortedVector<IOProfile *> profiles;
for (size_t i = 0; i < mHwModules.size(); i++)
{
if (mHwModules[i]->mHandle == 0) {
continue;
}
for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
{
if (mHwModules[i]->mOutputProfiles[j]->mSupportedDevices & device) {
ALOGV("checkOutputsForDevice(): adding profile %zu from module %zu", j, i);
profiles.add(mHwModules[i]->mOutputProfiles[j]);
}
}
}
if (profiles.isEmpty() && outputs.isEmpty()) {
ALOGW("checkOutputsForDevice(): No output available for device %04x", device);
return BAD_VALUE;
}
// open outputs for matching profiles if needed. Direct outputs are also opened to
// query for dynamic parameters and will be closed later by setDeviceConnectionState()
for (ssize_t profile_index = 0; profile_index < (ssize_t)profiles.size(); profile_index++) {
IOProfile *profile = profiles[profile_index];
// nothing to do if one output is already opened for this profile
size_t j;
for (j = 0; j < mOutputs.size(); j++) {
desc = mOutputs.valueAt(j);
if (!desc->isDuplicated() && desc->mProfile == profile) {
break;
}
}
if (j != mOutputs.size()) {
continue;
}
ALOGV("opening output for device %08x with params %s", device, paramStr.string());
desc = new AudioOutputDescriptor(profile);
desc->mDevice = device;
audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER;
offloadInfo.sample_rate = desc->mSamplingRate;
offloadInfo.format = desc->mFormat;
offloadInfo.channel_mask = desc->mChannelMask;
audio_io_handle_t output = mpClientInterface->openOutput(profile->mModule->mHandle,
&desc->mDevice,
&desc->mSamplingRate,
&desc->mFormat,
&desc->mChannelMask,
&desc->mLatency,
desc->mFlags,
&offloadInfo);
if (output != 0) {
if (!paramStr.isEmpty()) {
// Here is where the out_set_parameters() for card & device gets called
mpClientInterface->setParameters(output, paramStr);
}
// Here is where we step through and resolve any "dynamic" fields
String8 reply;
char *value;
if (profile->mSamplingRates[0] == 0) {
reply = mpClientInterface->getParameters(output,
String8(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES));
ALOGV("checkOutputsForDevice() direct output sup sampling rates %s",
reply.string());
value = strpbrk((char *)reply.string(), "=");
if (value != NULL) {
loadSamplingRates(value + 1, profile);
}
}
if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) {
reply = mpClientInterface->getParameters(output,
String8(AUDIO_PARAMETER_STREAM_SUP_FORMATS));
ALOGV("checkOutputsForDevice() direct output sup formats %s",
reply.string());
value = strpbrk((char *)reply.string(), "=");
if (value != NULL) {
loadFormats(value + 1, profile);
}
}
if (profile->mChannelMasks[0] == 0) {
reply = mpClientInterface->getParameters(output,
String8(AUDIO_PARAMETER_STREAM_SUP_CHANNELS));
ALOGV("checkOutputsForDevice() direct output sup channel masks %s",
reply.string());
value = strpbrk((char *)reply.string(), "=");
if (value != NULL) {
loadOutChannels(value + 1, profile);
}
}
if (((profile->mSamplingRates[0] == 0) &&
(profile->mSamplingRates.size() < 2)) ||
((profile->mFormats[0] == 0) &&
(profile->mFormats.size() < 2)) ||
((profile->mChannelMasks[0] == 0) &&
(profile->mChannelMasks.size() < 2))) {
ALOGW("checkOutputsForDevice() direct output missing param");
mpClientInterface->closeOutput(output);
output = 0;
} else if (profile->mSamplingRates[0] == 0) {
mpClientInterface->closeOutput(output);
desc->mSamplingRate = profile->mSamplingRates[1];
offloadInfo.sample_rate = desc->mSamplingRate;
output = mpClientInterface->openOutput(
profile->mModule->mHandle,
&desc->mDevice,
&desc->mSamplingRate,
&desc->mFormat,
&desc->mChannelMask,
&desc->mLatency,
desc->mFlags,
&offloadInfo);
}
if (output != 0) {
addOutput(output, desc);
if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) {
audio_io_handle_t duplicatedOutput = 0;
// set initial stream volume for device
applyStreamVolumes(output, device, 0, true);
//TODO: configure audio effect output stage here
// open a duplicating output thread for the new output and the primary output
duplicatedOutput = mpClientInterface->openDuplicateOutput(output,
mPrimaryOutput);
if (duplicatedOutput != 0) {
// add duplicated output descriptor
AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor(NULL);
dupOutputDesc->mOutput1 = mOutputs.valueFor(mPrimaryOutput);
dupOutputDesc->mOutput2 = mOutputs.valueFor(output);
dupOutputDesc->mSamplingRate = desc->mSamplingRate;
dupOutputDesc->mFormat = desc->mFormat;
dupOutputDesc->mChannelMask = desc->mChannelMask;
dupOutputDesc->mLatency = desc->mLatency;
addOutput(duplicatedOutput, dupOutputDesc);
applyStreamVolumes(duplicatedOutput, device, 0, true);
} else {
ALOGW("checkOutputsForDevice() could not open dup output for %d and %d",
mPrimaryOutput, output);
mpClientInterface->closeOutput(output);
mOutputs.removeItem(output);
output = 0;
}
}
}
}
if (output == 0) {
ALOGW("checkOutputsForDevice() could not open output for device %x", device);
delete desc;
profiles.removeAt(profile_index);
profile_index--;
} else {
outputs.add(output);
ALOGV("checkOutputsForDevice(): adding output %d", output);
}
}
if (profiles.isEmpty()) {
ALOGW("checkOutputsForDevice(): No output available for device %04x", device);
return BAD_VALUE;
}
} else { // Disconnect
// check if one opened output is not needed any more after disconnecting one device
for (size_t i = 0; i < mOutputs.size(); i++) {
desc = mOutputs.valueAt(i);
if (!desc->isDuplicated() &&
!(desc->mProfile->mSupportedDevices & mAvailableOutputDevices)) {
ALOGV("checkOutputsForDevice(): disconnecting adding output %d", mOutputs.keyAt(i));
outputs.add(mOutputs.keyAt(i));
}
}
// Clear any profiles associated with the disconnected device.
for (size_t i = 0; i < mHwModules.size(); i++)
{
if (mHwModules[i]->mHandle == 0) {
continue;
}
for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
{
IOProfile *profile = mHwModules[i]->mOutputProfiles[j];
if (profile->mSupportedDevices & device) {
ALOGV("checkOutputsForDevice(): clearing direct output profile %zu on module %zu",
j, i);
if (profile->mSamplingRates[0] == 0) {
profile->mSamplingRates.clear();
profile->mSamplingRates.add(0);
}
if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) {
profile->mFormats.clear();
profile->mFormats.add(AUDIO_FORMAT_DEFAULT);
}
if (profile->mChannelMasks[0] == 0) {
profile->mChannelMasks.clear();
profile->mChannelMasks.add(0);
}
}
}
}
}
return NO_ERROR;
}
status_t AudioPolicyManagerBase::checkInputsForDevice(audio_devices_t device,
AudioSystem::device_connection_state state,
SortedVector<audio_io_handle_t>& inputs,
const String8 paramStr)
{
AudioInputDescriptor *desc;
if (state == AudioSystem::DEVICE_STATE_AVAILABLE) {
// first list already open inputs that can be routed to this device
for (size_t input_index = 0; input_index < mInputs.size(); input_index++) {
desc = mInputs.valueAt(input_index);
if (desc->mProfile->mSupportedDevices & (device & ~AUDIO_DEVICE_BIT_IN)) {
ALOGV("checkInputsForDevice(): adding opened input %d", mInputs.keyAt(input_index));
inputs.add(mInputs.keyAt(input_index));
}
}
// then look for input profiles that can be routed to this device
SortedVector<IOProfile *> profiles;
for (size_t module_index = 0; module_index < mHwModules.size(); module_index++)
{
if (mHwModules[module_index]->mHandle == 0) {
continue;
}
for (size_t profile_index = 0;
profile_index < mHwModules[module_index]->mInputProfiles.size();
profile_index++)
{
if (mHwModules[module_index]->mInputProfiles[profile_index]->mSupportedDevices
& (device & ~AUDIO_DEVICE_BIT_IN)) {
ALOGV("checkInputsForDevice(): adding profile %zu from module %zu",
profile_index, module_index);
profiles.add(mHwModules[module_index]->mInputProfiles[profile_index]);
}
}
}
if (profiles.isEmpty() && inputs.isEmpty()) {
ALOGW("checkInputsForDevice(): No input available for device 0x%X", device);
return BAD_VALUE;
}
// open inputs for matching profiles if needed. Direct inputs are also opened to
// query for dynamic parameters and will be closed later by setDeviceConnectionState()
for (ssize_t profile_index = 0; profile_index < (ssize_t)profiles.size(); profile_index++) {
IOProfile *profile = profiles[profile_index];
// nothing to do if one input is already opened for this profile
size_t input_index;
for (input_index = 0; input_index < mInputs.size(); input_index++) {
desc = mInputs.valueAt(input_index);
if (desc->mProfile == profile) {
break;
}
}
if (input_index != mInputs.size()) {
continue;
}
ALOGV("opening input for device 0x%X with params %s", device, paramStr.string());
desc = new AudioInputDescriptor(profile);
desc->mDevice = device;
audio_io_handle_t input = mpClientInterface->openInput(profile->mModule->mHandle,
&desc->mDevice,
&desc->mSamplingRate,
&desc->mFormat,
&desc->mChannelMask);
if (input != 0) {
if (!paramStr.isEmpty()) {
mpClientInterface->setParameters(input, paramStr);
}
// Here is where we step through and resolve any "dynamic" fields
String8 reply;
char *value;
if (profile->mSamplingRates[0] == 0) {
reply = mpClientInterface->getParameters(input,
String8(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES));
ALOGV("checkInputsForDevice() direct input sup sampling rates %s",
reply.string());
value = strpbrk((char *)reply.string(), "=");
if (value != NULL) {
loadSamplingRates(value + 1, profile);
}
}
if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) {
reply = mpClientInterface->getParameters(input,
String8(AUDIO_PARAMETER_STREAM_SUP_FORMATS));
ALOGV("checkInputsForDevice() direct input sup formats %s", reply.string());
value = strpbrk((char *)reply.string(), "=");
if (value != NULL) {
loadFormats(value + 1, profile);
}
}
if (profile->mChannelMasks[0] == 0) {
reply = mpClientInterface->getParameters(input,
String8(AUDIO_PARAMETER_STREAM_SUP_CHANNELS));
ALOGV("checkInputsForDevice() direct input sup channel masks %s",
reply.string());
value = strpbrk((char *)reply.string(), "=");
if (value != NULL) {
loadInChannels(value + 1, profile);
}
}
if (((profile->mSamplingRates[0] == 0) && (profile->mSamplingRates.size() < 2)) ||
((profile->mFormats[0] == 0) && (profile->mFormats.size() < 2)) ||
((profile->mChannelMasks[0] == 0) && (profile->mChannelMasks.size() < 2))) {
ALOGW("checkInputsForDevice() direct input missing param");
mpClientInterface->closeInput(input);
input = 0;
}
if (input != 0) {
addInput(input, desc);
}
} // endif input != 0
if (input == 0) {
ALOGW("checkInputsForDevice() could not open input for device 0x%X", device);
delete desc;
profiles.removeAt(profile_index);
profile_index--;
} else {
inputs.add(input);
ALOGV("checkInputsForDevice(): adding input %d", input);
}
} // end scan profiles
if (profiles.isEmpty()) {
ALOGW("checkInputsForDevice(): No input available for device 0x%X", device);
return BAD_VALUE;
}
} else {
// Disconnect
// check if one opened input is not needed any more after disconnecting one device
for (size_t input_index = 0; input_index < mInputs.size(); input_index++) {
desc = mInputs.valueAt(input_index);
if (!(desc->mProfile->mSupportedDevices & mAvailableInputDevices)) {
ALOGV("checkInputsForDevice(): disconnecting adding input %d",
mInputs.keyAt(input_index));
inputs.add(mInputs.keyAt(input_index));
}
}
// Clear any profiles associated with the disconnected device.
for (size_t module_index = 0; module_index < mHwModules.size(); module_index++)
{
if (mHwModules[module_index]->mHandle == 0) {
continue;
}
for (size_t profile_index = 0;
profile_index < mHwModules[module_index]->mInputProfiles.size();
profile_index++)
{
IOProfile *profile = mHwModules[module_index]->mInputProfiles[profile_index];
if (profile->mSupportedDevices & device) {
ALOGV("checkInputsForDevice(): clearing direct input profile %zu on module %zu",
profile_index, module_index);
if (profile->mSamplingRates[0] == 0) {
profile->mSamplingRates.clear();
profile->mSamplingRates.add(0);
}
if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) {
profile->mFormats.clear();
profile->mFormats.add(AUDIO_FORMAT_DEFAULT);
}
if (profile->mChannelMasks[0] == 0) {
profile->mChannelMasks.clear();
profile->mChannelMasks.add(0);
}
}
}
}
} // end disconnect
return NO_ERROR;
}
void AudioPolicyManagerBase::closeOutput(audio_io_handle_t output)
{
ALOGV("closeOutput(%d)", output);
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
if (outputDesc == NULL) {
ALOGW("closeOutput() unknown output %d", output);
return;
}
// look for duplicated outputs connected to the output being removed.
for (size_t i = 0; i < mOutputs.size(); i++) {
AudioOutputDescriptor *dupOutputDesc = mOutputs.valueAt(i);
if (dupOutputDesc->isDuplicated() &&
(dupOutputDesc->mOutput1 == outputDesc ||
dupOutputDesc->mOutput2 == outputDesc)) {
AudioOutputDescriptor *outputDesc2;
if (dupOutputDesc->mOutput1 == outputDesc) {
outputDesc2 = dupOutputDesc->mOutput2;
} else {
outputDesc2 = dupOutputDesc->mOutput1;
}
// As all active tracks on duplicated output will be deleted,
// and as they were also referenced on the other output, the reference
// count for their stream type must be adjusted accordingly on
// the other output.
for (int j = 0; j < (int)AudioSystem::NUM_STREAM_TYPES; j++) {
int refCount = dupOutputDesc->mRefCount[j];
outputDesc2->changeRefCount((AudioSystem::stream_type)j,-refCount);
}
audio_io_handle_t duplicatedOutput = mOutputs.keyAt(i);
ALOGV("closeOutput() closing also duplicated output %d", duplicatedOutput);
mpClientInterface->closeOutput(duplicatedOutput);
delete mOutputs.valueFor(duplicatedOutput);
mOutputs.removeItem(duplicatedOutput);
}
}
AudioParameter param;
param.add(String8("closing"), String8("true"));
mpClientInterface->setParameters(output, param.toString());
mpClientInterface->closeOutput(output);
delete outputDesc;
mOutputs.removeItem(output);
mPreviousOutputs = mOutputs;
}
SortedVector<audio_io_handle_t> AudioPolicyManagerBase::getOutputsForDevice(audio_devices_t device,
DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> openOutputs)
{
SortedVector<audio_io_handle_t> outputs;
ALOGVV("getOutputsForDevice() device %04x", device);
for (size_t i = 0; i < openOutputs.size(); i++) {
ALOGVV("output %d isDuplicated=%d device=%04x",
i, openOutputs.valueAt(i)->isDuplicated(), openOutputs.valueAt(i)->supportedDevices());
if ((device & openOutputs.valueAt(i)->supportedDevices()) == device) {
ALOGVV("getOutputsForDevice() found output %d", openOutputs.keyAt(i));
outputs.add(openOutputs.keyAt(i));
}
}
return outputs;
}
bool AudioPolicyManagerBase::vectorsEqual(SortedVector<audio_io_handle_t>& outputs1,
SortedVector<audio_io_handle_t>& outputs2)
{
if (outputs1.size() != outputs2.size()) {
return false;
}
for (size_t i = 0; i < outputs1.size(); i++) {
if (outputs1[i] != outputs2[i]) {
return false;
}
}
return true;
}
void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy)
{
audio_devices_t oldDevice = getDeviceForStrategy(strategy, true /*fromCache*/);
audio_devices_t newDevice = getDeviceForStrategy(strategy, false /*fromCache*/);
SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevice(oldDevice, mPreviousOutputs);
SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(newDevice, mOutputs);
if (!vectorsEqual(srcOutputs,dstOutputs)) {
ALOGV("checkOutputForStrategy() strategy %d, moving from output %d to output %d",
strategy, srcOutputs[0], dstOutputs[0]);
// mute strategy while moving tracks from one output to another
for (size_t i = 0; i < srcOutputs.size(); i++) {
AudioOutputDescriptor *desc = mOutputs.valueFor(srcOutputs[i]);
if (desc->isStrategyActive(strategy)) {
setStrategyMute(strategy, true, srcOutputs[i]);
setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice);
}
}
// Move effects associated to this strategy from previous output to new output
if (strategy == STRATEGY_MEDIA) {
audio_io_handle_t fxOutput = selectOutputForEffects(dstOutputs);
SortedVector<audio_io_handle_t> moved;
for (size_t i = 0; i < mEffects.size(); i++) {
EffectDescriptor *desc = mEffects.valueAt(i);
if (desc->mSession == AUDIO_SESSION_OUTPUT_MIX &&
desc->mIo != fxOutput) {
if (moved.indexOf(desc->mIo) < 0) {
ALOGV("checkOutputForStrategy() moving effect %d to output %d",
mEffects.keyAt(i), fxOutput);
mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, desc->mIo,
fxOutput);
moved.add(desc->mIo);
}
desc->mIo = fxOutput;
}
}
}
// 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->invalidateStream((AudioSystem::stream_type)i);
}
}
}
}
void AudioPolicyManagerBase::checkOutputForAllStrategies()
{
checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE);
checkOutputForStrategy(STRATEGY_PHONE);
checkOutputForStrategy(STRATEGY_SONIFICATION);
checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL);
checkOutputForStrategy(STRATEGY_MEDIA);
checkOutputForStrategy(STRATEGY_DTMF);
}
audio_io_handle_t AudioPolicyManagerBase::getA2dpOutput()
{
if (!mHasA2dp) {
return 0;
}
for (size_t i = 0; i < mOutputs.size(); i++) {
AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i);
if (!outputDesc->isDuplicated() && outputDesc->device() & AUDIO_DEVICE_OUT_ALL_A2DP) {
return mOutputs.keyAt(i);
}
}
return 0;
}
void AudioPolicyManagerBase::checkA2dpSuspend()
{
if (!mHasA2dp) {
return;
}
audio_io_handle_t a2dpOutput = getA2dpOutput();
if (a2dpOutput == 0) {
return;
}
// 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 (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(a2dpOutput);
mA2dpSuspended = false;
}
} else {
if (((mScoDeviceAddress != "") &&
((mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
(mForceUse[