/*
* 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 "AudioPolicyManager"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include "AudioPolicyManager.h"
#include <media/mediarecorder.h>
namespace android {
// Max volume for streams when playing over bluetooth SCO device while in call: -18dB
#define IN_CALL_SCO_VOLUME_MAX 0.126
// Min music volume for 3.5mm jack in car dock: -10dB
#define CAR_DOCK_MUSIC_MINI_JACK_VOLUME_MIN 0.316
// ----------------------------------------------------------------------------
// AudioPolicyManager implementation for qsd8k platform
// Common audio policy manager code is implemented in AudioPolicyManagerBase class
// ----------------------------------------------------------------------------
// --- class factory
extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
{
return new AudioPolicyManager(clientInterface);
}
extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface)
{
delete interface;
}
// ---
uint32_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy, bool fromCache)
{
uint32_t device = 0;
if (fromCache) {
device = mDeviceForStrategy[strategy];
LOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, device);
return device;
}
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;
}
// otherwise (not docked) continue with selection
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;
// when not in call:
if (mPhoneState != AudioSystem::MODE_IN_CALL) {
// - if we are docked to a BT CAR dock, give A2DP preference over earpiece
// - if we are docked to a BT DESK dock, give speaker preference over earpiece
if (mForceUse[AudioSystem::FOR_DOCK] == AudioSystem::FORCE_BT_CAR_DOCK) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
} else if (mForceUse[AudioSystem::FOR_DOCK] == AudioSystem::FORCE_BT_DESK_DOCK) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
}
if (device) break;
// - phone strategy should route STREAM_VOICE_CALL to A2DP
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
if (device) break;
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
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;
}
// when not in call:
if (mPhoneState != AudioSystem::MODE_IN_CALL) {
// - if we are docked to a BT CAR dock, give A2DP preference over phone spkr
if (mForceUse[AudioSystem::FOR_DOCK] == AudioSystem::FORCE_BT_CAR_DOCK) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
if (device) break;
}
// - phone strategy should route STREAM_VOICE_CALL to A2DP speaker
// when forcing to speaker output
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
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;
}
// If not incall:
// - if we are docked to a BT CAR dock, don't duplicate for the sonification strategy
// - if we are docked to a BT DESK dock, use only speaker for the sonification strategy
if (mForceUse[AudioSystem::FOR_DOCK] != AudioSystem::FORCE_BT_CAR_DOCK) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
if (device == 0) {
LOGE("getDeviceForStrategy() speaker device not found");
}
if (mForceUse[AudioSystem::FOR_DOCK] == AudioSystem::FORCE_BT_DESK_DOCK) {
if (mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE) {
device |= AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
} else if (mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET) {
device |= AudioSystem::DEVICE_OUT_WIRED_HEADSET;
}
break;
}
} else {
device = 0;
}
// The second device used for sonification is the same as the device used by media strategy
// Note that when docked, we pick the device below (no duplication)
// FALL THROUGH
case STRATEGY_MEDIA: {
uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
#ifdef WITH_A2DP
if (mA2dpOutput != 0) {
if (device2 == 0) {
// play ringtone over speaker (or speaker + headset) if in car dock
// because A2DP is suspended in this case
if (mForceUse[AudioSystem::FOR_DOCK] == AudioSystem::FORCE_BT_CAR_DOCK &&
strategy == STRATEGY_SONIFICATION &&
mPhoneState == AudioSystem::MODE_RINGTONE) {
device2 = mAvailableOutputDevices &
(AudioSystem::DEVICE_OUT_SPEAKER |
AudioSystem::DEVICE_OUT_WIRED_HEADPHONE |
AudioSystem::DEVICE_OUT_WIRED_HEADSET);
}
}
}
#endif
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 (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) {
device = 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");
}
// Do not play media stream if in call and the requested device would change the hardware
// output routing
if (mPhoneState == AudioSystem::MODE_IN_CALL &&
!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device) &&
device != getDeviceForStrategy(STRATEGY_PHONE, false)) {
device = 0;
LOGV("getDeviceForStrategy() incompatible media and phone devices");
}
} break;
default:
LOGW("getDeviceForStrategy() unknown strategy: %d", strategy);
break;
}
LOGV("getDeviceForStrategy() strategy %d, device %x", strategy, device);
return device;
}
float AudioPolicyManager::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device)
{
// force volume on A2DP output to maximum if playing through car dock speakers
// as volume is applied on the car dock and controlled via car dock keys.
#ifdef WITH_A2DP
if (output == mA2dpOutput &&
mForceUse[AudioSystem::FOR_DOCK] == AudioSystem::FORCE_BT_CAR_DOCK) {
return 1.0;
}
#endif
float volume = AudioPolicyManagerBase::computeVolume(stream, index, output, device);
// limit stream volume when in call and playing over bluetooth SCO device to
// avoid saturation
if (mPhoneState == AudioSystem::MODE_IN_CALL && AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) {
if (volume > IN_CALL_SCO_VOLUME_MAX) {
LOGV("computeVolume limiting SYSTEM volume %f to %f",volume, IN_CALL_SCO_VOLUME_MAX);
volume = IN_CALL_SCO_VOLUME_MAX;
}
}
// in car dock: when using the 3.5mm jack to play media, set a fixed volume as access to the
// physical volume keys is blocked by the car dock frame.
if ((mForceUse[AudioSystem::FOR_DOCK] == AudioSystem::FORCE_BT_CAR_DOCK) &&
(stream == AudioSystem::MUSIC) &&
(device & (AudioSystem::DEVICE_OUT_WIRED_HEADPHONE |
AudioSystem::DEVICE_OUT_WIRED_HEADSET))) {
volume = CAR_DOCK_MUSIC_MINI_JACK_VOLUME_MIN;
}
return volume;
}
}; // namespace android