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

Commit 194daaac authored by Mikhail Naganov's avatar Mikhail Naganov
Browse files

audio: Allow going to 'IDLE' for synchronous drain

For small buffers, the driver can perform draining
synhronously, returning control to the HAL only after
the buffer is empty. This makes going through
the 'DRAINING' state artificial. Thus, we allow going
to the 'IDLE' state directly.

In order to make sure that VTS handles both transitions:
to 'DRAINING' and to 'IDLE', correctly, add an "AOSP as
vendor" parameter "aosp.forceSynchronousDrain" to induce
this behavior in the default implementation.

Bug: 262402957
Test: atest VtsHalAudioCoreTargetTest
Change-Id: Ic8eaee53cb4596afb5317b4b905e004af3f112aa
parent 20047bc1
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -31,6 +31,7 @@ digraph stream_out_state_machine {
    ACTIVE -> ACTIVE [label="burst"];
    ACTIVE -> ACTIVE [label="burst"];
    ACTIVE -> PAUSED [label="pause"];          // consumer -> passive (not consuming)
    ACTIVE -> PAUSED [label="pause"];          // consumer -> passive (not consuming)
    ACTIVE -> DRAINING [label="drain"];        // producer -> passive
    ACTIVE -> DRAINING [label="drain"];        // producer -> passive
    ACTIVE -> IDLE [label="drain"];            // synchronous drain
    PAUSED -> PAUSED [label="burst"];
    PAUSED -> PAUSED [label="burst"];
    PAUSED -> ACTIVE [label="start"];          // consumer -> active
    PAUSED -> ACTIVE [label="start"];          // consumer -> active
    PAUSED -> IDLE [label="flush"];            // producer -> passive, buffer is cleared
    PAUSED -> IDLE [label="flush"];            // producer -> passive, buffer is cleared
+29 −8
Original line number Original line Diff line number Diff line
@@ -140,7 +140,8 @@ ndk::ScopedAStatus Module::createStreamContext(int32_t in_portConfigId, int64_t
         !isBitPositionFlagSet(flags.get<AudioIoFlags::Tag::output>(),
         !isBitPositionFlagSet(flags.get<AudioIoFlags::Tag::output>(),
                               AudioOutputFlags::MMAP_NOIRQ))) {
                               AudioOutputFlags::MMAP_NOIRQ))) {
        StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
        StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
                                              mVendorDebug.forceTransientBurst};
                                              mVendorDebug.forceTransientBurst,
                                              mVendorDebug.forceSynchronousDrain};
        StreamContext temp(
        StreamContext temp(
                std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
                std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
                std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
                std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
@@ -980,6 +981,7 @@ ndk::ScopedAStatus Module::generateHwAvSyncId(int32_t* _aidl_return) {
}
}


const std::string Module::VendorDebug::kForceTransientBurstName = "aosp.forceTransientBurst";
const std::string Module::VendorDebug::kForceTransientBurstName = "aosp.forceTransientBurst";
const std::string Module::VendorDebug::kForceSynchronousDrainName = "aosp.forceSynchronousDrain";


ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& in_ids,
ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& in_ids,
                                               std::vector<VendorParameter>* _aidl_return) {
                                               std::vector<VendorParameter>* _aidl_return) {
@@ -990,6 +992,10 @@ ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& i
            VendorParameter forceTransientBurst{.id = id};
            VendorParameter forceTransientBurst{.id = id};
            forceTransientBurst.ext.setParcelable(Boolean{mVendorDebug.forceTransientBurst});
            forceTransientBurst.ext.setParcelable(Boolean{mVendorDebug.forceTransientBurst});
            _aidl_return->push_back(std::move(forceTransientBurst));
            _aidl_return->push_back(std::move(forceTransientBurst));
        } else if (id == VendorDebug::kForceSynchronousDrainName) {
            VendorParameter forceSynchronousDrain{.id = id};
            forceSynchronousDrain.ext.setParcelable(Boolean{mVendorDebug.forceSynchronousDrain});
            _aidl_return->push_back(std::move(forceSynchronousDrain));
        } else {
        } else {
            allParametersKnown = false;
            allParametersKnown = false;
            LOG(ERROR) << __func__ << ": unrecognized parameter \"" << id << "\"";
            LOG(ERROR) << __func__ << ": unrecognized parameter \"" << id << "\"";
@@ -999,6 +1005,23 @@ ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& i
    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
}


namespace {

template <typename W>
bool extractParameter(const VendorParameter& p, decltype(W::value)* v) {
    std::optional<W> value;
    binder_status_t result = p.ext.getParcelable(&value);
    if (result == STATUS_OK && value.has_value()) {
        *v = value.value().value;
        return true;
    }
    LOG(ERROR) << __func__ << ": failed to read the value of the parameter \"" << p.id
               << "\": " << result;
    return false;
}

}  // namespace

ndk::ScopedAStatus Module::setVendorParameters(const std::vector<VendorParameter>& in_parameters,
ndk::ScopedAStatus Module::setVendorParameters(const std::vector<VendorParameter>& in_parameters,
                                               bool in_async) {
                                               bool in_async) {
    LOG(DEBUG) << __func__ << ": parameter count " << in_parameters.size()
    LOG(DEBUG) << __func__ << ": parameter count " << in_parameters.size()
@@ -1006,13 +1029,11 @@ ndk::ScopedAStatus Module::setVendorParameters(const std::vector<VendorParameter
    bool allParametersKnown = true;
    bool allParametersKnown = true;
    for (const auto& p : in_parameters) {
    for (const auto& p : in_parameters) {
        if (p.id == VendorDebug::kForceTransientBurstName) {
        if (p.id == VendorDebug::kForceTransientBurstName) {
            std::optional<Boolean> value;
            if (!extractParameter<Boolean>(p, &mVendorDebug.forceTransientBurst)) {
            binder_status_t result = p.ext.getParcelable(&value);
                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
            if (result == STATUS_OK) {
            }
                mVendorDebug.forceTransientBurst = value.value().value;
        } else if (p.id == VendorDebug::kForceSynchronousDrainName) {
            } else {
            if (!extractParameter<Boolean>(p, &mVendorDebug.forceSynchronousDrain)) {
                LOG(ERROR) << __func__ << ": failed to read the value of the parameter \"" << p.id
                           << "\"";
                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
                return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
            }
            }
        } else {
        } else {
+5 −1
Original line number Original line Diff line number Diff line
@@ -402,7 +402,11 @@ StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
                    usleep(1000);  // Simulate a blocking call into the driver.
                    usleep(1000);  // Simulate a blocking call into the driver.
                    populateReply(&reply, mIsConnected);
                    populateReply(&reply, mIsConnected);
                    // Can switch the state to ERROR if a driver error occurs.
                    // Can switch the state to ERROR if a driver error occurs.
                    if (mState == StreamDescriptor::State::ACTIVE && mForceSynchronousDrain) {
                        mState = StreamDescriptor::State::IDLE;
                    } else {
                        switchToTransientState(StreamDescriptor::State::DRAINING);
                        switchToTransientState(StreamDescriptor::State::DRAINING);
                    }
                } else if (mState == StreamDescriptor::State::TRANSFER_PAUSED) {
                } else if (mState == StreamDescriptor::State::TRANSFER_PAUSED) {
                    mState = StreamDescriptor::State::DRAIN_PAUSED;
                    mState = StreamDescriptor::State::DRAIN_PAUSED;
                    populateReply(&reply, mIsConnected);
                    populateReply(&reply, mIsConnected);
+2 −0
Original line number Original line Diff line number Diff line
@@ -38,7 +38,9 @@ class Module : public BnModule {
  private:
  private:
    struct VendorDebug {
    struct VendorDebug {
        static const std::string kForceTransientBurstName;
        static const std::string kForceTransientBurstName;
        static const std::string kForceSynchronousDrainName;
        bool forceTransientBurst = false;
        bool forceTransientBurst = false;
        bool forceSynchronousDrain = false;
    };
    };


    ndk::ScopedAStatus setModuleDebug(
    ndk::ScopedAStatus setModuleDebug(
+6 −1
Original line number Original line Diff line number Diff line
@@ -67,6 +67,8 @@ class StreamContext {
        int transientStateDelayMs = 0;
        int transientStateDelayMs = 0;
        // Force the "burst" command to move the SM to the TRANSFERRING state.
        // Force the "burst" command to move the SM to the TRANSFERRING state.
        bool forceTransientBurst = false;
        bool forceTransientBurst = false;
        // Force the "drain" command to be synchronous, going directly to the IDLE state.
        bool forceSynchronousDrain = false;
    };
    };


    StreamContext() = default;
    StreamContext() = default;
@@ -115,6 +117,7 @@ class StreamContext {
        return mFormat;
        return mFormat;
    }
    }
    bool getForceTransientBurst() const { return mDebugParameters.forceTransientBurst; }
    bool getForceTransientBurst() const { return mDebugParameters.forceTransientBurst; }
    bool getForceSynchronousDrain() const { return mDebugParameters.forceSynchronousDrain; }
    size_t getFrameSize() const;
    size_t getFrameSize() const;
    int getInternalCommandCookie() const { return mInternalCommandCookie; }
    int getInternalCommandCookie() const { return mInternalCommandCookie; }
    ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
    ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
@@ -150,7 +153,8 @@ class StreamWorkerCommonLogic : public ::android::hardware::audio::common::Strea
          mDataMQ(context.getDataMQ()),
          mDataMQ(context.getDataMQ()),
          mAsyncCallback(context.getAsyncCallback()),
          mAsyncCallback(context.getAsyncCallback()),
          mTransientStateDelayMs(context.getTransientStateDelayMs()),
          mTransientStateDelayMs(context.getTransientStateDelayMs()),
          mForceTransientBurst(context.getForceTransientBurst()) {}
          mForceTransientBurst(context.getForceTransientBurst()),
          mForceSynchronousDrain(context.getForceSynchronousDrain()) {}
    std::string init() override;
    std::string init() override;
    void populateReply(StreamDescriptor::Reply* reply, bool isConnected) const;
    void populateReply(StreamDescriptor::Reply* reply, bool isConnected) const;
    void populateReplyWrongState(StreamDescriptor::Reply* reply,
    void populateReplyWrongState(StreamDescriptor::Reply* reply,
@@ -174,6 +178,7 @@ class StreamWorkerCommonLogic : public ::android::hardware::audio::common::Strea
    const std::chrono::duration<int, std::milli> mTransientStateDelayMs;
    const std::chrono::duration<int, std::milli> mTransientStateDelayMs;
    std::chrono::time_point<std::chrono::steady_clock> mTransientStateStart;
    std::chrono::time_point<std::chrono::steady_clock> mTransientStateStart;
    const bool mForceTransientBurst;
    const bool mForceTransientBurst;
    const bool mForceSynchronousDrain;
    // We use an array and the "size" field instead of a vector to be able to detect
    // We use an array and the "size" field instead of a vector to be able to detect
    // memory allocation issues.
    // memory allocation issues.
    std::unique_ptr<int8_t[]> mDataBuffer;
    std::unique_ptr<int8_t[]> mDataBuffer;
Loading