status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream, int session) { LOGV("stopOutput() output %d, stream %d", output, stream); ssize_t index = mOutputs.indexOfKey(output); if (index < 0) { LOGW("stopOutput() unknow output %d", output); return BAD_VALUE; } AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); routing_strategy strategy = AudioPolicyManagerBase::getStrategy((AudioSystem::stream_type)stream); // handle special case for sonification while in call if (isInCall()) { AudioPolicyManagerBase::handleIncallSonification(stream, false, false); } if (outputDesc->mRefCount[stream] > 0) { // decrement usage count of this stream on the output outputDesc->changeRefCount(stream, -1); // store time at which the last music track was stopped - see computeVolume() if (stream == AudioSystem::MUSIC) { outputDesc->mStopTime[stream] = systemTime(); // mMusicStopTime = systemTime(); } #ifdef WITH_QCOM_LPA uint32_t newDevice = AudioPolicyManagerBase::getNewDevice(mHardwareOutput, false); if(newDevice == 0 && mLPADecodeOutput != -1) { newDevice = AudioPolicyManagerBase::getNewDevice(mLPADecodeOutput, false); } setOutputDevice(output, newDevice); #else setOutputDevice(output, AudioPolicyManagerBase::getNewDevice(output)); #endif #ifdef WITH_A2DP if (mA2dpOutput != 0 && !a2dpUsedForSonification() && (strategy == STRATEGY_SONIFICATION || strategy == STRATEGY_ENFORCED_AUDIBLE)) { setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput, mOutputs.valueFor(mHardwareOutput)->mLatency*2); } #endif if (output != mHardwareOutput) { setOutputDevice(mHardwareOutput, AudioPolicyManagerBase::getNewDevice(mHardwareOutput), true); } return NO_ERROR; } else { LOGW("stopOutput() refcount is already 0 for output %d", output); return INVALID_OPERATION; } }
status_t AudioPolicyManager::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream, int session) { LOGV("startOutput() output %d, stream %d", output, stream); ssize_t index = mOutputs.indexOfKey(output); if (index < 0) { LOGW("startOutput() unknow output %d", output); return BAD_VALUE; } AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream); #ifdef WITH_A2DP if (mA2dpOutput != 0 && !a2dpUsedForSonification() && (strategy == STRATEGY_SONIFICATION || strategy == STRATEGY_ENFORCED_AUDIBLE)) { setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput); } #endif // incremenent usage count for this stream on the requested output: // NOTE that the usage count is the same for duplicated output and hardware output which is // necassary for a correct control of hardware output routing by startOutput() and stopOutput() outputDesc->changeRefCount(stream, 1); #ifdef FM_RADIO #ifdef WITH_QCOM_LPA if ((stream == AudioSystem::FM && output == mA2dpOutput) || output == mLPADecodeOutput) setOutputDevice(output, AudioPolicyManagerBase::getNewDevice(output), true); else setOutputDevice(output, AudioPolicyManagerBase::getNewDevice(output)); #else if (stream == AudioSystem::FM && output == mA2dpOutput) setOutputDevice(output, AudioPolicyManagerBase::getNewDevice(output), true); else setOutputDevice(output, AudioPolicyManagerBase::getNewDevice(output)); #endif #else #ifdef WITH_QCOM_LPA if (output == mLPADecodeOutput) setOutputDevice(output, AudioPolicyManagerBase::getNewDevice(output), true); else setOutputDevice(output, AudioPolicyManagerBase::getNewDevice(output)); #else setOutputDevice(output, AudioPolicyManagerBase::getNewDevice(output)); #endif #endif // handle special case for sonification while in call if (isInCall()) { AudioPolicyManagerBase::handleIncallSonification(stream, true, false); } // apply volume rules for current stream and device if necessary checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, outputDesc->device()); return NO_ERROR; }
void AudioPolicyManager::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; } 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; } 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(); } // when changing from ring tone to in call mode, mute the ringing tone // immediately and delay the route change to avoid sending the ring tone // tail into the earpiece or headset. int delayMs = 0; if (isStateInCall(state) && oldState == AudioSystem::MODE_RINGTONE) { // delay the device change command by twice the output latency to have some margin // and be sure that audio buffers not yet affected by the mute are out when // we actually apply the route change delayMs = hwOutputDesc->mLatency*2; setStreamMute(AudioSystem::RING, true, mPrimaryOutput); } if (isStateInCall(state)) { for (size_t i = 0; i < mOutputs.size(); i++) { AudioOutputDescriptor *desc = mOutputs.valueAt(i); //take the biggest latency for all outputs if (delayMs < desc->mLatency*2) { delayMs = desc->mLatency*2; } //mute STRATEGY_MEDIA on all outputs if (desc->strategyRefCount(STRATEGY_MEDIA) != 0) { setStrategyMute(STRATEGY_MEDIA, true, mOutputs.keyAt(i)); setStrategyMute(STRATEGY_MEDIA, false, mOutputs.keyAt(i), MUTE_TIME_MS, getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/)); } } } // Ignore the delay to enable voice call on this target as the enabling the // voice call has enough delay to make sure the ringtone audio completely // played out if (state == AudioSystem::MODE_IN_CALL && oldState == AudioSystem::MODE_RINGTONE) { delayMs = 40; } // 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); // unmute the ringing tone after a sufficient delay if it was muted before // setting output device above if (oldState == AudioSystem::MODE_RINGTONE) { setStreamMute(AudioSystem::RING, false, mPrimaryOutput, MUTE_TIME_MS); } for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { handleIncallSonification(stream, true, true); } } // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE if (state == AudioSystem::MODE_RINGTONE && isStreamActive(AudioSystem::MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY)) { mLimitRingtoneVolume = true; } else { mLimitRingtoneVolume = false; } }
void AudioPolicyManager::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs) { LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs); AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); if (outputDesc->isDuplicated()) { setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs); setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs); return; } #ifdef WITH_A2DP // filter devices according to output selected if (output == mA2dpOutput) { device &= AudioSystem::DEVICE_OUT_ALL_A2DP; } else { device &= ~AudioSystem::DEVICE_OUT_ALL_A2DP; } #endif uint32_t prevDevice = (uint32_t)outputDesc->device(); // Do not change the routing if: // - the requestede device is 0 // - the requested device is the same as current device and force is not specified. // Doing this check here allows the caller to call setOutputDevice() without conditions if ((device == 0 || device == prevDevice) && !force) { LOGV("setOutputDevice() setting same device %x or null device for output %d", device, output); return; } outputDesc->mDevice = device; // mute media streams if both speaker and headset are selected if (device == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADSET) #ifdef QCOM_ANC || device == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_ANC_HEADSET) || device == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_ANC_HEADPHONE) #endif || device == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADPHONE) #ifdef FM_RADIO || device == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADSET | AudioSystem::DEVICE_OUT_FM) || device == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADPHONE | AudioSystem::DEVICE_OUT_FM) || device == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_FM_TX) #endif ) { setStrategyMute(STRATEGY_MEDIA, true, output); #ifdef WITH_QCOM_LPA // Mute LPA output also if it belongs to STRATEGY_MEDIA if(((mLPADecodeOutput != -1) && (mLPADecodeOutput != output) && mOutputs.valueFor(mLPADecodeOutput)->isUsedByStrategy(STRATEGY_MEDIA))) { LOGV("setOutputDevice: Muting mLPADecodeOutput:%d",mLPADecodeOutput); setStrategyMute(STRATEGY_MEDIA, true, mLPADecodeOutput); } #endif // Mute hardware output also if it belongs to STRATEGY_MEDIA if(((mHardwareOutput != -1) && (mHardwareOutput != output) && mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_MEDIA))) { LOGV("setOutputDevice: Muting mHardwareOutput:%d",mHardwareOutput); setStrategyMute(STRATEGY_MEDIA, true, mHardwareOutput); } // wait for the PCM output buffers to empty before proceeding with the rest of the command usleep(outputDesc->mLatency*2*1000); } #ifdef WITH_A2DP // suspend A2DP output if SCO device is selected if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) { if ((mA2dpOutput != 0) && (output == mHardwareOutput)) { mpClientInterface->suspendOutput(mA2dpOutput); } } #endif // wait for output buffers to be played on the HDMI device before routing to new device if(prevDevice == AudioSystem::DEVICE_OUT_AUX_DIGITAL) { #ifdef WITH_QCOM_LPA if((mLPADecodeOutput != -1 && output == mLPADecodeOutput && mOutputs.valueFor(mLPADecodeOutput)->isUsedByStrategy(STRATEGY_MEDIA))) { AudioPolicyManagerBase::checkAndSetVolume(AudioSystem::MUSIC, mStreams[AudioSystem::MUSIC].mIndexCur, mLPADecodeOutput, device, delayMs, force); usleep(75*1000); } else { #endif AudioPolicyManagerBase::checkAndSetVolume(AudioSystem::MUSIC, mStreams[AudioSystem::MUSIC].mIndexCur, output, device, delayMs, force); usleep(outputDesc->mLatency*3*1000); #ifdef WITH_QCOM_LPA } #endif } // do the routing AudioParameter param = AudioParameter(); param.addInt(String8(AudioParameter::keyRouting), (int)device); mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs); // update stream volumes according to new device AudioPolicyManagerBase::applyStreamVolumes(output, device, delayMs); #ifdef WITH_QCOM_LPA if((mLPADecodeOutput != -1 && mOutputs.valueFor(mLPADecodeOutput)->isUsedByStrategy(STRATEGY_MEDIA))) { AudioPolicyManagerBase::applyStreamVolumes(mLPADecodeOutput, device, delayMs); } #endif #ifdef WITH_A2DP // if disconnecting SCO device, restore A2DP output if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)prevDevice)) { if (mA2dpOutput != 0) { LOGV("restore A2DP output"); mpClientInterface->restoreOutput(mA2dpOutput); } } #endif // if changing from a combined headset + speaker route, unmute media streams if (prevDevice == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADSET) #ifdef QCOM_ANC || prevDevice == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_ANC_HEADSET) || prevDevice == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_ANC_HEADPHONE) #endif || prevDevice == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADPHONE) #ifdef FM_RADIO || prevDevice == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADSET | AudioSystem::DEVICE_OUT_FM) || prevDevice == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADPHONE | AudioSystem::DEVICE_OUT_FM) || prevDevice == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_FM_TX) #endif ) { setStrategyMute(STRATEGY_MEDIA, false, output, delayMs); #ifdef WITH_QCOM_LPA // Unmute LPA output also if it belongs to STRATEGY_MEDIA if(((mLPADecodeOutput != -1) && (mLPADecodeOutput != output) && mOutputs.valueFor(mLPADecodeOutput)->isUsedByStrategy(STRATEGY_MEDIA))) { LOGV("setOutputDevice: Unmuting mLPADecodeOutput:%d delayMs:%d",mLPADecodeOutput,delayMs); setStrategyMute(STRATEGY_MEDIA, false, mLPADecodeOutput, delayMs); } #endif // Unmute hardware output also if it belongs to STRATEGY_MEDIA if(((mHardwareOutput != -1) && (mHardwareOutput != output) && mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_MEDIA))) { LOGV("setOutputDevice: Unmuting mHardwareOutput:%d delayMs:%d",mHardwareOutput,delayMs); setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, delayMs); } } }