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 Diff line number Diff line
@@ -31,6 +31,7 @@ digraph stream_out_state_machine {
    ACTIVE -> ACTIVE [label="burst"];
    ACTIVE -> PAUSED [label="pause"];          // consumer -> passive (not consuming)
    ACTIVE -> DRAINING [label="drain"];        // producer -> passive
    ACTIVE -> IDLE [label="drain"];            // synchronous drain
    PAUSED -> PAUSED [label="burst"];
    PAUSED -> ACTIVE [label="start"];          // consumer -> active
    PAUSED -> IDLE [label="flush"];            // producer -> passive, buffer is cleared
+29 −8
Original line number 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>(),
                               AudioOutputFlags::MMAP_NOIRQ))) {
        StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
                                              mVendorDebug.forceTransientBurst};
                                              mVendorDebug.forceTransientBurst,
                                              mVendorDebug.forceSynchronousDrain};
        StreamContext temp(
                std::make_unique<StreamContext::CommandMQ>(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::kForceSynchronousDrainName = "aosp.forceSynchronousDrain";

ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& in_ids,
                                               std::vector<VendorParameter>* _aidl_return) {
@@ -990,6 +992,10 @@ ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& i
            VendorParameter forceTransientBurst{.id = id};
            forceTransientBurst.ext.setParcelable(Boolean{mVendorDebug.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 {
            allParametersKnown = false;
            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);
}

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

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

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