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

Commit 2b685436 authored by Mikhail Naganov's avatar Mikhail Naganov Committed by Gerrit Code Review
Browse files

Merge "audio: Make StreamDescriptor::Command a union"

parents c6eb5c91 98334439
Loading
Loading
Loading
Loading
+8 −12
Original line number Diff line number Diff line
@@ -55,19 +55,15 @@ parcelable StreamDescriptor {
    DRAIN_PAUSED = 6,
    ERROR = 100,
  }
  @Backing(type="int") @VintfStability
  enum CommandCode {
    START = 1,
    BURST = 2,
    DRAIN = 3,
    STANDBY = 4,
    PAUSE = 5,
    FLUSH = 6,
  }
  @FixedSize @VintfStability
  parcelable Command {
    android.hardware.audio.core.StreamDescriptor.CommandCode code = android.hardware.audio.core.StreamDescriptor.CommandCode.START;
    int fmqByteCount;
  union Command {
    int hal_reserved_exit;
    android.media.audio.common.Void start;
    int burst;
    android.media.audio.common.Void drain;
    android.media.audio.common.Void standby;
    android.media.audio.common.Void pause;
    android.media.audio.common.Void flush;
  }
  @FixedSize @VintfStability
  parcelable Reply {
+57 −55
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.hardware.audio.core;
import android.hardware.audio.core.MmapBufferDescriptor;
import android.hardware.common.fmq.MQDescriptor;
import android.hardware.common.fmq.SynchronizedReadWrite;
import android.media.audio.common.Void;

/**
 * Stream descriptor contains fast message queues and buffers used for sending
@@ -177,34 +178,71 @@ parcelable StreamDescriptor {
        ERROR = 100,
    }

    /**
     * Used for sending commands to the HAL module. The client writes into
     * the queue, the HAL module reads. The queue can only contain a single
     * command.
     *
     * Variants of type 'Void' correspond to commands without
     * arguments. Variants of other types correspond to commands with an
     * argument. Would in future a need for a command with multiple argument
     * arise, a Parcelable type should be used for the corresponding variant.
     */
    @VintfStability
    @Backing(type="int")
    enum CommandCode {
    @FixedSize
    union Command {
        /**
         * Reserved for the HAL implementation to allow unblocking the wait on a
         * command and exiting the I/O thread. A command of this variant must
         * never be sent from the client side. To prevent that, the
         * implementation must pass a random cookie as the command argument,
         * which is only known to the implementation.
         */
        int hal_reserved_exit;
        /**
         * See the state machines on the applicability of this command to
         * different states. The 'fmqByteCount' field must always be set to 0.
         * different states.
         */
        START = 1,
        Void start;
        /**
         * The BURST command used for audio I/O, see 'AudioBuffer'. Differences
         * for the MMap No IRQ mode:
         * The 'burst' command used for audio I/O, see 'AudioBuffer'. The value
         * specifies:
         *
         *  - for output streams: the amount of bytes that the client requests the
         *    HAL module to use out of the data contained in the 'audio.fmq' queue.
         *
         *  - for input streams: the amount of bytes requested by the client to
         *    read from the hardware into the 'audio.fmq' queue.
         *
         * In both cases it is allowed for this field to contain any
         * non-negative number. The value 0 can be used if the client only needs
         * to retrieve current positions and latency. Any sufficiently big value
         * which exceeds the size of the queue's area which is currently
         * available for reading or writing by the HAL module must be trimmed by
         * the HAL module to the available size. Note that the HAL module is
         * allowed to consume or provide less data than requested, and it must
         * return the amount of actually read or written data via the
         * 'Reply.fmqByteCount' field. Thus, only attempts to pass a negative
         * number must be constituted as a client's error.
         *
         * Differences for the MMap No IRQ mode:
         *
         *  - this command only provides updated positions and latency because
         *    actual audio I/O is done via the 'AudioBuffer.mmap' shared buffer.
         *    The client does not synchronize reads and writes into the buffer
         *    with sending of this command.
         *
         *  - the 'fmqByteCount' must always be set to 0.
         *  - the value must always be set to 0.
         */
        BURST = 2,
        int burst;
        /**
         * See the state machines on the applicability of this command to
         * different states. The 'fmqByteCount' field must always be set to 0.
         * different states.
         */
        DRAIN = 3,
        Void drain;
        /**
         * See the state machines on the applicability of this command to
         * different states. The 'fmqByteCount' field must always be set to 0.
         * different states.
         *
         * Note that it's left on the discretion of the HAL implementation to
         * assess all the necessary conditions that could prevent hardware from
@@ -213,53 +251,17 @@ parcelable StreamDescriptor {
         * buffer must remain empty in this state, even if capturing hardware is
         * still active, captured data must be discarded.
         */
        STANDBY = 4,
        Void standby;
        /**
         * See the state machines on the applicability of this command to
         * different states. The 'fmqByteCount' field must always be set to 0.
         * different states.
         */
        PAUSE = 5,
        Void pause;
        /**
         * See the state machines on the applicability of this command to
         * different states. The 'fmqByteCount' field must always be set to 0.
         */
        FLUSH = 6,
    }

    /**
     * Used for sending commands to the HAL module. The client writes into
     * the queue, the HAL module reads. The queue can only contain a single
     * command.
     */
    @VintfStability
    @FixedSize
    parcelable Command {
        /**
         * The code of the command.
         * different states.
         */
        CommandCode code = CommandCode.START;
        /**
         * This field is only used for the BURST command. For all other commands
         * it must be set to 0. The following description applies to the use
         * of this field for the BURST command.
         *
         * For output streams: the amount of bytes that the client requests the
         *   HAL module to use out of the data contained in the 'audio.fmq' queue.
         * For input streams: the amount of bytes requested by the client to
         *   read from the hardware into the 'audio.fmq' queue.
         *
         * In both cases it is allowed for this field to contain any
         * non-negative number. The value 0 can be used if the client only needs
         * to retrieve current positions and latency. Any sufficiently big value
         * which exceeds the size of the queue's area which is currently
         * available for reading or writing by the HAL module must be trimmed by
         * the HAL module to the available size. Note that the HAL module is
         * allowed to consume or provide less data than requested, and it must
         * return the amount of actually read or written data via the
         * 'Reply.fmqByteCount' field. Thus, only attempts to pass a negative
         * number must be constituted as a client's error.
         */
        int fmqByteCount;
        Void flush;
    }
    MQDescriptor<Command, SynchronizedReadWrite> command;

@@ -293,15 +295,15 @@ parcelable StreamDescriptor {
         */
        int status;
        /**
         * Used with the BURST command only.
         * Used with the 'burst' command only.
         *
         * For output streams: the amount of bytes of data actually consumed
         *   by the HAL module.
         * For input streams: the amount of bytes actually provided by the HAL
         *   in the 'audio.fmq' queue.
         *
         * The returned value must not exceed the value passed in the
         * 'fmqByteCount' field of the corresponding command or be negative.
         * The returned value must not exceed the value passed as the
         * argument of the corresponding command, or be negative.
         */
        int fmqByteCount;
        /**
+10 −10
Original line number Diff line number Diff line
@@ -23,16 +23,16 @@ digraph stream_in_state_machine {
    node [style=dashed] ANY_STATE;
    node [fillcolor=lightblue style=filled];
    I -> STANDBY;
    STANDBY -> IDLE [label="START"];    // producer -> active
    IDLE -> STANDBY [label="STANDBY"];  // producer -> passive, buffer is cleared
    IDLE -> ACTIVE [label="BURST"];     // consumer -> active
    ACTIVE -> ACTIVE [label="BURST"];
    ACTIVE -> PAUSED [label="PAUSE"];   // consumer -> passive
    ACTIVE -> DRAINING [label="DRAIN"]; // producer -> passive
    PAUSED -> ACTIVE [label="BURST"];   // consumer -> active
    PAUSED -> STANDBY [label="FLUSH"];  // producer -> passive, buffer is cleared
    DRAINING -> DRAINING [label="BURST"];
    DRAINING -> ACTIVE [label="START"];  // producer -> active
    STANDBY -> IDLE [label="start"];    // producer -> active
    IDLE -> STANDBY [label="standby"];  // producer -> passive, buffer is cleared
    IDLE -> ACTIVE [label="burst"];     // consumer -> active
    ACTIVE -> ACTIVE [label="burst"];
    ACTIVE -> PAUSED [label="pause"];   // consumer -> passive
    ACTIVE -> DRAINING [label="drain"]; // producer -> passive
    PAUSED -> ACTIVE [label="burst"];   // consumer -> active
    PAUSED -> STANDBY [label="flush"];  // producer -> passive, buffer is cleared
    DRAINING -> DRAINING [label="burst"];
    DRAINING -> ACTIVE [label="start"];  // producer -> active
    DRAINING -> STANDBY [label="<empty buffer>"];  // consumer deactivates
    IDLE -> ERROR [label="<hardware failure>"];
    ACTIVE -> ERROR [label="<hardware failure>"];
+15 −15
Original line number Diff line number Diff line
@@ -24,22 +24,22 @@ digraph stream_out_state_machine {
    node [style=dashed] ANY_STATE;
    node [fillcolor=lightblue style=filled];
    I -> STANDBY;
    STANDBY -> IDLE [label="START"];           // consumer -> active
    STANDBY -> PAUSED [label="BURST"];         // producer -> active
    IDLE -> STANDBY [label="STANDBY"];         // consumer -> passive
    IDLE -> ACTIVE [label="BURST"];            // producer -> active
    ACTIVE -> ACTIVE [label="BURST"];
    ACTIVE -> PAUSED [label="PAUSE"];          // consumer -> passive (not consuming)
    ACTIVE -> DRAINING [label="DRAIN"];        // producer -> passive
    PAUSED -> PAUSED [label="BURST"];
    PAUSED -> ACTIVE [label="START"];          // consumer -> active
    PAUSED -> IDLE [label="FLUSH"];            // producer -> passive, buffer is cleared
    STANDBY -> IDLE [label="start"];           // consumer -> active
    STANDBY -> PAUSED [label="burst"];         // producer -> active
    IDLE -> STANDBY [label="standby"];         // consumer -> passive
    IDLE -> ACTIVE [label="burst"];            // producer -> active
    ACTIVE -> ACTIVE [label="burst"];
    ACTIVE -> PAUSED [label="pause"];          // consumer -> passive (not consuming)
    ACTIVE -> DRAINING [label="drain"];        // producer -> passive
    PAUSED -> PAUSED [label="burst"];
    PAUSED -> ACTIVE [label="start"];          // consumer -> active
    PAUSED -> IDLE [label="flush"];            // producer -> passive, buffer is cleared
    DRAINING -> IDLE [label="<empty buffer>"];
    DRAINING -> ACTIVE [label="BURST"];        // producer -> active
    DRAINING -> DRAIN_PAUSED [label="PAUSE"];  // consumer -> passive (not consuming)
    DRAIN_PAUSED -> DRAINING [label="START"];  // consumer -> active
    DRAIN_PAUSED -> PAUSED [label="BURST"];    // producer -> active
    DRAIN_PAUSED -> IDLE [label="FLUSH"];      // buffer is cleared
    DRAINING -> ACTIVE [label="burst"];        // producer -> active
    DRAINING -> DRAIN_PAUSED [label="pause"];  // consumer -> passive (not consuming)
    DRAIN_PAUSED -> DRAINING [label="start"];  // consumer -> active
    DRAIN_PAUSED -> PAUSED [label="burst"];    // producer -> active
    DRAIN_PAUSED -> IDLE [label="flush"];      // buffer is cleared
    IDLE -> ERROR [label="<hardware failure>"];
    ACTIVE -> ERROR [label="<hardware failure>"];
    DRAINING -> ERROR [label="<hardware failure>"];
+222 −193
Original line number Diff line number Diff line
@@ -106,31 +106,43 @@ StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
        return Status::ABORT;
    }
    StreamDescriptor::Reply reply{};
    if (static_cast<int32_t>(command.code) == StreamContext::COMMAND_EXIT &&
        command.fmqByteCount == mInternalCommandCookie) {
    reply.status = STATUS_BAD_VALUE;
    using Tag = StreamDescriptor::Command::Tag;
    switch (command.getTag()) {
        case Tag::hal_reserved_exit:
            if (const int32_t cookie = command.get<Tag::hal_reserved_exit>();
                cookie == mInternalCommandCookie) {
                LOG(DEBUG) << __func__ << ": received EXIT command";
                setClosed();
                // This is an internal command, no need to reply.
                return Status::EXIT;
    } else if (command.code == StreamDescriptor::CommandCode::START && command.fmqByteCount >= 0) {
            } else {
                LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
            }
            break;
        case Tag::start:
            LOG(DEBUG) << __func__ << ": received START read command";
            if (mState == StreamDescriptor::State::STANDBY ||
                mState == StreamDescriptor::State::DRAINING) {
                populateReply(&reply, mIsConnected);
            mState = mState == StreamDescriptor::State::STANDBY ? StreamDescriptor::State::IDLE
                mState = mState == StreamDescriptor::State::STANDBY
                                 ? StreamDescriptor::State::IDLE
                                 : StreamDescriptor::State::ACTIVE;
            } else {
                LOG(WARNING) << __func__ << ": START command can not be handled in the state "
                             << toString(mState);
                reply.status = STATUS_INVALID_OPERATION;
            }
    } else if (command.code == StreamDescriptor::CommandCode::BURST && command.fmqByteCount >= 0) {
        LOG(DEBUG) << __func__ << ": received BURST read command for " << command.fmqByteCount
            break;
        case Tag::burst:
            if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
                LOG(DEBUG) << __func__ << ": received BURST read command for " << fmqByteCount
                           << " bytes";
        if (mState == StreamDescriptor::State::IDLE || mState == StreamDescriptor::State::ACTIVE ||
                if (mState == StreamDescriptor::State::IDLE ||
                    mState == StreamDescriptor::State::ACTIVE ||
                    mState == StreamDescriptor::State::PAUSED ||
                    mState == StreamDescriptor::State::DRAINING) {
            if (!read(command.fmqByteCount, &reply)) {
                    if (!read(fmqByteCount, &reply)) {
                        mState = StreamDescriptor::State::ERROR;
                    }
                    if (mState == StreamDescriptor::State::IDLE ||
@@ -148,7 +160,11 @@ StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
                                 << toString(mState);
                    reply.status = STATUS_INVALID_OPERATION;
                }
    } else if (command.code == StreamDescriptor::CommandCode::DRAIN && command.fmqByteCount == 0) {
            } else {
                LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
            }
            break;
        case Tag::drain:
            LOG(DEBUG) << __func__ << ": received DRAIN read command";
            if (mState == StreamDescriptor::State::ACTIVE) {
                usleep(1000);  // Simulate a blocking call into the driver.
@@ -160,34 +176,36 @@ StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
                             << toString(mState);
                reply.status = STATUS_INVALID_OPERATION;
            }
    } else if (command.code == StreamDescriptor::CommandCode::PAUSE && command.fmqByteCount == 0) {
        LOG(DEBUG) << __func__ << ": received PAUSE read command";
        if (mState == StreamDescriptor::State::ACTIVE) {
            break;
        case Tag::standby:
            LOG(DEBUG) << __func__ << ": received STANDBY read command";
            if (mState == StreamDescriptor::State::IDLE) {
                usleep(1000);  // Simulate a blocking call into the driver.
                populateReply(&reply, mIsConnected);
                // Can switch the state to ERROR if a driver error occurs.
            mState = StreamDescriptor::State::PAUSED;
                mState = StreamDescriptor::State::STANDBY;
            } else {
            LOG(WARNING) << __func__ << ": PAUSE command can not be handled in the state "
                LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
                             << toString(mState);
                reply.status = STATUS_INVALID_OPERATION;
            }
    } else if (command.code == StreamDescriptor::CommandCode::FLUSH && command.fmqByteCount == 0) {
        LOG(DEBUG) << __func__ << ": received FLUSH read command";
        if (mState == StreamDescriptor::State::PAUSED) {
            break;
        case Tag::pause:
            LOG(DEBUG) << __func__ << ": received PAUSE read command";
            if (mState == StreamDescriptor::State::ACTIVE) {
                usleep(1000);  // Simulate a blocking call into the driver.
                populateReply(&reply, mIsConnected);
                // Can switch the state to ERROR if a driver error occurs.
            mState = StreamDescriptor::State::STANDBY;
                mState = StreamDescriptor::State::PAUSED;
            } else {
            LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
                LOG(WARNING) << __func__ << ": PAUSE command can not be handled in the state "
                             << toString(mState);
                reply.status = STATUS_INVALID_OPERATION;
            }
    } else if (command.code == StreamDescriptor::CommandCode::STANDBY &&
               command.fmqByteCount == 0) {
        LOG(DEBUG) << __func__ << ": received STANDBY read command";
        if (mState == StreamDescriptor::State::IDLE) {
            break;
        case Tag::flush:
            LOG(DEBUG) << __func__ << ": received FLUSH read command";
            if (mState == StreamDescriptor::State::PAUSED) {
                usleep(1000);  // Simulate a blocking call into the driver.
                populateReply(&reply, mIsConnected);
                // Can switch the state to ERROR if a driver error occurs.
@@ -197,10 +215,7 @@ StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
                             << toString(mState);
                reply.status = STATUS_INVALID_OPERATION;
            }
    } else {
        LOG(WARNING) << __func__ << ": invalid command (" << command.toString()
                     << ") or count: " << command.fmqByteCount;
        reply.status = STATUS_BAD_VALUE;
            break;
    }
    reply.state = mState;
    LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
@@ -253,14 +268,22 @@ StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
        return Status::ABORT;
    }
    StreamDescriptor::Reply reply{};
    if (static_cast<int32_t>(command.code) == StreamContext::COMMAND_EXIT &&
        command.fmqByteCount == mInternalCommandCookie) {
    reply.status = STATUS_BAD_VALUE;
    using Tag = StreamDescriptor::Command::Tag;
    switch (command.getTag()) {
        case Tag::hal_reserved_exit:
            if (const int32_t cookie = command.get<Tag::hal_reserved_exit>();
                cookie == mInternalCommandCookie) {
                LOG(DEBUG) << __func__ << ": received EXIT command";
                setClosed();
                // This is an internal command, no need to reply.
                return Status::EXIT;
    } else if (command.code == StreamDescriptor::CommandCode::START && command.fmqByteCount >= 0) {
        LOG(DEBUG) << __func__ << ": received START read command";
            } else {
                LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
            }
            break;
        case Tag::start:
            LOG(DEBUG) << __func__ << ": received START write command";
            switch (mState) {
                case StreamDescriptor::State::STANDBY:
                    mState = StreamDescriptor::State::IDLE;
@@ -279,11 +302,14 @@ StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
            if (reply.status != STATUS_INVALID_OPERATION) {
                populateReply(&reply, mIsConnected);
            }
    } else if (command.code == StreamDescriptor::CommandCode::BURST && command.fmqByteCount >= 0) {
        LOG(DEBUG) << __func__ << ": received BURST write command for " << command.fmqByteCount
            break;
        case Tag::burst:
            if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
                LOG(DEBUG) << __func__ << ": received BURST write command for " << fmqByteCount
                           << " bytes";
        if (mState != StreamDescriptor::State::ERROR) {  // BURST can be handled in all valid states
            if (!write(command.fmqByteCount, &reply)) {
                if (mState !=
                    StreamDescriptor::State::ERROR) {  // BURST can be handled in all valid states
                    if (!write(fmqByteCount, &reply)) {
                        mState = StreamDescriptor::State::ERROR;
                    }
                    if (mState == StreamDescriptor::State::STANDBY ||
@@ -298,7 +324,11 @@ StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
                                 << toString(mState);
                    reply.status = STATUS_INVALID_OPERATION;
                }
    } else if (command.code == StreamDescriptor::CommandCode::DRAIN && command.fmqByteCount == 0) {
            } else {
                LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
            }
            break;
        case Tag::drain:
            LOG(DEBUG) << __func__ << ": received DRAIN write command";
            if (mState == StreamDescriptor::State::ACTIVE) {
                usleep(1000);  // Simulate a blocking call into the driver.
@@ -315,8 +345,8 @@ StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
                             << toString(mState);
                reply.status = STATUS_INVALID_OPERATION;
            }
    } else if (command.code == StreamDescriptor::CommandCode::STANDBY &&
               command.fmqByteCount == 0) {
            break;
        case Tag::standby:
            LOG(DEBUG) << __func__ << ": received STANDBY write command";
            if (mState == StreamDescriptor::State::IDLE) {
                usleep(1000);  // Simulate a blocking call into the driver.
@@ -328,7 +358,8 @@ StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
                             << toString(mState);
                reply.status = STATUS_INVALID_OPERATION;
            }
    } else if (command.code == StreamDescriptor::CommandCode::PAUSE && command.fmqByteCount == 0) {
            break;
        case Tag::pause:
            LOG(DEBUG) << __func__ << ": received PAUSE write command";
            if (mState == StreamDescriptor::State::ACTIVE ||
                mState == StreamDescriptor::State::DRAINING) {
@@ -341,7 +372,8 @@ StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
                             << toString(mState);
                reply.status = STATUS_INVALID_OPERATION;
            }
    } else if (command.code == StreamDescriptor::CommandCode::FLUSH && command.fmqByteCount == 0) {
            break;
        case Tag::flush:
            LOG(DEBUG) << __func__ << ": received FLUSH write command";
            if (mState == StreamDescriptor::State::PAUSED ||
                mState == StreamDescriptor::State::DRAIN_PAUSED) {
@@ -352,10 +384,7 @@ StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
                             << toString(mState);
                reply.status = STATUS_INVALID_OPERATION;
            }
    } else {
        LOG(WARNING) << __func__ << ": invalid command (" << command.toString()
                     << ") or count: " << command.fmqByteCount;
        reply.status = STATUS_BAD_VALUE;
            break;
    }
    reply.state = mState;
    LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
@@ -421,9 +450,9 @@ template <class Metadata, class StreamWorker>
void StreamCommon<Metadata, StreamWorker>::stopWorker() {
    if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
        LOG(DEBUG) << __func__ << ": asking the worker to exit...";
        StreamDescriptor::Command cmd;
        cmd.code = StreamDescriptor::CommandCode(StreamContext::COMMAND_EXIT);
        cmd.fmqByteCount = mContext.getInternalCommandCookie();
        auto cmd =
                StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::hal_reserved_exit>(
                        mContext.getInternalCommandCookie());
        // Note: never call 'pause' and 'resume' methods of StreamWorker
        // in the HAL implementation. These methods are to be used by
        // the client side only. Preventing the worker loop from running
Loading