Loading audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl +8 −12 Original line number Diff line number Diff line Loading @@ -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 { Loading audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl +57 −55 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading @@ -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; Loading Loading @@ -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; /** Loading audio/aidl/android/hardware/audio/core/stream-in-sm.gv +10 −10 Original line number Diff line number Diff line Loading @@ -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>"]; Loading audio/aidl/android/hardware/audio/core/stream-out-sm.gv +15 −15 Original line number Diff line number Diff line Loading @@ -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>"]; Loading audio/aidl/default/Stream.cpp +222 −193 Original line number Diff line number Diff line Loading @@ -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 || Loading @@ -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. Loading @@ -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. Loading @@ -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(); Loading Loading @@ -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; Loading @@ -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 || Loading @@ -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. Loading @@ -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. Loading @@ -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) { Loading @@ -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) { Loading @@ -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(); Loading Loading @@ -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 Loading
audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl +8 −12 Original line number Diff line number Diff line Loading @@ -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 { Loading
audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl +57 −55 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading @@ -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; Loading Loading @@ -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; /** Loading
audio/aidl/android/hardware/audio/core/stream-in-sm.gv +10 −10 Original line number Diff line number Diff line Loading @@ -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>"]; Loading
audio/aidl/android/hardware/audio/core/stream-out-sm.gv +15 −15 Original line number Diff line number Diff line Loading @@ -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>"]; Loading
audio/aidl/default/Stream.cpp +222 −193 Original line number Diff line number Diff line Loading @@ -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 || Loading @@ -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. Loading @@ -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. Loading @@ -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(); Loading Loading @@ -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; Loading @@ -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 || Loading @@ -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. Loading @@ -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. Loading @@ -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) { Loading @@ -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) { Loading @@ -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(); Loading Loading @@ -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