Merge "Revert^2 "audiopolicy: use profile flags during opening an input"" into main
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 91bc700..7af6eb1 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -130,11 +130,7 @@
}
AudioRecord::AudioRecord(const AttributionSourceState &client)
- : mActive(false), mStatus(NO_INIT), mClientAttributionSource(client),
- mSessionId(AUDIO_SESSION_ALLOCATE), mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT), mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE), mSelectedMicDirection(MIC_DIRECTION_UNSPECIFIED),
- mSelectedMicFieldDimension(MIC_FIELD_DIMENSION_DEFAULT)
+ : mClientAttributionSource(client)
{
}
@@ -154,13 +150,7 @@
audio_port_handle_t selectedDeviceId,
audio_microphone_direction_t selectedMicDirection,
float microphoneFieldDimension)
- : mActive(false),
- mStatus(NO_INIT),
- mClientAttributionSource(client),
- mSessionId(AUDIO_SESSION_ALLOCATE),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mProxy(nullptr)
+ : mClientAttributionSource(client)
{
uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mClientAttributionSource.uid));
pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientAttributionSource.pid));
@@ -199,9 +189,6 @@
}
void AudioRecord::stopAndJoinCallbacks() {
- // Prevent nullptr crash if it did not open properly.
- if (mStatus != NO_ERROR) return;
-
// Make sure that callback function exits in the case where
// it is looping on buffer empty condition in obtainBuffer().
// Otherwise the callback thread will never exit.
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 6772201..1a575a7 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -319,13 +319,6 @@
const audio_attributes_t* pAttributes,
bool doNotReconnect,
float maxRequiredSpeed)
- : mStatus(NO_INIT),
- mState(STATE_STOPPED),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mPausedPosition(0),
- mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mAudioTrackCallback(new AudioTrackCallback())
{
mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
diff --git a/media/libaudioclient/PolicyAidlConversion.cpp b/media/libaudioclient/PolicyAidlConversion.cpp
index a71bb18..7588582 100644
--- a/media/libaudioclient/PolicyAidlConversion.cpp
+++ b/media/libaudioclient/PolicyAidlConversion.cpp
@@ -377,6 +377,8 @@
return AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS;
case media::AudioPolicyForcedConfig::ENCODED_SURROUND_MANUAL:
return AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL;
+ case media::AudioPolicyForcedConfig::BT_BLE:
+ return AUDIO_POLICY_FORCE_BT_BLE;
}
return unexpected(BAD_VALUE);
}
@@ -416,6 +418,8 @@
return media::AudioPolicyForcedConfig::ENCODED_SURROUND_ALWAYS;
case AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL:
return media::AudioPolicyForcedConfig::ENCODED_SURROUND_MANUAL;
+ case AUDIO_POLICY_FORCE_BT_BLE:
+ return media::AudioPolicyForcedConfig::BT_BLE;
case AUDIO_POLICY_FORCE_CFG_CNT:
break;
}
diff --git a/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl b/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl
index 2255d4c..111bb2f 100644
--- a/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioPolicyForcedConfig.aidl
@@ -36,4 +36,5 @@
ENCODED_SURROUND_NEVER = 13,
ENCODED_SURROUND_ALWAYS = 14,
ENCODED_SURROUND_MANUAL = 15,
+ BT_BLE = 16,
}
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index 00f2c7a..d4479ef 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -681,7 +681,7 @@
// Current client state: false = stopped, true = active. Protected by mLock. If more states
// are added, consider changing this to enum State { ... } mState as in AudioTrack.
- bool mActive;
+ bool mActive = false;
// for client callback handler
@@ -708,7 +708,7 @@
Modulo<uint32_t> mNewPosition; // in frames
uint32_t mUpdatePeriod; // in frames, zero means no EVENT_NEW_POS
- status_t mStatus;
+ status_t mStatus = NO_INIT;
android::content::AttributionSourceState mClientAttributionSource; // Owner's attribution source
@@ -736,8 +736,8 @@
// held to read or write those bits reliably.
audio_input_flags_t mOrigFlags; // as specified in constructor or set(), const
- audio_session_t mSessionId;
- audio_port_handle_t mPortId; // Id from Audio Policy Manager
+ audio_session_t mSessionId = AUDIO_SESSION_ALLOCATE;
+ audio_port_handle_t mPortId = AUDIO_PORT_HANDLE_NONE;
/**
* mLogSessionId is a string identifying this AudioRecord for the metrics service.
@@ -756,9 +756,9 @@
sp<IMemory> mBufferMemory;
audio_io_handle_t mInput = AUDIO_IO_HANDLE_NONE; // from AudioSystem::getInputforAttr()
- int mPreviousPriority; // before start()
- SchedPolicy mPreviousSchedulingGroup;
- bool mAwaitBoost; // thread should wait for priority boost before running
+ int mPreviousPriority = ANDROID_PRIORITY_NORMAL; // before start()
+ SchedPolicy mPreviousSchedulingGroup = SP_DEFAULT;
+ bool mAwaitBoost = false; // thread should wait for priority boost before running
// The proxy should only be referenced while a lock is held because the proxy isn't
// multi-thread safe.
@@ -799,14 +799,17 @@
// For Device Selection API
// a value of AUDIO_PORT_HANDLE_NONE indicated default (AudioPolicyManager) routing.
- audio_port_handle_t mSelectedDeviceId; // Device requested by the application.
- audio_port_handle_t mRoutedDeviceId; // Device actually selected by audio policy manager:
- // May not match the app selection depending on other
- // activity and connected devices
+
+ // Device requested by the application.
+ audio_port_handle_t mSelectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ // Device actually selected by AudioPolicyManager: This may not match the app
+ // selection depending on other activity and connected devices
+ audio_port_handle_t mRoutedDeviceId = AUDIO_PORT_HANDLE_NONE;
+
wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
- audio_microphone_direction_t mSelectedMicDirection;
- float mSelectedMicFieldDimension;
+ audio_microphone_direction_t mSelectedMicDirection = MIC_DIRECTION_UNSPECIFIED;
+ float mSelectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT;
int32_t mMaxSharedAudioHistoryMs = 0;
std::string mSharedAudioPackageName = {};
diff --git a/media/libaudioclient/tests/audiorecord_tests.cpp b/media/libaudioclient/tests/audiorecord_tests.cpp
index 68b0e7b..f2fee8b 100644
--- a/media/libaudioclient/tests/audiorecord_tests.cpp
+++ b/media/libaudioclient/tests/audiorecord_tests.cpp
@@ -28,6 +28,25 @@
using namespace android;
+// Test that the basic constructor returns an object that doesn't crash
+// on stop() or destruction.
+
+TEST(AudioRecordTestBasic, EmptyAudioRecord) {
+ AttributionSourceState attributionSource;
+ attributionSource.packageName = "AudioRecordTest";
+ attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+ attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+ attributionSource.token = sp<BBinder>::make();
+ const auto ar = sp<AudioRecord>::make(attributionSource);
+
+ // test key commands on an unset AudioRecord.
+ EXPECT_EQ(NO_INIT, ar->initCheck());
+ EXPECT_EQ(true, ar->stopped());
+
+ // just don't crash.
+ ar->stop();
+}
+
class AudioRecordTest : public ::testing::Test {
public:
void SetUp() override {
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
index ff6126d..a13903b 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
@@ -29,6 +29,7 @@
#include <system/audio_effects/effect_visualizer.h>
#include <utils/Log.h>
+#include <Utils.h>
#include "EffectConversionHelperAidl.h"
#include "EffectProxy.h"
@@ -37,18 +38,20 @@
namespace effect {
using ::aidl::android::aidl_utils::statusTFromBinderStatus;
+using ::aidl::android::hardware::audio::common::getChannelCount;
using ::aidl::android::hardware::audio::effect::CommandId;
using ::aidl::android::hardware::audio::effect::Descriptor;
using ::aidl::android::hardware::audio::effect::Flags;
using ::aidl::android::hardware::audio::effect::IEffect;
using ::aidl::android::hardware::audio::effect::Parameter;
using ::aidl::android::hardware::audio::effect::State;
+using ::aidl::android::media::audio::common::AudioChannelLayout;
using ::aidl::android::media::audio::common::AudioDeviceDescription;
using ::aidl::android::media::audio::common::AudioMode;
using ::aidl::android::media::audio::common::AudioSource;
-using ::android::hardware::EventFlag;
using android::effect::utils::EffectParamReader;
using android::effect::utils::EffectParamWriter;
+using android::hardware::EventFlag;
using ::android::status_t;
@@ -519,5 +522,15 @@
return OK;
}
+size_t EffectConversionHelperAidl::getAudioChannelCount() const {
+ return getChannelCount(mCommon.input.base.channelMask,
+ ~AudioChannelLayout::LAYOUT_HAPTIC_AB /* mask */);
+}
+
+size_t EffectConversionHelperAidl::getHapticChannelCount() const {
+ return getChannelCount(mCommon.input.base.channelMask,
+ AudioChannelLayout::LAYOUT_HAPTIC_AB /* mask */);
+}
+
} // namespace effect
} // namespace android
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.h b/media/libaudiohal/impl/EffectConversionHelperAidl.h
index 29c5a83..50b47a9 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.h
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.h
@@ -49,6 +49,9 @@
::aidl::android::hardware::audio::effect::Descriptor getDescriptor() const;
status_t reopen();
+ size_t getAudioChannelCount() const;
+ size_t getHapticChannelCount() const;
+
uint8_t mOutputAccessMode = EFFECT_BUFFER_ACCESS_WRITE;
protected:
diff --git a/media/libaudiohal/impl/EffectHalAidl.cpp b/media/libaudiohal/impl/EffectHalAidl.cpp
index 0c7f78e..ea4dbf6 100644
--- a/media/libaudiohal/impl/EffectHalAidl.cpp
+++ b/media/libaudiohal/impl/EffectHalAidl.cpp
@@ -15,6 +15,7 @@
*/
#include <cstddef>
+#include <cstring>
#define LOG_TAG "EffectHalAidl"
//#define LOG_NDEBUG 0
@@ -128,6 +129,7 @@
::aidl::android::hardware::audio::effect::getEffectTypeUuidHapticGenerator()) {
mConversion = std::make_unique<android::effect::AidlConversionHapticGenerator>(
effect, sessionId, ioId, desc, mIsProxyEffect);
+ mIsHapticGenerator = true;
} else if (typeUuid ==
::aidl::android::hardware::audio::effect::getEffectTypeUuidLoudnessEnhancer()) {
mConversion = std::make_unique<android::effect::AidlConversionLoudnessEnhancer>(
@@ -200,7 +202,7 @@
::android::OK == efGroup->wait(kEventFlagDataMqUpdate, &efState,
1 /* ns */, true /* retry */) &&
efState & kEventFlagDataMqUpdate) {
- ALOGV("%s %s V%d receive dataMQUpdate eventFlag from HAL", __func__, effectName.c_str(),
+ ALOGD("%s %s V%d receive dataMQUpdate eventFlag from HAL", __func__, effectName.c_str(),
halVersion);
mConversion->reopen();
@@ -216,7 +218,7 @@
}
size_t available = inputQ->availableToWrite();
- size_t floatsToWrite = std::min(available, mInBuffer->getSize() / sizeof(float));
+ const size_t floatsToWrite = std::min(available, mInBuffer->getSize() / sizeof(float));
if (floatsToWrite == 0) {
ALOGE("%s not able to write, floats in buffer %zu, space in FMQ %zu", __func__,
mInBuffer->getSize() / sizeof(float), available);
@@ -248,7 +250,7 @@
}
available = outputQ->availableToRead();
- size_t floatsToRead = std::min(available, mOutBuffer->getSize() / sizeof(float));
+ const size_t floatsToRead = std::min(available, mOutBuffer->getSize() / sizeof(float));
if (floatsToRead == 0) {
ALOGE("%s not able to read, buffer space %zu, floats in FMQ %zu", __func__,
mOutBuffer->getSize() / sizeof(float), available);
@@ -257,7 +259,8 @@
float *outputRawBuffer = mOutBuffer->audioBuffer()->f32;
std::vector<float> tempBuffer;
- if (mConversion->mOutputAccessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+ // keep original data in the output buffer for accumulate mode or HapticGenerator effect
+ if (mConversion->mOutputAccessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE || mIsHapticGenerator) {
tempBuffer.resize(floatsToRead);
outputRawBuffer = tempBuffer.data();
}
@@ -267,7 +270,31 @@
mOutBuffer->audioBuffer());
return INVALID_OPERATION;
}
- if (mConversion->mOutputAccessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+
+ // HapticGenerator needs special handling because the generated haptic samples should append to
+ // the end of audio samples, the generated haptic data pass back from HAL in output FMQ at same
+ // offset as input buffer, here we skip the audio samples in output FMQ and append haptic
+ // samples to the end of input buffer
+ if (mIsHapticGenerator) {
+ static constexpr float kHalFloatSampleLimit = 2.0f;
+ assert(floatsToRead == floatsToWrite);
+ const auto audioChNum = mConversion->getAudioChannelCount();
+ const auto audioSamples =
+ floatsToWrite * audioChNum / (audioChNum + mConversion->getHapticChannelCount());
+ // accumulate or copy input to output, haptic samples remains all zero
+ if (mConversion->mOutputAccessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+ accumulate_float(mOutBuffer->audioBuffer()->f32, mInBuffer->audioBuffer()->f32,
+ audioSamples);
+ } else {
+ memcpy_to_float_from_float_with_clamping(mOutBuffer->audioBuffer()->f32,
+ mInBuffer->audioBuffer()->f32, audioSamples,
+ kHalFloatSampleLimit);
+ }
+ // append the haptic sample at the end of input audio samples
+ memcpy_to_float_from_float_with_clamping(mInBuffer->audioBuffer()->f32 + audioSamples,
+ outputRawBuffer + audioSamples,
+ floatsToRead - audioSamples, kHalFloatSampleLimit);
+ } else if (mConversion->mOutputAccessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
accumulate_float(mOutBuffer->audioBuffer()->f32, outputRawBuffer, floatsToRead);
}
diff --git a/media/libaudiohal/impl/EffectHalAidl.h b/media/libaudiohal/impl/EffectHalAidl.h
index bbcb7e2..4f7de7c 100644
--- a/media/libaudiohal/impl/EffectHalAidl.h
+++ b/media/libaudiohal/impl/EffectHalAidl.h
@@ -73,6 +73,7 @@
const int32_t mSessionId;
const int32_t mIoId;
const bool mIsProxyEffect;
+ bool mIsHapticGenerator = false;
std::unique_ptr<EffectConversionHelperAidl> mConversion;
diff --git a/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp
index 64cc7ed..740bf60 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp
+++ b/media/libaudiohal/impl/EffectsFactoryHalAidl.cpp
@@ -174,9 +174,6 @@
if (uuid == nullptr || effect == nullptr) {
return BAD_VALUE;
}
- if (sessionId == AUDIO_SESSION_DEVICE && ioId == AUDIO_IO_HANDLE_NONE) {
- return INVALID_OPERATION;
- }
ALOGV("%s session %d ioId %d", __func__, sessionId, ioId);
AudioUuid aidlUuid =
diff --git a/media/libaudiohal/impl/StreamHalAidl.cpp b/media/libaudiohal/impl/StreamHalAidl.cpp
index fb4b549..94de8ea 100644
--- a/media/libaudiohal/impl/StreamHalAidl.cpp
+++ b/media/libaudiohal/impl/StreamHalAidl.cpp
@@ -796,6 +796,14 @@
}
status_t StreamOutHalAidl::drain(bool earlyNotify) {
+ if (!mStream) return NO_INIT;
+
+ if(const auto state = getState(); state == StreamDescriptor::State::IDLE) {
+ ALOGD("%p %s stream already in IDLE state", this, __func__);
+ if(mContext.isAsynchronous()) onDrainReady();
+ return OK;
+ }
+
return StreamHalAidl::drain(earlyNotify);
}
diff --git a/media/libaudiohal/impl/StreamHalAidl.h b/media/libaudiohal/impl/StreamHalAidl.h
index 9cb2cff..0587640 100644
--- a/media/libaudiohal/impl/StreamHalAidl.h
+++ b/media/libaudiohal/impl/StreamHalAidl.h
@@ -215,6 +215,11 @@
~StreamHalAidl() override;
+ ::aidl::android::hardware::audio::core::StreamDescriptor::State getState() {
+ std::lock_guard l(mLock);
+ return mLastReply.state;
+ }
+
status_t getLatency(uint32_t *latency);
// Always returns non-negative values.
@@ -268,10 +273,6 @@
result.format = config.format;
return result;
}
- ::aidl::android::hardware::audio::core::StreamDescriptor::State getState() {
- std::lock_guard l(mLock);
- return mLastReply.state;
- }
// Note: Since `sendCommand` takes mLock while holding mCommandReplyLock, never call
// it with `mLock` being held.
status_t sendCommand(
diff --git a/media/libeffects/hapticgenerator/Android.bp b/media/libeffects/hapticgenerator/Android.bp
index 396662e..e4ac38e 100644
--- a/media/libeffects/hapticgenerator/Android.bp
+++ b/media/libeffects/hapticgenerator/Android.bp
@@ -37,6 +37,14 @@
header_libs: [
"libaudioeffects",
],
+ cflags: [
+ // This is needed for the non-zero coefficients optimization for
+ // BiquadFilter. Try the biquad_filter_benchmark test in audio_utils
+ // with/without `-ffast-math` for more context.
+ "-ffast-math",
+ "-fhonor-infinities",
+ "-fhonor-nans",
+ ],
relative_install_path: "soundfx",
}
@@ -59,12 +67,6 @@
"-O2",
"-Wall",
"-Werror",
- // This is needed for the non-zero coefficients optimization for
- // BiquadFilter. Try the biquad_filter_benchmark test in audio_utils
- // with/without `-ffast-math` for more context.
- "-ffast-math",
- "-fhonor-infinities",
- "-fhonor-nans",
"-fvisibility=hidden",
],
}
diff --git a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
index 0a04250..9b2f443 100644
--- a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
+++ b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.cpp
@@ -14,30 +14,47 @@
* limitations under the License.
*/
-#include <cstddef>
#define LOG_TAG "AHAL_HapticGeneratorContext"
-#include <Utils.h>
+#include "HapticGeneratorContext.h"
#include <android-base/logging.h>
#include <android-base/parsedouble.h>
#include <android-base/properties.h>
+#include <audio_utils/primitives.h>
+#include <audio_utils/safe_math.h>
+#include <Utils.h>
-#include "HapticGeneratorContext.h"
+#include <cstddef>
+
+using aidl::android::hardware::audio::common::getChannelCount;
+using aidl::android::hardware::audio::common::getPcmSampleSizeInBytes;
+using aidl::android::media::audio::common::AudioChannelLayout;
namespace aidl::android::hardware::audio::effect {
HapticGeneratorContext::HapticGeneratorContext(int statusDepth, const Parameter::Common& common)
: EffectContext(statusDepth, common) {
mState = HAPTIC_GENERATOR_STATE_UNINITIALIZED;
- mSampleRate = common.input.base.sampleRate;
- mFrameCount = common.input.frameCount;
- init_params(common.input.base.channelMask, common.output.base.channelMask);
+
+ mParams.mMaxVibratorScale = HapticGenerator::VibratorScale::MUTE;
+ mParams.mVibratorInfo.resonantFrequencyHz = DEFAULT_RESONANT_FREQUENCY;
+ mParams.mVibratorInfo.qFactor = DEFAULT_BSF_ZERO_Q;
+ mParams.mVibratorInfo.maxAmplitude = 0.f;
+
+ init_params(common);
+ mState = HAPTIC_GENERATOR_STATE_INITIALIZED;
}
HapticGeneratorContext::~HapticGeneratorContext() {
mState = HAPTIC_GENERATOR_STATE_UNINITIALIZED;
}
+// Override EffectImpl::setCommon for HapticGenerator because we need init_params
+RetCode HapticGeneratorContext::setCommon(const Parameter::Common& common) {
+ init_params(common);
+ return EffectContext::setCommon(common);
+}
+
RetCode HapticGeneratorContext::enable() {
if (mState != HAPTIC_GENERATOR_STATE_INITIALIZED) {
return RetCode::ERROR_EFFECT_LIB_ERROR;
@@ -75,14 +92,15 @@
for (const auto& [id, vibratorScale] : mParams.mHapticScales) {
mParams.mMaxVibratorScale = std::max(mParams.mMaxVibratorScale, vibratorScale);
}
+ LOG(INFO) << " HapticGenerator VibratorScale set to " << toString(mParams.mMaxVibratorScale);
return RetCode::SUCCESS;
}
-HapticGenerator::VibratorInformation HapticGeneratorContext::getHgVibratorInformation() {
+HapticGenerator::VibratorInformation HapticGeneratorContext::getHgVibratorInformation() const {
return mParams.mVibratorInfo;
}
-std::vector<HapticGenerator::HapticScale> HapticGeneratorContext::getHgHapticScales() {
+std::vector<HapticGenerator::HapticScale> HapticGeneratorContext::getHgHapticScales() const {
std::vector<HapticGenerator::HapticScale> result;
for (const auto& [id, vibratorScale] : mParams.mHapticScales) {
result.push_back({id, vibratorScale});
@@ -93,6 +111,15 @@
RetCode HapticGeneratorContext::setHgVibratorInformation(
const HapticGenerator::VibratorInformation& vibratorInfo) {
mParams.mVibratorInfo = vibratorInfo;
+ if (::android::audio_utils::safe_isnan(mParams.mVibratorInfo.resonantFrequencyHz)) {
+ LOG(WARNING) << __func__ << " resonantFrequencyHz reset from nan to "
+ << DEFAULT_RESONANT_FREQUENCY;
+ mParams.mVibratorInfo.resonantFrequencyHz = DEFAULT_RESONANT_FREQUENCY;
+ }
+ if (::android::audio_utils::safe_isnan(mParams.mVibratorInfo.qFactor)) {
+ LOG(WARNING) << __func__ << " qFactor reset from nan to " << DEFAULT_BSF_ZERO_Q;
+ mParams.mVibratorInfo.qFactor = DEFAULT_BSF_ZERO_Q;
+ }
if (mProcessorsRecord.bpf != nullptr) {
mProcessorsRecord.bpf->setCoefficients(::android::audio_effect::haptic_generator::bpfCoefs(
@@ -117,15 +144,8 @@
auto frameSize = getInputFrameSize();
RETURN_VALUE_IF(0 == frameSize, status, "zeroFrameSize");
- // The audio data must not be modified but just written to
- // output buffer according the access mode.
- if (in != out) {
- for (int i = 0; i < samples; i++) {
- out[i] = in[i];
- }
- }
-
if (mState != HAPTIC_GENERATOR_STATE_ACTIVE) {
+ LOG(WARNING) << " HapticGenerator in wrong state " << mState;
return status;
}
@@ -135,7 +155,8 @@
}
// Resize buffer if the haptic sample count is greater than buffer size.
- size_t hapticSampleCount = mFrameCount * mParams.mHapticChannelCount;
+ const size_t hapticSampleCount = mFrameCount * mParams.mHapticChannelCount;
+ const size_t audioSampleCount = mFrameCount * mParams.mAudioChannelCount;
if (hapticSampleCount > mInputBuffer.size()) {
// The inputBuffer and outputBuffer must have the same size, which must be at least
// the haptic sample count.
@@ -155,45 +176,45 @@
runProcessingChain(mInputBuffer.data(), mOutputBuffer.data(), mFrameCount);
::android::os::scaleHapticData(
hapticOutBuffer, hapticSampleCount,
- {/*level=*/static_cast<::android::os::HapticLevel>(mParams.mMaxVibratorScale) },
- mParams.mVibratorInfo.qFactor);
+ {static_cast<::android::os::HapticLevel>(mParams.mMaxVibratorScale)} /* scale */,
+ mParams.mVibratorInfo.maxAmplitude /* limit */);
// For haptic data, the haptic playback thread will copy the data from effect input
// buffer, which contains haptic data at the end of the buffer, directly to sink buffer.
- // In that case, copy haptic data to input buffer instead of output buffer.
- // Note: this may not work with rpc/binder calls
- for (size_t i = 0; i < hapticSampleCount; ++i) {
- in[samples + i] = hapticOutBuffer[i];
- }
- return {STATUS_OK, samples, static_cast<int32_t>(samples + hapticSampleCount)};
+ // In AIDL only output buffer is send back to the audio framework via FMQ. Here the effect copy
+ // the generated haptic data to the target position of output buffer, the framework then append
+ // it to the same position of input buffer.
+ memcpy_to_float_from_float_with_clamping(out + audioSampleCount, hapticOutBuffer,
+ hapticSampleCount, 2.f /* absMax */);
+ return {STATUS_OK, samples, samples};
}
-void HapticGeneratorContext::init_params(media::audio::common::AudioChannelLayout inputChMask,
- media::audio::common::AudioChannelLayout outputChMask) {
- mParams.mMaxVibratorScale = HapticGenerator::VibratorScale::MUTE;
- mParams.mVibratorInfo.resonantFrequencyHz = DEFAULT_RESONANT_FREQUENCY;
- mParams.mVibratorInfo.qFactor = DEFAULT_BSF_ZERO_Q;
+void HapticGeneratorContext::init_params(const Parameter::Common& common) {
+ mSampleRate = common.input.base.sampleRate;
+ mFrameCount = common.input.frameCount;
mParams.mAudioChannelCount = ::aidl::android::hardware::audio::common::getChannelCount(
- inputChMask, ~media::audio::common::AudioChannelLayout::LAYOUT_HAPTIC_AB);
+ common.input.base.channelMask,
+ ~media::audio::common::AudioChannelLayout::LAYOUT_HAPTIC_AB);
mParams.mHapticChannelCount = ::aidl::android::hardware::audio::common::getChannelCount(
- outputChMask, media::audio::common::AudioChannelLayout::LAYOUT_HAPTIC_AB);
+ common.output.base.channelMask,
+ media::audio::common::AudioChannelLayout::LAYOUT_HAPTIC_AB);
LOG_ALWAYS_FATAL_IF(mParams.mHapticChannelCount > 2, "haptic channel count is too large");
for (int i = 0; i < mParams.mHapticChannelCount; ++i) {
// By default, use the first audio channel to generate haptic channels.
mParams.mHapticChannelSource[i] = 0;
}
-
- mState = HAPTIC_GENERATOR_STATE_INITIALIZED;
+ configure();
+ LOG(DEBUG) << " HapticGenerator init context:\n" << contextToString();
}
-float HapticGeneratorContext::getDistortionOutputGain() {
+float HapticGeneratorContext::getDistortionOutputGain() const {
float distortionOutputGain = getFloatProperty(
"vendor.audio.hapticgenerator.distortion.output.gain", DEFAULT_DISTORTION_OUTPUT_GAIN);
return distortionOutputGain;
}
-float HapticGeneratorContext::getFloatProperty(const std::string& key, float defaultValue) {
+float HapticGeneratorContext::getFloatProperty(const std::string& key, float defaultValue) const {
float result;
std::string value = ::android::base::GetProperty(key, "");
if (!value.empty() && ::android::base::ParseFloat(value, &result)) {
@@ -322,4 +343,34 @@
return in;
}
+std::string HapticGeneratorContext::paramToString(const struct HapticGeneratorParam& param) const {
+ std::stringstream ss;
+ ss << "\t\ttHapticGenerator Parameters:\n";
+ ss << "\t\t- mHapticChannelCount: " << param.mHapticChannelCount << '\n';
+ ss << "\t\t- mAudioChannelCount: " << param.mAudioChannelCount << '\n';
+ ss << "\t\t- mHapticChannelSource: " << param.mHapticChannelSource[0] << ", "
+ << param.mHapticChannelSource[1] << '\n';
+ ss << "\t\t- mMaxVibratorScale: " << ::android::internal::ToString(param.mMaxVibratorScale)
+ << '\n';
+ ss << "\t\t- mVibratorInfo: " << param.mVibratorInfo.toString() << '\n';
+ for (const auto& it : param.mHapticScales)
+ ss << "\t\t\t" << it.first << ": " << toString(it.second) << '\n';
+
+ return ss.str();
+}
+
+std::string HapticGeneratorContext::contextToString() const {
+ std::stringstream ss;
+ ss << "\t\tHapticGenerator Context:\n";
+ ss << "\t\t- state: " << mState << '\n';
+ ss << "\t\t- bpf Q: " << DEFAULT_BPF_Q << '\n';
+ ss << "\t\t- slow env normalization power: " << DEFAULT_SLOW_ENV_NORMALIZATION_POWER << '\n';
+ ss << "\t\t- distortion corner frequency: " << DEFAULT_DISTORTION_CORNER_FREQUENCY << '\n';
+ ss << "\t\t- distortion input gain: " << DEFAULT_DISTORTION_INPUT_GAIN << '\n';
+ ss << "\t\t- distortion cube threshold: " << DEFAULT_DISTORTION_CUBE_THRESHOLD << '\n';
+ ss << "\t\t- distortion output gain: " << getDistortionOutputGain() << '\n';
+ ss << "\t\tHapticGenerator Parameters:\n" << paramToString(mParams) << "\n";
+ return ss.str();
+}
+
} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
index 3a2ad1c..8a736e3 100644
--- a/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
+++ b/media/libeffects/hapticgenerator/aidl/HapticGeneratorContext.h
@@ -16,11 +16,13 @@
#pragma once
-#include <vibrator/ExternalVibrationUtils.h>
-#include <map>
-
-#include "Processors.h"
#include "effect-impl/EffectContext.h"
+#include "Processors.h"
+
+#include <vibrator/ExternalVibrationUtils.h>
+
+#include <cstddef>
+#include <map>
namespace aidl::android::hardware::audio::effect {
@@ -39,7 +41,6 @@
int mHapticChannelCount;
int mAudioChannelCount;
- HapticGenerator::HapticScale mHapticScale;
std::map<int, HapticGenerator::VibratorScale> mHapticScales;
// max intensity will be used to scale haptic data.
HapticGenerator::VibratorScale mMaxVibratorScale;
@@ -69,13 +70,15 @@
void reset();
RetCode setHgHapticScales(const std::vector<HapticGenerator::HapticScale>& hapticScales);
- std::vector<HapticGenerator::HapticScale> getHgHapticScales();
+ std::vector<HapticGenerator::HapticScale> getHgHapticScales() const;
RetCode setHgVibratorInformation(const HapticGenerator::VibratorInformation& vibratorInfo);
- HapticGenerator::VibratorInformation getHgVibratorInformation();
+ HapticGenerator::VibratorInformation getHgVibratorInformation() const;
IEffect::Status process(float* in, float* out, int samples);
+ RetCode setCommon(const Parameter::Common& common) override;
+
private:
static constexpr float DEFAULT_RESONANT_FREQUENCY = 150.0f;
static constexpr float DEFAULT_BSF_ZERO_Q = 8.0f;
@@ -108,15 +111,17 @@
// intermediate buffer in the generating algorithm.
std::vector<float> mOutputBuffer;
- void init_params(media::audio::common::AudioChannelLayout inputChMask,
- media::audio::common::AudioChannelLayout outputChMask);
+ void init_params(const Parameter::Common& common);
void configure();
- float getDistortionOutputGain();
- float getFloatProperty(const std::string& key, float defaultValue);
+ float getDistortionOutputGain() const;
+ float getFloatProperty(const std::string& key, float defaultValue) const;
void addBiquadFilter(std::shared_ptr<HapticBiquadFilter> filter);
void buildProcessingChain();
float* runProcessingChain(float* buf1, float* buf2, size_t frameCount);
+
+ std::string paramToString(const struct HapticGeneratorParam& param) const;
+ std::string contextToString() const;
};
} // namespace aidl::android::hardware::audio::effect
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 9ed0b83..1f4e16e 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -3812,7 +3812,11 @@
IAfPlaybackThread* AudioFlinger::primaryPlaybackThread_l() const
{
- audio_utils::lock_guard lock(hardwareMutex());
+ // The atomic ptr mPrimaryHardwareDev requires both the
+ // AudioFlinger and the Hardware mutex for modification.
+ // As we hold the AudioFlinger mutex, we access it
+ // safely without the Hardware mutex, to avoid mutex order
+ // inversion with Thread methods and the ThreadBase mutex.
if (mPrimaryHardwareDev == nullptr) {
return nullptr;
}
diff --git a/services/audioflinger/DeviceEffectManager.cpp b/services/audioflinger/DeviceEffectManager.cpp
index feae97e..7cb9329 100644
--- a/services/audioflinger/DeviceEffectManager.cpp
+++ b/services/audioflinger/DeviceEffectManager.cpp
@@ -71,10 +71,15 @@
void DeviceEffectManager::onReleaseAudioPatch(audio_patch_handle_t handle) {
ALOGV("%s", __func__);
+ // Keep a reference on disconnected handle to delay destruction without lock held.
+ std::vector<sp<IAfEffectHandle>> disconnectedHandles{};
audio_utils::lock_guard _l(mutex());
for (auto& effectProxies : mDeviceEffects) {
for (auto& effect : effectProxies.second) {
- effect->onReleasePatch(handle);
+ sp<IAfEffectHandle> disconnectedHandle = effect->onReleasePatch(handle);
+ if (disconnectedHandle != nullptr) {
+ disconnectedHandles.push_back(std::move(disconnectedHandle));
+ }
}
}
}
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index e85329d..a6a2cdf 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -562,12 +562,9 @@
#undef LOG_TAG
#define LOG_TAG "EffectModule"
-EffectModule::EffectModule(const sp<EffectCallbackInterface>& callback,
- effect_descriptor_t *desc,
- int id,
- audio_session_t sessionId,
- bool pinned,
- audio_port_handle_t deviceId)
+EffectModule::EffectModule(const sp<EffectCallbackInterface>& callback, effect_descriptor_t* desc,
+ int id, audio_session_t sessionId, bool pinned,
+ audio_port_handle_t deviceId)
: EffectBase(callback, desc, id, sessionId, pinned),
// clear mConfig to ensure consistent initial value of buffer framecount
// in case buffers are associated by setInBuffer() or setOutBuffer()
@@ -577,9 +574,9 @@
mMaxDisableWaitCnt(1), // set by configure_l(), should be >= 1
mDisableWaitCnt(0), // set by process() and updateState()
mOffloaded(false),
- mIsOutput(false)
- , mSupportsFloat(false)
-{
+ mIsOutput(false),
+ mSupportsFloat(false),
+ mEffectInterfaceDebug(desc->name) {
ALOGV("Constructor %p pinned %d", this, pinned);
int lStatus;
@@ -587,6 +584,7 @@
mStatus = callback->createEffectHal(
&desc->uuid, sessionId, deviceId, &mEffectInterface);
if (mStatus != NO_ERROR) {
+ ALOGE("%s createEffectHal failed: %d", __func__, mStatus);
return;
}
lStatus = init_l();
@@ -596,12 +594,14 @@
}
setOffloaded_l(callback->isOffload(), callback->io());
- ALOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface.get());
+ ALOGV("%s Constructor success name %s, Interface %p", __func__, mDescriptor.name,
+ mEffectInterface.get());
return;
Error:
mEffectInterface.clear();
- ALOGV("Constructor Error %d", mStatus);
+ mEffectInterfaceDebug += " init failed:" + std::to_string(lStatus);
+ ALOGE("%s Constructor Error %d", __func__, mStatus);
}
EffectModule::~EffectModule()
@@ -612,7 +612,7 @@
AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
ALOGW("EffectModule %p destructor called with unreleased interface, effect %s",
this, uuidStr);
- release_l();
+ release_l("~EffectModule");
}
}
@@ -1046,8 +1046,21 @@
return;
}
- (void)getCallback()->addEffectToHal(mEffectInterface);
- mCurrentHalStream = getCallback()->io();
+ status_t status = getCallback()->addEffectToHal(mEffectInterface);
+ if (status == NO_ERROR) {
+ mCurrentHalStream = getCallback()->io();
+ }
+ }
+}
+
+void HwAccDeviceEffectModule::addEffectToHal_l()
+{
+ if (mAddedToHal) {
+ return;
+ }
+ status_t status = getCallback()->addEffectToHal(mEffectInterface);
+ if (status == NO_ERROR) {
+ mAddedToHal = true;
}
}
@@ -1129,13 +1142,14 @@
}
// must be called with EffectChain::mutex() held
-void EffectModule::release_l()
+void EffectModule::release_l(const std::string& from)
{
if (mEffectInterface != 0) {
removeEffectFromHal_l();
// release effect engine
mEffectInterface->close();
mEffectInterface.clear();
+ mEffectInterfaceDebug += " released by: " + from;
}
}
@@ -1152,6 +1166,16 @@
return NO_ERROR;
}
+status_t HwAccDeviceEffectModule::removeEffectFromHal_l()
+{
+ if (!mAddedToHal) {
+ return NO_ERROR;
+ }
+ getCallback()->removeEffectFromHal(mEffectInterface);
+ mAddedToHal = false;
+ return NO_ERROR;
+}
+
// round up delta valid if value and divisor are positive.
template <typename T>
static T roundUpDelta(const T &value, const T &divisor) {
@@ -1372,12 +1396,12 @@
((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL ||
(mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_IND ||
(mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_MONITOR)) {
- status = setVolumeInternal(left, right, controller);
+ status = setVolumeInternal_ll(left, right, controller);
}
return status;
}
-status_t EffectModule::setVolumeInternal(
+status_t EffectModule::setVolumeInternal_ll(
uint32_t *left, uint32_t *right, bool controller) {
if (mVolume.has_value() && *left == mVolume.value()[0] && *right == mVolume.value()[1] &&
!controller) {
@@ -1388,6 +1412,7 @@
*right = mReturnedVolume.value()[1];
return NO_ERROR;
}
+ LOG_ALWAYS_FATAL_IF(mEffectInterface == nullptr, "%s", mEffectInterfaceDebug.c_str());
uint32_t volume[2] = {*left, *right};
uint32_t* pVolume = isVolumeControl() ? volume : nullptr;
uint32_t size = sizeof(volume);
@@ -1732,6 +1757,9 @@
const sp<media::IEffectClient>& effectClient,
int32_t priority, bool notifyFramesProcessed)
{
+ if (client == nullptr && effectClient == nullptr) {
+ return sp<InternalEffectHandle>::make(effect, notifyFramesProcessed);
+ }
return sp<EffectHandle>::make(
effect, client, effectClient, priority, notifyFramesProcessed);
}
@@ -1739,12 +1767,14 @@
EffectHandle::EffectHandle(const sp<IAfEffectBase>& effect,
const sp<Client>& client,
const sp<media::IEffectClient>& effectClient,
- int32_t priority, bool notifyFramesProcessed)
- : BnEffect(),
+ int32_t priority, bool notifyFramesProcessed,
+ bool isInternal,
+ audio_utils::MutexOrder mutexOrder)
+ : BnEffect(), mMutex(audio_utils::mutex{mutexOrder}),
mEffect(effect), mEffectClient(media::EffectClientAsyncProxy::makeIfNeeded(effectClient)),
mClient(client), mCblk(nullptr),
mPriority(priority), mHasControl(false), mEnabled(false), mDisconnected(false),
- mNotifyFramesProcessed(notifyFramesProcessed)
+ mNotifyFramesProcessed(notifyFramesProcessed), mIsInternal(isInternal)
{
ALOGV("constructor %p client %p", this, client.get());
setMinSchedulerPolicy(SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
@@ -1911,7 +1941,7 @@
void EffectHandle::disconnect(bool unpinIfLast)
{
- audio_utils::lock_guard _l(mutex());
+ audio_utils::unique_lock _l(mutex());
ALOGV("disconnect(%s) %p", unpinIfLast ? "true" : "false", this);
if (mDisconnected) {
if (unpinIfLast) {
@@ -1923,11 +1953,19 @@
{
sp<IAfEffectBase> effect = mEffect.promote();
if (effect != 0) {
+ // Unlock e.g. for device effect: may need to acquire AudioFlinger lock
+ // Also Internal Effect Handle would require Proxy lock (and vice versa).
+ if (isInternal()) {
+ _l.unlock();
+ }
if (effect->disconnectHandle(this, unpinIfLast) > 0) {
ALOGW("%s Effect handle %p disconnected after thread destruction",
__func__, this);
}
effect->updatePolicyState();
+ if (isInternal()) {
+ _l.lock();
+ }
}
}
@@ -2534,7 +2572,7 @@
mEffects[i]->stop_l();
}
if (release) {
- mEffects[i]->release_l();
+ mEffects[i]->release_l("EffectChain::removeEffect");
}
// Skip operation when no thread attached (could lead to sigfpe as framecount is 0...)
if (hasThreadAttached && type != EFFECT_FLAG_TYPE_AUXILIARY) {
@@ -3506,19 +3544,17 @@
ALOGV("%s reusing HAL effect", __func__);
} else {
mDevicePort = *port;
- mHalEffect = new EffectModule(mMyCallback,
- const_cast<effect_descriptor_t *>(&mDescriptor),
- mMyCallback->newEffectId(), AUDIO_SESSION_DEVICE,
- false /* pinned */, port->id);
+ mHalEffect = sp<HwAccDeviceEffectModule>::make(mMyCallback,
+ const_cast<effect_descriptor_t *>(&mDescriptor), mMyCallback->newEffectId(),
+ port->id);
+ mHalEffect->configure_l();
if (audio_is_input_device(mDevice.mType)) {
mHalEffect->setInputDevice(mDevice);
} else {
mHalEffect->setDevices({mDevice});
}
- mHalEffect->configure_l();
}
- *handle = new EffectHandle(mHalEffect, nullptr, nullptr, 0 /*priority*/,
- mNotifyFramesProcessed);
+ *handle = new InternalEffectHandle(mHalEffect, mNotifyFramesProcessed);
status = (*handle)->initCheck();
if (status == OK) {
status = mHalEffect->addHandle((*handle).get());
@@ -3565,15 +3601,16 @@
return status;
}
-void DeviceEffectProxy::onReleasePatch(audio_patch_handle_t patchHandle) {
- sp<IAfEffectHandle> effect;
+sp<IAfEffectHandle> DeviceEffectProxy::onReleasePatch(audio_patch_handle_t patchHandle) {
+ sp<IAfEffectHandle> disconnectedHandle;
{
audio_utils::lock_guard _l(proxyMutex());
if (mEffectHandles.find(patchHandle) != mEffectHandles.end()) {
- effect = mEffectHandles.at(patchHandle);
+ disconnectedHandle = std::move(mEffectHandles.at(patchHandle));
mEffectHandles.erase(patchHandle);
}
}
+ return disconnectedHandle;
}
@@ -3581,7 +3618,7 @@
{
audio_utils::lock_guard _l(proxyMutex());
if (effect == mHalEffect) {
- mHalEffect->release_l();
+ mHalEffect->release_l("DeviceEffectProxy::removeEffect");
mHalEffect.clear();
mDevicePort.id = AUDIO_PORT_HANDLE_NONE;
}
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 965ad43..9ecf89e 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -179,7 +179,7 @@
// the attached track(s) to accumulate their auxiliary channel.
class EffectModule : public IAfEffectModule, public EffectBase {
public:
- EffectModule(const sp<EffectCallbackInterface>& callabck,
+ EffectModule(const sp<EffectCallbackInterface>& callback,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
@@ -228,8 +228,8 @@
REQUIRES(audio_utils::EffectChain_Mutex) EXCLUDES_EffectBase_Mutex;
bool isOffloaded_l() const final
REQUIRES(audio_utils::EffectChain_Mutex) EXCLUDES_EffectBase_Mutex;
- void addEffectToHal_l() final REQUIRES(audio_utils::EffectChain_Mutex);
- void release_l() final REQUIRES(audio_utils::EffectChain_Mutex);
+ void addEffectToHal_l() override REQUIRES(audio_utils::EffectChain_Mutex);
+ void release_l(const std::string& from = "") final REQUIRES(audio_utils::EffectChain_Mutex);
sp<IAfEffectModule> asEffectModule() final { return this; }
@@ -250,6 +250,9 @@
void dump(int fd, const Vector<String16>& args) const final;
+protected:
+ sp<EffectHalInterface> mEffectInterface; // Effect module HAL
+
private:
// Maximum time allocated to effect engines to complete the turn off sequence
@@ -259,18 +262,18 @@
status_t start_ll() REQUIRES(audio_utils::EffectChain_Mutex, audio_utils::EffectBase_Mutex);
status_t stop_ll() REQUIRES(audio_utils::EffectChain_Mutex, audio_utils::EffectBase_Mutex);
- status_t removeEffectFromHal_l() REQUIRES(audio_utils::EffectChain_Mutex);
+ status_t removeEffectFromHal_l() override REQUIRES(audio_utils::EffectChain_Mutex);
status_t sendSetAudioDevicesCommand(const AudioDeviceTypeAddrVector &devices, uint32_t cmdCode);
effect_buffer_access_e requiredEffectBufferAccessMode() const {
return mConfig.inputCfg.buffer.raw == mConfig.outputCfg.buffer.raw
? EFFECT_BUFFER_ACCESS_WRITE : EFFECT_BUFFER_ACCESS_ACCUMULATE;
}
- status_t setVolumeInternal(uint32_t* left, uint32_t* right,
- bool controller /* the volume controller effect of the chain */);
+ status_t setVolumeInternal_ll(uint32_t* left, uint32_t* right,
+ bool controller /* the volume controller effect of the chain */)
+ REQUIRES(audio_utils::EffectChain_Mutex, audio_utils::EffectBase_Mutex);
effect_config_t mConfig; // input and output audio configuration
- sp<EffectHalInterface> mEffectInterface; // Effect module HAL
sp<EffectBufferHalInterface> mInBuffer; // Buffers for interacting with HAL
sp<EffectBufferHalInterface> mOutBuffer;
status_t mStatus; // initialization status
@@ -292,12 +295,12 @@
template <typename MUTEX>
class AutoLockReentrant {
public:
- AutoLockReentrant(MUTEX& mutex, pid_t allowedTid)
+ AutoLockReentrant(MUTEX& mutex, pid_t allowedTid) ACQUIRE(audio_utils::EffectBase_Mutex)
: mMutex(gettid() == allowedTid ? nullptr : &mutex)
{
if (mMutex != nullptr) mMutex->lock();
}
- ~AutoLockReentrant() {
+ ~AutoLockReentrant() RELEASE(audio_utils::EffectBase_Mutex) {
if (mMutex != nullptr) mMutex->unlock();
}
private:
@@ -313,6 +316,20 @@
// Cache the volume that returned from the effect when setting volume successfully. The value
// here is used to indicate the volume to apply before this effect.
std::optional<std::vector<uint32_t>> mReturnedVolume;
+ // TODO: b/315995877, remove this debugging string after root cause
+ std::string mEffectInterfaceDebug GUARDED_BY(audio_utils::EffectChain_Mutex);
+};
+
+class HwAccDeviceEffectModule : public EffectModule {
+public:
+ HwAccDeviceEffectModule(const sp<EffectCallbackInterface>& callback, effect_descriptor_t *desc,
+ int id, audio_port_handle_t deviceId) :
+ EffectModule(callback, desc, id, AUDIO_SESSION_DEVICE, /* pinned */ false, deviceId) {}
+ void addEffectToHal_l() final REQUIRES(audio_utils::EffectChain_Mutex);
+
+private:
+ status_t removeEffectFromHal_l() final REQUIRES(audio_utils::EffectChain_Mutex);
+ bool mAddedToHal = false;
};
// The EffectHandle class implements the IEffect interface. It provides resources
@@ -327,7 +344,8 @@
EffectHandle(const sp<IAfEffectBase>& effect,
const sp<Client>& client,
const sp<media::IEffectClient>& effectClient,
- int32_t priority, bool notifyFramesProcessed);
+ int32_t priority, bool notifyFramesProcessed, bool isInternal = false,
+ audio_utils::MutexOrder mutexOrder = audio_utils::MutexOrder::kEffectHandle_Mutex);
~EffectHandle() override;
status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) final;
@@ -347,6 +365,11 @@
int32_t* _aidl_return) final;
const sp<Client>& client() const final { return mClient; }
+ /**
+ * Checks if the handle is internal, aka created by AudioFlinger for internal needs (e.g.
+ * device effect HAL handle or device effect thread handle).
+ */
+ virtual bool isInternal() const { return mIsInternal; }
sp<android::media::IEffect> asIEffect() final {
return sp<android::media::IEffect>::fromExisting(this);
@@ -384,15 +407,18 @@
void dumpToBuffer(char* buffer, size_t size) const final;
+protected:
+ // protects IEffect method calls
+ mutable audio_utils::mutex mMutex;
private:
DISALLOW_COPY_AND_ASSIGN(EffectHandle);
- audio_utils::mutex& mutex() const RETURN_CAPABILITY(android::audio_utils::EffectHandle_Mutex) {
+ virtual audio_utils::mutex& mutex() const
+ RETURN_CAPABILITY(android::audio_utils::EffectHandle_Mutex) {
return mMutex;
}
- // protects IEffect method calls
- mutable audio_utils::mutex mMutex{audio_utils::MutexOrder::kEffectHandle_Mutex};
+
const wp<IAfEffectBase> mEffect; // pointer to controlled EffectModule
const sp<media::IEffectClient> mEffectClient; // callback interface for client notifications
/*const*/ sp<Client> mClient; // client for shared memory allocation, see
@@ -408,6 +434,28 @@
bool mDisconnected; // Set to true by disconnect()
const bool mNotifyFramesProcessed; // true if the client callback event
// EVENT_FRAMES_PROCESSED must be generated
+ const bool mIsInternal;
+};
+
+/**
+ * There are 2 types of effects:
+ * -Session Effect: handle is directly called from the client, without AudioFlinger lock.
+ * -Device Effect: a device effect proxy is aggregating a collection of internal effect handles that
+ * controls the same effect added on all audio patches involving the device effect selected port
+ * requested either by a client or by AudioPolicyEffects. These internal effect handles do not have
+ * client. Sequence flow implies a different locking order, hence the lock is specialied.
+ */
+class InternalEffectHandle : public EffectHandle {
+public:
+ InternalEffectHandle(const sp<IAfEffectBase>& effect, bool notifyFramesProcessed) :
+ EffectHandle(effect, /* client= */ nullptr, /* effectClient= */ nullptr,
+ /* priority= */ 0, notifyFramesProcessed, /* isInternal */ true,
+ audio_utils::MutexOrder::kDeviceEffectHandle_Mutex) {}
+
+ virtual audio_utils::mutex& mutex() const
+ RETURN_CAPABILITY(android::audio_utils::DeviceEffectHandle_Mutex) {
+ return mMutex;
+ }
};
// the EffectChain class represents a group of effects associated to one audio session.
@@ -745,7 +793,7 @@
status_t onUpdatePatch(audio_patch_handle_t oldPatchHandle, audio_patch_handle_t newPatchHandle,
const IAfPatchPanel::Patch& patch) final;
- void onReleasePatch(audio_patch_handle_t patchHandle) final;
+ sp<IAfEffectHandle> onReleasePatch(audio_patch_handle_t patchHandle) final;
size_t removeEffect(const sp<IAfEffectModule>& effect) final;
@@ -794,7 +842,10 @@
audio_channel_mask_t outChannelMask() const override;
uint32_t outChannelCount() const override;
audio_channel_mask_t hapticChannelMask() const override { return AUDIO_CHANNEL_NONE; }
- size_t frameCount() const override { return 0; }
+ /**
+ * frameCount cannot be zero.
+ */
+ size_t frameCount() const override { return 1; }
uint32_t latency() const override { return 0; }
status_t addEffectToHal(const sp<EffectHalInterface>& effect) override;
diff --git a/services/audioflinger/IAfEffect.h b/services/audioflinger/IAfEffect.h
index abd3cb9..3452e94 100644
--- a/services/audioflinger/IAfEffect.h
+++ b/services/audioflinger/IAfEffect.h
@@ -153,7 +153,7 @@
public:
static sp<IAfEffectModule> create(
- const sp<EffectCallbackInterface>& callabck,
+ const sp<EffectCallbackInterface>& callback,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
@@ -214,7 +214,8 @@
virtual status_t stop_l() = 0;
virtual void addEffectToHal_l() = 0;
- virtual void release_l() = 0;
+ virtual status_t removeEffectFromHal_l() = 0;
+ virtual void release_l(const std::string& from) = 0;
};
class IAfEffectChain : public RefBase {
@@ -399,7 +400,14 @@
virtual status_t onUpdatePatch(audio_patch_handle_t oldPatchHandle,
audio_patch_handle_t newPatchHandle,
const IAfPatchPanel::Patch& patch) = 0;
- virtual void onReleasePatch(audio_patch_handle_t patchHandle) = 0;
+ /**
+ * Checks (and release) of the effect handle is linked with the given release patch handle.
+ *
+ * @param patchHandle handle of the released patch
+ * @return a reference on the effect handle released if any, nullptr otherwise.
+ * It allows to delay the destruction of the handle.
+ */
+ virtual sp<IAfEffectHandle> onReleasePatch(audio_patch_handle_t patchHandle) = 0;
virtual void dump2(int fd, int spaces) const = 0; // TODO(b/291319101) naming?
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 9c0176a..d24f90e 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1486,8 +1486,8 @@
}
if (IAfEffectModule::isHapticGenerator(&desc->type) && mHapticChannelCount == 0) {
- ALOGW("%s: thread doesn't support haptic playback while the effect is HapticGenerator",
- __func__);
+ ALOGW("%s: thread (%s) doesn't support haptic playback while the effect is HapticGenerator",
+ __func__, threadTypeToString(mType));
return BAD_VALUE;
}
diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
index c2e4b11..f7b9b33 100644
--- a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
@@ -32,11 +32,15 @@
class EffectDescriptor : public RefBase
{
public:
- EffectDescriptor(const effect_descriptor_t *desc, bool isMusicEffect,
- int id, audio_io_handle_t io, audio_session_t session) :
- mId(id), mIo(io), mSession(session), mEnabled(false), mSuspended(false),
- mIsMusicEffect(isMusicEffect)
- {
+ EffectDescriptor(const effect_descriptor_t* desc, bool isMusicEffect, int id,
+ audio_io_handle_t io, audio_session_t session)
+ : mId(id),
+ mIo(io),
+ mIsOrphan(io == AUDIO_IO_HANDLE_NONE),
+ mSession(session),
+ mEnabled(false),
+ mSuspended(false),
+ mIsMusicEffect(isMusicEffect) {
memcpy (&mDesc, desc, sizeof(effect_descriptor_t));
}
@@ -95,8 +99,18 @@
* @return ioHandle if found, AUDIO_IO_HANDLE_NONE otherwise.
*/
audio_io_handle_t getIoForSession(audio_session_t sessionId,
- const effect_uuid_t *effectType = nullptr);
- bool hasOrphansForSession(audio_session_t sessionId);
+ const effect_uuid_t* effectType = nullptr) const;
+
+ /**
+ * @brief Checks if there is at least one orphan effect with given sessionId and optional effect
+ * type uuid.
+ * @param sessionId Session ID.
+ * @param effectType Optional effect type UUID pointer to effect_uuid_t, nullptr by default.
+ * @return True if there is an orphan effect for given sessionId and type UUID, false otherwise.
+ */
+ bool hasOrphansForSession(audio_session_t sessionId,
+ const effect_uuid_t* effectType = nullptr) const;
+
EffectDescriptorCollection getOrphanEffectsForSession(audio_session_t sessionId) const;
void dump(String8 *dst, int spaces = 0, bool verbose = true) const;
diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
index c85df0f..090da6c 100644
--- a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
@@ -210,11 +210,13 @@
}
}
-bool EffectDescriptorCollection::hasOrphansForSession(audio_session_t sessionId)
-{
+bool EffectDescriptorCollection::hasOrphansForSession(audio_session_t sessionId,
+ const effect_uuid_t* effectType) const {
for (size_t i = 0; i < size(); ++i) {
sp<EffectDescriptor> effect = valueAt(i);
- if (effect->mSession == sessionId && effect->mIsOrphan) {
+ if (effect->mSession == sessionId && effect->mIsOrphan &&
+ (effectType == nullptr ||
+ memcmp(&effect->mDesc.type, effectType, sizeof(effect_uuid_t)) == 0)) {
return true;
}
}
@@ -235,7 +237,7 @@
}
audio_io_handle_t EffectDescriptorCollection::getIoForSession(audio_session_t sessionId,
- const effect_uuid_t *effectType)
+ const effect_uuid_t *effectType) const
{
for (size_t i = 0; i < size(); ++i) {
sp<EffectDescriptor> effect = valueAt(i);
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 13cc165..2c2f90d 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -142,7 +142,8 @@
}
break;
case AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING:
- if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_NONE) {
+ if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_BT_BLE
+ && config != AUDIO_POLICY_FORCE_NONE) {
ALOGW("setForceUse() invalid config %d for VIBRATE_RINGING", config);
return BAD_VALUE;
}
@@ -355,6 +356,40 @@
}
}
}
+
+ // if LEA headset is connected and we are told to use it, play ringtone over
+ // speaker and BT LEA
+ if (!availableOutputDevices.getDevicesFromTypes(getAudioDeviceOutAllBleSet()).isEmpty()) {
+ DeviceVector devices2;
+ devices2 = availableOutputDevices.getFirstDevicesFromTypes({
+ AUDIO_DEVICE_OUT_BLE_HEADSET, AUDIO_DEVICE_OUT_BLE_SPEAKER});
+ // Use ONLY Bluetooth LEA output when ringing in vibration mode
+ if (!((getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)
+ && (strategy == STRATEGY_ENFORCED_AUDIBLE))) {
+ if (getForceUse(AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING)
+ == AUDIO_POLICY_FORCE_BT_BLE) {
+ if (!devices2.isEmpty()) {
+ devices = devices2;
+ break;
+ }
+ }
+ }
+ // Use both Bluetooth LEA and phone default output when ringing in normal mode
+ if (audio_is_ble_out_device(getPreferredDeviceTypeForLegacyStrategy(
+ availableOutputDevices, STRATEGY_PHONE))) {
+ if (strategy == STRATEGY_SONIFICATION) {
+ devices.replaceDevicesByType(
+ AUDIO_DEVICE_OUT_SPEAKER,
+ availableOutputDevices.getDevicesFromType(
+ AUDIO_DEVICE_OUT_SPEAKER_SAFE));
+ }
+ if (!devices2.isEmpty()) {
+ devices.add(devices2);
+ break;
+ }
+ }
+ }
+
// The second device used for sonification is the same as the device used by media strategy
FALLTHROUGH_INTENDED;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index ce597e2..811ca12 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1654,12 +1654,14 @@
}
// Use the spatializer output if the content can be spatialized, no preferred mixer
- // was specified and offload or direct playback is not explicitly requested.
+ // was specified and offload or direct playback is not explicitly requested, and there is no
+ // haptic channel included in playback
*isSpatialized = false;
- if (mSpatializerOutput != nullptr
- && canBeSpatializedInt(attr, config, devices.toTypeAddrVector())
- && prefMixerConfigInfo == nullptr
- && ((*flags & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0)) {
+ if (mSpatializerOutput != nullptr &&
+ canBeSpatializedInt(attr, config, devices.toTypeAddrVector()) &&
+ prefMixerConfigInfo == nullptr &&
+ ((*flags & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0) &&
+ checkHapticCompatibilityOnSpatializerOutput(config, session)) {
*isSpatialized = true;
return mSpatializerOutput->mIoHandle;
}
@@ -2085,6 +2087,7 @@
// matching criteria values in priority order for best matching output so far
std::vector<uint32_t> bestMatchCriteria(8, 0);
+ const bool hasOrphanHaptic = mEffects.hasOrphansForSession(sessionId, FX_IID_HAPTICGENERATOR);
const uint32_t channelCount = audio_channel_count_from_out_mask(channelMask);
const uint32_t hapticChannelCount = audio_channel_count_from_out_mask(
channelMask & AUDIO_CHANNEL_HAPTIC_ALL);
@@ -2105,13 +2108,20 @@
// When using haptic output, same audio format and sample rate are required.
const uint32_t outputHapticChannelCount = audio_channel_count_from_out_mask(
outputDesc->getChannelMask() & AUDIO_CHANNEL_HAPTIC_ALL);
- if ((hapticChannelCount == 0) != (outputHapticChannelCount == 0)) {
+ // skip if haptic channel specified but output does not support it, or output support haptic
+ // but there is no haptic channel requested AND no orphan haptic effect exist
+ if ((hapticChannelCount != 0 && outputHapticChannelCount == 0) ||
+ (hapticChannelCount == 0 && outputHapticChannelCount != 0 && !hasOrphanHaptic)) {
continue;
}
- if (outputHapticChannelCount >= hapticChannelCount
- && format == outputDesc->getFormat()
- && samplingRate == outputDesc->getSamplingRate()) {
- currentMatchCriteria[0] = outputHapticChannelCount;
+ // In the case of audio-coupled-haptic playback, there is no format conversion and
+ // resampling in the framework, same format/channel/sampleRate for client and the output
+ // thread is required. In the case of HapticGenerator effect, do not require format
+ // matching.
+ if ((outputHapticChannelCount >= hapticChannelCount && format == outputDesc->getFormat() &&
+ samplingRate == outputDesc->getSamplingRate()) ||
+ (outputHapticChannelCount != 0 && hasOrphanHaptic)) {
+ currentMatchCriteria[0] = outputHapticChannelCount;
}
// functional flags match
@@ -6110,6 +6120,34 @@
return true;
}
+// The Spatializer output is compatible with Haptic use cases if:
+// 1. the Spatializer output thread supports Haptic, and format/sampleRate are same
+// with client if client haptic channel bits were set, or
+// 2. the Spatializer output thread does not support Haptic, and client did not ask haptic by
+// including the haptic bits or creating the HapticGenerator effect for same session.
+bool AudioPolicyManager::checkHapticCompatibilityOnSpatializerOutput(
+ const audio_config_t* config, audio_session_t sessionId) const {
+ const auto clientHapticChannel =
+ audio_channel_count_from_out_mask(config->channel_mask & AUDIO_CHANNEL_HAPTIC_ALL);
+ const auto threadOutputHapticChannel = audio_channel_count_from_out_mask(
+ mSpatializerOutput->getChannelMask() & AUDIO_CHANNEL_HAPTIC_ALL);
+
+ if (threadOutputHapticChannel) {
+ // check format and sampleRate match if client haptic channel mask exist
+ if (clientHapticChannel) {
+ return mSpatializerOutput->getFormat() == config->format &&
+ mSpatializerOutput->getSamplingRate() == config->sample_rate;
+ }
+ return true;
+ } else {
+ // in the case of the Spatializer output channel mask does not have haptic channel bits, it
+ // means haptic use cases (either the client channelmask includes haptic bits, or created a
+ // HapticGenerator effect for this session) are not supported.
+ return clientHapticChannel == 0 &&
+ !mEffects.hasOrphansForSession(sessionId, FX_IID_HAPTICGENERATOR);
+ }
+}
+
void AudioPolicyManager::checkVirtualizerClientRoutes() {
std::set<audio_stream_type_t> streamsToInvalidate;
for (size_t i = 0; i < mOutputs.size(); i++) {
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 2db0445..c8cced6 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -1359,6 +1359,9 @@
PortHandleVector getClientsForStream(audio_stream_type_t streamType) const;
void invalidateStreams(StreamTypeVector streams) const;
+
+ bool checkHapticCompatibilityOnSpatializerOutput(const audio_config_t* config,
+ audio_session_t sessionId) const;
};
};