policy-hal: upgrade custom audio policy for concurrency
- this change contains all the change and fixes
for concurrecny feautre went in audio policy made
by QTI as below
Audio: voice concurrency support for Bear family
Change-Id: I6879081dd30969a7d1f162ea73b912442db2bef6
audio_policy: Null check for output profile types
Change-Id: I6abc2e28c93a6de9922a8ac7b2a81a477b3ea566
audio: add support for VoIP & playback record concurrency
Change-Id: Ia93a51f0d0d2cfdd447d73ce65bfc07965190ab4
audio: Fix ringtone mute issue
Change-Id: I2bcdcb2591942f48eac4635a3b36ee575787ee92
audio: Fix for ringtone playback issue on USB AOA
Change-Id: I323e80470e64b58a53d68ca57a29605768324e14
audio: Fix for camcorder shutter playback issue on BT
Change-Id: Ib6f46bacd70da5de5958f65d921c7c219a5b0dc1
audiopolicy: fix crash in camcorder during voice call
Change-Id: Id83111957dab04a3db396401547989db8e8ed573
Change-Id: I37d811a2c947b6ba333b5937ffd5c94fa50714b8
diff --git a/policy_hal/Android.mk b/policy_hal/Android.mk
index 874b3dd..9df7f93 100644
--- a/policy_hal/Android.mk
+++ b/policy_hal/Android.mk
@@ -27,6 +27,14 @@
LOCAL_STATIC_LIBRARIES := \
libmedia_helper \
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_VOICE_CONCURRENCY)),true)
+LOCAL_CFLAGS += -DVOICE_CONCURRENCY
+endif
+
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_RECORD_PLAY_CONCURRENCY)),true)
+LOCAL_CFLAGS += -DRECORD_PLAY_CONCURRENCY
+endif
+
LOCAL_MODULE := libaudiopolicymanager
include $(BUILD_SHARED_LIBRARY)
diff --git a/policy_hal/AudioPolicyManager.cpp b/policy_hal/AudioPolicyManager.cpp
index 03dec57..39386bb 100644
--- a/policy_hal/AudioPolicyManager.cpp
+++ b/policy_hal/AudioPolicyManager.cpp
@@ -53,7 +53,34 @@
#include <policy.h>
namespace android {
+#ifdef VOICE_CONCURRENCY
+audio_output_flags_t AudioPolicyManagerCustom::getFallBackPath()
+{
+ audio_output_flags_t flag = AUDIO_OUTPUT_FLAG_FAST;
+ char propValue[PROPERTY_VALUE_MAX];
+ if (property_get("voice.conc.fallbackpath", propValue, NULL)) {
+ if (!strncmp(propValue, "deep-buffer", 11)) {
+ flag = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+ }
+ else if (!strncmp(propValue, "fast", 4)) {
+ flag = AUDIO_OUTPUT_FLAG_FAST;
+ }
+ else {
+ ALOGD("voice_conc:not a recognised path(%s) in prop voice.conc.fallbackpath",
+ propValue);
+ }
+ }
+ else {
+ ALOGD("voice_conc:prop voice.conc.fallbackpath not set");
+ }
+
+ ALOGD("voice_conc:picked up flag(0x%x) from prop voice.conc.fallbackpath",
+ flag);
+
+ return flag;
+}
+#endif /*VOICE_CONCURRENCY*/
// ----------------------------------------------------------------------------
// AudioPolicyInterface implementation
// ----------------------------------------------------------------------------
@@ -297,7 +324,33 @@
offloadInfo.format,
offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us,
offloadInfo.has_video);
+#ifdef VOICE_CONCURRENCY
+ char concpropValue[PROPERTY_VALUE_MAX];
+ if (property_get("voice.playback.conc.disabled", concpropValue, NULL)) {
+ bool propenabled = atoi(concpropValue) || !strncmp("true", concpropValue, 4);
+ if (propenabled) {
+ if (isInCall())
+ {
+ ALOGD("\n copl: blocking compress offload on call mode\n");
+ return false;
+ }
+ }
+ }
+#endif
+#ifdef RECORD_PLAY_CONCURRENCY
+ char recConcPropValue[PROPERTY_VALUE_MAX];
+ bool prop_rec_play_enabled = false;
+ if (property_get("rec.playback.conc.disabled", recConcPropValue, NULL)) {
+ prop_rec_play_enabled = atoi(recConcPropValue) || !strncmp("true", recConcPropValue, 4);
+ }
+
+ if ((prop_rec_play_enabled) &&
+ ((true == mIsInputRequestOnProgress) || (mInputs.activeInputsCount() > 0))) {
+ ALOGD("copl: blocking compress offload for record concurrency");
+ return false;
+ }
+#endif
// Check if offload has been disabled
char propValue[PROPERTY_VALUE_MAX];
if (property_get("audio.offload.disable", propValue, "0")) {
@@ -316,12 +369,21 @@
//check if it's multi-channel AAC (includes sub formats) and FLAC format
if ((popcount(offloadInfo.channel_mask) > 2) &&
(((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_AAC) ||
- ((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_FLAC)||
((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_VORBIS))) {
ALOGD("offload disabled for multi-channel AAC,FLAC and VORBIS format");
return false;
}
-
+#ifdef AUDIO_EXTN_FORMATS_ENABLED
+ //check if it's multi-channel FLAC/ALAC/WMA format with sample rate > 48k
+ if ((popcount(offloadInfo.channel_mask) > 2) &&
+ (((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_FLAC) ||
+ (((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_ALAC) && offloadInfo.sample_rate > 48000) ||
+ (((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_WMA) && offloadInfo.sample_rate > 48000) ||
+ (((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_WMA_PRO) && offloadInfo.sample_rate > 48000))) {
+ ALOGD("offload disabled for multi-channel FLAC/ALAC/WMA clips with sample rate > 48kHz");
+ return false;
+ }
+#endif
//TODO: enable audio offloading with video when ready
const bool allowOffloadWithVideo =
property_get_bool("audio.offload.video", false /* default_value */);
@@ -342,12 +404,15 @@
//do not check duration for other audio formats, e.g. dolby AAC/AC3 and amrwb+ formats
if ((offloadInfo.format == AUDIO_FORMAT_MP3) ||
((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_AAC) ||
+ ((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_VORBIS)
+#ifdef AUDIO_EXTN_FORMATS_ENABLED
((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_FLAC) ||
- ((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_VORBIS) ||
((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_WMA) ||
((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_WMA_PRO) ||
((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_ALAC) ||
- ((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_APE))
+ ((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_APE)
+#endif
+ )
return false;
}
@@ -490,7 +555,202 @@
updateDevicesAndOutputs();
sp<SwAudioOutputDescriptor> hwOutputDesc = mPrimaryOutput;
+#ifdef VOICE_CONCURRENCY
+ int voice_call_state = 0;
+ char propValue[PROPERTY_VALUE_MAX];
+ bool prop_playback_enabled = false, prop_rec_enabled=false, prop_voip_enabled = false;
+ if(property_get("voice.playback.conc.disabled", propValue, NULL)) {
+ prop_playback_enabled = atoi(propValue) || !strncmp("true", propValue, 4);
+ }
+
+ if(property_get("voice.record.conc.disabled", propValue, NULL)) {
+ prop_rec_enabled = atoi(propValue) || !strncmp("true", propValue, 4);
+ }
+
+ if(property_get("voice.voip.conc.disabled", propValue, NULL)) {
+ prop_voip_enabled = atoi(propValue) || !strncmp("true", propValue, 4);
+ }
+
+ bool mode_in_call = (AUDIO_MODE_IN_CALL != oldState) && (AUDIO_MODE_IN_CALL == state);
+ //query if it is a actual voice call initiated by telephony
+ if (mode_in_call) {
+ String8 valueStr = mpClientInterface->getParameters((audio_io_handle_t)0, String8("in_call"));
+ AudioParameter result = AudioParameter(valueStr);
+ if (result.getInt(String8("in_call"), voice_call_state) == NO_ERROR)
+ ALOGD("voice_conc:SetPhoneState: Voice call state = %d", voice_call_state);
+ }
+
+ if (mode_in_call && voice_call_state && !mvoice_call_state) {
+ ALOGD("voice_conc:Entering to call mode oldState :: %d state::%d ",
+ oldState, state);
+ mvoice_call_state = voice_call_state;
+ if (prop_rec_enabled) {
+ //Close all active inputs
+ audio_io_handle_t activeInput = mInputs.getActiveInput();
+ if (activeInput != 0) {
+ sp<AudioInputDescriptor> activeDesc = mInputs.valueFor(activeInput);
+ switch(activeDesc->mInputSource) {
+ case AUDIO_SOURCE_VOICE_UPLINK:
+ case AUDIO_SOURCE_VOICE_DOWNLINK:
+ case AUDIO_SOURCE_VOICE_CALL:
+ ALOGD("voice_conc:FOUND active input during call active: %d",activeDesc->mInputSource);
+ break;
+
+ case AUDIO_SOURCE_VOICE_COMMUNICATION:
+ if(prop_voip_enabled) {
+ ALOGD("voice_conc:CLOSING VoIP input source on call setup :%d ",activeDesc->mInputSource);
+ stopInput(activeInput, activeDesc->mSessions.itemAt(0));
+ releaseInput(activeInput, activeDesc->mSessions.itemAt(0));
+ }
+ break;
+
+ default:
+ ALOGD("voice_conc:CLOSING input on call setup for inputSource: %d",activeDesc->mInputSource);
+ stopInput(activeInput, activeDesc->mSessions.itemAt(0));
+ releaseInput(activeInput, activeDesc->mSessions.itemAt(0));
+ break;
+ }
+ }
+ } else if (prop_voip_enabled) {
+ audio_io_handle_t activeInput = mInputs.getActiveInput();
+ if (activeInput != 0) {
+ sp<AudioInputDescriptor> activeDesc = mInputs.valueFor(activeInput);
+ if (AUDIO_SOURCE_VOICE_COMMUNICATION == activeDesc->mInputSource) {
+ ALOGD("voice_conc:CLOSING VoIP on call setup : %d",activeDesc->mInputSource);
+ stopInput(activeInput, activeDesc->mSessions.itemAt(0));
+ releaseInput(activeInput, activeDesc->mSessions.itemAt(0));
+ }
+ }
+ }
+ if (prop_playback_enabled) {
+ // Move tracks associated to this strategy from previous output to new output
+ for (int i = AUDIO_STREAM_SYSTEM; i < (int)AUDIO_STREAM_CNT; i++) {
+ ALOGV("voice_conc:Invalidate on call mode for stream :: %d ", i);
+ if (i == AUDIO_STREAM_PATCH) {
+ ALOGV("voice_conc:not calling invalidate for AUDIO_STREAM_PATCH");
+ continue;
+ }
+ if (AUDIO_OUTPUT_FLAG_DEEP_BUFFER == mFallBackflag) {
+ if ((AUDIO_STREAM_MUSIC == i) ||
+ (AUDIO_STREAM_VOICE_CALL == i) ) {
+ ALOGD("voice_conc:Invalidate stream type %d", i);
+ mpClientInterface->invalidateStream((audio_stream_type_t)i);
+ }
+ } else if (AUDIO_OUTPUT_FLAG_FAST == mFallBackflag) {
+ ALOGD("voice_conc:Invalidate stream type %d", i);
+ mpClientInterface->invalidateStream((audio_stream_type_t)i);
+ }
+ }
+ }
+
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
+ if ( (outputDesc == NULL) || (outputDesc->mProfile == NULL)) {
+ ALOGD("voice_conc:ouput desc / profile is NULL");
+ continue;
+ }
+
+ if (AUDIO_OUTPUT_FLAG_FAST == mFallBackflag) {
+ if (((!outputDesc->isDuplicated() &&outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY))
+ && prop_playback_enabled) {
+ ALOGD("voice_conc:calling suspendOutput on call mode for primary output");
+ mpClientInterface->suspendOutput(mOutputs.keyAt(i));
+ } //Close compress all sessions
+ else if ((outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
+ && prop_playback_enabled) {
+ ALOGD("voice_conc:calling closeOutput on call mode for COMPRESS output");
+ closeOutput(mOutputs.keyAt(i));
+ }
+ else if ((outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_VOIP_RX)
+ && prop_voip_enabled) {
+ ALOGD("voice_conc:calling closeOutput on call mode for DIRECT output");
+ closeOutput(mOutputs.keyAt(i));
+ }
+ } else if (AUDIO_OUTPUT_FLAG_DEEP_BUFFER == mFallBackflag) {
+ if ((outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_DIRECT)
+ && prop_playback_enabled) {
+ ALOGD("voice_conc:calling closeOutput on call mode for COMPRESS output");
+ closeOutput(mOutputs.keyAt(i));
+ }
+ }
+ }
+ }
+
+ if ((AUDIO_MODE_IN_CALL == oldState || AUDIO_MODE_IN_COMMUNICATION == oldState) &&
+ (AUDIO_MODE_NORMAL == state) && prop_playback_enabled && mvoice_call_state) {
+ ALOGD("voice_conc:EXITING from call mode oldState :: %d state::%d \n",oldState, state);
+ mvoice_call_state = 0;
+ if (AUDIO_OUTPUT_FLAG_FAST == mFallBackflag) {
+ //restore PCM (deep-buffer) output after call termination
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
+ if ( (outputDesc == NULL) || (outputDesc->mProfile == NULL)) {
+ ALOGD("voice_conc:ouput desc / profile is NULL");
+ continue;
+ }
+ if (!outputDesc->isDuplicated() && outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
+ ALOGD("voice_conc:calling restoreOutput after call mode for primary output");
+ mpClientInterface->restoreOutput(mOutputs.keyAt(i));
+ }
+ }
+ }
+ //call invalidate tracks so that any open streams can fall back to deep buffer/compress path from ULL
+ for (int i = AUDIO_STREAM_SYSTEM; i < (int)AUDIO_STREAM_CNT; i++) {
+ ALOGV("voice_conc:Invalidate on call mode for stream :: %d ", i);
+ if (i == AUDIO_STREAM_PATCH) {
+ ALOGV("voice_conc:not calling invalidate for AUDIO_STREAM_PATCH");
+ continue;
+ }
+ if (AUDIO_OUTPUT_FLAG_DEEP_BUFFER == mFallBackflag) {
+ if ((AUDIO_STREAM_MUSIC == i) ||
+ (AUDIO_STREAM_VOICE_CALL == i) ) {
+ mpClientInterface->invalidateStream((audio_stream_type_t)i);
+ }
+ } else if (AUDIO_OUTPUT_FLAG_FAST == mFallBackflag) {
+ mpClientInterface->invalidateStream((audio_stream_type_t)i);
+ }
+ }
+ }
+
+#endif
+#ifdef RECORD_PLAY_CONCURRENCY
+ char recConcPropValue[PROPERTY_VALUE_MAX];
+ bool prop_rec_play_enabled = false;
+
+ if (property_get("rec.playback.conc.disabled", recConcPropValue, NULL)) {
+ prop_rec_play_enabled = atoi(recConcPropValue) || !strncmp("true", recConcPropValue, 4);
+ }
+ if (prop_rec_play_enabled) {
+ if (AUDIO_MODE_IN_COMMUNICATION == mEngine->getPhoneState()) {
+ ALOGD("phone state changed to MODE_IN_COMM invlaidating music and voice streams");
+ // call invalidate for voice streams, so that it can use deepbuffer with VoIP out device from HAL
+ mpClientInterface->invalidateStream(AUDIO_STREAM_VOICE_CALL);
+ // call invalidate for music, so that compress will fallback to deep-buffer with VoIP out device
+ mpClientInterface->invalidateStream(AUDIO_STREAM_MUSIC);
+
+ // close compress output to make sure session will be closed before timeout(60sec)
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
+ if ((outputDesc == NULL) || (outputDesc->mProfile == NULL)) {
+ ALOGD("ouput desc / profile is NULL");
+ continue;
+ }
+
+ if (outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+ ALOGD("calling closeOutput on call mode for COMPRESS output");
+ closeOutput(mOutputs.keyAt(i));
+ }
+ }
+ } else if ((oldState == AUDIO_MODE_IN_COMMUNICATION) &&
+ (mEngine->getPhoneState() == AUDIO_MODE_NORMAL)) {
+ // call invalidate for music so that music can fallback to compress
+ mpClientInterface->invalidateStream(AUDIO_STREAM_MUSIC);
+ }
+ }
+#endif
+ mPrevPhoneState = oldState;
int delayMs = 0;
if (isStateInCall(state)) {
nsecs_t sysTime = systemTime();
@@ -896,7 +1156,95 @@
// compress should not be used for non-music streams
ALOGE("Offloading only allowed with music stream");
return 0;
+ }
+#ifdef VOICE_CONCURRENCY
+ char propValue[PROPERTY_VALUE_MAX];
+ bool prop_play_enabled=false, prop_voip_enabled = false;
+
+ if(property_get("voice.playback.conc.disabled", propValue, NULL)) {
+ prop_play_enabled = atoi(propValue) || !strncmp("true", propValue, 4);
}
+
+ if(property_get("voice.voip.conc.disabled", propValue, NULL)) {
+ prop_voip_enabled = atoi(propValue) || !strncmp("true", propValue, 4);
+ }
+
+ if (prop_play_enabled && mvoice_call_state) {
+ //check if voice call is active / running in background
+ if((AUDIO_MODE_IN_CALL == mEngine->getPhoneState()) ||
+ ((AUDIO_MODE_IN_CALL == mPrevPhoneState)
+ && (AUDIO_MODE_IN_COMMUNICATION == mEngine->getPhoneState())))
+ {
+ if(AUDIO_OUTPUT_FLAG_VOIP_RX & flags) {
+ if(prop_voip_enabled) {
+ ALOGD("voice_conc:getoutput:IN call mode return no o/p for VoIP %x",
+ flags );
+ return 0;
+ }
+ }
+ else {
+ if (AUDIO_OUTPUT_FLAG_FAST == mFallBackflag) {
+ ALOGD("voice_conc:IN call mode adding ULL flags .. flags: %x ", flags );
+ flags = AUDIO_OUTPUT_FLAG_FAST;
+ } else if (AUDIO_OUTPUT_FLAG_DEEP_BUFFER == mFallBackflag) {
+ if (AUDIO_STREAM_MUSIC == stream) {
+ flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+ ALOGD("voice_conc:IN call mode adding deep-buffer flags %x ", flags );
+ }
+ else {
+ flags = AUDIO_OUTPUT_FLAG_FAST;
+ ALOGD("voice_conc:IN call mode adding fast flags %x ", flags );
+ }
+ }
+ }
+ }
+ } else if (prop_voip_enabled && mvoice_call_state) {
+ //check if voice call is active / running in background
+ //some of VoIP apps(like SIP2SIP call) supports resume of VoIP call when call in progress
+ //return only ULL ouput
+ if((AUDIO_MODE_IN_CALL == mEngine->getPhoneState()) ||
+ ((AUDIO_MODE_IN_CALL == mPrevPhoneState)
+ && (AUDIO_MODE_IN_COMMUNICATION == mEngine->getPhoneState())))
+ {
+ if(AUDIO_OUTPUT_FLAG_VOIP_RX & flags) {
+ ALOGD("voice_conc:getoutput:IN call mode return no o/p for VoIP %x",
+ flags );
+ return 0;
+ }
+ }
+ }
+#endif
+#ifdef RECORD_PLAY_CONCURRENCY
+ char recConcPropValue[PROPERTY_VALUE_MAX];
+ bool prop_rec_play_enabled = false;
+
+ if (property_get("rec.playback.conc.disabled", recConcPropValue, NULL)) {
+ prop_rec_play_enabled = atoi(recConcPropValue) || !strncmp("true", recConcPropValue, 4);
+ }
+ if ((prop_rec_play_enabled) &&
+ ((true == mIsInputRequestOnProgress) || (mInputs.activeInputsCount() > 0))) {
+ if (AUDIO_MODE_IN_COMMUNICATION == mEngine->getPhoneState()) {
+ if (AUDIO_OUTPUT_FLAG_VOIP_RX & flags) {
+ // allow VoIP using voice path
+ // Do nothing
+ } else if((flags & AUDIO_OUTPUT_FLAG_FAST) == 0) {
+ ALOGD("voice_conc:MODE_IN_COMM is setforcing deep buffer output for non ULL... flags: %x", flags);
+ // use deep buffer path for all non ULL outputs
+ flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+ }
+ } else if ((flags & AUDIO_OUTPUT_FLAG_FAST) == 0) {
+ ALOGD("voice_conc:Record mode is on forcing deep buffer output for non ULL... flags: %x ", flags);
+ // use deep buffer path for all non ULL outputs
+ flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+ }
+ }
+ if (prop_rec_play_enabled &&
+ (stream == AUDIO_STREAM_ENFORCED_AUDIBLE)) {
+ ALOGD("Record conc is on forcing ULL output for ENFORCED_AUDIBLE");
+ flags = AUDIO_OUTPUT_FLAG_FAST;
+ }
+#endif
+
/*
* WFD audio routes back to target speaker when starting a ringtone playback.
* This is because primary output is reused for ringtone, so output device is
@@ -934,6 +1282,24 @@
flags = AUDIO_OUTPUT_FLAG_TTS;
}
+ // 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 = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT);
+ }
+ if ((flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) {
+ flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT);
+ }
+ // only allow deep buffering for music stream type
+ if (stream != AUDIO_STREAM_MUSIC) {
+ flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER);
+ }
+ if (stream == AUDIO_STREAM_TTS) {
+ flags = AUDIO_OUTPUT_FLAG_TTS;
+ }
+
sp<IOProfile> profile;
// skip direct output selection if the request can obviously be attached to a mixed output
@@ -1076,4 +1442,258 @@
return output;
}
+
+status_t AudioPolicyManagerCustom::getInputForAttr(const audio_attributes_t *attr,
+ audio_io_handle_t *input,
+ audio_session_t session,
+ uid_t uid,
+ uint32_t samplingRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ audio_input_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
+ input_type_t *inputType)
+{
+ audio_source_t inputSource = attr->source;
+#ifdef VOICE_CONCURRENCY
+
+ char propValue[PROPERTY_VALUE_MAX];
+ bool prop_rec_enabled=false, prop_voip_enabled = false;
+
+ if(property_get("voice.record.conc.disabled", propValue, NULL)) {
+ prop_rec_enabled = atoi(propValue) || !strncmp("true", propValue, 4);
+ }
+
+ if(property_get("voice.voip.conc.disabled", propValue, NULL)) {
+ prop_voip_enabled = atoi(propValue) || !strncmp("true", propValue, 4);
+ }
+
+ if (prop_rec_enabled && mvoice_call_state) {
+ //check if voice call is active / running in background
+ //some of VoIP apps(like SIP2SIP call) supports resume of VoIP call when call in progress
+ //Need to block input request
+ if((AUDIO_MODE_IN_CALL == mEngine->getPhoneState()) ||
+ ((AUDIO_MODE_IN_CALL == mPrevPhoneState) &&
+ (AUDIO_MODE_IN_COMMUNICATION == mEngine->getPhoneState())))
+ {
+ switch(inputSource) {
+ case AUDIO_SOURCE_VOICE_UPLINK:
+ case AUDIO_SOURCE_VOICE_DOWNLINK:
+ case AUDIO_SOURCE_VOICE_CALL:
+ ALOGD("voice_conc:Creating input during incall mode for inputSource: %d",
+ inputSource);
+ break;
+
+ case AUDIO_SOURCE_VOICE_COMMUNICATION:
+ if(prop_voip_enabled) {
+ ALOGD("voice_conc:BLOCK VoIP requst incall mode for inputSource: %d",
+ inputSource);
+ return NO_INIT;
+ }
+ break;
+ default:
+ ALOGD("voice_conc:BLOCK VoIP requst incall mode for inputSource: %d",
+ inputSource);
+ return NO_INIT;
+ }
+ }
+ }//check for VoIP flag
+ else if(prop_voip_enabled && mvoice_call_state) {
+ //check if voice call is active / running in background
+ //some of VoIP apps(like SIP2SIP call) supports resume of VoIP call when call in progress
+ //Need to block input request
+ if((AUDIO_MODE_IN_CALL == mEngine->getPhoneState()) ||
+ ((AUDIO_MODE_IN_CALL == mPrevPhoneState) &&
+ (AUDIO_MODE_IN_COMMUNICATION == mEngine->getPhoneState())))
+ {
+ if(inputSource == AUDIO_SOURCE_VOICE_COMMUNICATION) {
+ ALOGD("BLOCKING VoIP request during incall mode for inputSource: %d ",inputSource);
+ return NO_INIT;
+ }
+ }
+ }
+
+#endif
+
+ return AudioPolicyManager::getInputForAttr(attr,
+ input,
+ session,
+ uid,
+ samplingRate,
+ format,
+ channelMask,
+ flags,
+ selectedDeviceId,
+ inputType);
+}
+status_t AudioPolicyManagerCustom::startInput(audio_io_handle_t input,
+ audio_session_t session)
+{
+ ALOGV("startInput() input %d", input);
+ ssize_t index = mInputs.indexOfKey(input);
+ if (index < 0) {
+ ALOGW("startInput() unknown input %d", input);
+ return BAD_VALUE;
+ }
+ sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
+
+ index = inputDesc->mSessions.indexOf(session);
+ if (index < 0) {
+ ALOGW("startInput() unknown session %d on input %d", session, input);
+ return BAD_VALUE;
+ }
+
+ // virtual input devices are compatible with other input devices
+ if (!is_virtual_input_device(inputDesc->mDevice)) {
+
+ // for a non-virtual input device, check if there is another (non-virtual) active input
+ audio_io_handle_t activeInput = mInputs.getActiveInput();
+ if (activeInput != 0 && activeInput != input) {
+
+ // If the already active input uses AUDIO_SOURCE_HOTWORD then it is closed,
+ // otherwise the active input continues and the new input cannot be started.
+ sp<AudioInputDescriptor> activeDesc = mInputs.valueFor(activeInput);
+ if (activeDesc->mInputSource == AUDIO_SOURCE_HOTWORD) {
+ ALOGW("startInput(%d) preempting low-priority input %d", input, activeInput);
+ stopInput(activeInput, activeDesc->mSessions.itemAt(0));
+ releaseInput(activeInput, activeDesc->mSessions.itemAt(0));
+ } else {
+ ALOGE("startInput(%d) failed: other input %d already started", input, activeInput);
+ return INVALID_OPERATION;
+ }
+ }
+ }
+
+ // Routing?
+ mInputRoutes.incRouteActivity(session);
+#ifdef RECORD_PLAY_CONCURRENCY
+ mIsInputRequestOnProgress = true;
+
+ char getPropValue[PROPERTY_VALUE_MAX];
+ bool prop_rec_play_enabled = false;
+
+ if (property_get("rec.playback.conc.disabled", getPropValue, NULL)) {
+ prop_rec_play_enabled = atoi(getPropValue) || !strncmp("true", getPropValue, 4);
+ }
+
+ if ((prop_rec_play_enabled) &&(mInputs.activeInputsCount() == 0)){
+ // send update to HAL on record playback concurrency
+ AudioParameter param = AudioParameter();
+ param.add(String8("rec_play_conc_on"), String8("true"));
+ ALOGD("startInput() setParameters rec_play_conc is setting to ON ");
+ mpClientInterface->setParameters(0, param.toString());
+
+ // Call invalidate to reset all opened non ULL audio tracks
+ // Move tracks associated to this strategy from previous output to new output
+ for (int i = AUDIO_STREAM_SYSTEM; i < (int)AUDIO_STREAM_CNT; i++) {
+ // Do not call invalidate for ENFORCED_AUDIBLE (otherwise pops are seen for camcorder)
+ if ((i != AUDIO_STREAM_ENFORCED_AUDIBLE && (i != AUDIO_STREAM_PATCH)) {
+ ALOGD("Invalidate on releaseInput for stream :: %d ", i);
+ //FIXME see fixme on name change
+ mpClientInterface->invalidateStream((audio_stream_type_t)i);
+ }
+ }
+ // close compress tracks
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
+ if ((outputDesc == NULL) || (outputDesc->mProfile == NULL)) {
+ ALOGD("ouput desc / profile is NULL");
+ continue;
+ }
+ if (outputDesc->mProfile->mFlags
+ & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+ // close compress sessions
+ ALOGD("calling closeOutput on record conc for COMPRESS output");
+ closeOutput(mOutputs.keyAt(i));
+ }
+ }
+ }
+#endif
+
+ if (inputDesc->mRefCount == 0 || mInputRoutes.hasRouteChanged(session)) {
+ // if input maps to a dynamic policy with an activity listener, notify of state change
+ if ((inputDesc->mPolicyMix != NULL)
+ && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
+ mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mRegistrationId,
+ MIX_STATE_MIXING);
+ }
+
+ if (mInputs.activeInputsCount() == 0) {
+ SoundTrigger::setCaptureState(true);
+ }
+ setInputDevice(input, getNewInputDevice(input), true /* force */);
+
+ // automatically enable the remote submix output when input is started if not
+ // used by a policy mix of type MIX_TYPE_RECORDERS
+ // For remote submix (a virtual device), we open only one input per capture request.
+ if (audio_is_remote_submix_device(inputDesc->mDevice)) {
+ String8 address = String8("");
+ if (inputDesc->mPolicyMix == NULL) {
+ address = String8("0");
+ } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) {
+ address = inputDesc->mPolicyMix->mRegistrationId;
+ }
+ if (address != "") {
+ setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ address, "remote-submix");
+ }
+ }
+ }
+
+ ALOGV("AudioPolicyManager::startInput() input source = %d", inputDesc->mInputSource);
+
+ inputDesc->mRefCount++;
+#ifdef RECORD_PLAY_CONCURRENCY
+ mIsInputRequestOnProgress = false;
+#endif
+ return NO_ERROR;
+}
+status_t AudioPolicyManagerCustom::stopInput(audio_io_handle_t input,
+ audio_session_t session)
+{
+ status_t status;
+ status = AudioPolicyManager::stopInput(input, session);
+#ifdef RECORD_PLAY_CONCURRENCY
+ char propValue[PROPERTY_VALUE_MAX];
+ bool prop_rec_play_enabled = false;
+
+ if (property_get("rec.playback.conc.disabled", propValue, NULL)) {
+ prop_rec_play_enabled = atoi(propValue) || !strncmp("true", propValue, 4);
+ }
+
+ if ((prop_rec_play_enabled) && (mInputs.activeInputsCount() == 0)) {
+
+ //send update to HAL on record playback concurrency
+ AudioParameter param = AudioParameter();
+ param.add(String8("rec_play_conc_on"), String8("false"));
+ ALOGD("stopInput() setParameters rec_play_conc is setting to OFF ");
+ mpClientInterface->setParameters(0, param.toString());
+
+ //call invalidate tracks so that any open streams can fall back to deep buffer/compress path from ULL
+ for (int i = AUDIO_STREAM_SYSTEM; i < (int)AUDIO_STREAM_CNT; i++) {
+ //Do not call invalidate for ENFORCED_AUDIBLE (otherwise pops are seen for camcorder stop tone)
+ if ((i != AUDIO_STREAM_ENFORCED_AUDIBLE) && (i != AUDIO_STREAM_PATCH)) {
+ ALOGD(" Invalidate on stopInput for stream :: %d ", i);
+ //FIXME see fixme on name change
+ mpClientInterface->invalidateStream((audio_stream_type_t)i);
+ }
+ }
+ }
+#endif
+ return status;
+}
+
+AudioPolicyManagerCustom::AudioPolicyManagerCustom(AudioPolicyClientInterface *clientInterface)
+ : AudioPolicyManager(clientInterface)
+{
+#ifdef RECORD_PLAY_CONCURRENCY
+ mIsInputRequestOnProgress = false;
+#endif
+
+
+#ifdef VOICE_CONCURRENCY
+ mFallBackflag = getFallBackPath();
+#endif
+}
}
diff --git a/policy_hal/AudioPolicyManager.h b/policy_hal/AudioPolicyManager.h
index b8c9b6e..eb05e38 100644
--- a/policy_hal/AudioPolicyManager.h
+++ b/policy_hal/AudioPolicyManager.h
@@ -50,10 +50,7 @@
{
public:
- AudioPolicyManagerCustom(AudioPolicyClientInterface *clientInterface)
- : AudioPolicyManager(clientInterface) {
- mHdmiAudioDisabled = false;
- mHdmiAudioEvent = false;}
+ AudioPolicyManagerCustom(AudioPolicyClientInterface *clientInterface);
virtual ~AudioPolicyManagerCustom() {}
@@ -66,7 +63,22 @@
virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo);
- // true if given state represents a device in a telephony or VoIP call
+ virtual status_t getInputForAttr(const audio_attributes_t *attr,
+ audio_io_handle_t *input,
+ audio_session_t session,
+ uid_t uid,
+ uint32_t samplingRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ audio_input_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
+ input_type_t *inputType);
+ // indicates to the audio policy manager that the input starts being used.
+ virtual status_t startInput(audio_io_handle_t input,
+ audio_session_t session);
+ // indicates to the audio policy manager that the input stops being used.
+ virtual status_t stopInput(audio_io_handle_t input,
+ audio_session_t session);
protected:
status_t checkAndSetVolume(audio_stream_type_t stream,
@@ -98,6 +110,10 @@
// the mute/unmute happened 315
uint32_t handleEventForBeacon(int){return 0;}
uint32_t setBeaconMute(bool){return 0;}
+#ifdef VOICE_CONCURRENCY
+ static audio_output_flags_t getFallBackPath();
+ int mFallBackflag;
+#endif /*VOICE_CONCURRENCY*/
// handle special cases for sonification strategy while in call: mute streams or replace by
// a special tone in the device used for communication
@@ -128,6 +144,11 @@
// Used for voip + voice concurrency usecase
int mPrevPhoneState;
int mvoice_call_state;
+#ifdef RECORD_PLAY_CONCURRENCY
+ // Used for record + playback concurrency
+ bool mIsInputRequestOnProgress;
+#endif
+
};