Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0785980c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Update audio playback session for fast compatible effects"

parents fe5d6074 e7bb3a0d
Loading
Loading
Loading
Loading
+290 −66
Original line number Diff line number Diff line
@@ -19,19 +19,43 @@

#include <gtest/gtest.h>
#include <media/AudioEffect.h>
#include <system/audio_effects/effect_hapticgenerator.h>
#include <system/audio_effects/effect_spatializer.h>
#include <system/audio_effects/effect_visualizer.h>

#include "audio_test_utils.h"

using namespace android;

class AudioEffectCallback : public AudioEffect::IAudioEffectCallback {
  public:
    bool receivedFramesProcessed = false;

    void onFramesProcessed(int32_t framesProcessed) override {
        ALOGE("number of frames processed %d", framesProcessed);
        receivedFramesProcessed = true;
    }
};

static constexpr int kDefaultInputEffectPriority = -1;
static constexpr int kDefaultOutputEffectPriority = 0;

static const char* gPackageName = "AudioEffectTest";

bool isEffectExistsOnAudioSession(const effect_uuid_t* type, int priority,
                                  audio_session_t sessionId) {
bool doesDeviceSupportLowLatencyMode(std::vector<struct audio_port_v7>& ports) {
    for (const auto& port : ports) {
        if (port.role == AUDIO_PORT_ROLE_SOURCE && port.type == AUDIO_PORT_TYPE_MIX) {
            if ((port.active_config.flags.output & AUDIO_OUTPUT_FLAG_FAST) != 0) {
                return true;
            }
        }
    }
    return false;
}

sp<AudioEffect> createEffect(const effect_uuid_t* type, const effect_uuid_t* uuid = nullptr,
                             int priority = 0, audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
                             const wp<AudioEffectCallback>& callback = nullptr) {
    std::string packageName{gPackageName};
    AttributionSourceState attributionSource;
    attributionSource.packageName = packageName;
@@ -39,11 +63,19 @@ bool isEffectExistsOnAudioSession(const effect_uuid_t* type, int priority,
    attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
    attributionSource.token = sp<BBinder>::make();
    sp<AudioEffect> effect = new AudioEffect(attributionSource);
    effect->set(type, nullptr /* uid */, priority, nullptr /* callback */, sessionId);
    return effect->initCheck() == ALREADY_EXISTS;
    effect->set(type, uuid, priority, callback, sessionId, AUDIO_IO_HANDLE_NONE, {}, false,
                (callback != nullptr));
    return effect;
}

bool isEffectDefaultOnRecord(const effect_uuid_t* type, const sp<AudioRecord>& audioRecord) {
status_t isEffectExistsOnAudioSession(const effect_uuid_t* type, const effect_uuid_t* uuid,
                                      int priority, audio_session_t sessionId) {
    sp<AudioEffect> effect = createEffect(type, uuid, priority, sessionId);
    return effect->initCheck();
}

bool isEffectDefaultOnRecord(const effect_uuid_t* type, const effect_uuid_t* uuid,
                             const sp<AudioRecord>& audioRecord) {
    effect_descriptor_t descriptors[AudioEffect::kMaxPreProcessing];
    uint32_t numEffects = AudioEffect::kMaxPreProcessing;
    status_t ret = AudioEffect::queryDefaultPreProcessing(audioRecord->getSessionId(), descriptors,
@@ -52,7 +84,8 @@ bool isEffectDefaultOnRecord(const effect_uuid_t* type, const sp<AudioRecord>& a
        return false;
    }
    for (int i = 0; i < numEffects; i++) {
        if (memcmp(&descriptors[i].type, type, sizeof(effect_uuid_t)) == 0) {
        if ((memcmp(&descriptors[i].type, type, sizeof(effect_uuid_t)) == 0) &&
            (memcmp(&descriptors[i].uuid, uuid, sizeof(effect_uuid_t)) == 0)) {
            return true;
        }
    }
@@ -61,11 +94,11 @@ bool isEffectDefaultOnRecord(const effect_uuid_t* type, const sp<AudioRecord>& a

void listEffectsAvailable(std::vector<effect_descriptor_t>& descriptors) {
    uint32_t numEffects = 0;
    if (NO_ERROR == AudioEffect::queryNumberEffects(&numEffects)) {
    ASSERT_EQ(NO_ERROR, AudioEffect::queryNumberEffects(&numEffects));
    for (auto i = 0; i < numEffects; i++) {
        effect_descriptor_t des;
            if (NO_ERROR == AudioEffect::queryEffect(i, &des)) descriptors.push_back(des);
        }
        ASSERT_EQ(NO_ERROR, AudioEffect::queryEffect(i, &des));
        descriptors.push_back(des);
    }
}

@@ -81,11 +114,31 @@ bool isAux(effect_descriptor_t& descriptor) {
    return ((descriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY);
}

bool isPostproc(effect_descriptor_t& descriptor) {
    return ((descriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC);
}

bool isFastCompatible(effect_descriptor_t& descriptor) {
    return !(((descriptor.flags & EFFECT_FLAG_HW_ACC_MASK) == 0) &&
             ((descriptor.flags & EFFECT_FLAG_NO_PROCESS) == 0));
}

bool isSpatializer(effect_descriptor_t& descriptor) {
    return (memcmp(&descriptor.type, FX_IID_SPATIALIZER, sizeof(effect_uuid_t)) == 0);
}

bool isHapticGenerator(effect_descriptor_t& descriptor) {
    return (memcmp(&descriptor.type, FX_IID_HAPTICGENERATOR, sizeof(effect_uuid_t)) == 0);
}

std::tuple<std::string, std::string> typeAndUuidToString(const effect_descriptor_t& desc) {
    char type[512];
    AudioEffect::guidToString(&desc.type, type, sizeof(type));
    char uuid[512];
    AudioEffect::guidToString(&desc.uuid, uuid, sizeof(uuid));
    return std::make_tuple(type, uuid);
}

// UNIT TESTS
TEST(AudioEffectTest, getEffectDescriptor) {
    effect_uuid_t randomType = {
@@ -99,7 +152,7 @@ TEST(AudioEffectTest, getEffectDescriptor) {
                                                               EFFECT_FLAG_TYPE_MASK, &descriptor));

    std::vector<effect_descriptor_t> descriptors;
    listEffectsAvailable(descriptors);
    ASSERT_NO_FATAL_FAILURE(listEffectsAvailable(descriptors));

    for (auto i = 0; i < descriptors.size(); i++) {
        EXPECT_EQ(NO_ERROR,
@@ -124,15 +177,7 @@ TEST(AudioEffectTest, getEffectDescriptor) {
}

TEST(AudioEffectTest, DISABLED_GetSetParameterForEffect) {
    std::string packageName{gPackageName};
    AttributionSourceState attributionSource;
    attributionSource.packageName = packageName;
    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();
    sp<AudioEffect> visualizer = new AudioEffect(attributionSource);
    ASSERT_NE(visualizer, nullptr) << "effect not created";
    visualizer->set(SL_IID_VISUALIZATION);
    sp<AudioEffect> visualizer = createEffect(SL_IID_VISUALIZATION);
    status_t status = visualizer->initCheck();
    ASSERT_TRUE(status == NO_ERROR || status == ALREADY_EXISTS) << "Init check error";
    ASSERT_EQ(NO_ERROR, visualizer->setEnabled(true)) << "visualizer not enabled";
@@ -195,32 +240,35 @@ TEST(AudioEffectTest, ManageSourceDefaultEffects) {
    sp<AudioCapture> capture = nullptr;

    std::vector<effect_descriptor_t> descriptors;
    listEffectsAvailable(descriptors);
    ASSERT_NO_FATAL_FAILURE(listEffectsAvailable(descriptors));
    for (auto i = 0; i < descriptors.size(); i++) {
        if (isPreprocessing(descriptors[i])) {
            capture = new AudioCapture(AUDIO_SOURCE_MIC, sampleRate, format, channelMask);
            ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
            EXPECT_EQ(NO_ERROR, capture->create());
            EXPECT_EQ(NO_ERROR, capture->start());
            if (!isEffectDefaultOnRecord(&descriptors[i].type, capture->getAudioRecordHandle())) {
            if (!isEffectDefaultOnRecord(&descriptors[i].type, &descriptors[i].uuid,
                                         capture->getAudioRecordHandle())) {
                selectedEffect = i;
                break;
            }
        }
    }
    if (selectedEffect == -1) GTEST_SKIP() << " expected at least one preprocessing effect";
    effect_uuid_t selectedEffectType = descriptors[selectedEffect].type;

    char type[512];
    AudioEffect::guidToString(&selectedEffectType, type, sizeof(type));

    effect_uuid_t* selectedEffectType = &descriptors[selectedEffect].type;
    effect_uuid_t* selectedEffectUuid = &descriptors[selectedEffect].uuid;
    auto [type, uuid] = typeAndUuidToString(descriptors[selectedEffect]);
    capture = new AudioCapture(AUDIO_SOURCE_MIC, sampleRate, format, channelMask);
    ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
    EXPECT_EQ(NO_ERROR, capture->create());
    EXPECT_EQ(NO_ERROR, capture->start());
    EXPECT_FALSE(isEffectDefaultOnRecord(&selectedEffectType, capture->getAudioRecordHandle()))
    EXPECT_FALSE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
                                         capture->getAudioRecordHandle()))
            << "Effect should not have been default on record. " << type;
    EXPECT_FALSE(isEffectExistsOnAudioSession(&selectedEffectType, kDefaultInputEffectPriority - 1,
    EXPECT_EQ(NO_ERROR,
              isEffectExistsOnAudioSession(selectedEffectType, selectedEffectUuid,
                                           kDefaultInputEffectPriority - 1,
                                           capture->getAudioRecordHandle()->getSessionId()))
            << "Effect should not have been added. " << type;
    EXPECT_EQ(OK, capture->audioProcess());
@@ -228,17 +276,21 @@ TEST(AudioEffectTest, ManageSourceDefaultEffects) {

    String16 name{gPackageName};
    audio_unique_id_t effectId;
    status_t status = AudioEffect::addSourceDefaultEffect(
            type, name, nullptr, kDefaultInputEffectPriority, AUDIO_SOURCE_MIC, &effectId);
    status_t status = AudioEffect::addSourceDefaultEffect(type.c_str(), name, uuid.c_str(),
                                                          kDefaultInputEffectPriority,
                                                          AUDIO_SOURCE_MIC, &effectId);
    EXPECT_EQ(NO_ERROR, status) << "Adding default effect failed: " << type;

    capture = new AudioCapture(AUDIO_SOURCE_MIC, sampleRate, format, channelMask);
    ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
    EXPECT_EQ(NO_ERROR, capture->create());
    EXPECT_EQ(NO_ERROR, capture->start());
    EXPECT_TRUE(isEffectDefaultOnRecord(&selectedEffectType, capture->getAudioRecordHandle()))
    EXPECT_TRUE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
                                        capture->getAudioRecordHandle()))
            << "Effect should have been default on record. " << type;
    EXPECT_TRUE(isEffectExistsOnAudioSession(&selectedEffectType, kDefaultInputEffectPriority - 1,
    EXPECT_EQ(ALREADY_EXISTS,
              isEffectExistsOnAudioSession(selectedEffectType, selectedEffectUuid,
                                           kDefaultInputEffectPriority - 1,
                                           capture->getAudioRecordHandle()->getSessionId()))
            << "Effect should have been added. " << type;
    EXPECT_EQ(OK, capture->audioProcess());
@@ -250,86 +302,258 @@ TEST(AudioEffectTest, ManageSourceDefaultEffects) {
    ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
    EXPECT_EQ(NO_ERROR, capture->create());
    EXPECT_EQ(NO_ERROR, capture->start());
    EXPECT_FALSE(isEffectDefaultOnRecord(&selectedEffectType, capture->getAudioRecordHandle()))
    EXPECT_FALSE(isEffectDefaultOnRecord(selectedEffectType, selectedEffectUuid,
                                         capture->getAudioRecordHandle()))
            << "Effect should not have been default on record. " << type;
    EXPECT_FALSE(isEffectExistsOnAudioSession(&selectedEffectType, kDefaultInputEffectPriority - 1,
    EXPECT_EQ(NO_ERROR,
              isEffectExistsOnAudioSession(selectedEffectType, selectedEffectUuid,
                                           kDefaultInputEffectPriority - 1,
                                           capture->getAudioRecordHandle()->getSessionId()))
            << "Effect should not have been added. " << type;
    EXPECT_EQ(OK, capture->audioProcess());
    EXPECT_EQ(OK, capture->stop());
}

TEST(AudioEffectTest, ManageStreamDefaultEffects) {
TEST(AudioEffectTest, AuxEffectSanityTest) {
    int32_t selectedEffect = -1;

    std::vector<effect_descriptor_t> descriptors;
    listEffectsAvailable(descriptors);
    ASSERT_NO_FATAL_FAILURE(listEffectsAvailable(descriptors));
    for (auto i = 0; i < descriptors.size(); i++) {
        if (isAux(descriptors[i])) {
            selectedEffect = i;
            break;
        }
    }
    if (selectedEffect == -1) GTEST_SKIP() << " expected at least one Aux effect";
    if (selectedEffect == -1) GTEST_SKIP() << "expected at least one aux effect";
    effect_uuid_t* selectedEffectType = &descriptors[selectedEffect].type;
    effect_uuid_t* selectedEffectUuid = &descriptors[selectedEffect].uuid;
    auto [type, uuid] = typeAndUuidToString(descriptors[selectedEffect]);
    String16 name{gPackageName};
    audio_session_t sessionId =
            (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
    sp<AudioEffect> audioEffect = createEffect(selectedEffectType, selectedEffectUuid,
                                               kDefaultInputEffectPriority, sessionId);
    EXPECT_EQ(NO_INIT, audioEffect->initCheck())
            << "error, creating auxiliary effect (" << type << ") on session id " << (int)sessionId
            << " successful ";
    audio_unique_id_t id;
    status_t status = AudioEffect::addStreamDefaultEffect(
            type.c_str(), name, uuid.c_str(), kDefaultOutputEffectPriority, AUDIO_USAGE_MEDIA, &id);
    if (status == NO_ERROR) {
        EXPECT_EQ(NO_ERROR, AudioEffect::removeStreamDefaultEffect(id));
        EXPECT_NE(NO_ERROR, status) << "error, adding auxiliary effect (" << type
                                    << ") as stream default effect is successful";
    }
}

class AudioPlaybackEffectTest : public ::testing::TestWithParam<bool> {
  public:
    AudioPlaybackEffectTest() : mSelectFastMode(GetParam()){};

    const bool mSelectFastMode;

    bool mIsFastCompatibleEffect;
    effect_uuid_t mType;
    effect_uuid_t mUuid;
    std::string mTypeStr;
    std::string mUuidStr;

    void SetUp() override {
        if (mSelectFastMode) {
            std::vector<struct audio_port_v7> ports;
            ASSERT_EQ(OK, listAudioPorts(ports));
            if (!doesDeviceSupportLowLatencyMode(ports)) {
                GTEST_SKIP() << "device does not support low latency mode";
            }
        }

        int32_t selectedEffect = -1;
        std::vector<effect_descriptor_t> descriptors;
        ASSERT_NO_FATAL_FAILURE(listEffectsAvailable(descriptors));
        for (auto i = 0; i < descriptors.size(); i++) {
            if (isSpatializer(descriptors[i])) continue;
            if (isHapticGenerator(descriptors[i]) && !AudioSystem::isHapticPlaybackSupported())
                continue;
            if (!isInsert(descriptors[i])) continue;
            selectedEffect = i;
            mIsFastCompatibleEffect = isFastCompatible(descriptors[i]);
            // in fast mode, pick fast compatible effect if available
            if (mSelectFastMode == mIsFastCompatibleEffect) break;
        }
        if (selectedEffect == -1) {
            GTEST_SKIP() << "expected at least one valid effect";
        }

        mType = descriptors[selectedEffect].type;
        mUuid = descriptors[selectedEffect].uuid;
        std::tie(mTypeStr, mUuidStr) = typeAndUuidToString(descriptors[selectedEffect]);
    }
};

TEST_P(AudioPlaybackEffectTest, StreamDefaultEffectTest) {
    SCOPED_TRACE(testing::Message()
                 << "\n selected effect type is :: " << mTypeStr
                 << "\n selected effect uuid is :: " << mUuidStr
                 << "\n audiotrack output flag : " << (mSelectFastMode ? "fast" : "default")
                 << "\n audio effect is fast compatible : "
                 << (mIsFastCompatibleEffect ? "yes" : "no"));

    bool compatCheck = !mSelectFastMode || (mSelectFastMode && mIsFastCompatibleEffect);

    char type[512];
    AudioEffect::guidToString(selectedEffectType, type, sizeof(type));
    // create track
    audio_attributes_t attributes;
    attributes.usage = AUDIO_USAGE_MEDIA;
    attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
    auto playback = sp<AudioPlayback>::make(
            44100 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
            AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_SHARED, &attributes);
            0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
            mSelectFastMode ? AUDIO_OUTPUT_FLAG_FAST : AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE,
            AudioTrack::TRANSFER_SHARED, &attributes);
    ASSERT_NE(nullptr, playback);
    ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
    EXPECT_EQ(NO_ERROR, playback->create());
    EXPECT_EQ(NO_ERROR, playback->start());
    EXPECT_FALSE(isEffectExistsOnAudioSession(selectedEffectType, kDefaultOutputEffectPriority - 1,
    EXPECT_EQ(compatCheck ? NO_ERROR : NO_INIT,
              isEffectExistsOnAudioSession(&mType, &mUuid, kDefaultOutputEffectPriority - 1,
                                           playback->getAudioTrackHandle()->getSessionId()))
            << "Effect should not have been added. " << type;
            << "Effect should not have been added. " << mTypeStr;
    EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
    playback->stop();
    playback.clear();

    String16 name{gPackageName};
    audio_unique_id_t id;
    status_t status = AudioEffect::addStreamDefaultEffect(
            type, name, nullptr, kDefaultOutputEffectPriority, AUDIO_USAGE_MEDIA, &id);
    EXPECT_EQ(NO_ERROR, status) << "Adding default effect failed: " << type;
    status_t status = AudioEffect::addStreamDefaultEffect(mTypeStr.c_str(), name, mUuidStr.c_str(),
                                                          kDefaultOutputEffectPriority,
                                                          AUDIO_USAGE_MEDIA, &id);
    EXPECT_EQ(NO_ERROR, status) << "Adding default effect failed: " << mTypeStr;

    playback = sp<AudioPlayback>::make(
            44100 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
            AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_SHARED, &attributes);
            0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
            mSelectFastMode ? AUDIO_OUTPUT_FLAG_FAST : AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE,
            AudioTrack::TRANSFER_SHARED, &attributes);
    ASSERT_NE(nullptr, playback);
    ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
    EXPECT_EQ(NO_ERROR, playback->create());
    float level = 0.2f, levelGot;
    playback->getAudioTrackHandle()->setAuxEffectSendLevel(level);
    EXPECT_EQ(NO_ERROR, playback->start());
    EXPECT_TRUE(isEffectExistsOnAudioSession(selectedEffectType, kDefaultOutputEffectPriority - 1,
    // If effect chosen is not compatible with the session, then effect won't be applied
    EXPECT_EQ(compatCheck ? ALREADY_EXISTS : NO_INIT,
              isEffectExistsOnAudioSession(&mType, &mUuid, kDefaultOutputEffectPriority - 1,
                                           playback->getAudioTrackHandle()->getSessionId()))
            << "Effect should have been added. " << type;
            << "Effect should have been added. " << mTypeStr;
    EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
    playback->getAudioTrackHandle()->getAuxEffectSendLevel(&levelGot);
    EXPECT_EQ(level, levelGot);
    if (mSelectFastMode) {
        EXPECT_EQ(AUDIO_OUTPUT_FLAG_FAST,
                  playback->getAudioTrackHandle()->getFlags() & AUDIO_OUTPUT_FLAG_FAST);
    }
    playback->stop();
    playback.clear();

    status = AudioEffect::removeStreamDefaultEffect(id);
    EXPECT_EQ(NO_ERROR, status);
    playback = sp<AudioPlayback>::make(
            44100 /*sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
            AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_SHARED, &attributes);
            0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
            mSelectFastMode ? AUDIO_OUTPUT_FLAG_FAST : AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE,
            AudioTrack::TRANSFER_SHARED, &attributes);
    ASSERT_NE(nullptr, playback);
    ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
    EXPECT_EQ(NO_ERROR, playback->create());
    EXPECT_EQ(NO_ERROR, playback->start());
    EXPECT_FALSE(isEffectExistsOnAudioSession(selectedEffectType, kDefaultOutputEffectPriority - 1,
    EXPECT_EQ(compatCheck ? NO_ERROR : NO_INIT,
              isEffectExistsOnAudioSession(&mType, &mUuid, kDefaultOutputEffectPriority - 1,
                                           playback->getAudioTrackHandle()->getSessionId()))
            << "Effect should not have been added. " << type;
            << "Effect should not have been added. " << mTypeStr;
    EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
    playback->stop();
    playback.clear();
}

TEST_P(AudioPlaybackEffectTest, CheckOutputFlagCompatibility) {
    SCOPED_TRACE(testing::Message()
                 << "\n selected effect type is :: " << mTypeStr
                 << "\n selected effect uuid is :: " << mUuidStr
                 << "\n audiotrack output flag : " << (mSelectFastMode ? "fast" : "default")
                 << "\n audio effect is fast compatible : "
                 << (mIsFastCompatibleEffect ? "yes" : "no"));

    audio_attributes_t attributes;
    attributes.usage = AUDIO_USAGE_MEDIA;
    attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
    audio_session_t sessionId =
            (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
    sp<AudioEffectCallback> cb = sp<AudioEffectCallback>::make();
    sp<AudioEffect> audioEffect =
            createEffect(&mType, &mUuid, kDefaultOutputEffectPriority, sessionId, cb);
    ASSERT_EQ(OK, audioEffect->initCheck());
    ASSERT_EQ(NO_ERROR, audioEffect->setEnabled(true));
    auto playback = sp<AudioPlayback>::make(
            0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_MONO,
            mSelectFastMode ? AUDIO_OUTPUT_FLAG_FAST : AUDIO_OUTPUT_FLAG_NONE, sessionId,
            AudioTrack::TRANSFER_SHARED, &attributes);
    ASSERT_NE(nullptr, playback);
    ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_1ch_8kHz_s16le.raw"));
    EXPECT_EQ(NO_ERROR, playback->create());
    EXPECT_EQ(NO_ERROR, playback->start());

    EXPECT_EQ(ALREADY_EXISTS, isEffectExistsOnAudioSession(
                                      &mType, &mUuid, kDefaultOutputEffectPriority - 1, sessionId))
            << "Effect should have been added. " << mTypeStr;
    if (mSelectFastMode) {
        EXPECT_EQ(mIsFastCompatibleEffect ? AUDIO_OUTPUT_FLAG_FAST : 0,
                  playback->getAudioTrackHandle()->getFlags() & AUDIO_OUTPUT_FLAG_FAST);
    }
    EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
    EXPECT_EQ(NO_ERROR, playback->getAudioTrackHandle()->attachAuxEffect(0));
    playback->stop();
    playback.clear();
    EXPECT_TRUE(cb->receivedFramesProcessed)
            << "AudioEffect frames processed callback not received";
}

INSTANTIATE_TEST_SUITE_P(EffectParameterizedTests, AudioPlaybackEffectTest, ::testing::Bool());

TEST(AudioEffectTest, TestHapticEffect) {
    if (!AudioSystem::isHapticPlaybackSupported())
        GTEST_SKIP() << "Haptic playback is not supported";
    int32_t selectedEffect = -1;
    std::vector<effect_descriptor_t> descriptors;
    ASSERT_NO_FATAL_FAILURE(listEffectsAvailable(descriptors));
    for (auto i = 0; i < descriptors.size(); i++) {
        if (!isHapticGenerator(descriptors[i])) continue;
        selectedEffect = i;
        break;
    }
    if (selectedEffect == -1) GTEST_SKIP() << "expected at least one valid effect";

    effect_uuid_t* selectedEffectType = &descriptors[selectedEffect].type;
    effect_uuid_t* selectedEffectUuid = &descriptors[selectedEffect].uuid;
    auto [type, uuid] = typeAndUuidToString(descriptors[selectedEffect]);

    SCOPED_TRACE(testing::Message() << "\n selected effect type is :: " << type
                                    << "\n selected effect uuid is :: " << uuid);

    audio_attributes_t attributes;
    attributes.usage = AUDIO_USAGE_MEDIA;
    attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
    audio_session_t sessionId =
            (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
    sp<AudioEffectCallback> cb = sp<AudioEffectCallback>::make();
    sp<AudioEffect> audioEffect = createEffect(selectedEffectType, selectedEffectUuid,
                                               kDefaultOutputEffectPriority, sessionId, cb);
    ASSERT_EQ(OK, audioEffect->initCheck());
    ASSERT_EQ(NO_ERROR, audioEffect->setEnabled(true));
    auto playback = sp<AudioPlayback>::make(0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT,
                                            AUDIO_CHANNEL_OUT_STEREO, AUDIO_OUTPUT_FLAG_NONE,
                                            sessionId, AudioTrack::TRANSFER_SHARED, &attributes);
    ASSERT_NE(nullptr, playback);
    ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
    EXPECT_EQ(NO_ERROR, playback->create());
    EXPECT_EQ(NO_ERROR, playback->start());
    EXPECT_TRUE(isEffectExistsOnAudioSession(selectedEffectType, selectedEffectUuid,
                                             kDefaultOutputEffectPriority - 1, sessionId))
            << "Effect should have been added. " << type;
    EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
    playback->stop();
    playback.clear();
    EXPECT_TRUE(cb->receivedFramesProcessed)
            << "AudioEffect frames processed callback not received";
}