| /* |
| * Copyright (c) 2013, The Linux Foundation. All rights reserved. |
| * Not a contribution. |
| * |
| * 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 |
| |
| //#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 <utils/Log.h> |
| #include "AudioPolicyManager.h" |
| #include <hardware/audio_effect.h> |
| #include <hardware/audio.h> |
| #include <math.h> |
| #include <hardware_legacy/audio_policy_conf.h> |
| #include <cutils/properties.h> |
| |
| namespace android_audio_legacy { |
| |
| // ---------------------------------------------------------------------------- |
| // AudioPolicyInterface implementation |
| // ---------------------------------------------------------------------------- |
| |
| audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy, |
| bool fromCache) |
| { |
| uint32_t device = AUDIO_DEVICE_NONE; |
| |
| if (fromCache) { |
| ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x", |
| strategy, mDeviceForStrategy[strategy]); |
| return mDeviceForStrategy[strategy]; |
| } |
| |
| switch (strategy) { |
| |
| case STRATEGY_SONIFICATION_RESPECTFUL: |
| if (isInCall()) { |
| device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); |
| } else if (isStreamActiveRemotely(AudioSystem::MUSIC, |
| SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { |
| // while media is playing on a remote device, use the the sonification behavior. |
| // Note that we test this usecase before testing if media is playing because |
| // the isStreamActive() method only informs about the activity of a stream, not |
| // if it's for local playback. Note also that we use the same delay between both tests |
| device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); |
| } else if (isStreamActive(AudioSystem::MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { |
| // while media is playing (or has recently played), use the same device |
| device = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); |
| } else { |
| // when media is not playing anymore, fall back on the sonification behavior |
| device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); |
| } |
| |
| break; |
| |
| case STRATEGY_DTMF: |
| if (!isInCall()) { |
| // when off call, DTMF strategy follows the same rules as MEDIA strategy |
| device = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); |
| break; |
| } |
| // when in call, DTMF and PHONE strategies follow the same rules |
| // FALL THROUGH |
| |
| case STRATEGY_PHONE: |
| // for phone strategy, we first consider the forced use and then the available devices by order |
| // of priority |
| switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) { |
| case AudioSystem::FORCE_BT_SCO: |
| if (!isInCall() || strategy != STRATEGY_DTMF) { |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT; |
| if (device) break; |
| } |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET; |
| if (device) break; |
| device = mAvailableOutputDevices & AUDIO_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 |
| // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP |
| if (mHasA2dp && !isInCall() && |
| (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) && |
| (getA2dpOutput() != 0) && !mA2dpSuspended) { |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; |
| if (device) break; |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; |
| if (device) break; |
| } |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; |
| if (device) break; |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADSET; |
| if (device) break; |
| if (mPhoneState != AudioSystem::MODE_IN_CALL) { |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY; |
| if (device) break; |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE; |
| if (device) break; |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL; |
| if (device) break; |
| } |
| |
| // Allow voice call on USB ANLG DOCK headset |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; |
| if (device) break; |
| |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; |
| if (device) break; |
| |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_EARPIECE; |
| if (device) break; |
| device = mDefaultOutputDevice; |
| if (device == AUDIO_DEVICE_NONE) { |
| ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE"); |
| } |
| break; |
| |
| case AudioSystem::FORCE_SPEAKER: |
| // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to |
| // A2DP speaker when forcing to speaker output |
| if (mHasA2dp && !isInCall() && |
| (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) && |
| (getA2dpOutput() != 0) && !mA2dpSuspended) { |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; |
| if (device) break; |
| } |
| if (mPhoneState != AudioSystem::MODE_IN_CALL) { |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY; |
| if (device) break; |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE; |
| if (device) break; |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; |
| if (device) break; |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL; |
| if (device) break; |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; |
| if (device) break; |
| } |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER; |
| if (device) break; |
| device = mDefaultOutputDevice; |
| if (device == AUDIO_DEVICE_NONE) { |
| ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE, FORCE_SPEAKER"); |
| } |
| break; |
| } |
| break; |
| |
| case STRATEGY_SONIFICATION: |
| |
| // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by |
| // handleIncallSonification(). |
| if (isInCall()) { |
| device = getDeviceForStrategy(STRATEGY_PHONE, false /*fromCache*/); |
| break; |
| } |
| // FALL THROUGH |
| |
| case STRATEGY_ENFORCED_AUDIBLE: |
| // strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION |
| // except: |
| // - when in call where it doesn't default to STRATEGY_PHONE behavior |
| // - in countries where not enforced in which case it follows STRATEGY_MEDIA |
| |
| if ((strategy == STRATEGY_SONIFICATION) || |
| (mForceUse[AudioSystem::FOR_SYSTEM] == AudioSystem::FORCE_SYSTEM_ENFORCED)) { |
| device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER; |
| if (device == AUDIO_DEVICE_NONE) { |
| ALOGE("getDeviceForStrategy() speaker device not found for STRATEGY_SONIFICATION"); |
| } |
| } |
| // The second device used for sonification is the same as the device used by media strategy |
| // FALL THROUGH |
| |
| case STRATEGY_MEDIA: { |
| uint32_t device2 = AUDIO_DEVICE_NONE; |
| if (strategy != STRATEGY_SONIFICATION) { |
| // no sonification on remote submix (e.g. WFD) |
| device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_REMOTE_SUBMIX; |
| } |
| if ((device2 == AUDIO_DEVICE_NONE) && |
| mHasA2dp && (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) && |
| (getA2dpOutput() != 0) && !mA2dpSuspended) { |
| device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; |
| if (device2 == AUDIO_DEVICE_NONE) { |
| device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; |
| } |
| if (device2 == AUDIO_DEVICE_NONE) { |
| device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; |
| } |
| } |
| if (device2 == AUDIO_DEVICE_NONE) { |
| device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; |
| } |
| if (device2 == AUDIO_DEVICE_NONE) { |
| device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADSET; |
| } |
| if (device2 == AUDIO_DEVICE_NONE) { |
| device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY; |
| } |
| if (device2 == AUDIO_DEVICE_NONE) { |
| device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE; |
| } |
| if (device2 == AUDIO_DEVICE_NONE) { |
| device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; |
| } |
| if ((device2 == AUDIO_DEVICE_NONE) && (strategy != STRATEGY_SONIFICATION)) { |
| // no sonification on aux digital (e.g. HDMI) |
| device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL; |
| } |
| if ((device2 == AUDIO_DEVICE_NONE) && |
| (mForceUse[AudioSystem::FOR_DOCK] == AudioSystem::FORCE_ANALOG_DOCK)) { |
| device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; |
| } |
| if (device2 == AUDIO_DEVICE_NONE) { |
| device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER; |
| } |
| |
| // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or |
| // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise |
| device |= device2; |
| if (device) break; |
| device = mDefaultOutputDevice; |
| if (device == AUDIO_DEVICE_NONE) { |
| ALOGE("getDeviceForStrategy() no device found for STRATEGY_MEDIA"); |
| } |
| } break; |
| |
| default: |
| ALOGW("getDeviceForStrategy() unknown strategy: %d", strategy); |
| break; |
| } |
| |
| ALOGVV("getDeviceForStrategy() strategy %d, device %x", strategy, device); |
| return device; |
| } |
| |
| AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface) |
| { |
| return new AudioPolicyManager(clientInterface); |
| } |
| |
| void destroyAudioPolicyManager(AudioPolicyInterface *interface) |
| { |
| delete interface; |
| } |
| |
| }; // namespace android |