Loading audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl +23 −2 Original line number Original line Diff line number Diff line Loading @@ -39,15 +39,34 @@ parcelable StreamDescriptor { int frameSizeBytes; int frameSizeBytes; long bufferSizeFrames; long bufferSizeFrames; android.hardware.audio.core.StreamDescriptor.AudioBuffer audio; android.hardware.audio.core.StreamDescriptor.AudioBuffer audio; const int COMMAND_BURST = 1; const int LATENCY_UNKNOWN = -1; @FixedSize @VintfStability @FixedSize @VintfStability parcelable Position { parcelable Position { long frames; long frames; long timeNs; long timeNs; } } @Backing(type="int") @VintfStability enum State { STANDBY = 1, IDLE = 2, ACTIVE = 3, PAUSED = 4, DRAINING = 5, 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 @FixedSize @VintfStability parcelable Command { parcelable Command { int code; android.hardware.audio.core.StreamDescriptor.CommandCode code = android.hardware.audio.core.StreamDescriptor.CommandCode.START; int fmqByteCount; int fmqByteCount; } } @FixedSize @VintfStability @FixedSize @VintfStability Loading @@ -57,6 +76,8 @@ parcelable StreamDescriptor { android.hardware.audio.core.StreamDescriptor.Position observable; android.hardware.audio.core.StreamDescriptor.Position observable; android.hardware.audio.core.StreamDescriptor.Position hardware; android.hardware.audio.core.StreamDescriptor.Position hardware; int latencyMs; int latencyMs; int xrunFrames; android.hardware.audio.core.StreamDescriptor.State state = android.hardware.audio.core.StreamDescriptor.State.STANDBY; } } @VintfStability @VintfStability union AudioBuffer { union AudioBuffer { Loading audio/aidl/android/hardware/audio/core/IModule.aidl +6 −0 Original line number Original line Diff line number Diff line Loading @@ -263,6 +263,9 @@ interface IModule { * be completing with an error, although data (zero filled) will still be * be completing with an error, although data (zero filled) will still be * provided. * provided. * * * After the stream has been opened, it remains in the STANDBY state, see * StreamDescriptor for more details. * * @return An opened input stream and the associated descriptor. * @return An opened input stream and the associated descriptor. * @param args The pack of arguments, see 'OpenInputStreamArguments' parcelable. * @param args The pack of arguments, see 'OpenInputStreamArguments' parcelable. * @throws EX_ILLEGAL_ARGUMENT In the following cases: * @throws EX_ILLEGAL_ARGUMENT In the following cases: Loading Loading @@ -325,6 +328,9 @@ interface IModule { * StreamDescriptor will be completing with an error, although the data * StreamDescriptor will be completing with an error, although the data * will still be accepted and immediately discarded. * will still be accepted and immediately discarded. * * * After the stream has been opened, it remains in the STANDBY state, see * StreamDescriptor for more details. * * @return An opened output stream and the associated descriptor. * @return An opened output stream and the associated descriptor. * @param args The pack of arguments, see 'OpenOutputStreamArguments' parcelable. * @param args The pack of arguments, see 'OpenOutputStreamArguments' parcelable. * @throws EX_ILLEGAL_ARGUMENT In the following cases: * @throws EX_ILLEGAL_ARGUMENT In the following cases: Loading audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl +247 −30 Original line number Original line Diff line number Diff line Loading @@ -33,6 +33,72 @@ import android.hardware.common.fmq.SynchronizedReadWrite; * internal components of the stream while serving commands invoked via the * internal components of the stream while serving commands invoked via the * stream's AIDL interface and commands invoked via the command queue of the * stream's AIDL interface and commands invoked via the command queue of the * descriptor. * descriptor. * * There is a state machine defined for the stream, which executes on the * thread handling the commands from the queue. The states are defined based * on the model of idealized producer and consumer connected via a ring buffer. * For input streams, the "producer" is hardware, the "consumer" is software, * for outputs streams it's the opposite. When the producer is active, but * the buffer is full, the following actions are possible: * - if the consumer is active, the producer blocks until there is space, * this behavior is only possible for software producers; * - if the consumer is passive: * - the producer can preserve the buffer contents—a s/w producer can * keep the data on their side, while a h/w producer can only drop captured * data in this case; * - or the producer overwrites old data in the buffer. * Similarly, when an active consumer faces an empty buffer, it can: * - block until there is data (producer must be active), only possible * for software consumers; * - walk away with no data; when the consumer is hardware, it must emit * silence in this case. * * The model is defined below, note the asymmetry regarding the 'IDLE' state * between input and output streams: * * Producer | Buffer state | Consumer | Applies | State * active? | | active? | to | * ==========|==============|==========|=========|============================== * No | Empty | No | Both | STANDBY * ----------|--------------|----------|---------|----------------------------- * Yes | Filling up | No | Input | IDLE, overwrite behavior * ----------|--------------|----------|---------|----------------------------- * No | Empty | Yes† | Output | IDLE, h/w emits silence * ----------|--------------|----------|---------|----------------------------- * Yes | Not empty | Yes | Both | ACTIVE, s/w x-runs counted * ----------|--------------|----------|---------|----------------------------- * Yes | Filling up | No | Input | PAUSED, drop behavior * ----------|--------------|----------|---------|----------------------------- * Yes | Filling up | No† | Output | PAUSED, s/w stops writing once * | | | | the buffer is filled up; * | | | | h/w emits silence. * ----------|--------------|----------|---------|----------------------------- * No | Not empty | Yes | Both | DRAINING * ----------|--------------|----------|---------|----------------------------- * No | Not empty | No† | Output | DRAIN_PAUSED, * | | | | h/w emits silence. * * † - note that for output, "buffer empty, h/w consuming" has the same outcome * as "buffer not empty, h/w not consuming", but logically these conditions * are different. * * State machines of both input and output streams start from the 'STANDBY' * state. Transitions between states happen naturally with changes in the * states of the model elements. For simplicity, we restrict the change to one * element only, for example, in the 'STANDBY' state, either the producer or the * consumer can become active, but not both at the same time. States 'STANDBY', * 'IDLE', 'READY', and '*PAUSED' are "stable"—they require an external event, * whereas a change from the 'DRAINING' state can happen with time as the buffer * gets empty. * * The state machine for input streams is defined in the `stream-in-sm.gv` file, * for output streams—in the `stream-out-sm.gv` file. State machines define how * commands (from the enum 'CommandCode') trigger state changes. The full list * of states and commands is defined by constants of the 'State' enum. Note that * the 'CLOSED' state does not have a constant in the interface because the * client can never observe a stream with a functioning command queue in this * state. The 'ERROR' state is a special state which the state machine enters * when an unrecoverable hardware error is detected by the HAL module. */ */ @JavaDerive(equals=true, toString=true) @JavaDerive(equals=true, toString=true) @VintfStability @VintfStability Loading @@ -55,12 +121,110 @@ parcelable StreamDescriptor { long timeNs; long timeNs; } } @VintfStability @Backing(type="int") enum State { /** * 'STANDBY' is the initial state of the stream, entered after * opening. Since both the producer and the consumer are inactive in * this state, it allows the HAL module to put associated hardware into * "standby" mode to save power. */ STANDBY = 1, /** * In the 'IDLE' state the audio hardware is active. For input streams, * the hardware is filling buffer with captured data, overwriting old * contents on buffer wraparounds. For output streams, the buffer is * still empty, and the hardware is outputting zeroes. The HAL module * must not account for any under- or overruns as the client is not * expected to perform audio I/O. */ IDLE = 2, /** * The active state of the stream in which it handles audio I/O. The HAL * module can assume that the audio I/O will be periodic, thus inability * of the client to provide or consume audio data on time must be * considered as an under- or overrun and indicated via the 'xrunFrames' * field of the reply. */ ACTIVE = 3, /** * In the 'PAUSED' state the consumer is inactive. For input streams, * the hardware stops updating the buffer as soon as it fills up (this * is the difference from the 'IDLE' state). For output streams, * "inactivity" of hardware means that it does not consume audio data, * but rather emits silence. */ PAUSED = 4, /** * In the 'DRAINING' state the producer is inactive, the consumer is * finishing up on the buffer contents, emptying it up. As soon as it * gets empty, the stream transfers itself into the next state. */ DRAINING = 5, /** * Used for output streams only, pauses draining. This state is similar * to the 'PAUSED' state, except that the client is not adding any * new data. If it emits a 'BURST' command, this brings the stream * into the regular 'PAUSED' state. */ DRAIN_PAUSED = 6, /** * The ERROR state is entered when the stream has encountered an * irrecoverable error from the lower layer. After entering it, the * stream can only be closed. */ ERROR = 100, } @VintfStability @Backing(type="int") enum CommandCode { /** * See the state machines on the applicability of this command to * different states. The 'fmqByteCount' field must always be set to 0. */ START = 1, /** /** * The command used for audio I/O, see 'AudioBuffer'. For MMap No IRQ mode * The BURST command used for audio I/O, see 'AudioBuffer'. Differences * this command only provides updated positions and latency because actual * for the MMap No IRQ mode: * audio I/O is done via the 'AudioBuffer.mmap' shared buffer. * * - 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. */ BURST = 2, /** * See the state machines on the applicability of this command to * different states. The 'fmqByteCount' field must always be set to 0. */ DRAIN = 3, /** * See the state machines on the applicability of this command to * different states. The 'fmqByteCount' field must always be set to 0. * * Note that it's left on the discretion of the HAL implementation to * assess all the necessary conditions that could prevent hardware from * being suspended. Even if it can not be suspended, the state machine * must still enter the 'STANDBY' state for consistency. Since the * buffer must remain empty in this state, even if capturing hardware is * still active, captured data must be discarded. */ */ const int COMMAND_BURST = 1; STANDBY = 4, /** * See the state machines on the applicability of this command to * different states. The 'fmqByteCount' field must always be set to 0. */ PAUSE = 5, /** * 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 * Used for sending commands to the HAL module. The client writes into Loading @@ -71,12 +235,16 @@ parcelable StreamDescriptor { @FixedSize @FixedSize parcelable Command { parcelable Command { /** /** * One of COMMAND_* codes. * The code of the command. */ */ int code; 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 * For output streams: the amount of bytes that the client requests the * HAL module to read from the 'audio.fmq' queue. * 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 * For input streams: the amount of bytes requested by the client to * read from the hardware into the 'audio.fmq' queue. * read from the hardware into the 'audio.fmq' queue. * * Loading @@ -95,6 +263,12 @@ parcelable StreamDescriptor { } } MQDescriptor<Command, SynchronizedReadWrite> command; MQDescriptor<Command, SynchronizedReadWrite> command; /** * The value used for the 'Reply.latencyMs' field when the effective * latency can not be reported by the HAL module. */ const int LATENCY_UNKNOWN = -1; /** /** * Used for providing replies to commands. The HAL module writes into * Used for providing replies to commands. The HAL module writes into * the queue, the client reads. The queue can only contain a single reply, * the queue, the client reads. The queue can only contain a single reply, Loading @@ -107,17 +281,22 @@ parcelable StreamDescriptor { * One of Binder STATUS_* statuses: * One of Binder STATUS_* statuses: * - STATUS_OK: the command has completed successfully; * - STATUS_OK: the command has completed successfully; * - STATUS_BAD_VALUE: invalid value in the 'Command' structure; * - STATUS_BAD_VALUE: invalid value in the 'Command' structure; * - STATUS_INVALID_OPERATION: the mix port is not connected * - STATUS_INVALID_OPERATION: the command is not applicable in the * to any producer or consumer, thus * current state of the stream, or to this * positions can not be reported; * type of the stream; * - STATUS_NO_INIT: positions can not be reported because the mix port * is not connected to any producer or consumer, or * because the HAL module does not support positions * reporting for this AudioSource (on input streams). * - STATUS_NOT_ENOUGH_DATA: a read or write error has * - STATUS_NOT_ENOUGH_DATA: a read or write error has * occurred for the 'audio.fmq' queue; * occurred for the 'audio.fmq' queue; * */ */ int status; int status; /** /** * For output streams: the amount of bytes actually consumed by the HAL * Used with the BURST command only. * module from the 'audio.fmq' queue. * * 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 * For input streams: the amount of bytes actually provided by the HAL * in the 'audio.fmq' queue. * in the 'audio.fmq' queue. * * Loading @@ -126,10 +305,18 @@ parcelable StreamDescriptor { */ */ int fmqByteCount; int fmqByteCount; /** /** * It is recommended to report the current position for any command. * If the position can not be reported, the 'status' field must be * set to 'NO_INIT'. * * For output streams: the moment when the specified stream position * For output streams: the moment when the specified stream position * was presented to an external observer (i.e. presentation position). * was presented to an external observer (i.e. presentation position). * For input streams: the moment when data at the specified stream position * For input streams: the moment when data at the specified stream position * was acquired (i.e. capture position). * was acquired (i.e. capture position). * * The observable position must never be reset by the HAL module. * The data type of the frame counter is large enough to support * continuous counting for years of operation. */ */ Position observable; Position observable; /** /** Loading @@ -138,9 +325,22 @@ parcelable StreamDescriptor { */ */ Position hardware; Position hardware; /** /** * Current latency reported by the hardware. * Current latency reported by the hardware. It is recommended to * report the current latency for any command. If the value of latency * can not be determined, this field must be set to 'LATENCY_UNKNOWN'. */ */ int latencyMs; int latencyMs; /** * Number of frames lost due to an underrun (for input streams), * or not provided on time (for output streams) for the **previous** * transfer operation. */ int xrunFrames; /** * The state that the stream was in while the HAL module was sending the * reply. */ State state = State.STANDBY; } } MQDescriptor<Reply, SynchronizedReadWrite> reply; MQDescriptor<Reply, SynchronizedReadWrite> reply; Loading Loading @@ -170,42 +370,59 @@ parcelable StreamDescriptor { @VintfStability @VintfStability union AudioBuffer { union AudioBuffer { /** /** * The fast message queue used for all modes except MMap No IRQ. Both * The fast message queue used for BURST commands in all modes except * reads and writes into this queue are non-blocking because access to * MMap No IRQ. Both reads and writes into this queue are non-blocking * this queue is synchronized via the 'command' and 'reply' queues as * because access to this queue is synchronized via the 'command' and * described below. The queue nevertheless uses 'SynchronizedReadWrite' * 'reply' queues as described below. The queue nevertheless uses * because there is only one reader, and the reading position must be * 'SynchronizedReadWrite' because there is only one reader, and the * shared. * reading position must be shared. * * Note that the fast message queue is a transient buffer, only used for * data transfer. Neither of the sides can use it to store any data * outside of the 'BURST' operation. The consumer must always retrieve * all data available in the fast message queue, even if it can not use * it. The producer must re-send any unconsumed data on the next * transfer operation. This restriction is posed in order to make the * fast message queue fully transparent from the latency perspective. * * * For output streams the following sequence of operations is used: * For output streams the following sequence of operations is used: * 1. The client writes audio data into the 'audio.fmq' queue. * 1. The client writes audio data into the 'audio.fmq' queue. * 2. The client writes the 'BURST' command into the 'command' queue, * 2. The client writes the BURST command into the 'command' queue, * and hangs on waiting on a read from the 'reply' queue. * and hangs on waiting on a read from the 'reply' queue. * 3. The high priority thread in the HAL module wakes up due to 2. * 3. The high priority thread in the HAL module wakes up due to 2. * 4. The HAL module reads the command and audio data. * 4. The HAL module reads the command and audio data. According * to the statement above, the HAL module must always read * from the FMQ all the data it contains. The amount of data that * the HAL module has actually consumed is indicated to the client * via the 'reply.fmqByteCount' field. * 5. The HAL module writes the command status and current positions * 5. The HAL module writes the command status and current positions * into 'reply' queue, and hangs on waiting on a read from * into 'reply' queue, and hangs on waiting on a read from * the 'command' queue. * the 'command' queue. * 6. The client wakes up due to 5. and reads the reply. * 6. The client wakes up due to 5. and reads the reply. * * * For input streams the following sequence of operations is used: * For input streams the following sequence of operations is used: * 1. The client writes the 'BURST' command into the 'command' queue, * 1. The client writes the BURST command into the 'command' queue, * and hangs on waiting on a read from the 'reply' queue. * and hangs on waiting on a read from the 'reply' queue. * 2. The high priority thread in the HAL module wakes up due to 1. * 2. The high priority thread in the HAL module wakes up due to 1. * 3. The HAL module writes audio data into the 'audio.fmq' queue. * 3. The HAL module writes audio data into the 'audio.fmq' queue. * The value of 'reply.fmqByteCount' must be the equal to the amount * of data in the queue. * 4. The HAL module writes the command status and current positions * 4. The HAL module writes the command status and current positions * into 'reply' queue, and hangs on waiting on a read from * into 'reply' queue, and hangs on waiting on a read from * the 'command' queue. * the 'command' queue. * 5. The client wakes up due to 4. * 5. The client wakes up due to 4. * 6. The client reads the reply and audio data. * 6. The client reads the reply and audio data. The client must * always read from the FMQ all the data it contains. * */ */ MQDescriptor<byte, SynchronizedReadWrite> fmq; MQDescriptor<byte, SynchronizedReadWrite> fmq; /** /** * MMap buffers are shared directly with the DSP, which operates * MMap buffers are shared directly with the DSP, which operates * independently from the CPU. Writes and reads into these buffers * independently from the CPU. Writes and reads into these buffers are * are not synchronized with 'command' and 'reply' queues. However, * not synchronized with 'command' and 'reply' queues. However, the * the client still uses the 'BURST' command for obtaining current * client still uses the same commands for controlling the audio data * positions from the HAL module. * exchange and for obtaining current positions and latency from the HAL * module. */ */ MmapBufferDescriptor mmap; MmapBufferDescriptor mmap; } } Loading audio/aidl/android/hardware/audio/core/stream-in-sm.gv 0 → 100644 +42 −0 Original line number Original line Diff line number Diff line // Copyright (C) 2022 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // To render: dot -Tpng stream-in-sm.gv -o stream-in-sm.png digraph stream_in_state_machine { node [shape=doublecircle style=filled fillcolor=black width=0.5] I; node [shape=point width=0.5] F; node [shape=oval width=1]; node [fillcolor=lightgreen] STANDBY; // buffer is empty node [fillcolor=tomato] CLOSED; node [fillcolor=tomato] ERROR; 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 DRAINING -> STANDBY [label="<empty buffer>"]; // consumer deactivates IDLE -> ERROR [label="<hardware failure>"]; ACTIVE -> ERROR [label="<hardware failure>"]; PAUSED -> ERROR [label="<hardware failure>"]; ANY_STATE -> CLOSED [label="→IStream*.close"]; CLOSED -> F; } audio/aidl/android/hardware/audio/core/stream-out-sm.gv 0 → 100644 +48 −0 Original line number Original line Diff line number Diff line // Copyright (C) 2022 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // To render: dot -Tpng stream-out-sm.gv -o stream-out-sm.png digraph stream_out_state_machine { node [shape=doublecircle style=filled fillcolor=black width=0.5] I; node [shape=point width=0.5] F; node [shape=oval width=1]; node [fillcolor=lightgreen] STANDBY; // buffer is empty node [fillcolor=lightgreen] IDLE; // buffer is empty node [fillcolor=tomato] CLOSED; node [fillcolor=tomato] ERROR; 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 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 IDLE -> ERROR [label="<hardware failure>"]; ACTIVE -> ERROR [label="<hardware failure>"]; DRAINING -> ERROR [label="<hardware failure>"]; ANY_STATE -> CLOSED [label="→IStream*.close"]; CLOSED -> F; } Loading
audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl +23 −2 Original line number Original line Diff line number Diff line Loading @@ -39,15 +39,34 @@ parcelable StreamDescriptor { int frameSizeBytes; int frameSizeBytes; long bufferSizeFrames; long bufferSizeFrames; android.hardware.audio.core.StreamDescriptor.AudioBuffer audio; android.hardware.audio.core.StreamDescriptor.AudioBuffer audio; const int COMMAND_BURST = 1; const int LATENCY_UNKNOWN = -1; @FixedSize @VintfStability @FixedSize @VintfStability parcelable Position { parcelable Position { long frames; long frames; long timeNs; long timeNs; } } @Backing(type="int") @VintfStability enum State { STANDBY = 1, IDLE = 2, ACTIVE = 3, PAUSED = 4, DRAINING = 5, 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 @FixedSize @VintfStability parcelable Command { parcelable Command { int code; android.hardware.audio.core.StreamDescriptor.CommandCode code = android.hardware.audio.core.StreamDescriptor.CommandCode.START; int fmqByteCount; int fmqByteCount; } } @FixedSize @VintfStability @FixedSize @VintfStability Loading @@ -57,6 +76,8 @@ parcelable StreamDescriptor { android.hardware.audio.core.StreamDescriptor.Position observable; android.hardware.audio.core.StreamDescriptor.Position observable; android.hardware.audio.core.StreamDescriptor.Position hardware; android.hardware.audio.core.StreamDescriptor.Position hardware; int latencyMs; int latencyMs; int xrunFrames; android.hardware.audio.core.StreamDescriptor.State state = android.hardware.audio.core.StreamDescriptor.State.STANDBY; } } @VintfStability @VintfStability union AudioBuffer { union AudioBuffer { Loading
audio/aidl/android/hardware/audio/core/IModule.aidl +6 −0 Original line number Original line Diff line number Diff line Loading @@ -263,6 +263,9 @@ interface IModule { * be completing with an error, although data (zero filled) will still be * be completing with an error, although data (zero filled) will still be * provided. * provided. * * * After the stream has been opened, it remains in the STANDBY state, see * StreamDescriptor for more details. * * @return An opened input stream and the associated descriptor. * @return An opened input stream and the associated descriptor. * @param args The pack of arguments, see 'OpenInputStreamArguments' parcelable. * @param args The pack of arguments, see 'OpenInputStreamArguments' parcelable. * @throws EX_ILLEGAL_ARGUMENT In the following cases: * @throws EX_ILLEGAL_ARGUMENT In the following cases: Loading Loading @@ -325,6 +328,9 @@ interface IModule { * StreamDescriptor will be completing with an error, although the data * StreamDescriptor will be completing with an error, although the data * will still be accepted and immediately discarded. * will still be accepted and immediately discarded. * * * After the stream has been opened, it remains in the STANDBY state, see * StreamDescriptor for more details. * * @return An opened output stream and the associated descriptor. * @return An opened output stream and the associated descriptor. * @param args The pack of arguments, see 'OpenOutputStreamArguments' parcelable. * @param args The pack of arguments, see 'OpenOutputStreamArguments' parcelable. * @throws EX_ILLEGAL_ARGUMENT In the following cases: * @throws EX_ILLEGAL_ARGUMENT In the following cases: Loading
audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl +247 −30 Original line number Original line Diff line number Diff line Loading @@ -33,6 +33,72 @@ import android.hardware.common.fmq.SynchronizedReadWrite; * internal components of the stream while serving commands invoked via the * internal components of the stream while serving commands invoked via the * stream's AIDL interface and commands invoked via the command queue of the * stream's AIDL interface and commands invoked via the command queue of the * descriptor. * descriptor. * * There is a state machine defined for the stream, which executes on the * thread handling the commands from the queue. The states are defined based * on the model of idealized producer and consumer connected via a ring buffer. * For input streams, the "producer" is hardware, the "consumer" is software, * for outputs streams it's the opposite. When the producer is active, but * the buffer is full, the following actions are possible: * - if the consumer is active, the producer blocks until there is space, * this behavior is only possible for software producers; * - if the consumer is passive: * - the producer can preserve the buffer contents—a s/w producer can * keep the data on their side, while a h/w producer can only drop captured * data in this case; * - or the producer overwrites old data in the buffer. * Similarly, when an active consumer faces an empty buffer, it can: * - block until there is data (producer must be active), only possible * for software consumers; * - walk away with no data; when the consumer is hardware, it must emit * silence in this case. * * The model is defined below, note the asymmetry regarding the 'IDLE' state * between input and output streams: * * Producer | Buffer state | Consumer | Applies | State * active? | | active? | to | * ==========|==============|==========|=========|============================== * No | Empty | No | Both | STANDBY * ----------|--------------|----------|---------|----------------------------- * Yes | Filling up | No | Input | IDLE, overwrite behavior * ----------|--------------|----------|---------|----------------------------- * No | Empty | Yes† | Output | IDLE, h/w emits silence * ----------|--------------|----------|---------|----------------------------- * Yes | Not empty | Yes | Both | ACTIVE, s/w x-runs counted * ----------|--------------|----------|---------|----------------------------- * Yes | Filling up | No | Input | PAUSED, drop behavior * ----------|--------------|----------|---------|----------------------------- * Yes | Filling up | No† | Output | PAUSED, s/w stops writing once * | | | | the buffer is filled up; * | | | | h/w emits silence. * ----------|--------------|----------|---------|----------------------------- * No | Not empty | Yes | Both | DRAINING * ----------|--------------|----------|---------|----------------------------- * No | Not empty | No† | Output | DRAIN_PAUSED, * | | | | h/w emits silence. * * † - note that for output, "buffer empty, h/w consuming" has the same outcome * as "buffer not empty, h/w not consuming", but logically these conditions * are different. * * State machines of both input and output streams start from the 'STANDBY' * state. Transitions between states happen naturally with changes in the * states of the model elements. For simplicity, we restrict the change to one * element only, for example, in the 'STANDBY' state, either the producer or the * consumer can become active, but not both at the same time. States 'STANDBY', * 'IDLE', 'READY', and '*PAUSED' are "stable"—they require an external event, * whereas a change from the 'DRAINING' state can happen with time as the buffer * gets empty. * * The state machine for input streams is defined in the `stream-in-sm.gv` file, * for output streams—in the `stream-out-sm.gv` file. State machines define how * commands (from the enum 'CommandCode') trigger state changes. The full list * of states and commands is defined by constants of the 'State' enum. Note that * the 'CLOSED' state does not have a constant in the interface because the * client can never observe a stream with a functioning command queue in this * state. The 'ERROR' state is a special state which the state machine enters * when an unrecoverable hardware error is detected by the HAL module. */ */ @JavaDerive(equals=true, toString=true) @JavaDerive(equals=true, toString=true) @VintfStability @VintfStability Loading @@ -55,12 +121,110 @@ parcelable StreamDescriptor { long timeNs; long timeNs; } } @VintfStability @Backing(type="int") enum State { /** * 'STANDBY' is the initial state of the stream, entered after * opening. Since both the producer and the consumer are inactive in * this state, it allows the HAL module to put associated hardware into * "standby" mode to save power. */ STANDBY = 1, /** * In the 'IDLE' state the audio hardware is active. For input streams, * the hardware is filling buffer with captured data, overwriting old * contents on buffer wraparounds. For output streams, the buffer is * still empty, and the hardware is outputting zeroes. The HAL module * must not account for any under- or overruns as the client is not * expected to perform audio I/O. */ IDLE = 2, /** * The active state of the stream in which it handles audio I/O. The HAL * module can assume that the audio I/O will be periodic, thus inability * of the client to provide or consume audio data on time must be * considered as an under- or overrun and indicated via the 'xrunFrames' * field of the reply. */ ACTIVE = 3, /** * In the 'PAUSED' state the consumer is inactive. For input streams, * the hardware stops updating the buffer as soon as it fills up (this * is the difference from the 'IDLE' state). For output streams, * "inactivity" of hardware means that it does not consume audio data, * but rather emits silence. */ PAUSED = 4, /** * In the 'DRAINING' state the producer is inactive, the consumer is * finishing up on the buffer contents, emptying it up. As soon as it * gets empty, the stream transfers itself into the next state. */ DRAINING = 5, /** * Used for output streams only, pauses draining. This state is similar * to the 'PAUSED' state, except that the client is not adding any * new data. If it emits a 'BURST' command, this brings the stream * into the regular 'PAUSED' state. */ DRAIN_PAUSED = 6, /** * The ERROR state is entered when the stream has encountered an * irrecoverable error from the lower layer. After entering it, the * stream can only be closed. */ ERROR = 100, } @VintfStability @Backing(type="int") enum CommandCode { /** * See the state machines on the applicability of this command to * different states. The 'fmqByteCount' field must always be set to 0. */ START = 1, /** /** * The command used for audio I/O, see 'AudioBuffer'. For MMap No IRQ mode * The BURST command used for audio I/O, see 'AudioBuffer'. Differences * this command only provides updated positions and latency because actual * for the MMap No IRQ mode: * audio I/O is done via the 'AudioBuffer.mmap' shared buffer. * * - 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. */ BURST = 2, /** * See the state machines on the applicability of this command to * different states. The 'fmqByteCount' field must always be set to 0. */ DRAIN = 3, /** * See the state machines on the applicability of this command to * different states. The 'fmqByteCount' field must always be set to 0. * * Note that it's left on the discretion of the HAL implementation to * assess all the necessary conditions that could prevent hardware from * being suspended. Even if it can not be suspended, the state machine * must still enter the 'STANDBY' state for consistency. Since the * buffer must remain empty in this state, even if capturing hardware is * still active, captured data must be discarded. */ */ const int COMMAND_BURST = 1; STANDBY = 4, /** * See the state machines on the applicability of this command to * different states. The 'fmqByteCount' field must always be set to 0. */ PAUSE = 5, /** * 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 * Used for sending commands to the HAL module. The client writes into Loading @@ -71,12 +235,16 @@ parcelable StreamDescriptor { @FixedSize @FixedSize parcelable Command { parcelable Command { /** /** * One of COMMAND_* codes. * The code of the command. */ */ int code; 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 * For output streams: the amount of bytes that the client requests the * HAL module to read from the 'audio.fmq' queue. * 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 * For input streams: the amount of bytes requested by the client to * read from the hardware into the 'audio.fmq' queue. * read from the hardware into the 'audio.fmq' queue. * * Loading @@ -95,6 +263,12 @@ parcelable StreamDescriptor { } } MQDescriptor<Command, SynchronizedReadWrite> command; MQDescriptor<Command, SynchronizedReadWrite> command; /** * The value used for the 'Reply.latencyMs' field when the effective * latency can not be reported by the HAL module. */ const int LATENCY_UNKNOWN = -1; /** /** * Used for providing replies to commands. The HAL module writes into * Used for providing replies to commands. The HAL module writes into * the queue, the client reads. The queue can only contain a single reply, * the queue, the client reads. The queue can only contain a single reply, Loading @@ -107,17 +281,22 @@ parcelable StreamDescriptor { * One of Binder STATUS_* statuses: * One of Binder STATUS_* statuses: * - STATUS_OK: the command has completed successfully; * - STATUS_OK: the command has completed successfully; * - STATUS_BAD_VALUE: invalid value in the 'Command' structure; * - STATUS_BAD_VALUE: invalid value in the 'Command' structure; * - STATUS_INVALID_OPERATION: the mix port is not connected * - STATUS_INVALID_OPERATION: the command is not applicable in the * to any producer or consumer, thus * current state of the stream, or to this * positions can not be reported; * type of the stream; * - STATUS_NO_INIT: positions can not be reported because the mix port * is not connected to any producer or consumer, or * because the HAL module does not support positions * reporting for this AudioSource (on input streams). * - STATUS_NOT_ENOUGH_DATA: a read or write error has * - STATUS_NOT_ENOUGH_DATA: a read or write error has * occurred for the 'audio.fmq' queue; * occurred for the 'audio.fmq' queue; * */ */ int status; int status; /** /** * For output streams: the amount of bytes actually consumed by the HAL * Used with the BURST command only. * module from the 'audio.fmq' queue. * * 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 * For input streams: the amount of bytes actually provided by the HAL * in the 'audio.fmq' queue. * in the 'audio.fmq' queue. * * Loading @@ -126,10 +305,18 @@ parcelable StreamDescriptor { */ */ int fmqByteCount; int fmqByteCount; /** /** * It is recommended to report the current position for any command. * If the position can not be reported, the 'status' field must be * set to 'NO_INIT'. * * For output streams: the moment when the specified stream position * For output streams: the moment when the specified stream position * was presented to an external observer (i.e. presentation position). * was presented to an external observer (i.e. presentation position). * For input streams: the moment when data at the specified stream position * For input streams: the moment when data at the specified stream position * was acquired (i.e. capture position). * was acquired (i.e. capture position). * * The observable position must never be reset by the HAL module. * The data type of the frame counter is large enough to support * continuous counting for years of operation. */ */ Position observable; Position observable; /** /** Loading @@ -138,9 +325,22 @@ parcelable StreamDescriptor { */ */ Position hardware; Position hardware; /** /** * Current latency reported by the hardware. * Current latency reported by the hardware. It is recommended to * report the current latency for any command. If the value of latency * can not be determined, this field must be set to 'LATENCY_UNKNOWN'. */ */ int latencyMs; int latencyMs; /** * Number of frames lost due to an underrun (for input streams), * or not provided on time (for output streams) for the **previous** * transfer operation. */ int xrunFrames; /** * The state that the stream was in while the HAL module was sending the * reply. */ State state = State.STANDBY; } } MQDescriptor<Reply, SynchronizedReadWrite> reply; MQDescriptor<Reply, SynchronizedReadWrite> reply; Loading Loading @@ -170,42 +370,59 @@ parcelable StreamDescriptor { @VintfStability @VintfStability union AudioBuffer { union AudioBuffer { /** /** * The fast message queue used for all modes except MMap No IRQ. Both * The fast message queue used for BURST commands in all modes except * reads and writes into this queue are non-blocking because access to * MMap No IRQ. Both reads and writes into this queue are non-blocking * this queue is synchronized via the 'command' and 'reply' queues as * because access to this queue is synchronized via the 'command' and * described below. The queue nevertheless uses 'SynchronizedReadWrite' * 'reply' queues as described below. The queue nevertheless uses * because there is only one reader, and the reading position must be * 'SynchronizedReadWrite' because there is only one reader, and the * shared. * reading position must be shared. * * Note that the fast message queue is a transient buffer, only used for * data transfer. Neither of the sides can use it to store any data * outside of the 'BURST' operation. The consumer must always retrieve * all data available in the fast message queue, even if it can not use * it. The producer must re-send any unconsumed data on the next * transfer operation. This restriction is posed in order to make the * fast message queue fully transparent from the latency perspective. * * * For output streams the following sequence of operations is used: * For output streams the following sequence of operations is used: * 1. The client writes audio data into the 'audio.fmq' queue. * 1. The client writes audio data into the 'audio.fmq' queue. * 2. The client writes the 'BURST' command into the 'command' queue, * 2. The client writes the BURST command into the 'command' queue, * and hangs on waiting on a read from the 'reply' queue. * and hangs on waiting on a read from the 'reply' queue. * 3. The high priority thread in the HAL module wakes up due to 2. * 3. The high priority thread in the HAL module wakes up due to 2. * 4. The HAL module reads the command and audio data. * 4. The HAL module reads the command and audio data. According * to the statement above, the HAL module must always read * from the FMQ all the data it contains. The amount of data that * the HAL module has actually consumed is indicated to the client * via the 'reply.fmqByteCount' field. * 5. The HAL module writes the command status and current positions * 5. The HAL module writes the command status and current positions * into 'reply' queue, and hangs on waiting on a read from * into 'reply' queue, and hangs on waiting on a read from * the 'command' queue. * the 'command' queue. * 6. The client wakes up due to 5. and reads the reply. * 6. The client wakes up due to 5. and reads the reply. * * * For input streams the following sequence of operations is used: * For input streams the following sequence of operations is used: * 1. The client writes the 'BURST' command into the 'command' queue, * 1. The client writes the BURST command into the 'command' queue, * and hangs on waiting on a read from the 'reply' queue. * and hangs on waiting on a read from the 'reply' queue. * 2. The high priority thread in the HAL module wakes up due to 1. * 2. The high priority thread in the HAL module wakes up due to 1. * 3. The HAL module writes audio data into the 'audio.fmq' queue. * 3. The HAL module writes audio data into the 'audio.fmq' queue. * The value of 'reply.fmqByteCount' must be the equal to the amount * of data in the queue. * 4. The HAL module writes the command status and current positions * 4. The HAL module writes the command status and current positions * into 'reply' queue, and hangs on waiting on a read from * into 'reply' queue, and hangs on waiting on a read from * the 'command' queue. * the 'command' queue. * 5. The client wakes up due to 4. * 5. The client wakes up due to 4. * 6. The client reads the reply and audio data. * 6. The client reads the reply and audio data. The client must * always read from the FMQ all the data it contains. * */ */ MQDescriptor<byte, SynchronizedReadWrite> fmq; MQDescriptor<byte, SynchronizedReadWrite> fmq; /** /** * MMap buffers are shared directly with the DSP, which operates * MMap buffers are shared directly with the DSP, which operates * independently from the CPU. Writes and reads into these buffers * independently from the CPU. Writes and reads into these buffers are * are not synchronized with 'command' and 'reply' queues. However, * not synchronized with 'command' and 'reply' queues. However, the * the client still uses the 'BURST' command for obtaining current * client still uses the same commands for controlling the audio data * positions from the HAL module. * exchange and for obtaining current positions and latency from the HAL * module. */ */ MmapBufferDescriptor mmap; MmapBufferDescriptor mmap; } } Loading
audio/aidl/android/hardware/audio/core/stream-in-sm.gv 0 → 100644 +42 −0 Original line number Original line Diff line number Diff line // Copyright (C) 2022 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // To render: dot -Tpng stream-in-sm.gv -o stream-in-sm.png digraph stream_in_state_machine { node [shape=doublecircle style=filled fillcolor=black width=0.5] I; node [shape=point width=0.5] F; node [shape=oval width=1]; node [fillcolor=lightgreen] STANDBY; // buffer is empty node [fillcolor=tomato] CLOSED; node [fillcolor=tomato] ERROR; 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 DRAINING -> STANDBY [label="<empty buffer>"]; // consumer deactivates IDLE -> ERROR [label="<hardware failure>"]; ACTIVE -> ERROR [label="<hardware failure>"]; PAUSED -> ERROR [label="<hardware failure>"]; ANY_STATE -> CLOSED [label="→IStream*.close"]; CLOSED -> F; }
audio/aidl/android/hardware/audio/core/stream-out-sm.gv 0 → 100644 +48 −0 Original line number Original line Diff line number Diff line // Copyright (C) 2022 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // To render: dot -Tpng stream-out-sm.gv -o stream-out-sm.png digraph stream_out_state_machine { node [shape=doublecircle style=filled fillcolor=black width=0.5] I; node [shape=point width=0.5] F; node [shape=oval width=1]; node [fillcolor=lightgreen] STANDBY; // buffer is empty node [fillcolor=lightgreen] IDLE; // buffer is empty node [fillcolor=tomato] CLOSED; node [fillcolor=tomato] ERROR; 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 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 IDLE -> ERROR [label="<hardware failure>"]; ACTIVE -> ERROR [label="<hardware failure>"]; DRAINING -> ERROR [label="<hardware failure>"]; ANY_STATE -> CLOSED [label="→IStream*.close"]; CLOSED -> F; }