Merge "benchmark: updated benchmark tests for strongbox"
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
index db1ac22..da24a10 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
@@ -39,15 +39,34 @@
int frameSizeBytes;
long bufferSizeFrames;
android.hardware.audio.core.StreamDescriptor.AudioBuffer audio;
- const int COMMAND_BURST = 1;
+ const int LATENCY_UNKNOWN = -1;
@FixedSize @VintfStability
parcelable Position {
long frames;
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
parcelable Command {
- int code;
+ android.hardware.audio.core.StreamDescriptor.CommandCode code = android.hardware.audio.core.StreamDescriptor.CommandCode.START;
int fmqByteCount;
}
@FixedSize @VintfStability
@@ -57,6 +76,8 @@
android.hardware.audio.core.StreamDescriptor.Position observable;
android.hardware.audio.core.StreamDescriptor.Position hardware;
int latencyMs;
+ int xrunFrames;
+ android.hardware.audio.core.StreamDescriptor.State state = android.hardware.audio.core.StreamDescriptor.State.STANDBY;
}
@VintfStability
union AudioBuffer {
diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl
index 735f87f..0959840 100644
--- a/audio/aidl/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/android/hardware/audio/core/IModule.aidl
@@ -263,6 +263,9 @@
* be completing with an error, although data (zero filled) will still be
* 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.
* @param args The pack of arguments, see 'OpenInputStreamArguments' parcelable.
* @throws EX_ILLEGAL_ARGUMENT In the following cases:
@@ -325,6 +328,9 @@
* StreamDescriptor will be completing with an error, although the data
* 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.
* @param args The pack of arguments, see 'OpenOutputStreamArguments' parcelable.
* @throws EX_ILLEGAL_ARGUMENT In the following cases:
diff --git a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
index 2b1ed8c..e5e56fc 100644
--- a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
+++ b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
@@ -33,6 +33,72 @@
* internal components of the stream while serving commands invoked via the
* stream's AIDL interface and commands invoked via the command queue of the
* 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)
@VintfStability
@@ -55,12 +121,110 @@
long timeNs;
}
- /**
- * The command used for audio I/O, see 'AudioBuffer'. For 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.
- */
- const int COMMAND_BURST = 1;
+ @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 BURST command used for audio I/O, see 'AudioBuffer'. 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.
+ */
+ 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.
+ */
+ 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
@@ -71,12 +235,16 @@
@FixedSize
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
- * 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
* read from the hardware into the 'audio.fmq' queue.
*
@@ -96,6 +264,12 @@
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
* the queue, the client reads. The queue can only contain a single reply,
* corresponding to the last command sent by the client.
@@ -107,17 +281,22 @@
* One of Binder STATUS_* statuses:
* - STATUS_OK: the command has completed successfully;
* - STATUS_BAD_VALUE: invalid value in the 'Command' structure;
- * - STATUS_INVALID_OPERATION: the mix port is not connected
- * to any producer or consumer, thus
- * positions can not be reported;
+ * - STATUS_INVALID_OPERATION: the command is not applicable in the
+ * current state of the stream, or to this
+ * 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
* occurred for the 'audio.fmq' queue;
- *
*/
int status;
/**
- * For output streams: the amount of bytes actually consumed by the HAL
- * module from the 'audio.fmq' queue.
+ * 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.
*
@@ -126,10 +305,18 @@
*/
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
* was presented to an external observer (i.e. presentation position).
* For input streams: the moment when data at the specified stream 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;
/**
@@ -138,9 +325,22 @@
*/
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;
+ /**
+ * 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;
@@ -170,42 +370,59 @@
@VintfStability
union AudioBuffer {
/**
- * The fast message queue used for all modes except MMap No IRQ. Both
- * reads and writes into this queue are non-blocking because access to
- * this queue is synchronized via the 'command' and 'reply' queues as
- * described below. The queue nevertheless uses 'SynchronizedReadWrite'
- * because there is only one reader, and the reading position must be
- * shared.
+ * The fast message queue used for BURST commands in all modes except
+ * MMap No IRQ. Both reads and writes into this queue are non-blocking
+ * because access to this queue is synchronized via the 'command' and
+ * 'reply' queues as described below. The queue nevertheless uses
+ * 'SynchronizedReadWrite' because there is only one reader, and the
+ * 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:
* 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.
* 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
* into 'reply' queue, and hangs on waiting on a read from
* the 'command' queue.
* 6. The client wakes up due to 5. and reads the reply.
*
* 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.
* 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.
+ * 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
* into 'reply' queue, and hangs on waiting on a read from
* the 'command' queue.
* 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;
/**
* MMap buffers are shared directly with the DSP, which operates
- * independently from the CPU. Writes and reads into these buffers
- * are not synchronized with 'command' and 'reply' queues. However,
- * the client still uses the 'BURST' command for obtaining current
- * positions from the HAL module.
+ * independently from the CPU. Writes and reads into these buffers are
+ * not synchronized with 'command' and 'reply' queues. However, the
+ * client still uses the same commands for controlling the audio data
+ * exchange and for obtaining current positions and latency from the HAL
+ * module.
*/
MmapBufferDescriptor mmap;
}
diff --git a/audio/aidl/android/hardware/audio/core/stream-in-sm.gv b/audio/aidl/android/hardware/audio/core/stream-in-sm.gv
new file mode 100644
index 0000000..889a14b
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/stream-in-sm.gv
@@ -0,0 +1,42 @@
+// 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;
+}
diff --git a/audio/aidl/android/hardware/audio/core/stream-out-sm.gv b/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
new file mode 100644
index 0000000..56dd5290
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
@@ -0,0 +1,48 @@
+// 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;
+}
diff --git a/audio/aidl/common/StreamWorker.cpp b/audio/aidl/common/StreamWorker.cpp
index 9bca760..dda0e4a 100644
--- a/audio/aidl/common/StreamWorker.cpp
+++ b/audio/aidl/common/StreamWorker.cpp
@@ -44,6 +44,10 @@
mWorkerStateChangeRequest = true;
}
}
+ join();
+}
+
+void ThreadController::join() {
if (mWorker.joinable()) {
mWorker.join();
}
diff --git a/audio/aidl/common/include/StreamWorker.h b/audio/aidl/common/include/StreamWorker.h
index 6260eca..ab2ec26 100644
--- a/audio/aidl/common/include/StreamWorker.h
+++ b/audio/aidl/common/include/StreamWorker.h
@@ -39,6 +39,9 @@
~ThreadController() { stop(); }
bool start(const std::string& name, int priority);
+ // Note: 'pause' and 'resume' methods should only be used on the "driving" side.
+ // In the case of audio HAL I/O, the driving side is the client, because the HAL
+ // implementation always blocks on getting a command.
void pause() { switchWorkerStateSync(WorkerState::RUNNING, WorkerState::PAUSE_REQUESTED); }
void resume() { switchWorkerStateSync(WorkerState::PAUSED, WorkerState::RESUME_REQUESTED); }
bool hasError() {
@@ -50,6 +53,10 @@
return mError;
}
void stop();
+ // Direct use of 'join' assumes that the StreamLogic is not intended
+ // to run forever, and is guaranteed to exit by itself. This normally
+ // only happen in tests.
+ void join();
bool waitForAtLeastOneCycle();
// Only used by unit tests.
@@ -133,7 +140,8 @@
void resume() { mThread.resume(); }
bool hasError() { return mThread.hasError(); }
std::string getError() { return mThread.getError(); }
- void stop() { return mThread.stop(); }
+ void stop() { mThread.stop(); }
+ void join() { mThread.join(); }
bool waitForAtLeastOneCycle() { return mThread.waitForAtLeastOneCycle(); }
// Only used by unit tests.
diff --git a/audio/aidl/common/tests/streamworker_tests.cpp b/audio/aidl/common/tests/streamworker_tests.cpp
index e3e484d..8ea8424 100644
--- a/audio/aidl/common/tests/streamworker_tests.cpp
+++ b/audio/aidl/common/tests/streamworker_tests.cpp
@@ -160,6 +160,14 @@
EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
}
+TEST_P(StreamWorkerTest, WorkerJoin) {
+ ASSERT_TRUE(worker.start());
+ stream.setStopStatus();
+ worker.join();
+ EXPECT_FALSE(worker.hasError());
+ EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+}
+
TEST_P(StreamWorkerTest, WorkerError) {
ASSERT_TRUE(worker.start());
stream.setErrorStatus();
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 312df72..7b544a1 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -85,126 +85,315 @@
return "";
}
+void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
+ bool isConnected) const {
+ if (isConnected) {
+ reply->status = STATUS_OK;
+ reply->observable.frames = mFrameCount;
+ reply->observable.timeNs = ::android::elapsedRealtimeNano();
+ } else {
+ reply->status = STATUS_NO_INIT;
+ }
+}
+
const std::string StreamInWorkerLogic::kThreadName = "reader";
StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
StreamDescriptor::Command command{};
if (!mCommandMQ->readBlocking(&command, 1)) {
LOG(ERROR) << __func__ << ": reading of command from MQ failed";
+ mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
}
StreamDescriptor::Reply reply{};
- if (command.code == StreamContext::COMMAND_EXIT &&
+ if (static_cast<int32_t>(command.code) == StreamContext::COMMAND_EXIT &&
command.fmqByteCount == 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::COMMAND_BURST && command.fmqByteCount >= 0) {
+ } else if (command.code == StreamDescriptor::CommandCode::START && command.fmqByteCount >= 0) {
+ 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
+ : 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
<< " bytes";
- usleep(3000); // Simulate a blocking call into the driver.
- const size_t byteCount = std::min({static_cast<size_t>(command.fmqByteCount),
- mDataMQ->availableToWrite(), mDataBufferSize});
- const bool isConnected = mIsConnected;
- // Simulate reading of data, or provide zeroes if the stream is not connected.
- for (size_t i = 0; i < byteCount; ++i) {
- using buffer_type = decltype(mDataBuffer)::element_type;
- constexpr int kBufferValueRange = std::numeric_limits<buffer_type>::max() -
- std::numeric_limits<buffer_type>::min() + 1;
- mDataBuffer[i] = isConnected ? (std::rand() % kBufferValueRange) +
- std::numeric_limits<buffer_type>::min()
- : 0;
- }
- bool success = byteCount > 0 ? mDataMQ->write(&mDataBuffer[0], byteCount) : true;
- if (success) {
- LOG(DEBUG) << __func__ << ": writing of " << byteCount << " bytes into data MQ"
- << " succeeded; connected? " << isConnected;
- // Frames are provided and counted regardless of connection status.
- reply.fmqByteCount = byteCount;
- mFrameCount += byteCount / mFrameSize;
- if (isConnected) {
- reply.status = STATUS_OK;
- reply.observable.frames = mFrameCount;
- reply.observable.timeNs = ::android::elapsedRealtimeNano();
- } else {
- reply.status = STATUS_INVALID_OPERATION;
+ if (mState == StreamDescriptor::State::IDLE || mState == StreamDescriptor::State::ACTIVE ||
+ mState == StreamDescriptor::State::PAUSED ||
+ mState == StreamDescriptor::State::DRAINING) {
+ if (!read(command.fmqByteCount, &reply)) {
+ mState = StreamDescriptor::State::ERROR;
+ }
+ if (mState == StreamDescriptor::State::IDLE ||
+ mState == StreamDescriptor::State::PAUSED) {
+ mState = StreamDescriptor::State::ACTIVE;
+ } else if (mState == StreamDescriptor::State::DRAINING) {
+ // To simplify the reference code, we assume that the read operation
+ // has consumed all the data remaining in the hardware buffer.
+ // TODO: Provide parametrization on the duration of draining to test
+ // handling of commands during the 'DRAINING' state.
+ mState = StreamDescriptor::State::STANDBY;
}
} else {
- LOG(WARNING) << __func__ << ": writing of " << byteCount
- << " bytes of data to MQ failed";
- reply.status = STATUS_NOT_ENOUGH_DATA;
+ LOG(WARNING) << __func__ << ": BURST command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
}
- reply.latencyMs = Module::kLatencyMs;
+ } else if (command.code == StreamDescriptor::CommandCode::DRAIN && command.fmqByteCount == 0) {
+ LOG(DEBUG) << __func__ << ": received DRAIN 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::DRAINING;
+ } else {
+ LOG(WARNING) << __func__ << ": DRAIN command can not be handled in the state "
+ << 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) {
+ 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;
+ } else {
+ 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::FLUSH && command.fmqByteCount == 0) {
+ 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.
+ mState = StreamDescriptor::State::STANDBY;
+ } else {
+ 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::STANDBY &&
+ command.fmqByteCount == 0) {
+ 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::STANDBY;
+ } else {
+ LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
} else {
LOG(WARNING) << __func__ << ": invalid command (" << command.toString()
<< ") or count: " << command.fmqByteCount;
reply.status = STATUS_BAD_VALUE;
}
+ reply.state = mState;
LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
if (!mReplyMQ->writeBlocking(&reply, 1)) {
LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
+ mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
}
return Status::CONTINUE;
}
+bool StreamInWorkerLogic::read(size_t clientSize, StreamDescriptor::Reply* reply) {
+ // Can switch the state to ERROR if a driver error occurs.
+ const size_t byteCount = std::min({clientSize, mDataMQ->availableToWrite(), mDataBufferSize});
+ const bool isConnected = mIsConnected;
+ bool fatal = false;
+ // Simulate reading of data, or provide zeroes if the stream is not connected.
+ for (size_t i = 0; i < byteCount; ++i) {
+ using buffer_type = decltype(mDataBuffer)::element_type;
+ constexpr int kBufferValueRange = std::numeric_limits<buffer_type>::max() -
+ std::numeric_limits<buffer_type>::min() + 1;
+ mDataBuffer[i] = isConnected ? (std::rand() % kBufferValueRange) +
+ std::numeric_limits<buffer_type>::min()
+ : 0;
+ }
+ usleep(3000); // Simulate a blocking call into the driver.
+ // Set 'fatal = true' if a driver error occurs.
+ if (bool success = byteCount > 0 ? mDataMQ->write(&mDataBuffer[0], byteCount) : true; success) {
+ LOG(DEBUG) << __func__ << ": writing of " << byteCount << " bytes into data MQ"
+ << " succeeded; connected? " << isConnected;
+ // Frames are provided and counted regardless of connection status.
+ reply->fmqByteCount += byteCount;
+ mFrameCount += byteCount / mFrameSize;
+ populateReply(reply, isConnected);
+ } else {
+ LOG(WARNING) << __func__ << ": writing of " << byteCount << " bytes of data to MQ failed";
+ reply->status = STATUS_NOT_ENOUGH_DATA;
+ }
+ reply->latencyMs = Module::kLatencyMs;
+ return !fatal;
+}
+
const std::string StreamOutWorkerLogic::kThreadName = "writer";
StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
StreamDescriptor::Command command{};
if (!mCommandMQ->readBlocking(&command, 1)) {
LOG(ERROR) << __func__ << ": reading of command from MQ failed";
+ mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
}
StreamDescriptor::Reply reply{};
- if (command.code == StreamContext::COMMAND_EXIT &&
+ if (static_cast<int32_t>(command.code) == StreamContext::COMMAND_EXIT &&
command.fmqByteCount == 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::COMMAND_BURST && command.fmqByteCount >= 0) {
+ } else if (command.code == StreamDescriptor::CommandCode::START && command.fmqByteCount >= 0) {
+ LOG(DEBUG) << __func__ << ": received START read command";
+ switch (mState) {
+ case StreamDescriptor::State::STANDBY:
+ mState = StreamDescriptor::State::IDLE;
+ break;
+ case StreamDescriptor::State::PAUSED:
+ mState = StreamDescriptor::State::ACTIVE;
+ break;
+ case StreamDescriptor::State::DRAIN_PAUSED:
+ mState = StreamDescriptor::State::PAUSED;
+ break;
+ default:
+ LOG(WARNING) << __func__ << ": START command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ 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
<< " bytes";
- const size_t byteCount = std::min({static_cast<size_t>(command.fmqByteCount),
- mDataMQ->availableToRead(), mDataBufferSize});
- bool success = byteCount > 0 ? mDataMQ->read(&mDataBuffer[0], byteCount) : true;
- if (success) {
- const bool isConnected = mIsConnected;
- LOG(DEBUG) << __func__ << ": reading of " << byteCount << " bytes from data MQ"
- << " succeeded; connected? " << isConnected;
- // Frames are consumed and counted regardless of connection status.
- reply.fmqByteCount = byteCount;
- mFrameCount += byteCount / mFrameSize;
- if (isConnected) {
- reply.status = STATUS_OK;
- reply.observable.frames = mFrameCount;
- reply.observable.timeNs = ::android::elapsedRealtimeNano();
- } else {
- reply.status = STATUS_INVALID_OPERATION;
+ if (mState != StreamDescriptor::State::ERROR) { // BURST can be handled in all valid states
+ if (!write(command.fmqByteCount, &reply)) {
+ mState = StreamDescriptor::State::ERROR;
}
- usleep(3000); // Simulate a blocking call into the driver.
+ if (mState == StreamDescriptor::State::STANDBY ||
+ mState == StreamDescriptor::State::DRAIN_PAUSED) {
+ mState = StreamDescriptor::State::PAUSED;
+ } else if (mState == StreamDescriptor::State::IDLE ||
+ mState == StreamDescriptor::State::DRAINING) {
+ mState = StreamDescriptor::State::ACTIVE;
+ } // When in 'ACTIVE' and 'PAUSED' do not need to change the state.
} else {
- LOG(WARNING) << __func__ << ": reading of " << byteCount
- << " bytes of data from MQ failed";
- reply.status = STATUS_NOT_ENOUGH_DATA;
+ LOG(WARNING) << __func__ << ": BURST command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
}
- reply.latencyMs = Module::kLatencyMs;
+ } else if (command.code == StreamDescriptor::CommandCode::DRAIN && command.fmqByteCount == 0) {
+ LOG(DEBUG) << __func__ << ": received DRAIN write 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::IDLE;
+ // Since there is no actual hardware that would be draining the buffer,
+ // in order to simplify the reference code, we assume that draining
+ // happens instantly, thus skipping the 'DRAINING' state.
+ // TODO: Provide parametrization on the duration of draining to test
+ // handling of commands during the 'DRAINING' state.
+ } else {
+ LOG(WARNING) << __func__ << ": DRAIN 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 write 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::STANDBY;
+ } else {
+ LOG(WARNING) << __func__ << ": STANDBY command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ } else if (command.code == StreamDescriptor::CommandCode::PAUSE && command.fmqByteCount == 0) {
+ LOG(DEBUG) << __func__ << ": received PAUSE write command";
+ if (mState == StreamDescriptor::State::ACTIVE ||
+ mState == StreamDescriptor::State::DRAINING) {
+ populateReply(&reply, mIsConnected);
+ mState = mState == StreamDescriptor::State::ACTIVE
+ ? StreamDescriptor::State::PAUSED
+ : StreamDescriptor::State::DRAIN_PAUSED;
+ } else {
+ 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::FLUSH && command.fmqByteCount == 0) {
+ LOG(DEBUG) << __func__ << ": received FLUSH write command";
+ if (mState == StreamDescriptor::State::PAUSED ||
+ mState == StreamDescriptor::State::DRAIN_PAUSED) {
+ populateReply(&reply, mIsConnected);
+ mState = StreamDescriptor::State::IDLE;
+ } else {
+ LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
} else {
LOG(WARNING) << __func__ << ": invalid command (" << command.toString()
<< ") or count: " << command.fmqByteCount;
reply.status = STATUS_BAD_VALUE;
}
+ reply.state = mState;
LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
if (!mReplyMQ->writeBlocking(&reply, 1)) {
LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
+ mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
}
return Status::CONTINUE;
}
+bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* reply) {
+ const size_t readByteCount = mDataMQ->availableToRead();
+ // Amount of data that the HAL module is going to actually use.
+ const size_t byteCount = std::min({clientSize, readByteCount, mDataBufferSize});
+ bool fatal = false;
+ if (bool success = readByteCount > 0 ? mDataMQ->read(&mDataBuffer[0], readByteCount) : true) {
+ const bool isConnected = mIsConnected;
+ LOG(DEBUG) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
+ << " succeeded; connected? " << isConnected;
+ // Frames are consumed and counted regardless of connection status.
+ reply->fmqByteCount += byteCount;
+ mFrameCount += byteCount / mFrameSize;
+ populateReply(reply, isConnected);
+ usleep(3000); // Simulate a blocking call into the driver.
+ // Set 'fatal = true' if a driver error occurs.
+ } else {
+ LOG(WARNING) << __func__ << ": reading of " << readByteCount
+ << " bytes of data from MQ failed";
+ reply->status = STATUS_NOT_ENOUGH_DATA;
+ }
+ reply->latencyMs = Module::kLatencyMs;
+ return !fatal;
+}
+
template <class Metadata, class StreamWorker>
StreamCommon<Metadata, StreamWorker>::~StreamCommon() {
- if (!mIsClosed) {
+ if (!isClosed()) {
LOG(ERROR) << __func__ << ": stream was not closed prior to destruction, resource leak";
stopWorker();
// The worker and the context should clean up by themselves via destructors.
@@ -214,13 +403,13 @@
template <class Metadata, class StreamWorker>
ndk::ScopedAStatus StreamCommon<Metadata, StreamWorker>::close() {
LOG(DEBUG) << __func__;
- if (!mIsClosed) {
+ if (!isClosed()) {
stopWorker();
LOG(DEBUG) << __func__ << ": joining the worker thread...";
mWorker.stop();
LOG(DEBUG) << __func__ << ": worker thread joined";
mContext.reset();
- mIsClosed = true;
+ mWorker.setClosed();
return ndk::ScopedAStatus::ok();
} else {
LOG(ERROR) << __func__ << ": stream was already closed";
@@ -231,13 +420,14 @@
template <class Metadata, class StreamWorker>
void StreamCommon<Metadata, StreamWorker>::stopWorker() {
if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
- LOG(DEBUG) << __func__ << ": asking the worker to stop...";
+ LOG(DEBUG) << __func__ << ": asking the worker to exit...";
StreamDescriptor::Command cmd;
- cmd.code = StreamContext::COMMAND_EXIT;
+ cmd.code = StreamDescriptor::CommandCode(StreamContext::COMMAND_EXIT);
cmd.fmqByteCount = mContext.getInternalCommandCookie();
- // FIXME: This can block in the case when the client wrote a command
- // while the stream worker's cycle is not running. Need to revisit
- // when implementing standby and pause/resume.
+ // 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
+ // on the HAL side can cause a deadlock.
if (!commandMQ->writeBlocking(&cmd, 1)) {
LOG(ERROR) << __func__ << ": failed to write exit command to the MQ";
}
@@ -248,7 +438,7 @@
template <class Metadata, class StreamWorker>
ndk::ScopedAStatus StreamCommon<Metadata, StreamWorker>::updateMetadata(const Metadata& metadata) {
LOG(DEBUG) << __func__;
- if (!mIsClosed) {
+ if (!isClosed()) {
mMetadata = metadata;
return ndk::ScopedAStatus::ok();
}
diff --git a/audio/aidl/default/config/audio_policy_configuration.xsd b/audio/aidl/default/config/audio_policy_configuration.xsd
new file mode 100644
index 0000000..823b217
--- /dev/null
+++ b/audio/aidl/default/config/audio_policy_configuration.xsd
@@ -0,0 +1,827 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<xs:schema version="2.0"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <!-- List the config versions supported by audio policy. -->
+ <xs:simpleType name="version">
+ <xs:restriction base="xs:decimal">
+ <xs:enumeration value="7.0"/>
+ <xs:enumeration value="7.1"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="halVersion">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Version of the interface the hal implements. Note that this
+ relates to legacy HAL API versions since HIDL APIs are versioned
+ using other mechanisms.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:decimal">
+ <!-- List of HAL versions supported by the framework. -->
+ <xs:enumeration value="2.0"/>
+ <xs:enumeration value="3.0"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:element name="audioPolicyConfiguration">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="globalConfiguration" type="globalConfiguration"/>
+ <xs:element name="modules" type="modules" maxOccurs="unbounded"/>
+ <xs:element name="volumes" type="volumes" maxOccurs="unbounded"/>
+ <xs:element name="surroundSound" type="surroundSound" minOccurs="0" />
+ </xs:sequence>
+ <xs:attribute name="version" type="version"/>
+ </xs:complexType>
+ <xs:key name="moduleNameKey">
+ <xs:selector xpath="modules/module"/>
+ <xs:field xpath="@name"/>
+ </xs:key>
+ <xs:unique name="volumeTargetUniqueness">
+ <xs:selector xpath="volumes/volume"/>
+ <xs:field xpath="@stream"/>
+ <xs:field xpath="@deviceCategory"/>
+ </xs:unique>
+ <xs:key name="volumeCurveNameKey">
+ <xs:selector xpath="volumes/reference"/>
+ <xs:field xpath="@name"/>
+ </xs:key>
+ <xs:keyref name="volumeCurveRef" refer="volumeCurveNameKey">
+ <xs:selector xpath="volumes/volume"/>
+ <xs:field xpath="@ref"/>
+ </xs:keyref>
+ </xs:element>
+ <xs:complexType name="globalConfiguration">
+ <xs:attribute name="speaker_drc_enabled" type="xs:boolean" use="required"/>
+ <xs:attribute name="call_screen_mode_supported" type="xs:boolean" use="optional"/>
+ <xs:attribute name="engine_library" type="engineSuffix" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="modules">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ There should be one section per audio HW module present on the platform.
+ Each <module/> contains two mandatory tags: “halVersion” and “name”.
+ The module "name" is the same as in previous .conf file.
+ Each module must contain the following sections:
+ - <devicePorts/>: a list of device descriptors for all
+ input and output devices accessible via this module.
+ This contains both permanently attached devices and removable devices.
+ - <mixPorts/>: listing all output and input streams exposed by the audio HAL
+ - <routes/>: list of possible connections between input
+ and output devices or between stream and devices.
+ A <route/> is defined by a set of 3 attributes:
+ -"type": mux|mix means all sources are mutual exclusive (mux) or can be mixed (mix)
+ -"sink": the sink involved in this route
+ -"sources": all the sources than can be connected to the sink via this route
+ - <attachedDevices/>: permanently attached devices.
+ The attachedDevices section is a list of devices names.
+ Their names correspond to device names defined in "devicePorts" section.
+ - <defaultOutputDevice/> is the device to be used when no policy rule applies
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="module" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="attachedDevices" type="attachedDevices" minOccurs="0">
+ <xs:unique name="attachedDevicesUniqueness">
+ <xs:selector xpath="item"/>
+ <xs:field xpath="."/>
+ </xs:unique>
+ </xs:element>
+ <xs:element name="defaultOutputDevice" type="xs:token" minOccurs="0"/>
+ <xs:element name="mixPorts" type="mixPorts" minOccurs="0"/>
+ <xs:element name="devicePorts" type="devicePorts" minOccurs="0"/>
+ <xs:element name="routes" type="routes" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="halVersion" type="halVersion" use="required"/>
+ </xs:complexType>
+ <xs:unique name="mixPortNameUniqueness">
+ <xs:selector xpath="mixPorts/mixPort"/>
+ <xs:field xpath="@name"/>
+ </xs:unique>
+ <xs:key name="devicePortNameKey">
+ <xs:selector xpath="devicePorts/devicePort"/>
+ <xs:field xpath="@tagName"/>
+ </xs:key>
+ <xs:unique name="devicePortUniqueness">
+ <xs:selector xpath="devicePorts/devicePort"/>
+ <xs:field xpath="@type"/>
+ <xs:field xpath="@address"/>
+ </xs:unique>
+ <xs:keyref name="defaultOutputDeviceRef" refer="devicePortNameKey">
+ <xs:selector xpath="defaultOutputDevice"/>
+ <xs:field xpath="."/>
+ </xs:keyref>
+ <xs:keyref name="attachedDeviceRef" refer="devicePortNameKey">
+ <xs:selector xpath="attachedDevices/item"/>
+ <xs:field xpath="."/>
+ </xs:keyref>
+ <!-- The following 3 constraints try to make sure each sink port
+ is reference in one an only one route. -->
+ <xs:key name="routeSinkKey">
+ <!-- predicate [@type='sink'] does not work in xsd 1.0 -->
+ <xs:selector xpath="devicePorts/devicePort|mixPorts/mixPort"/>
+ <xs:field xpath="@tagName|@name"/>
+ </xs:key>
+ <xs:keyref name="routeSinkRef" refer="routeSinkKey">
+ <xs:selector xpath="routes/route"/>
+ <xs:field xpath="@sink"/>
+ </xs:keyref>
+ <xs:unique name="routeUniqueness">
+ <xs:selector xpath="routes/route"/>
+ <xs:field xpath="@sink"/>
+ </xs:unique>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="attachedDevices">
+ <xs:sequence>
+ <xs:element name="item" type="xs:token" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="audioInOutFlag">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ The flags indicate suggested stream attributes supported by the profile.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_DIRECT" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_PRIMARY" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_FAST" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_DEEP_BUFFER" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_NON_BLOCKING" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_HW_AV_SYNC" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_TTS" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_RAW" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_SYNC" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_DIRECT_PCM" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_MMAP_NOIRQ" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_VOIP_RX" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_INCALL_MUSIC" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_SPATIALIZER" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_ULTRASOUND" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_FAST" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_HW_HOTWORD" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_RAW" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_SYNC" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_MMAP_NOIRQ" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_VOIP_TX" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_HW_AV_SYNC" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_DIRECT" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_ULTRASOUND" />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="audioInOutFlags">
+ <xs:list itemType="audioInOutFlag" />
+ </xs:simpleType>
+ <xs:simpleType name="role">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="sink"/>
+ <xs:enumeration value="source"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="mixPorts">
+ <xs:sequence>
+ <xs:element name="mixPort" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="profile" type="profile" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="gains" type="gains" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:token" use="required"/>
+ <xs:attribute name="role" type="role" use="required"/>
+ <xs:attribute name="flags" type="audioInOutFlags"/>
+ <xs:attribute name="maxOpenCount" type="xs:unsignedInt"/>
+ <xs:attribute name="maxActiveCount" type="xs:unsignedInt"/>
+ <xs:attribute name="preferredUsage" type="audioUsageList">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ When choosing the mixPort of an audio track, the audioPolicy
+ first considers the mixPorts with a preferredUsage including
+ the track AudioUsage preferred .
+ If non support the track format, the other mixPorts are considered.
+ Eg: a <mixPort preferredUsage="AUDIO_USAGE_MEDIA" /> will receive
+ the audio of all apps playing with a MEDIA usage.
+ It may receive audio from ALARM if there are no audio compatible
+ <mixPort preferredUsage="AUDIO_USAGE_ALARM" />.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="recommendedMuteDurationMs" type="xs:unsignedInt"/>
+ </xs:complexType>
+ <xs:unique name="mixPortProfileUniqueness">
+ <xs:selector xpath="profile"/>
+ <xs:field xpath="format"/>
+ <xs:field xpath="samplingRate"/>
+ <xs:field xpath="channelMasks"/>
+ </xs:unique>
+ <xs:unique name="mixPortGainUniqueness">
+ <xs:selector xpath="gains/gain"/>
+ <xs:field xpath="@name"/>
+ </xs:unique>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="audioDevice">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_DEVICE_NONE"/>
+
+ <xs:enumeration value="AUDIO_DEVICE_OUT_EARPIECE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADPHONE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI_EARC"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_DIGITAL"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_USB_ACCESSORY"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_USB_DEVICE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_REMOTE_SUBMIX"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_TELEPHONY_TX"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_LINE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI_ARC"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_SPDIF"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_FM"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_LINE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER_SAFE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_IP"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BUS"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_PROXY"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_USB_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_HEARING_AID"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_ECHO_CANCELLER"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLE_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLE_SPEAKER"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLE_BROADCAST"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_DEFAULT"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_STUB"/>
+
+ <xs:enumeration value="AUDIO_DEVICE_IN_COMMUNICATION"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_AMBIENT"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BUILTIN_MIC"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_WIRED_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_HDMI"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_AUX_DIGITAL"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_VOICE_CALL"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_TELEPHONY_RX"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BACK_MIC"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_REMOTE_SUBMIX"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_USB_ACCESSORY"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_USB_DEVICE"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_FM_TUNER"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_TV_TUNER"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_LINE"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_SPDIF"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_A2DP"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_LOOPBACK"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_IP"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BUS"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_PROXY"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_USB_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_BLE"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_HDMI_ARC"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_HDMI_EARC"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_ECHO_REFERENCE"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BLE_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_DEFAULT"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_STUB"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="vendorExtension">
+ <!-- Vendor extension names must be prefixed by "VX_" to distinguish them from
+ AOSP values. Vendors must namespace their names to avoid conflicts. The
+ namespace part must only use capital latin characters and decimal digits and
+ consist of at least 3 characters. The part of the extension name after the
+ namespace may in addition include underscores. Example for a hypothetical
+ Google virtual reality device:
+
+ <devicePort tagName="VR" type="VX_GOOGLE_VR" role="sink" />
+ -->
+ <xs:restriction base="xs:string">
+ <xs:pattern value="VX_[A-Z0-9]{3,}_[_A-Z0-9]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="extendableAudioDevice">
+ <xs:union memberTypes="audioDevice vendorExtension"/>
+ </xs:simpleType>
+ <xs:simpleType name="audioFormat">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_FORMAT_DEFAULT" />
+ <xs:enumeration value="AUDIO_FORMAT_PCM_16_BIT" />
+ <xs:enumeration value="AUDIO_FORMAT_PCM_8_BIT"/>
+ <xs:enumeration value="AUDIO_FORMAT_PCM_32_BIT"/>
+ <xs:enumeration value="AUDIO_FORMAT_PCM_8_24_BIT"/>
+ <xs:enumeration value="AUDIO_FORMAT_PCM_FLOAT"/>
+ <xs:enumeration value="AUDIO_FORMAT_PCM_24_BIT_PACKED"/>
+ <xs:enumeration value="AUDIO_FORMAT_MP3"/>
+ <xs:enumeration value="AUDIO_FORMAT_AMR_NB"/>
+ <xs:enumeration value="AUDIO_FORMAT_AMR_WB"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_MAIN"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LC"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_SSR"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LTP"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_HE_V1"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_SCALABLE"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ERLC"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LD"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_HE_V2"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ELD"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_XHE"/>
+ <xs:enumeration value="AUDIO_FORMAT_HE_AAC_V1"/>
+ <xs:enumeration value="AUDIO_FORMAT_HE_AAC_V2"/>
+ <xs:enumeration value="AUDIO_FORMAT_VORBIS"/>
+ <xs:enumeration value="AUDIO_FORMAT_OPUS"/>
+ <xs:enumeration value="AUDIO_FORMAT_AC3"/>
+ <xs:enumeration value="AUDIO_FORMAT_E_AC3"/>
+ <xs:enumeration value="AUDIO_FORMAT_E_AC3_JOC"/>
+ <xs:enumeration value="AUDIO_FORMAT_DTS"/>
+ <xs:enumeration value="AUDIO_FORMAT_DTS_HD"/>
+ <xs:enumeration value="AUDIO_FORMAT_IEC61937"/>
+ <xs:enumeration value="AUDIO_FORMAT_DOLBY_TRUEHD"/>
+ <xs:enumeration value="AUDIO_FORMAT_EVRC"/>
+ <xs:enumeration value="AUDIO_FORMAT_EVRCB"/>
+ <xs:enumeration value="AUDIO_FORMAT_EVRCWB"/>
+ <xs:enumeration value="AUDIO_FORMAT_EVRCNW"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADIF"/>
+ <xs:enumeration value="AUDIO_FORMAT_WMA"/>
+ <xs:enumeration value="AUDIO_FORMAT_WMA_PRO"/>
+ <xs:enumeration value="AUDIO_FORMAT_AMR_WB_PLUS"/>
+ <xs:enumeration value="AUDIO_FORMAT_MP2"/>
+ <xs:enumeration value="AUDIO_FORMAT_QCELP"/>
+ <xs:enumeration value="AUDIO_FORMAT_DSD"/>
+ <xs:enumeration value="AUDIO_FORMAT_FLAC"/>
+ <xs:enumeration value="AUDIO_FORMAT_ALAC"/>
+ <xs:enumeration value="AUDIO_FORMAT_APE"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_MAIN"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_LC"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_SSR"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_LTP"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_HE_V1"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_SCALABLE"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_ERLC"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_LD"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_HE_V2"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_ELD"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_XHE"/>
+ <xs:enumeration value="AUDIO_FORMAT_SBC"/>
+ <xs:enumeration value="AUDIO_FORMAT_APTX"/>
+ <xs:enumeration value="AUDIO_FORMAT_APTX_HD"/>
+ <xs:enumeration value="AUDIO_FORMAT_AC4"/>
+ <xs:enumeration value="AUDIO_FORMAT_LDAC"/>
+ <xs:enumeration value="AUDIO_FORMAT_MAT"/>
+ <xs:enumeration value="AUDIO_FORMAT_MAT_1_0"/>
+ <xs:enumeration value="AUDIO_FORMAT_MAT_2_0"/>
+ <xs:enumeration value="AUDIO_FORMAT_MAT_2_1"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LATM"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LATM_LC"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LATM_HE_V1"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LATM_HE_V2"/>
+ <xs:enumeration value="AUDIO_FORMAT_CELT"/>
+ <xs:enumeration value="AUDIO_FORMAT_APTX_ADAPTIVE"/>
+ <xs:enumeration value="AUDIO_FORMAT_LHDC"/>
+ <xs:enumeration value="AUDIO_FORMAT_LHDC_LL"/>
+ <xs:enumeration value="AUDIO_FORMAT_APTX_TWSP"/>
+ <xs:enumeration value="AUDIO_FORMAT_LC3"/>
+ <xs:enumeration value="AUDIO_FORMAT_MPEGH_BL_L3"/>
+ <xs:enumeration value="AUDIO_FORMAT_MPEGH_BL_L4"/>
+ <xs:enumeration value="AUDIO_FORMAT_MPEGH_LC_L3"/>
+ <xs:enumeration value="AUDIO_FORMAT_MPEGH_LC_L4"/>
+ <xs:enumeration value="AUDIO_FORMAT_IEC60958"/>
+ <xs:enumeration value="AUDIO_FORMAT_DTS_UHD"/>
+ <xs:enumeration value="AUDIO_FORMAT_DRA"/>
+ <xs:enumeration value="AUDIO_FORMAT_APTX_ADAPTIVE_QLEA"/>
+ <xs:enumeration value="AUDIO_FORMAT_APTX_ADAPTIVE_R4"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="extendableAudioFormat">
+ <xs:union memberTypes="audioFormat vendorExtension"/>
+ </xs:simpleType>
+ <xs:simpleType name="audioUsage">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Audio usage specifies the intended use case for the sound being played.
+ Please consult frameworks/base/media/java/android/media/AudioAttributes.java
+ for the description of each value.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_USAGE_UNKNOWN" />
+ <xs:enumeration value="AUDIO_USAGE_MEDIA" />
+ <xs:enumeration value="AUDIO_USAGE_VOICE_COMMUNICATION" />
+ <xs:enumeration value="AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING" />
+ <xs:enumeration value="AUDIO_USAGE_ALARM" />
+ <xs:enumeration value="AUDIO_USAGE_NOTIFICATION" />
+ <xs:enumeration value="AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE" />
+ <xs:enumeration value="AUDIO_USAGE_NOTIFICATION_EVENT" />
+ <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY" />
+ <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE" />
+ <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_SONIFICATION" />
+ <xs:enumeration value="AUDIO_USAGE_GAME" />
+ <xs:enumeration value="AUDIO_USAGE_VIRTUAL_SOURCE" />
+ <xs:enumeration value="AUDIO_USAGE_ASSISTANT" />
+ <xs:enumeration value="AUDIO_USAGE_CALL_ASSISTANT" />
+ <xs:enumeration value="AUDIO_USAGE_EMERGENCY" />
+ <xs:enumeration value="AUDIO_USAGE_SAFETY" />
+ <xs:enumeration value="AUDIO_USAGE_VEHICLE_STATUS" />
+ <xs:enumeration value="AUDIO_USAGE_ANNOUNCEMENT" />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="audioUsageList">
+ <xs:list itemType="audioUsage"/>
+ </xs:simpleType>
+ <xs:simpleType name="audioContentType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Audio content type expresses the general category of the content.
+ Please consult frameworks/base/media/java/android/media/AudioAttributes.java
+ for the description of each value.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_CONTENT_TYPE_UNKNOWN"/>
+ <xs:enumeration value="AUDIO_CONTENT_TYPE_SPEECH"/>
+ <xs:enumeration value="AUDIO_CONTENT_TYPE_MUSIC"/>
+ <xs:enumeration value="AUDIO_CONTENT_TYPE_MOVIE"/>
+ <xs:enumeration value="AUDIO_CONTENT_TYPE_SONIFICATION"/>
+ <xs:enumeration value="AUDIO_CONTENT_TYPE_ULTRASOUND"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="samplingRates">
+ <xs:list itemType="xs:nonNegativeInteger" />
+ </xs:simpleType>
+ <xs:simpleType name="audioChannelMask">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Audio channel mask specifies presence of particular channels.
+ There are two representations:
+ - representation position (traditional discrete channel specification,
+ e.g. "left", "right");
+ - indexed (this is similar to "tracks" in audio mixing, channels
+ are represented using numbers).
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_CHANNEL_NONE"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_MONO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_2POINT1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_TRI"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_TRI_BACK"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_3POINT1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_2POINT0POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_2POINT1POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_3POINT0POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_3POINT1POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_QUAD"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_QUAD_BACK"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_QUAD_SIDE"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_SURROUND"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_PENTA"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1_BACK"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1POINT4"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_6POINT1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1POINT4"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_9POINT1POINT4"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_9POINT1POINT6"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_13POINT_360RA"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_22POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_MONO_HAPTIC_A"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_HAPTIC_AB"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_MONO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_STEREO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_FRONT_BACK"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_6"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_2POINT0POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_2POINT1POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_3POINT0POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_3POINT1POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_5POINT1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_VOICE_CALL_MONO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_3"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_4"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_5"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_6"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_7"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_8"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_9"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_10"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_11"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_12"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_13"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_14"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_15"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_16"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_17"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_18"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_19"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_20"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_21"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_22"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_23"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_24"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="channelMasks">
+ <xs:list itemType="audioChannelMask" />
+ </xs:simpleType>
+ <xs:simpleType name="audioEncapsulationType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_ENCAPSULATION_TYPE_NONE"/>
+ <xs:enumeration value="AUDIO_ENCAPSULATION_TYPE_IEC61937"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="profile">
+ <xs:attribute name="name" type="xs:token" use="optional"/>
+ <xs:attribute name="format" type="extendableAudioFormat" use="optional"/>
+ <xs:attribute name="samplingRates" type="samplingRates" use="optional"/>
+ <xs:attribute name="channelMasks" type="channelMasks" use="optional"/>
+ <xs:attribute name="encapsulationType" type="audioEncapsulationType" use="optional"/>
+ </xs:complexType>
+ <xs:simpleType name="audioGainMode">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_GAIN_MODE_JOINT"/>
+ <xs:enumeration value="AUDIO_GAIN_MODE_CHANNELS"/>
+ <xs:enumeration value="AUDIO_GAIN_MODE_RAMP"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="audioGainModeMaskUnrestricted">
+ <xs:list itemType="audioGainMode" />
+ </xs:simpleType>
+ <xs:simpleType name='audioGainModeMask'>
+ <xs:restriction base='audioGainModeMaskUnrestricted'>
+ <xs:minLength value='1' />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="gains">
+ <xs:sequence>
+ <xs:element name="gain" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:token" use="required"/>
+ <xs:attribute name="mode" type="audioGainModeMask" use="required"/>
+ <xs:attribute name="channel_mask" type="audioChannelMask" use="optional"/>
+ <xs:attribute name="minValueMB" type="xs:int" use="optional"/>
+ <xs:attribute name="maxValueMB" type="xs:int" use="optional"/>
+ <xs:attribute name="defaultValueMB" type="xs:int" use="optional"/>
+ <xs:attribute name="stepValueMB" type="xs:int" use="optional"/>
+ <xs:attribute name="minRampMs" type="xs:int" use="optional"/>
+ <xs:attribute name="maxRampMs" type="xs:int" use="optional"/>
+ <xs:attribute name="useForVolume" type="xs:boolean" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="devicePorts">
+ <xs:sequence>
+ <xs:element name="devicePort" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="profile" type="profile" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="gains" type="gains" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="tagName" type="xs:token" use="required"/>
+ <xs:attribute name="type" type="extendableAudioDevice" use="required"/>
+ <xs:attribute name="role" type="role" use="required"/>
+ <xs:attribute name="address" type="xs:string" use="optional" default=""/>
+ <!-- Note that XSD 1.0 can not check that a type only has one default. -->
+ <xs:attribute name="default" type="xs:boolean" use="optional">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ The default device will be used if multiple have the same type
+ and no explicit route request exists for a specific device of
+ that type.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="encodedFormats" type="audioFormatsList" use="optional"
+ default="" />
+ </xs:complexType>
+ <xs:unique name="devicePortProfileUniqueness">
+ <xs:selector xpath="profile"/>
+ <xs:field xpath="format"/>
+ <xs:field xpath="samplingRate"/>
+ <xs:field xpath="channelMasks"/>
+ </xs:unique>
+ <xs:unique name="devicePortGainUniqueness">
+ <xs:selector xpath="gains/gain"/>
+ <xs:field xpath="@name"/>
+ </xs:unique>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="mixType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="mix"/>
+ <xs:enumeration value="mux"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="routes">
+ <xs:sequence>
+ <xs:element name="route" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ List all available sources for a given sink.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="type" type="mixType" use="required"/>
+ <xs:attribute name="sink" type="xs:string" use="required"/>
+ <xs:attribute name="sources" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="volumes">
+ <xs:sequence>
+ <xs:element name="volume" type="volume" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="reference" type="reference" minOccurs="0" maxOccurs="unbounded">
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <!-- TODO: Always require a ref for better xsd validations.
+ Currently a volume could have no points nor ref
+ as it can not be forbidden by xsd 1.0.-->
+ <xs:simpleType name="volumePoint">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Comma separated pair of number.
+ The fist one is the framework level (between 0 and 100).
+ The second one is the volume to send to the HAL.
+ The framework will interpolate volumes not specified.
+ Their MUST be at least 2 points specified.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:pattern value="([0-9]{1,2}|100),-?[0-9]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="audioStreamType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Audio stream type describing the intended use case of a stream.
+ Please consult frameworks/base/media/java/android/media/AudioSystem.java
+ for the description of each value.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_STREAM_VOICE_CALL"/>
+ <xs:enumeration value="AUDIO_STREAM_SYSTEM"/>
+ <xs:enumeration value="AUDIO_STREAM_RING"/>
+ <xs:enumeration value="AUDIO_STREAM_MUSIC"/>
+ <xs:enumeration value="AUDIO_STREAM_ALARM"/>
+ <xs:enumeration value="AUDIO_STREAM_NOTIFICATION"/>
+ <xs:enumeration value="AUDIO_STREAM_BLUETOOTH_SCO"/>
+ <xs:enumeration value="AUDIO_STREAM_ENFORCED_AUDIBLE"/>
+ <xs:enumeration value="AUDIO_STREAM_DTMF"/>
+ <xs:enumeration value="AUDIO_STREAM_TTS"/>
+ <xs:enumeration value="AUDIO_STREAM_ACCESSIBILITY"/>
+ <xs:enumeration value="AUDIO_STREAM_ASSISTANT"/>
+ <xs:enumeration value="AUDIO_STREAM_REROUTING"/>
+ <xs:enumeration value="AUDIO_STREAM_PATCH"/>
+ <xs:enumeration value="AUDIO_STREAM_CALL_ASSISTANT"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="audioSource">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ An audio source defines the intended use case for the sound being recorded.
+ Please consult frameworks/base/media/java/android/media/MediaRecorder.java
+ for the description of each value.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_SOURCE_DEFAULT"/>
+ <xs:enumeration value="AUDIO_SOURCE_MIC"/>
+ <xs:enumeration value="AUDIO_SOURCE_VOICE_UPLINK"/>
+ <xs:enumeration value="AUDIO_SOURCE_VOICE_DOWNLINK"/>
+ <xs:enumeration value="AUDIO_SOURCE_VOICE_CALL"/>
+ <xs:enumeration value="AUDIO_SOURCE_CAMCORDER"/>
+ <xs:enumeration value="AUDIO_SOURCE_VOICE_RECOGNITION"/>
+ <xs:enumeration value="AUDIO_SOURCE_VOICE_COMMUNICATION"/>
+ <xs:enumeration value="AUDIO_SOURCE_REMOTE_SUBMIX"/>
+ <xs:enumeration value="AUDIO_SOURCE_UNPROCESSED"/>
+ <xs:enumeration value="AUDIO_SOURCE_VOICE_PERFORMANCE"/>
+ <xs:enumeration value="AUDIO_SOURCE_ECHO_REFERENCE"/>
+ <xs:enumeration value="AUDIO_SOURCE_FM_TUNER"/>
+ <xs:enumeration value="AUDIO_SOURCE_HOTWORD"/>
+ <xs:enumeration value="AUDIO_SOURCE_ULTRASOUND"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!-- Enum values of device_category from Volume.h. -->
+ <xs:simpleType name="deviceCategory">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="DEVICE_CATEGORY_HEADSET"/>
+ <xs:enumeration value="DEVICE_CATEGORY_SPEAKER"/>
+ <xs:enumeration value="DEVICE_CATEGORY_EARPIECE"/>
+ <xs:enumeration value="DEVICE_CATEGORY_EXT_MEDIA"/>
+ <xs:enumeration value="DEVICE_CATEGORY_HEARING_AID"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="volume">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Volume section defines a volume curve for a given use case and device category.
+ It contains a list of points of this curve expressing the attenuation in Millibels
+ for a given volume index from 0 to 100.
+ <volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_SPEAKER">
+ <point>0,-9600</point>
+ <point>100,0</point>
+ </volume>
+
+ It may also reference a reference/@name to avoid duplicating curves.
+ <volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_SPEAKER"
+ ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+ <reference name="DEFAULT_MEDIA_VOLUME_CURVE">
+ <point>0,-9600</point>
+ <point>100,0</point>
+ </reference>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="point" type="volumePoint" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="stream" type="audioStreamType"/>
+ <xs:attribute name="deviceCategory" type="deviceCategory"/>
+ <xs:attribute name="ref" type="xs:token" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="reference">
+ <xs:sequence>
+ <xs:element name="point" type="volumePoint" minOccurs="2" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:token" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="surroundSound">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Surround Sound section provides configuration related to handling of
+ multi-channel formats.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="formats" type="surroundFormats"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="audioFormatsList">
+ <xs:list itemType="extendableAudioFormat" />
+ </xs:simpleType>
+ <xs:complexType name="surroundFormats">
+ <xs:sequence>
+ <xs:element name="format" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="extendableAudioFormat" use="required"/>
+ <xs:attribute name="subformats" type="audioFormatsList" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="engineSuffix">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="default"/>
+ <xs:enumeration value="configurable"/>
+ </xs:restriction>
+ </xs:simpleType>
+</xs:schema>
diff --git a/audio/aidl/default/equalizer/Android.bp b/audio/aidl/default/equalizer/Android.bp
index 69d7450..8de6b1a 100644
--- a/audio/aidl/default/equalizer/Android.bp
+++ b/audio/aidl/default/equalizer/Android.bp
@@ -31,7 +31,7 @@
"latest_android_hardware_audio_effect_ndk_shared",
],
srcs: [
- "Equalizer.cpp",
+ "EqualizerSw.cpp",
":effectCommonFile",
],
visibility: [
diff --git a/audio/aidl/default/equalizer/Equalizer.cpp b/audio/aidl/default/equalizer/EqualizerSw.cpp
similarity index 99%
rename from audio/aidl/default/equalizer/Equalizer.cpp
rename to audio/aidl/default/equalizer/EqualizerSw.cpp
index 0e07d39..32c3969 100644
--- a/audio/aidl/default/equalizer/Equalizer.cpp
+++ b/audio/aidl/default/equalizer/EqualizerSw.cpp
@@ -23,7 +23,7 @@
#include <android-base/logging.h>
#include <fmq/AidlMessageQueue.h>
-#include "equalizer-impl/EqualizerSw.h"
+#include "EqualizerSw.h"
using aidl::android::hardware::audio::effect::EqualizerSw;
using aidl::android::hardware::audio::effect::EqualizerSwImplUUID;
diff --git a/audio/aidl/default/include/equalizer-impl/EqualizerSw.h b/audio/aidl/default/equalizer/EqualizerSw.h
similarity index 97%
rename from audio/aidl/default/include/equalizer-impl/EqualizerSw.h
rename to audio/aidl/default/equalizer/EqualizerSw.h
index dad03e1..8762f5d 100644
--- a/audio/aidl/default/include/equalizer-impl/EqualizerSw.h
+++ b/audio/aidl/default/equalizer/EqualizerSw.h
@@ -26,7 +26,7 @@
namespace aidl::android::hardware::audio::effect {
-class EqualizerSwContext : public EffectContext {
+class EqualizerSwContext final : public EffectContext {
public:
EqualizerSwContext(int statusDepth, const Parameter::Common& common)
: EffectContext(statusDepth, common) {
@@ -78,7 +78,7 @@
// Add equalizer specific context for processing here
};
-class EqualizerSw : public EffectImpl {
+class EqualizerSw final : public EffectImpl {
public:
EqualizerSw() { LOG(DEBUG) << __func__; }
~EqualizerSw() {
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 488edf1..539fa8b 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -54,8 +54,10 @@
int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
DataMQ;
- // Ensure that this value is not used by any of StreamDescriptor.COMMAND_*
- static constexpr int COMMAND_EXIT = -1;
+ // Ensure that this value is not used by any of StreamDescriptor.CommandCode enums
+ static constexpr int32_t COMMAND_EXIT = -1;
+ // Ensure that this value is not used by any of StreamDescriptor.State enums
+ static constexpr int32_t STATE_CLOSED = -1;
StreamContext() = default;
StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
@@ -99,6 +101,10 @@
class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic {
public:
+ bool isClosed() const {
+ return static_cast<int32_t>(mState.load()) == StreamContext::STATE_CLOSED;
+ }
+ void setClosed() { mState = static_cast<StreamDescriptor::State>(StreamContext::STATE_CLOSED); }
void setIsConnected(bool connected) { mIsConnected = connected; }
protected:
@@ -109,9 +115,12 @@
mReplyMQ(context.getReplyMQ()),
mDataMQ(context.getDataMQ()) {}
std::string init() override;
+ void populateReply(StreamDescriptor::Reply* reply, bool isConnected) const;
- // Used both by the main and worker threads.
+ // Atomic fields are used both by the main and worker threads.
std::atomic<bool> mIsConnected = false;
+ static_assert(std::atomic<StreamDescriptor::State>::is_always_lock_free);
+ std::atomic<StreamDescriptor::State> mState = StreamDescriptor::State::STANDBY;
// All fields are used on the worker thread only.
const int mInternalCommandCookie;
const size_t mFrameSize;
@@ -132,6 +141,9 @@
protected:
Status cycle() override;
+
+ private:
+ bool read(size_t clientSize, StreamDescriptor::Reply* reply);
};
using StreamInWorker = ::android::hardware::audio::common::StreamWorker<StreamInWorkerLogic>;
@@ -143,6 +155,9 @@
protected:
Status cycle() override;
+
+ private:
+ bool write(size_t clientSize, StreamDescriptor::Reply* reply);
};
using StreamOutWorker = ::android::hardware::audio::common::StreamWorker<StreamOutWorkerLogic>;
@@ -155,7 +170,7 @@
? ndk::ScopedAStatus::ok()
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
- bool isClosed() const { return mIsClosed; }
+ bool isClosed() const { return mWorker.isClosed(); }
void setIsConnected(bool connected) { mWorker.setIsConnected(connected); }
ndk::ScopedAStatus updateMetadata(const Metadata& metadata);
@@ -168,9 +183,6 @@
Metadata mMetadata;
StreamContext mContext;
StreamWorker mWorker;
- // This variable is checked in the destructor which can be called on an arbitrary Binder thread,
- // thus we need to ensure that any changes made by other threads are sequentially consistent.
- std::atomic<bool> mIsClosed = false;
};
class StreamIn
diff --git a/audio/aidl/vts/TestUtils.h b/audio/aidl/vts/TestUtils.h
index 2fc109a..5e4d56a 100644
--- a/audio/aidl/vts/TestUtils.h
+++ b/audio/aidl/vts/TestUtils.h
@@ -21,15 +21,6 @@
#include <android/binder_auto_utils.h>
#include <gtest/gtest_pred_impl.h>
-namespace ndk {
-
-std::ostream& operator<<(std::ostream& str, const ScopedAStatus& status) {
- str << status.getDescription();
- return str;
-}
-
-} // namespace ndk
-
namespace android::hardware::audio::common::testing {
namespace detail {
diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
index 2381200..b415da4 100644
--- a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
@@ -34,6 +34,7 @@
#include <aidl/android/media/audio/common/AudioIoFlags.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
#include <android-base/chrono_utils.h>
+#include <android/binder_enums.h>
#include <fmq/AidlMessageQueue.h>
#include "AudioHalBinderServiceUtil.h"
@@ -69,6 +70,7 @@
using android::hardware::audio::common::isBitPositionFlagSet;
using android::hardware::audio::common::StreamLogic;
using android::hardware::audio::common::StreamWorker;
+using ndk::enum_range;
using ndk::ScopedAStatus;
template <typename T>
@@ -171,25 +173,26 @@
AudioPortConfig mConfig;
};
-class AudioCoreModule : public testing::TestWithParam<std::string> {
+// Can be used as a base for any test here, does not depend on the fixture GTest parameters.
+class AudioCoreModuleBase {
public:
// The default buffer size is used mostly for negative tests.
static constexpr int kDefaultBufferSizeFrames = 256;
- void SetUp() override {
- ASSERT_NO_FATAL_FAILURE(ConnectToService());
+ void SetUpImpl(const std::string& moduleName) {
+ ASSERT_NO_FATAL_FAILURE(ConnectToService(moduleName));
debug.flags().simulateDeviceConnections = true;
ASSERT_NO_FATAL_FAILURE(debug.SetUp(module.get()));
}
- void TearDown() override {
+ void TearDownImpl() {
if (module != nullptr) {
EXPECT_IS_OK(module->setModuleDebug(ModuleDebug{}));
}
}
- void ConnectToService() {
- module = IModule::fromBinder(binderUtil.connectToService(GetParam()));
+ void ConnectToService(const std::string& moduleName) {
+ module = IModule::fromBinder(binderUtil.connectToService(moduleName));
ASSERT_NE(module, nullptr);
}
@@ -269,6 +272,13 @@
WithDebugFlags debug;
};
+class AudioCoreModule : public AudioCoreModuleBase, public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpImpl(GetParam())); }
+
+ void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownImpl()); }
+};
+
class WithDevicePortConnectedState {
public:
explicit WithDevicePortConnectedState(const AudioPort& idAndData) : mIdAndData(idAndData) {}
@@ -352,21 +362,36 @@
std::unique_ptr<DataMQ> mDataMQ;
};
-class StreamCommonLogic : public StreamLogic {
+class StreamLogicDriver {
public:
- StreamDescriptor::Position getLastObservablePosition() {
- std::lock_guard<std::mutex> lock(mLock);
- return mLastReply.observable;
- }
+ virtual ~StreamLogicDriver() = default;
+ // Return 'true' to stop the worker.
+ virtual bool done() = 0;
+ // For 'Writer' logic, if the 'actualSize' is 0, write is skipped.
+ // The 'fmqByteCount' from the returned command is passed as is to the HAL.
+ virtual StreamDescriptor::Command getNextCommand(int maxDataSize,
+ int* actualSize = nullptr) = 0;
+ // Return 'true' to indicate that no further processing is needed,
+ // for example, the driver is expecting a bad status to be returned.
+ // The logic cycle will return with 'CONTINUE' status. Otherwise,
+ // the reply will be validated and then passed to 'processValidReply'.
+ virtual bool interceptRawReply(const StreamDescriptor::Reply& reply) = 0;
+ // Return 'false' to indicate that the contents of the reply are unexpected.
+ // Will abort the logic cycle.
+ virtual bool processValidReply(const StreamDescriptor::Reply& reply) = 0;
+};
+class StreamCommonLogic : public StreamLogic {
protected:
- explicit StreamCommonLogic(const StreamContext& context)
+ StreamCommonLogic(const StreamContext& context, StreamLogicDriver* driver)
: mCommandMQ(context.getCommandMQ()),
mReplyMQ(context.getReplyMQ()),
mDataMQ(context.getDataMQ()),
- mData(context.getBufferSizeBytes()) {}
+ mData(context.getBufferSizeBytes()),
+ mDriver(driver) {}
StreamContext::CommandMQ* getCommandMQ() const { return mCommandMQ; }
StreamContext::ReplyMQ* getReplyMQ() const { return mReplyMQ; }
+ StreamLogicDriver* getDriver() const { return mDriver; }
std::string init() override { return ""; }
@@ -374,19 +399,20 @@
StreamContext::ReplyMQ* mReplyMQ;
StreamContext::DataMQ* mDataMQ;
std::vector<int8_t> mData;
- std::mutex mLock;
- StreamDescriptor::Reply mLastReply GUARDED_BY(mLock);
+ StreamLogicDriver* const mDriver;
};
class StreamReaderLogic : public StreamCommonLogic {
public:
- explicit StreamReaderLogic(const StreamContext& context) : StreamCommonLogic(context) {}
+ StreamReaderLogic(const StreamContext& context, StreamLogicDriver* driver)
+ : StreamCommonLogic(context, driver) {}
protected:
Status cycle() override {
- StreamDescriptor::Command command{};
- command.code = StreamDescriptor::COMMAND_BURST;
- command.fmqByteCount = mData.size();
+ if (getDriver()->done()) {
+ return Status::EXIT;
+ }
+ StreamDescriptor::Command command = getDriver()->getNextCommand(mData.size());
if (!mCommandMQ->writeBlocking(&command, 1)) {
LOG(ERROR) << __func__ << ": writing of command into MQ failed";
return Status::ABORT;
@@ -396,6 +422,9 @@
LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
return Status::ABORT;
}
+ if (getDriver()->interceptRawReply(reply)) {
+ return Status::CONTINUE;
+ }
if (reply.status != STATUS_OK) {
LOG(ERROR) << __func__ << ": received error status: " << statusToString(reply.status);
return Status::ABORT;
@@ -405,16 +434,41 @@
<< ": received invalid byte count in the reply: " << reply.fmqByteCount;
return Status::ABORT;
}
- {
- std::lock_guard<std::mutex> lock(mLock);
- mLastReply = reply;
+ if (static_cast<size_t>(reply.fmqByteCount) != mDataMQ->availableToRead()) {
+ LOG(ERROR) << __func__
+ << ": the byte count in the reply is not the same as the amount of "
+ << "data available in the MQ: " << reply.fmqByteCount
+ << " != " << mDataMQ->availableToRead();
}
- const size_t readCount = std::min({mDataMQ->availableToRead(),
- static_cast<size_t>(reply.fmqByteCount), mData.size()});
- if (readCount == 0 || mDataMQ->read(mData.data(), readCount)) {
+ if (reply.latencyMs < 0 && reply.latencyMs != StreamDescriptor::LATENCY_UNKNOWN) {
+ LOG(ERROR) << __func__ << ": received invalid latency value: " << reply.latencyMs;
+ return Status::ABORT;
+ }
+ if (reply.xrunFrames < 0) {
+ LOG(ERROR) << __func__ << ": received invalid xrunFrames value: " << reply.xrunFrames;
+ return Status::ABORT;
+ }
+ if (std::find(enum_range<StreamDescriptor::State>().begin(),
+ enum_range<StreamDescriptor::State>().end(),
+ reply.state) == enum_range<StreamDescriptor::State>().end()) {
+ LOG(ERROR) << __func__ << ": received invalid stream state: " << toString(reply.state);
+ return Status::ABORT;
+ }
+ const bool acceptedReply = getDriver()->processValidReply(reply);
+ if (const size_t readCount = mDataMQ->availableToRead(); readCount > 0) {
+ std::vector<int8_t> data(readCount);
+ if (mDataMQ->read(data.data(), readCount)) {
+ memcpy(mData.data(), data.data(), std::min(mData.size(), data.size()));
+ goto checkAcceptedReply;
+ }
+ LOG(ERROR) << __func__ << ": reading of " << readCount << " data bytes from MQ failed";
+ return Status::ABORT;
+ } // readCount == 0
+ checkAcceptedReply:
+ if (acceptedReply) {
return Status::CONTINUE;
}
- LOG(ERROR) << __func__ << ": reading of " << readCount << " data bytes from MQ failed";
+ LOG(ERROR) << __func__ << ": unacceptable reply: " << reply.toString();
return Status::ABORT;
}
};
@@ -422,17 +476,20 @@
class StreamWriterLogic : public StreamCommonLogic {
public:
- explicit StreamWriterLogic(const StreamContext& context) : StreamCommonLogic(context) {}
+ StreamWriterLogic(const StreamContext& context, StreamLogicDriver* driver)
+ : StreamCommonLogic(context, driver) {}
protected:
Status cycle() override {
- if (!mDataMQ->write(mData.data(), mData.size())) {
+ if (getDriver()->done()) {
+ return Status::EXIT;
+ }
+ int actualSize = 0;
+ StreamDescriptor::Command command = getDriver()->getNextCommand(mData.size(), &actualSize);
+ if (actualSize != 0 && !mDataMQ->write(mData.data(), mData.size())) {
LOG(ERROR) << __func__ << ": writing of " << mData.size() << " bytes to MQ failed";
return Status::ABORT;
}
- StreamDescriptor::Command command{};
- command.code = StreamDescriptor::COMMAND_BURST;
- command.fmqByteCount = mData.size();
if (!mCommandMQ->writeBlocking(&command, 1)) {
LOG(ERROR) << __func__ << ": writing of command into MQ failed";
return Status::ABORT;
@@ -442,6 +499,9 @@
LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
return Status::ABORT;
}
+ if (getDriver()->interceptRawReply(reply)) {
+ return Status::CONTINUE;
+ }
if (reply.status != STATUS_OK) {
LOG(ERROR) << __func__ << ": received error status: " << statusToString(reply.status);
return Status::ABORT;
@@ -451,11 +511,31 @@
<< ": received invalid byte count in the reply: " << reply.fmqByteCount;
return Status::ABORT;
}
- {
- std::lock_guard<std::mutex> lock(mLock);
- mLastReply = reply;
+ if (mDataMQ->availableToWrite() != mDataMQ->getQuantumCount()) {
+ LOG(ERROR) << __func__ << ": the HAL module did not consume all data from the data MQ: "
+ << "available to write " << mDataMQ->availableToWrite()
+ << ", total size: " << mDataMQ->getQuantumCount();
+ return Status::ABORT;
}
- return Status::CONTINUE;
+ if (reply.latencyMs < 0 && reply.latencyMs != StreamDescriptor::LATENCY_UNKNOWN) {
+ LOG(ERROR) << __func__ << ": received invalid latency value: " << reply.latencyMs;
+ return Status::ABORT;
+ }
+ if (reply.xrunFrames < 0) {
+ LOG(ERROR) << __func__ << ": received invalid xrunFrames value: " << reply.xrunFrames;
+ return Status::ABORT;
+ }
+ if (std::find(enum_range<StreamDescriptor::State>().begin(),
+ enum_range<StreamDescriptor::State>().end(),
+ reply.state) == enum_range<StreamDescriptor::State>().end()) {
+ LOG(ERROR) << __func__ << ": received invalid stream state: " << toString(reply.state);
+ return Status::ABORT;
+ }
+ if (getDriver()->processValidReply(reply)) {
+ return Status::CONTINUE;
+ }
+ LOG(ERROR) << __func__ << ": unacceptable reply: " << reply.toString();
+ return Status::ABORT;
}
};
using StreamWriter = StreamWorker<StreamWriterLogic>;
@@ -466,52 +546,6 @@
using Worker = std::conditional_t<is_input, StreamReader, StreamWriter>;
};
-// A dedicated version to test replies to invalid commands.
-class StreamInvalidCommandLogic : public StreamCommonLogic {
- public:
- StreamInvalidCommandLogic(const StreamContext& context,
- const std::vector<StreamDescriptor::Command>& commands)
- : StreamCommonLogic(context), mCommands(commands) {}
-
- std::vector<std::string> getUnexpectedStatuses() {
- std::lock_guard<std::mutex> lock(mLock);
- return mUnexpectedStatuses;
- }
-
- protected:
- Status cycle() override {
- // Send all commands in one cycle to simplify testing.
- // Extra logging helps to sort out issues with unexpected HAL behavior.
- for (const auto& command : mCommands) {
- LOG(INFO) << __func__ << ": writing command " << command.toString() << " into MQ...";
- if (!getCommandMQ()->writeBlocking(&command, 1)) {
- LOG(ERROR) << __func__ << ": writing of command into MQ failed";
- return Status::ABORT;
- }
- StreamDescriptor::Reply reply{};
- LOG(INFO) << __func__ << ": reading reply for command " << command.toString() << "...";
- if (!getReplyMQ()->readBlocking(&reply, 1)) {
- LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
- return Status::ABORT;
- }
- LOG(INFO) << __func__ << ": received status " << statusToString(reply.status)
- << " for command " << command.toString();
- if (reply.status != STATUS_BAD_VALUE) {
- std::string s = command.toString();
- s.append(", ").append(statusToString(reply.status));
- std::lock_guard<std::mutex> lock(mLock);
- mUnexpectedStatuses.push_back(std::move(s));
- }
- };
- return Status::EXIT;
- }
-
- private:
- const std::vector<StreamDescriptor::Command> mCommands;
- std::mutex mLock;
- std::vector<std::string> mUnexpectedStatuses GUARDED_BY(mLock);
-};
-
template <typename Stream>
class WithStream {
public:
@@ -1208,6 +1242,46 @@
}
}
+class StreamLogicDriverInvalidCommand : public StreamLogicDriver {
+ public:
+ StreamLogicDriverInvalidCommand(const std::vector<StreamDescriptor::Command>& commands)
+ : mCommands(commands) {}
+
+ std::string getUnexpectedStatuses() {
+ // This method is intended to be called after the worker thread has joined,
+ // thus no extra synchronization is needed.
+ std::string s;
+ if (!mStatuses.empty()) {
+ s = std::string("Pairs of (command, actual status): ")
+ .append((android::internal::ToString(mStatuses)));
+ }
+ return s;
+ }
+
+ bool done() override { return mNextCommand >= mCommands.size(); }
+ StreamDescriptor::Command getNextCommand(int, int* actualSize) override {
+ if (actualSize != nullptr) *actualSize = 0;
+ return mCommands[mNextCommand++];
+ }
+ bool interceptRawReply(const StreamDescriptor::Reply& reply) override {
+ if (reply.status != STATUS_BAD_VALUE) {
+ std::string s = mCommands[mNextCommand - 1].toString();
+ s.append(", ").append(statusToString(reply.status));
+ mStatuses.push_back(std::move(s));
+ // If the HAL does not recognize the command as invalid,
+ // retrieve the data etc.
+ return reply.status != STATUS_OK;
+ }
+ return true;
+ }
+ bool processValidReply(const StreamDescriptor::Reply&) override { return true; }
+
+ private:
+ const std::vector<StreamDescriptor::Command> mCommands;
+ size_t mNextCommand = 0;
+ std::vector<std::string> mStatuses;
+};
+
template <typename Stream>
class AudioStream : public AudioCoreModule {
public:
@@ -1315,19 +1389,6 @@
EXPECT_NO_FATAL_FAILURE(OpenTwiceSamePortConfigImpl(portConfig.value()));
}
- void ReadOrWrite(bool useSetupSequence2, bool validateObservablePosition) {
- const auto allPortConfigs =
- moduleConfig->getPortConfigsForMixPorts(IOTraits<Stream>::is_input);
- if (allPortConfigs.empty()) {
- GTEST_SKIP() << "No mix ports have attached devices";
- }
- for (const auto& portConfig : allPortConfigs) {
- EXPECT_NO_FATAL_FAILURE(
- ReadOrWriteImpl(portConfig, useSetupSequence2, validateObservablePosition))
- << portConfig.toString();
- }
- }
-
void ResetPortConfigWithOpenStream() {
const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
if (!portConfig.has_value()) {
@@ -1357,131 +1418,43 @@
<< stream1.getPortId();
}
- template <class Worker>
- void WaitForObservablePositionAdvance(Worker& worker) {
- static constexpr int kWriteDurationUs = 50 * 1000;
- static constexpr std::chrono::milliseconds kPositionChangeTimeout{10000};
- int64_t framesInitial;
- framesInitial = worker.getLastObservablePosition().frames;
- ASSERT_FALSE(worker.hasError());
- bool timedOut = false;
- int64_t frames = framesInitial;
- for (android::base::Timer elapsed;
- frames <= framesInitial && !worker.hasError() &&
- !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
- usleep(kWriteDurationUs);
- frames = worker.getLastObservablePosition().frames;
- }
- EXPECT_FALSE(timedOut);
- EXPECT_FALSE(worker.hasError()) << worker.getError();
- EXPECT_GT(frames, framesInitial);
- }
-
- void ReadOrWriteImpl(const AudioPortConfig& portConfig, bool useSetupSequence2,
- bool validateObservablePosition) {
- if (!useSetupSequence2) {
- ASSERT_NO_FATAL_FAILURE(
- ReadOrWriteSetupSequence1(portConfig, validateObservablePosition));
- } else {
- ASSERT_NO_FATAL_FAILURE(
- ReadOrWriteSetupSequence2(portConfig, validateObservablePosition));
- }
- }
-
- // Set up a patch first, then open a stream.
- void ReadOrWriteSetupSequence1(const AudioPortConfig& portConfig,
- bool validateObservablePosition) {
- auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
- IOTraits<Stream>::is_input, portConfig);
- ASSERT_FALSE(devicePorts.empty());
- auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
- WithAudioPatch patch(IOTraits<Stream>::is_input, portConfig, devicePortConfig);
- ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
-
- WithStream<Stream> stream(patch.getPortConfig(IOTraits<Stream>::is_input));
- ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
- typename IOTraits<Stream>::Worker worker(*stream.getContext());
-
- ASSERT_TRUE(worker.start());
- ASSERT_TRUE(worker.waitForAtLeastOneCycle());
- if (validateObservablePosition) {
- ASSERT_NO_FATAL_FAILURE(WaitForObservablePositionAdvance(worker));
- }
- }
-
- // Open a stream, then set up a patch for it.
- void ReadOrWriteSetupSequence2(const AudioPortConfig& portConfig,
- bool validateObservablePosition) {
- WithStream<Stream> stream(portConfig);
- ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
- typename IOTraits<Stream>::Worker worker(*stream.getContext());
-
- auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
- IOTraits<Stream>::is_input, portConfig);
- ASSERT_FALSE(devicePorts.empty());
- auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
- WithAudioPatch patch(IOTraits<Stream>::is_input, stream.getPortConfig(), devicePortConfig);
- ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
-
- ASSERT_TRUE(worker.start());
- ASSERT_TRUE(worker.waitForAtLeastOneCycle());
- if (validateObservablePosition) {
- ASSERT_NO_FATAL_FAILURE(WaitForObservablePositionAdvance(worker));
- }
- }
-
void SendInvalidCommandImpl(const AudioPortConfig& portConfig) {
std::vector<StreamDescriptor::Command> commands(6);
- commands[0].code = -1;
- commands[1].code = StreamDescriptor::COMMAND_BURST - 1;
- commands[2].code = std::numeric_limits<int32_t>::min();
- commands[3].code = std::numeric_limits<int32_t>::max();
- commands[4].code = StreamDescriptor::COMMAND_BURST;
+ commands[0].code = StreamDescriptor::CommandCode(-1);
+ commands[1].code = StreamDescriptor::CommandCode(
+ static_cast<int32_t>(StreamDescriptor::CommandCode::START) - 1);
+ commands[2].code = StreamDescriptor::CommandCode(std::numeric_limits<int32_t>::min());
+ commands[3].code = StreamDescriptor::CommandCode(std::numeric_limits<int32_t>::max());
+ // TODO: For proper testing of input streams, need to put the stream into
+ // a state which accepts BURST commands.
+ commands[4].code = StreamDescriptor::CommandCode::BURST;
commands[4].fmqByteCount = -1;
- commands[5].code = StreamDescriptor::COMMAND_BURST;
+ commands[5].code = StreamDescriptor::CommandCode::BURST;
commands[5].fmqByteCount = std::numeric_limits<int32_t>::min();
WithStream<Stream> stream(portConfig);
ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
- StreamWorker<StreamInvalidCommandLogic> writer(*stream.getContext(), commands);
- ASSERT_TRUE(writer.start());
- writer.waitForAtLeastOneCycle();
- auto unexpectedStatuses = writer.getUnexpectedStatuses();
- EXPECT_EQ(0UL, unexpectedStatuses.size())
- << "Pairs of (command, actual status): "
- << android::internal::ToString(unexpectedStatuses);
+ StreamLogicDriverInvalidCommand driver(commands);
+ typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver);
+ ASSERT_TRUE(worker.start());
+ worker.join();
+ EXPECT_EQ("", driver.getUnexpectedStatuses());
}
};
using AudioStreamIn = AudioStream<IStreamIn>;
using AudioStreamOut = AudioStream<IStreamOut>;
-#define TEST_IO_STREAM(method_name) \
+#define TEST_IN_AND_OUT_STREAM(method_name) \
TEST_P(AudioStreamIn, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); } \
TEST_P(AudioStreamOut, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); }
-#define TEST_IO_STREAM_2(method_name, arg1, arg2) \
- TEST_P(AudioStreamIn, method_name##_##arg1##_##arg2) { \
- ASSERT_NO_FATAL_FAILURE(method_name(arg1, arg2)); \
- } \
- TEST_P(AudioStreamOut, method_name##_##arg1##_##arg2) { \
- ASSERT_NO_FATAL_FAILURE(method_name(arg1, arg2)); \
- }
-TEST_IO_STREAM(CloseTwice);
-TEST_IO_STREAM(OpenAllConfigs);
-TEST_IO_STREAM(OpenInvalidBufferSize);
-TEST_IO_STREAM(OpenInvalidDirection);
-TEST_IO_STREAM(OpenOverMaxCount);
-TEST_IO_STREAM(OpenTwiceSamePortConfig);
-// Use of constants makes comprehensible test names.
-constexpr bool SetupSequence1 = false;
-constexpr bool SetupSequence2 = true;
-constexpr bool SetupOnly = false;
-constexpr bool ValidateObservablePosition = true;
-TEST_IO_STREAM_2(ReadOrWrite, SetupSequence1, SetupOnly);
-TEST_IO_STREAM_2(ReadOrWrite, SetupSequence2, SetupOnly);
-TEST_IO_STREAM_2(ReadOrWrite, SetupSequence1, ValidateObservablePosition);
-TEST_IO_STREAM_2(ReadOrWrite, SetupSequence2, ValidateObservablePosition);
-TEST_IO_STREAM(ResetPortConfigWithOpenStream);
-TEST_IO_STREAM(SendInvalidCommand);
+TEST_IN_AND_OUT_STREAM(CloseTwice);
+TEST_IN_AND_OUT_STREAM(OpenAllConfigs);
+TEST_IN_AND_OUT_STREAM(OpenInvalidBufferSize);
+TEST_IN_AND_OUT_STREAM(OpenInvalidDirection);
+TEST_IN_AND_OUT_STREAM(OpenOverMaxCount);
+TEST_IN_AND_OUT_STREAM(OpenTwiceSamePortConfig);
+TEST_IN_AND_OUT_STREAM(ResetPortConfigWithOpenStream);
+TEST_IN_AND_OUT_STREAM(SendInvalidCommand);
TEST_P(AudioStreamOut, OpenTwicePrimary) {
const auto mixPorts = moduleConfig->getMixPorts(false);
@@ -1523,6 +1496,163 @@
<< "when no offload info is provided for a compressed offload mix port";
}
+using CommandAndState = std::pair<StreamDescriptor::CommandCode, StreamDescriptor::State>;
+
+class StreamLogicDefaultDriver : public StreamLogicDriver {
+ public:
+ explicit StreamLogicDefaultDriver(const std::vector<CommandAndState>& commands)
+ : mCommands(commands) {}
+
+ // The three methods below is intended to be called after the worker
+ // thread has joined, thus no extra synchronization is needed.
+ bool hasObservablePositionIncrease() const { return mObservablePositionIncrease; }
+ bool hasRetrogradeObservablePosition() const { return mRetrogradeObservablePosition; }
+ std::string getUnexpectedStateTransition() const { return mUnexpectedTransition; }
+
+ bool done() override { return mNextCommand >= mCommands.size(); }
+ StreamDescriptor::Command getNextCommand(int maxDataSize, int* actualSize) override {
+ StreamDescriptor::Command command{};
+ command.code = mCommands[mNextCommand++].first;
+ const int dataSize = command.code == StreamDescriptor::CommandCode::BURST ? maxDataSize : 0;
+ command.fmqByteCount = dataSize;
+ if (actualSize != nullptr) {
+ // In the output scenario, reduce slightly the fmqByteCount to verify
+ // that the HAL module always consumes all data from the MQ.
+ if (command.fmqByteCount > 1) command.fmqByteCount--;
+ *actualSize = dataSize;
+ }
+ return command;
+ }
+ bool interceptRawReply(const StreamDescriptor::Reply&) override { return false; }
+ bool processValidReply(const StreamDescriptor::Reply& reply) override {
+ if (mPreviousFrames.has_value()) {
+ if (reply.observable.frames > mPreviousFrames.value()) {
+ mObservablePositionIncrease = true;
+ } else if (reply.observable.frames < mPreviousFrames.value()) {
+ mRetrogradeObservablePosition = true;
+ }
+ }
+ mPreviousFrames = reply.observable.frames;
+
+ const auto& lastCommandState = mCommands[mNextCommand - 1];
+ if (lastCommandState.second != reply.state) {
+ std::string s = std::string("Unexpected transition from the state ")
+ .append(mPreviousState)
+ .append(" to ")
+ .append(toString(reply.state))
+ .append(" caused by the command ")
+ .append(toString(lastCommandState.first));
+ LOG(ERROR) << __func__ << ": " << s;
+ mUnexpectedTransition = std::move(s);
+ return false;
+ }
+ return true;
+ }
+
+ protected:
+ const std::vector<CommandAndState>& mCommands;
+ size_t mNextCommand = 0;
+ std::optional<int64_t> mPreviousFrames;
+ std::string mPreviousState = "<initial state>";
+ bool mObservablePositionIncrease = false;
+ bool mRetrogradeObservablePosition = false;
+ std::string mUnexpectedTransition;
+};
+
+using NamedCommandSequence = std::pair<std::string, std::vector<CommandAndState>>;
+enum { PARAM_MODULE_NAME, PARAM_CMD_SEQ, PARAM_SETUP_SEQ };
+using StreamIoTestParameters =
+ std::tuple<std::string /*moduleName*/, NamedCommandSequence, bool /*useSetupSequence2*/>;
+template <typename Stream>
+class AudioStreamIo : public AudioCoreModuleBase,
+ public testing::TestWithParam<StreamIoTestParameters> {
+ public:
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(SetUpImpl(std::get<PARAM_MODULE_NAME>(GetParam())));
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ }
+
+ void Run() {
+ const auto allPortConfigs =
+ moduleConfig->getPortConfigsForMixPorts(IOTraits<Stream>::is_input);
+ if (allPortConfigs.empty()) {
+ GTEST_SKIP() << "No mix ports have attached devices";
+ }
+ for (const auto& portConfig : allPortConfigs) {
+ SCOPED_TRACE(portConfig.toString());
+ const auto& commandsAndStates = std::get<PARAM_CMD_SEQ>(GetParam()).second;
+ if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
+ ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates));
+ } else {
+ ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(portConfig, commandsAndStates));
+ }
+ }
+ }
+
+ bool ValidateObservablePosition(const AudioPortConfig& /*portConfig*/) {
+ // May return false based on the portConfig, e.g. for telephony ports.
+ return true;
+ }
+
+ // Set up a patch first, then open a stream.
+ void RunStreamIoCommandsImplSeq1(const AudioPortConfig& portConfig,
+ const std::vector<CommandAndState>& commandsAndStates) {
+ auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
+ IOTraits<Stream>::is_input, portConfig);
+ ASSERT_FALSE(devicePorts.empty());
+ auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
+ WithAudioPatch patch(IOTraits<Stream>::is_input, portConfig, devicePortConfig);
+ ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
+
+ WithStream<Stream> stream(patch.getPortConfig(IOTraits<Stream>::is_input));
+ ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+ StreamLogicDefaultDriver driver(commandsAndStates);
+ typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver);
+
+ ASSERT_TRUE(worker.start());
+ worker.join();
+ EXPECT_FALSE(worker.hasError()) << worker.getError();
+ EXPECT_EQ("", driver.getUnexpectedStateTransition());
+ if (ValidateObservablePosition(portConfig)) {
+ EXPECT_TRUE(driver.hasObservablePositionIncrease());
+ EXPECT_FALSE(driver.hasRetrogradeObservablePosition());
+ }
+ }
+
+ // Open a stream, then set up a patch for it.
+ void RunStreamIoCommandsImplSeq2(const AudioPortConfig& portConfig,
+ const std::vector<CommandAndState>& commandsAndStates) {
+ WithStream<Stream> stream(portConfig);
+ ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+ StreamLogicDefaultDriver driver(commandsAndStates);
+ typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver);
+
+ auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
+ IOTraits<Stream>::is_input, portConfig);
+ ASSERT_FALSE(devicePorts.empty());
+ auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
+ WithAudioPatch patch(IOTraits<Stream>::is_input, stream.getPortConfig(), devicePortConfig);
+ ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
+
+ ASSERT_TRUE(worker.start());
+ worker.join();
+ EXPECT_FALSE(worker.hasError()) << worker.getError();
+ EXPECT_EQ("", driver.getUnexpectedStateTransition());
+ if (ValidateObservablePosition(portConfig)) {
+ EXPECT_TRUE(driver.hasObservablePositionIncrease());
+ EXPECT_FALSE(driver.hasRetrogradeObservablePosition());
+ }
+ }
+};
+using AudioStreamIoIn = AudioStreamIo<IStreamIn>;
+using AudioStreamIoOut = AudioStreamIo<IStreamOut>;
+
+#define TEST_IN_AND_OUT_STREAM_IO(method_name) \
+ TEST_P(AudioStreamIoIn, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); } \
+ TEST_P(AudioStreamIoOut, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); }
+
+TEST_IN_AND_OUT_STREAM_IO(Run);
+
// Tests specific to audio patches. The fixure class is named 'AudioModulePatch'
// to avoid clashing with 'AudioPatch' class.
class AudioModulePatch : public AudioCoreModule {
@@ -1704,6 +1834,139 @@
testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
android::PrintInstanceNameToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioStreamOut);
+
+static const NamedCommandSequence kReadOrWriteSeq = std::make_pair(
+ std::string("ReadOrWrite"),
+ std::vector<CommandAndState>{
+ std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::ACTIVE),
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::ACTIVE),
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::ACTIVE)});
+static const NamedCommandSequence kDrainInSeq = std::make_pair(
+ std::string("Drain"),
+ std::vector<CommandAndState>{
+ std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::ACTIVE),
+ std::make_pair(StreamDescriptor::CommandCode::DRAIN,
+ StreamDescriptor::State::DRAINING),
+ std::make_pair(StreamDescriptor::CommandCode::START,
+ StreamDescriptor::State::ACTIVE),
+ std::make_pair(StreamDescriptor::CommandCode::DRAIN,
+ StreamDescriptor::State::DRAINING),
+ // TODO: This will need to be changed once DRAIN starts taking time.
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::STANDBY)});
+static const NamedCommandSequence kDrainOutSeq = std::make_pair(
+ std::string("Drain"),
+ std::vector<CommandAndState>{
+ std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::ACTIVE),
+ // TODO: This will need to be changed once DRAIN starts taking time.
+ std::make_pair(StreamDescriptor::CommandCode::DRAIN,
+ StreamDescriptor::State::IDLE)});
+// TODO: This will need to be changed once DRAIN starts taking time so we can pause it.
+static const NamedCommandSequence kDrainPauseOutSeq = std::make_pair(
+ std::string("DrainPause"),
+ std::vector<CommandAndState>{
+ std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::ACTIVE),
+ std::make_pair(StreamDescriptor::CommandCode::DRAIN,
+ StreamDescriptor::State::IDLE)});
+static const NamedCommandSequence kStandbySeq = std::make_pair(
+ std::string("Standby"),
+ std::vector<CommandAndState>{
+ std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+ std::make_pair(StreamDescriptor::CommandCode::STANDBY,
+ StreamDescriptor::State::STANDBY),
+ // Perform a read or write in order to advance observable position
+ // (this is verified by tests).
+ std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::ACTIVE)});
+static const NamedCommandSequence kPauseInSeq = std::make_pair(
+ std::string("Pause"),
+ std::vector<CommandAndState>{
+ std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::ACTIVE),
+ std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+ StreamDescriptor::State::PAUSED),
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::ACTIVE),
+ std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+ StreamDescriptor::State::PAUSED),
+ std::make_pair(StreamDescriptor::CommandCode::FLUSH,
+ StreamDescriptor::State::STANDBY)});
+static const NamedCommandSequence kPauseOutSeq = std::make_pair(
+ std::string("Pause"),
+ std::vector<CommandAndState>{
+ std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::ACTIVE),
+ std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+ StreamDescriptor::State::PAUSED),
+ std::make_pair(StreamDescriptor::CommandCode::START,
+ StreamDescriptor::State::ACTIVE),
+ std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+ StreamDescriptor::State::PAUSED),
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::PAUSED),
+ std::make_pair(StreamDescriptor::CommandCode::START,
+ StreamDescriptor::State::ACTIVE),
+ std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+ StreamDescriptor::State::PAUSED)});
+static const NamedCommandSequence kFlushInSeq = std::make_pair(
+ std::string("Flush"),
+ std::vector<CommandAndState>{
+ std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::ACTIVE),
+ std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+ StreamDescriptor::State::PAUSED),
+ std::make_pair(StreamDescriptor::CommandCode::FLUSH,
+ StreamDescriptor::State::STANDBY)});
+static const NamedCommandSequence kFlushOutSeq = std::make_pair(
+ std::string("Flush"),
+ std::vector<CommandAndState>{
+ std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
+ std::make_pair(StreamDescriptor::CommandCode::BURST,
+ StreamDescriptor::State::ACTIVE),
+ std::make_pair(StreamDescriptor::CommandCode::PAUSE,
+ StreamDescriptor::State::PAUSED),
+ std::make_pair(StreamDescriptor::CommandCode::FLUSH,
+ StreamDescriptor::State::IDLE)});
+std::string GetStreamIoTestName(const testing::TestParamInfo<StreamIoTestParameters>& info) {
+ return android::PrintInstanceNameToString(
+ testing::TestParamInfo<std::string>{std::get<PARAM_MODULE_NAME>(info.param),
+ info.index})
+ .append("_")
+ .append(std::get<PARAM_CMD_SEQ>(info.param).first)
+ .append("_SetupSeq")
+ .append(std::get<PARAM_SETUP_SEQ>(info.param) ? "2" : "1");
+}
+INSTANTIATE_TEST_SUITE_P(
+ AudioStreamIoInTest, AudioStreamIoIn,
+ testing::Combine(testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
+ testing::Values(kReadOrWriteSeq, kDrainInSeq, kStandbySeq, kPauseInSeq,
+ kFlushInSeq),
+ testing::Values(false, true)),
+ GetStreamIoTestName);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioStreamIoIn);
+INSTANTIATE_TEST_SUITE_P(
+ AudioStreamIoOutTest, AudioStreamIoOut,
+ testing::Combine(testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
+ testing::Values(kReadOrWriteSeq, kDrainOutSeq, kDrainPauseOutSeq,
+ kStandbySeq, kPauseOutSeq, kFlushOutSeq),
+ testing::Values(false, true)),
+ GetStreamIoTestName);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioStreamIoOut);
+
INSTANTIATE_TEST_SUITE_P(AudioPatchTest, AudioModulePatch,
testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
android::PrintInstanceNameToString);
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index e46e5b4..0ca6a58 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -1014,9 +1014,8 @@
if (mDataPosition == 0) mOnDataStart();
const size_t dataSize = std::min(mData.size() - mDataPosition, mDataMQ->availableToWrite());
bool success = mDataMQ->write(mData.data() + mDataPosition, dataSize);
+ bool wrapped = false;
ALOGE_IF(!success, "data message queue write failed");
- mDataPosition += dataSize;
- if (mDataPosition >= mData.size()) mDataPosition = 0;
mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
uint32_t efState = 0;
@@ -1034,6 +1033,11 @@
ALOGE("bad write status: %d", writeStatus.retval);
success = false;
}
+ mDataPosition += writeStatus.reply.written;
+ if (mDataPosition >= mData.size()) {
+ mDataPosition = 0;
+ wrapped = true;
+ }
}
if (ret == -EAGAIN || ret == -EINTR) {
// Spurious wakeup. This normally retries no more than once.
@@ -1042,7 +1046,7 @@
ALOGE("bad wait status: %d", ret);
success = false;
}
- if (success && mDataPosition == 0) {
+ if (wrapped) {
success = mOnDataWrap();
}
return success;
diff --git a/bluetooth/1.0/default/OWNERS b/bluetooth/1.0/default/OWNERS
deleted file mode 100644
index 5df5bfe..0000000
--- a/bluetooth/1.0/default/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-eisenbach@google.com
-mylesgw@google.com
-pavlin@google.com
diff --git a/bluetooth/1.0/default/bluetooth_hci.cc b/bluetooth/1.0/default/bluetooth_hci.cc
index a2211f4..869c723 100644
--- a/bluetooth/1.0/default/bluetooth_hci.cc
+++ b/bluetooth/1.0/default/bluetooth_hci.cc
@@ -33,7 +33,8 @@
class BluetoothDeathRecipient : public hidl_death_recipient {
public:
- BluetoothDeathRecipient(const sp<IBluetoothHci> hci) : mHci(hci) {}
+ BluetoothDeathRecipient(const sp<IBluetoothHci> hci)
+ : mHci(hci), has_died_(false) {}
virtual void serviceDied(
uint64_t /*cookie*/,
@@ -51,7 +52,7 @@
};
BluetoothHci::BluetoothHci()
- : death_recipient_(new BluetoothDeathRecipient(this)) {}
+ : death_recipient_(new BluetoothDeathRecipient(this)) {bt_enabled = 0;}
Return<void> BluetoothHci::initialize(
const ::android::sp<IBluetoothHciCallbacks>& cb) {
@@ -61,8 +62,19 @@
return Void();
}
+ if (bt_enabled == 1) {
+ ALOGE("initialize was called!");
+ return Void();
+ }
+ bt_enabled = 1;
death_recipient_->setHasDied(false);
cb->linkToDeath(death_recipient_, 0);
+ unlink_cb_ = [cb](sp<BluetoothDeathRecipient>& death_recipient) {
+ if (death_recipient->getHasDied())
+ ALOGI("Skipping unlink call, service died.");
+ else
+ cb->unlinkToDeath(death_recipient);
+ };
bool rc = VendorInterface::Initialize(
[cb](bool status) {
@@ -112,6 +124,12 @@
Return<void> BluetoothHci::close() {
ALOGI("BluetoothHci::close()");
+
+ if (bt_enabled != 1) {
+ ALOGE("should initialize first!");
+ return Void();
+ }
+ bt_enabled = 0;
unlink_cb_(death_recipient_);
VendorInterface::Shutdown();
return Void();
@@ -134,6 +152,11 @@
void BluetoothHci::sendDataToController(const uint8_t type,
const hidl_vec<uint8_t>& data) {
+ if (bt_enabled != 1) {
+ ALOGE("should initialize first!");
+ return;
+ }
+
VendorInterface::get()->Send(type, data.data(), data.size());
}
diff --git a/bluetooth/1.0/default/bluetooth_hci.h b/bluetooth/1.0/default/bluetooth_hci.h
index c966990..5130c87 100644
--- a/bluetooth/1.0/default/bluetooth_hci.h
+++ b/bluetooth/1.0/default/bluetooth_hci.h
@@ -48,6 +48,7 @@
void sendDataToController(const uint8_t type, const hidl_vec<uint8_t>& data);
::android::sp<BluetoothDeathRecipient> death_recipient_;
std::function<void(sp<BluetoothDeathRecipient>&)> unlink_cb_;
+ int bt_enabled;
};
extern "C" IBluetoothHci* HIDL_FETCH_IBluetoothHci(const char* name);
diff --git a/bluetooth/1.0/default/vendor_interface.cc b/bluetooth/1.0/default/vendor_interface.cc
index 1d15dd6..c23d667 100644
--- a/bluetooth/1.0/default/vendor_interface.cc
+++ b/bluetooth/1.0/default/vendor_interface.cc
@@ -36,6 +36,8 @@
"BLUETOOTH_VENDOR_LIB_INTERFACE";
static const int INVALID_FD = -1;
+std::mutex vendor_mutex_;
+std::mutex initcb_mutex_;
namespace {
@@ -47,13 +49,25 @@
uint16_t opcode;
} internal_command;
+enum {
+ VENDOR_STATE_INIT = 1,
+ VENDOR_STATE_OPENING, /* during opening */
+ VENDOR_STATE_OPENED, /* open in fops_open */
+ VENDOR_STATE_CLOSING, /* during closing */
+ VENDOR_STATE_CLOSED, /* closed */
+
+ VENDOR_STATE_MSG_NUM
+} ;
+
+uint8_t vstate = VENDOR_STATE_INIT;
+
// True when LPM is not enabled yet or wake is not asserted.
bool lpm_wake_deasserted;
uint32_t lpm_timeout_ms;
bool recent_activity_flag;
VendorInterface* g_vendor_interface = nullptr;
-std::mutex wakeup_mutex_;
+static VendorInterface vendor_interface;
HC_BT_HDR* WrapPacketAndCopy(uint16_t event, const hidl_vec<uint8_t>& data) {
size_t packet_size = data.size() + sizeof(HC_BT_HDR);
@@ -167,11 +181,8 @@
InitializeCompleteCallback initialize_complete_cb,
PacketReadCallback event_cb, PacketReadCallback acl_cb,
PacketReadCallback sco_cb, PacketReadCallback iso_cb) {
- if (g_vendor_interface) {
- ALOGE("%s: No previous Shutdown()?", __func__);
- return false;
- }
- g_vendor_interface = new VendorInterface();
+ ALOGI("%s: VendorInterface::Initialize", __func__);
+ g_vendor_interface = &vendor_interface;
return g_vendor_interface->Open(initialize_complete_cb, event_cb, acl_cb,
sco_cb, iso_cb);
}
@@ -179,9 +190,8 @@
void VendorInterface::Shutdown() {
LOG_ALWAYS_FATAL_IF(!g_vendor_interface, "%s: No Vendor interface!",
__func__);
+ ALOGI("%s: VendorInterface::Shutdown", __func__);
g_vendor_interface->Close();
- delete g_vendor_interface;
- g_vendor_interface = nullptr;
}
VendorInterface* VendorInterface::get() { return g_vendor_interface; }
@@ -191,144 +201,189 @@
PacketReadCallback acl_cb,
PacketReadCallback sco_cb,
PacketReadCallback iso_cb) {
- initialize_complete_cb_ = initialize_complete_cb;
+ {
+ std::unique_lock<std::mutex> guard(vendor_mutex_);
+ if (vstate == VENDOR_STATE_OPENED) {
+ ALOGW("VendorInterface opened!");
+ return true;
+ }
- // Initialize vendor interface
+ if ((vstate == VENDOR_STATE_CLOSING) ||
+ (vstate == VENDOR_STATE_OPENING)) {
+ ALOGW("VendorInterface open/close is on-going !");
+ return true;
+ }
- lib_handle_ = dlopen(VENDOR_LIBRARY_NAME, RTLD_NOW);
- if (!lib_handle_) {
- ALOGE("%s unable to open %s (%s)", __func__, VENDOR_LIBRARY_NAME,
- dlerror());
- return false;
- }
+ vstate = VENDOR_STATE_OPENING;
+ ALOGI("%s: VendorInterface::Open", __func__);
- lib_interface_ = reinterpret_cast<bt_vendor_interface_t*>(
- dlsym(lib_handle_, VENDOR_LIBRARY_SYMBOL_NAME));
- if (!lib_interface_) {
- ALOGE("%s unable to find symbol %s in %s (%s)", __func__,
- VENDOR_LIBRARY_SYMBOL_NAME, VENDOR_LIBRARY_NAME, dlerror());
- return false;
- }
+ initialize_complete_cb_ = initialize_complete_cb;
+ // Initialize vendor interface
+
+ lib_handle_ = dlopen(VENDOR_LIBRARY_NAME, RTLD_NOW);
+ if (!lib_handle_) {
+ ALOGE("%s unable to open %s (%s)", __func__, VENDOR_LIBRARY_NAME,
+ dlerror());
+ return false;
+ }
+
+ lib_interface_ = reinterpret_cast<bt_vendor_interface_t*>(
+ dlsym(lib_handle_, VENDOR_LIBRARY_SYMBOL_NAME));
+ if (!lib_interface_) {
+ ALOGE("%s unable to find symbol %s in %s (%s)", __func__,
+ VENDOR_LIBRARY_SYMBOL_NAME, VENDOR_LIBRARY_NAME, dlerror());
+ return false;
+ }
// Get the local BD address
- uint8_t local_bda[BluetoothAddress::kBytes];
- if (!BluetoothAddress::get_local_address(local_bda)) {
- LOG_ALWAYS_FATAL("%s: No Bluetooth Address!", __func__);
- }
- int status = lib_interface_->init(&lib_callbacks, (unsigned char*)local_bda);
- if (status) {
- ALOGE("%s unable to initialize vendor library: %d", __func__, status);
- return false;
- }
+ uint8_t local_bda[BluetoothAddress::kBytes] = {0, 0, 0, 0, 0, 0};
+ if (!BluetoothAddress::get_local_address(local_bda)) {
+ // BT driver will get BD address from NVRAM for MTK solution
+ ALOGW("%s: No pre-set Bluetooth Address!", __func__);
+ }
+ int status = lib_interface_->init(&lib_callbacks, (unsigned char*)local_bda);
+ if (status) {
+ ALOGE("%s unable to initialize vendor library: %d", __func__, status);
+ return false;
+ }
- ALOGD("%s vendor library loaded", __func__);
+ ALOGD("%s vendor library loaded", __func__);
// Power on the controller
- int power_state = BT_VND_PWR_ON;
- lib_interface_->op(BT_VND_OP_POWER_CTRL, &power_state);
+ int power_state = BT_VND_PWR_ON;
+ lib_interface_->op(BT_VND_OP_POWER_CTRL, &power_state);
// Get the UART socket(s)
- int fd_list[CH_MAX] = {0};
- int fd_count = lib_interface_->op(BT_VND_OP_USERIAL_OPEN, &fd_list);
+ int fd_list[CH_MAX] = {0};
+ int fd_count = lib_interface_->op(BT_VND_OP_USERIAL_OPEN, &fd_list);
- if (fd_count < 1 || fd_count > CH_MAX - 1) {
- ALOGE("%s: fd_count %d is invalid!", __func__, fd_count);
- return false;
- }
-
- for (int i = 0; i < fd_count; i++) {
- if (fd_list[i] == INVALID_FD) {
- ALOGE("%s: fd %d is invalid!", __func__, fd_list[i]);
+ if (fd_count < 1 || fd_count > CH_MAX - 1) {
+ ALOGE("%s: fd_count %d is invalid!", __func__, fd_count);
return false;
}
- }
- event_cb_ = event_cb;
- PacketReadCallback intercept_events = [this](const hidl_vec<uint8_t>& event) {
- HandleIncomingEvent(event);
- };
+ for (int i = 0; i < fd_count; i++) {
+ if (fd_list[i] == INVALID_FD) {
+ ALOGE("%s: fd %d is invalid!", __func__, fd_list[i]);
+ return false;
+ }
+ }
- if (fd_count == 1) {
- hci::H4Protocol* h4_hci =
- new hci::H4Protocol(fd_list[0], intercept_events, acl_cb, sco_cb, iso_cb);
- fd_watcher_.WatchFdForNonBlockingReads(
- fd_list[0], [h4_hci](int fd) { h4_hci->OnDataReady(fd); });
- hci_ = h4_hci;
- } else {
- hci::MctProtocol* mct_hci =
- new hci::MctProtocol(fd_list, intercept_events, acl_cb);
- fd_watcher_.WatchFdForNonBlockingReads(
- fd_list[CH_EVT], [mct_hci](int fd) { mct_hci->OnEventDataReady(fd); });
- fd_watcher_.WatchFdForNonBlockingReads(
- fd_list[CH_ACL_IN], [mct_hci](int fd) { mct_hci->OnAclDataReady(fd); });
- hci_ = mct_hci;
- }
+ event_cb_ = event_cb;
+ PacketReadCallback intercept_events = [this](const hidl_vec<uint8_t>& event) {
+ HandleIncomingEvent(event);
+ };
+
+ if (fd_count == 1) {
+ hci::H4Protocol* h4_hci =
+ new hci::H4Protocol(fd_list[0], intercept_events, acl_cb, sco_cb, iso_cb);
+ fd_watcher_.WatchFdForNonBlockingReads(
+ fd_list[0], [h4_hci](int fd) { h4_hci->OnDataReady(fd); });
+ hci_ = h4_hci;
+ } else {
+ hci::MctProtocol* mct_hci =
+ new hci::MctProtocol(fd_list, intercept_events, acl_cb);
+ fd_watcher_.WatchFdForNonBlockingReads(
+ fd_list[CH_EVT], [mct_hci](int fd) { mct_hci->OnEventDataReady(fd); });
+ fd_watcher_.WatchFdForNonBlockingReads(
+ fd_list[CH_ACL_IN],
+ [mct_hci](int fd) { mct_hci->OnAclDataReady(fd); });
+ hci_ = mct_hci;
+ }
// Initially, the power management is off.
- lpm_wake_deasserted = true;
+ lpm_wake_deasserted = true;
// Start configuring the firmware
- firmware_startup_timer_ = new FirmwareStartupTimer();
- lib_interface_->op(BT_VND_OP_FW_CFG, nullptr);
+ firmware_startup_timer_ = new FirmwareStartupTimer();
+ lib_interface_->op(BT_VND_OP_FW_CFG, nullptr);
+ vstate = VENDOR_STATE_OPENED;
+ ALOGI("%s: VendorInterface::Open done!!!", __func__);
+ } // vendor_mutex_ done
return true;
}
void VendorInterface::Close() {
// These callbacks may send HCI events (vendor-dependent), so make sure to
// StopWatching the file descriptor after this.
+
+ if (vstate != VENDOR_STATE_OPENED) {
+ ALOGW("VendorInterface is not allow close(%d)", vstate);
+ return;
+ }
+ vstate = VENDOR_STATE_CLOSING;
+ ALOGI("%s: VendorInterface::Close", __func__);
+
if (lib_interface_ != nullptr) {
+ lib_interface_->cleanup();
bt_vendor_lpm_mode_t mode = BT_VND_LPM_DISABLE;
lib_interface_->op(BT_VND_OP_LPM_SET_MODE, &mode);
}
- fd_watcher_.StopWatchingFileDescriptors();
+ {
+ std::unique_lock<std::mutex> guard(vendor_mutex_);
- if (hci_ != nullptr) {
- delete hci_;
- hci_ = nullptr;
- }
+ fd_watcher_.StopWatchingFileDescriptors();
+ if (hci_ != nullptr) {
+ delete hci_;
+ hci_ = nullptr;
+ }
- if (lib_interface_ != nullptr) {
- lib_interface_->op(BT_VND_OP_USERIAL_CLOSE, nullptr);
+ if (lib_interface_ != nullptr) {
+ lib_interface_->op(BT_VND_OP_USERIAL_CLOSE, nullptr);
- int power_state = BT_VND_PWR_OFF;
- lib_interface_->op(BT_VND_OP_POWER_CTRL, &power_state);
+ int power_state = BT_VND_PWR_OFF;
+ lib_interface_->op(BT_VND_OP_POWER_CTRL, &power_state);
- lib_interface_->cleanup();
- lib_interface_ = nullptr;
- }
+ lib_interface_ = nullptr;
+ }
- if (lib_handle_ != nullptr) {
- dlclose(lib_handle_);
- lib_handle_ = nullptr;
- }
+ if (lib_handle_ != nullptr) {
+ dlclose(lib_handle_);
+ lib_handle_ = nullptr;
+ }
- if (firmware_startup_timer_ != nullptr) {
- delete firmware_startup_timer_;
- firmware_startup_timer_ = nullptr;
- }
+ if (firmware_startup_timer_ != nullptr) {
+ delete firmware_startup_timer_;
+ firmware_startup_timer_ = nullptr;
+ }
+ vstate = VENDOR_STATE_CLOSED;
+ } // vendor_mutex_ done
+ ALOGI("%s: VendorInterface::Close done!!!", __func__);
}
size_t VendorInterface::Send(uint8_t type, const uint8_t* data, size_t length) {
- std::unique_lock<std::mutex> lock(wakeup_mutex_);
- recent_activity_flag = true;
+ {
+ std::unique_lock<std::mutex> guard(vendor_mutex_);
- if (lpm_wake_deasserted == true) {
- // Restart the timer.
- fd_watcher_.ConfigureTimeout(std::chrono::milliseconds(lpm_timeout_ms),
+ if (vstate != VENDOR_STATE_OPENED) {
+ ALOGW("VendorInterface is not open yet(%d)!", vstate);
+ return 0;
+ }
+ ALOGI("%s: VendorInterface::Send", __func__);
+
+ if (lib_interface_ == nullptr) {
+ ALOGE("lib_interface_ is null");
+ return 0;
+ }
+ recent_activity_flag = true;
+ if (lpm_wake_deasserted == true) {
+ // Restart the timer.
+ fd_watcher_.ConfigureTimeout(std::chrono::milliseconds(lpm_timeout_ms),
[this]() { OnTimeout(); });
- // Assert wake.
- lpm_wake_deasserted = false;
- bt_vendor_lpm_wake_state_t wakeState = BT_VND_LPM_WAKE_ASSERT;
- lib_interface_->op(BT_VND_OP_LPM_WAKE_SET_STATE, &wakeState);
- ALOGV("%s: Sent wake before (%02x)", __func__, data[0] | (data[1] << 8));
- }
+ // Assert wake.
+ lpm_wake_deasserted = false;
+ bt_vendor_lpm_wake_state_t wakeState = BT_VND_LPM_WAKE_ASSERT;
+ lib_interface_->op(BT_VND_OP_LPM_WAKE_SET_STATE, &wakeState);
+ ALOGV("%s: Sent wake before (%02x)", __func__, data[0] | (data[1] << 8));
+ }
- return hci_->Send(type, data, length);
+ return hci_ ? hci_->Send(type, data, length) : 0;
+ } // vendor_mutex_ done
}
void VendorInterface::OnFirmwareConfigured(uint8_t result) {
@@ -339,25 +394,36 @@
firmware_startup_timer_ = nullptr;
}
- if (initialize_complete_cb_ != nullptr) {
- initialize_complete_cb_(result == 0);
- initialize_complete_cb_ = nullptr;
+ {
+ std::unique_lock<std::mutex> guard(initcb_mutex_);
+ ALOGD("%s OnFirmwareConfigured get lock", __func__);
+ if (initialize_complete_cb_ != nullptr) {
+ LOG_ALWAYS_FATAL_IF((result != 0),
+ "%s: Failed to init firmware!", __func__);
+ initialize_complete_cb_(result == 0);
+ }
+ } // initcb_mutex_ done
+
+ if (lib_interface_ != nullptr) {
+ lib_interface_->op(BT_VND_OP_GET_LPM_IDLE_TIMEOUT, &lpm_timeout_ms);
+ ALOGI("%s: lpm_timeout_ms %d", __func__, lpm_timeout_ms);
+
+ bt_vendor_lpm_mode_t mode = BT_VND_LPM_ENABLE;
+ lib_interface_->op(BT_VND_OP_LPM_SET_MODE, &mode);
+
+ ALOGD("%s Calling StartLowPowerWatchdog()", __func__);
+ fd_watcher_.ConfigureTimeout(std::chrono::milliseconds(lpm_timeout_ms),
+ [this]() { OnTimeout(); });
+ }
+ else {
+ ALOGE("lib_interface_ is null");
}
- lib_interface_->op(BT_VND_OP_GET_LPM_IDLE_TIMEOUT, &lpm_timeout_ms);
- ALOGI("%s: lpm_timeout_ms %d", __func__, lpm_timeout_ms);
-
- bt_vendor_lpm_mode_t mode = BT_VND_LPM_ENABLE;
- lib_interface_->op(BT_VND_OP_LPM_SET_MODE, &mode);
-
- ALOGD("%s Calling StartLowPowerWatchdog()", __func__);
- fd_watcher_.ConfigureTimeout(std::chrono::milliseconds(lpm_timeout_ms),
- [this]() { OnTimeout(); });
+ initialize_complete_cb_ = nullptr;
}
void VendorInterface::OnTimeout() {
ALOGV("%s", __func__);
- std::unique_lock<std::mutex> lock(wakeup_mutex_);
if (recent_activity_flag == false) {
lpm_wake_deasserted = true;
bt_vendor_lpm_wake_state_t wakeState = BT_VND_LPM_WAKE_DEASSERT;
diff --git a/bluetooth/1.0/default/vendor_interface.h b/bluetooth/1.0/default/vendor_interface.h
index 040f31a..2df3946 100644
--- a/bluetooth/1.0/default/vendor_interface.h
+++ b/bluetooth/1.0/default/vendor_interface.h
@@ -22,6 +22,8 @@
#include "bt_vendor_lib.h"
#include "hci_protocol.h"
+extern std::mutex initcb_mutex_;
+
namespace android {
namespace hardware {
namespace bluetooth {
@@ -45,10 +47,9 @@
size_t Send(uint8_t type, const uint8_t* data, size_t length);
void OnFirmwareConfigured(uint8_t result);
-
- private:
virtual ~VendorInterface() = default;
+ private:
bool Open(InitializeCompleteCallback initialize_complete_cb,
PacketReadCallback event_cb, PacketReadCallback acl_cb,
PacketReadCallback sco_cb, PacketReadCallback iso_cb);
diff --git a/bluetooth/1.0/vts/OWNERS b/bluetooth/1.0/vts/OWNERS
deleted file mode 100644
index 58d3a66..0000000
--- a/bluetooth/1.0/vts/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-zachoverflow@google.com
-siyuanh@google.com
-mylesgw@google.com
-jpawlowski@google.com
-apanicke@google.com
-stng@google.com
-hsz@google.com
-
diff --git a/bluetooth/1.0/vts/functional/OWNERS b/bluetooth/1.0/vts/functional/OWNERS
deleted file mode 100644
index 7f02612..0000000
--- a/bluetooth/1.0/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 27441
-bluetooth-reviews@google.com
diff --git a/bluetooth/1.1/default/OWNERS b/bluetooth/1.1/default/OWNERS
deleted file mode 100644
index 0c01df6..0000000
--- a/bluetooth/1.1/default/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-zachoverflow@google.com
-mylesgw@google.com
-jpawlowski@google.com
diff --git a/bluetooth/1.1/vts/OWNERS b/bluetooth/1.1/vts/OWNERS
deleted file mode 100644
index ff6fd93..0000000
--- a/bluetooth/1.1/vts/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-zachoverflow@google.com
-siyuanh@google.com
-mylesgw@google.com
-jpawlowski@google.com
-hsz@google.com
-
diff --git a/bluetooth/OWNERS b/bluetooth/OWNERS
new file mode 100644
index 0000000..f401b8c
--- /dev/null
+++ b/bluetooth/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 27441
+
+henrichataing@google.com
+mylesgw@google.com
+siyuanh@google.com
diff --git a/bluetooth/aidl/Android.bp b/bluetooth/aidl/Android.bp
index 07a4c7f..1788ed3 100644
--- a/bluetooth/aidl/Android.bp
+++ b/bluetooth/aidl/Android.bp
@@ -1,5 +1,14 @@
// This is the expected build file, but it may not be right in all cases
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
aidl_interface {
name: "android.hardware.bluetooth",
vendor_available: true,
diff --git a/bluetooth/aidl/default/BluetoothHci.cpp b/bluetooth/aidl/default/BluetoothHci.cpp
index d0ad4a3..4d4896d 100644
--- a/bluetooth/aidl/default/BluetoothHci.cpp
+++ b/bluetooth/aidl/default/BluetoothHci.cpp
@@ -82,7 +82,7 @@
bool getHasDied() const { return has_died_; }
private:
- bool has_died_;
+ bool has_died_{false};
};
void OnDeath(void* cookie) {
diff --git a/bluetooth/audio/utils/Android.bp b/bluetooth/audio/utils/Android.bp
index 674dd11..70797a7 100644
--- a/bluetooth/audio/utils/Android.bp
+++ b/bluetooth/audio/utils/Android.bp
@@ -63,6 +63,31 @@
generated_headers: ["le_audio_codec_capabilities"],
}
+cc_test {
+ name: "BluetoothLeAudioCodecsProviderTest",
+ srcs: [
+ "aidl_session/BluetoothLeAudioCodecsProvider.cpp",
+ "aidl_session/BluetoothLeAudioCodecsProviderTest.cpp",
+ ],
+ header_libs: [
+ "libxsdc-utils",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "android.hardware.bluetooth.audio-V2-ndk",
+ "libxml2",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ test_options: {
+ unit_test: false,
+ },
+ generated_sources: ["le_audio_codec_capabilities"],
+ generated_headers: ["le_audio_codec_capabilities"],
+}
+
xsd_config {
name: "le_audio_codec_capabilities",
srcs: ["le_audio_codec_capabilities/le_audio_codec_capabilities.xsd"],
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
index 855dd28..faebbbf 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
@@ -398,8 +398,11 @@
}
if (kDefaultOffloadLeAudioCapabilities.empty()) {
+ auto le_audio_offload_setting =
+ BluetoothLeAudioCodecsProvider::ParseFromLeAudioOffloadSettingFile();
kDefaultOffloadLeAudioCapabilities =
- BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities();
+ BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
+ le_audio_offload_setting);
}
return kDefaultOffloadLeAudioCapabilities;
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
index bf49270..1dec900 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
@@ -34,20 +34,40 @@
static std::vector<LeAudioCodecCapabilitiesSetting> leAudioCodecCapabilities;
-std::vector<LeAudioCodecCapabilitiesSetting>
-BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities() {
- if (!leAudioCodecCapabilities.empty()) {
- return leAudioCodecCapabilities;
- }
+static bool isInvalidFileContent = false;
- const auto le_audio_offload_setting =
+std::optional<setting::LeAudioOffloadSetting>
+BluetoothLeAudioCodecsProvider::ParseFromLeAudioOffloadSettingFile() {
+ if (!leAudioCodecCapabilities.empty() || isInvalidFileContent) {
+ return std::nullopt;
+ }
+ auto le_audio_offload_setting =
setting::readLeAudioOffloadSetting(kLeAudioCodecCapabilitiesFile);
if (!le_audio_offload_setting.has_value()) {
LOG(ERROR) << __func__ << ": Failed to read "
<< kLeAudioCodecCapabilitiesFile;
+ }
+ return le_audio_offload_setting;
+}
+
+std::vector<LeAudioCodecCapabilitiesSetting>
+BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
+ const std::optional<setting::LeAudioOffloadSetting>&
+ le_audio_offload_setting) {
+ if (!leAudioCodecCapabilities.empty()) {
+ return leAudioCodecCapabilities;
+ }
+
+ if (!le_audio_offload_setting.has_value()) {
+ LOG(ERROR)
+ << __func__
+ << ": input le_audio_offload_setting content need to be non empty";
return {};
}
+ ClearLeAudioCodecCapabilities();
+ isInvalidFileContent = true;
+
std::vector<setting::Scenario> supported_scenarios =
GetScenarios(le_audio_offload_setting);
if (supported_scenarios.empty()) {
@@ -79,9 +99,18 @@
leAudioCodecCapabilities =
ComposeLeAudioCodecCapabilities(supported_scenarios);
+ isInvalidFileContent = leAudioCodecCapabilities.empty();
+
return leAudioCodecCapabilities;
}
+void BluetoothLeAudioCodecsProvider::ClearLeAudioCodecCapabilities() {
+ leAudioCodecCapabilities.clear();
+ configuration_map_.clear();
+ codec_configuration_map_.clear();
+ strategy_configuration_map_.clear();
+}
+
std::vector<setting::Scenario> BluetoothLeAudioCodecsProvider::GetScenarios(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting) {
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h
index 402235f..e879984 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h
@@ -31,8 +31,13 @@
class BluetoothLeAudioCodecsProvider {
public:
+ static std::optional<setting::LeAudioOffloadSetting>
+ ParseFromLeAudioOffloadSettingFile();
static std::vector<LeAudioCodecCapabilitiesSetting>
- GetLeAudioCodecCapabilities();
+ GetLeAudioCodecCapabilities(
+ const std::optional<setting::LeAudioOffloadSetting>&
+ le_audio_offload_setting);
+ static void ClearLeAudioCodecCapabilities();
private:
static inline std::unordered_map<std::string, setting::Configuration>
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProviderTest.cpp b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProviderTest.cpp
new file mode 100644
index 0000000..5393cd7
--- /dev/null
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProviderTest.cpp
@@ -0,0 +1,373 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <optional>
+#include <tuple>
+
+#include "BluetoothLeAudioCodecsProvider.h"
+
+using aidl::android::hardware::bluetooth::audio::BluetoothLeAudioCodecsProvider;
+using aidl::android::hardware::bluetooth::audio::
+ LeAudioCodecCapabilitiesSetting;
+using aidl::android::hardware::bluetooth::audio::setting::AudioLocation;
+using aidl::android::hardware::bluetooth::audio::setting::CodecConfiguration;
+using aidl::android::hardware::bluetooth::audio::setting::
+ CodecConfigurationList;
+using aidl::android::hardware::bluetooth::audio::setting::CodecType;
+using aidl::android::hardware::bluetooth::audio::setting::Configuration;
+using aidl::android::hardware::bluetooth::audio::setting::ConfigurationList;
+using aidl::android::hardware::bluetooth::audio::setting::LeAudioOffloadSetting;
+using aidl::android::hardware::bluetooth::audio::setting::Scenario;
+using aidl::android::hardware::bluetooth::audio::setting::ScenarioList;
+using aidl::android::hardware::bluetooth::audio::setting::StrategyConfiguration;
+using aidl::android::hardware::bluetooth::audio::setting::
+ StrategyConfigurationList;
+
+typedef std::tuple<std::vector<ScenarioList>, std::vector<ConfigurationList>,
+ std::vector<CodecConfigurationList>,
+ std::vector<StrategyConfigurationList>>
+ OffloadSetting;
+
+// Define valid components for each list
+// Scenario
+static const Scenario kValidScenario(std::make_optional("OneChanStereo_16_1"),
+ std::make_optional("OneChanStereo_16_1"));
+// Configuration
+static const Configuration kValidConfigOneChanStereo_16_1(
+ std::make_optional("OneChanStereo_16_1"), std::make_optional("LC3_16k_1"),
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"));
+// CodecConfiguration
+static const CodecConfiguration kValidCodecLC3_16k_1(
+ std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
+ std::nullopt, std::make_optional(16000), std::make_optional(7500),
+ std::make_optional(30), std::nullopt);
+// StrategyConfiguration
+static const StrategyConfiguration kValidStrategyStereoOneCis(
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
+ std::make_optional(AudioLocation::STEREO), std::make_optional(2),
+ std::make_optional(1));
+static const StrategyConfiguration kValidStrategyStereoTwoCis(
+ std::make_optional("STEREO_TWO_CISES_PER_DEVICE"),
+ std::make_optional(AudioLocation::STEREO), std::make_optional(1),
+ std::make_optional(2));
+static const StrategyConfiguration kValidStrategyMonoOneCis(
+ std::make_optional("MONO_ONE_CIS_PER_DEVICE"),
+ std::make_optional(AudioLocation::MONO), std::make_optional(1),
+ std::make_optional(1));
+
+// Define valid test list built from above valid components
+// Scenario, Configuration, CodecConfiguration, StrategyConfiguration
+static const std::vector<ScenarioList> kValidScenarioList = {
+ ScenarioList(std::vector<Scenario>{kValidScenario})};
+static const std::vector<ConfigurationList> kValidConfigurationList = {
+ ConfigurationList(
+ std::vector<Configuration>{kValidConfigOneChanStereo_16_1})};
+static const std::vector<CodecConfigurationList> kValidCodecConfigurationList =
+ {CodecConfigurationList(
+ std::vector<CodecConfiguration>{kValidCodecLC3_16k_1})};
+static const std::vector<StrategyConfigurationList>
+ kValidStrategyConfigurationList = {
+ StrategyConfigurationList(std::vector<StrategyConfiguration>{
+ kValidStrategyStereoOneCis, kValidStrategyStereoTwoCis,
+ kValidStrategyMonoOneCis})};
+
+class BluetoothLeAudioCodecsProviderTest
+ : public ::testing::TestWithParam<OffloadSetting> {
+ public:
+ static std::vector<OffloadSetting> CreateTestCases(
+ const std::vector<ScenarioList>& scenario_lists,
+ const std::vector<ConfigurationList>& configuration_lists,
+ const std::vector<CodecConfigurationList>& codec_configuration_lists,
+ const std::vector<StrategyConfigurationList>&
+ strategy_configuration_lists) {
+ // make each vector in output test_cases has only one element
+ // to match the input of test params
+ // normally only one vector in input has multiple elements
+ // we just split elements in this vector to several vector
+ std::vector<OffloadSetting> test_cases;
+ for (const auto& scenario_list : scenario_lists) {
+ for (const auto& configuration_list : configuration_lists) {
+ for (const auto& codec_configuration_list : codec_configuration_lists) {
+ for (const auto& strategy_configuration_list :
+ strategy_configuration_lists) {
+ test_cases.push_back(CreateTestCase(
+ scenario_list, configuration_list, codec_configuration_list,
+ strategy_configuration_list));
+ }
+ }
+ }
+ }
+ return test_cases;
+ }
+
+ protected:
+ void Initialize() {
+ BluetoothLeAudioCodecsProvider::ClearLeAudioCodecCapabilities();
+ }
+
+ std::vector<LeAudioCodecCapabilitiesSetting> RunTestCase() {
+ auto& [scenario_lists, configuration_lists, codec_configuration_lists,
+ strategy_configuration_lists] = GetParam();
+ LeAudioOffloadSetting le_audio_offload_setting(
+ scenario_lists, configuration_lists, codec_configuration_lists,
+ strategy_configuration_lists);
+ auto le_audio_codec_capabilities =
+ BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
+ std::make_optional(le_audio_offload_setting));
+ return le_audio_codec_capabilities;
+ }
+
+ private:
+ static inline OffloadSetting CreateTestCase(
+ const ScenarioList& scenario_list,
+ const ConfigurationList& configuration_list,
+ const CodecConfigurationList& codec_configuration_list,
+ const StrategyConfigurationList& strategy_configuration_list) {
+ return std::make_tuple(
+ std::vector<ScenarioList>{scenario_list},
+ std::vector<ConfigurationList>{configuration_list},
+ std::vector<CodecConfigurationList>{codec_configuration_list},
+ std::vector<StrategyConfigurationList>{strategy_configuration_list});
+ }
+};
+
+class GetScenariosTest : public BluetoothLeAudioCodecsProviderTest {
+ public:
+ static std::vector<ScenarioList> CreateInvalidScenarios() {
+ std::vector<ScenarioList> invalid_scenario_test_cases;
+ invalid_scenario_test_cases.push_back(ScenarioList(std::vector<Scenario>{
+ Scenario(std::nullopt, std::make_optional("OneChanStereo_16_1"))}));
+
+ invalid_scenario_test_cases.push_back(ScenarioList(std::vector<Scenario>{
+ Scenario(std::make_optional("OneChanStereo_16_1"), std::nullopt)}));
+
+ invalid_scenario_test_cases.push_back(ScenarioList(
+ std::vector<Scenario>{Scenario(std::nullopt, std::nullopt)}));
+
+ invalid_scenario_test_cases.push_back(
+ ScenarioList(std::vector<Scenario>{}));
+
+ return invalid_scenario_test_cases;
+ }
+};
+
+TEST_P(GetScenariosTest, InvalidScenarios) {
+ Initialize();
+ auto le_audio_codec_capabilities = RunTestCase();
+ ASSERT_TRUE(le_audio_codec_capabilities.empty());
+}
+
+class UpdateConfigurationsToMapTest
+ : public BluetoothLeAudioCodecsProviderTest {
+ public:
+ static std::vector<ConfigurationList> CreateInvalidConfigurations() {
+ std::vector<ConfigurationList> invalid_configuration_test_cases;
+ invalid_configuration_test_cases.push_back(
+ ConfigurationList(std::vector<Configuration>{
+ Configuration(std::nullopt, std::make_optional("LC3_16k_1"),
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"))}));
+
+ invalid_configuration_test_cases.push_back(
+ ConfigurationList(std::vector<Configuration>{Configuration(
+ std::make_optional("OneChanStereo_16_1"), std::nullopt,
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"))}));
+
+ invalid_configuration_test_cases.push_back(
+ ConfigurationList(std::vector<Configuration>{
+ Configuration(std::make_optional("OneChanStereo_16_1"),
+ std::make_optional("LC3_16k_1"), std::nullopt)}));
+
+ invalid_configuration_test_cases.push_back(
+ ConfigurationList(std::vector<Configuration>{}));
+
+ return invalid_configuration_test_cases;
+ }
+};
+
+TEST_P(UpdateConfigurationsToMapTest, InvalidConfigurations) {
+ Initialize();
+ auto le_audio_codec_capabilities = RunTestCase();
+ ASSERT_TRUE(le_audio_codec_capabilities.empty());
+}
+
+class UpdateCodecConfigurationsToMapTest
+ : public BluetoothLeAudioCodecsProviderTest {
+ public:
+ static std::vector<CodecConfigurationList>
+ CreateInvalidCodecConfigurations() {
+ std::vector<CodecConfigurationList> invalid_codec_configuration_test_cases;
+ invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
+ std::vector<CodecConfiguration>{CodecConfiguration(
+ std::nullopt, std::make_optional(CodecType::LC3), std::nullopt,
+ std::make_optional(16000), std::make_optional(7500),
+ std::make_optional(30), std::nullopt)}));
+
+ invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
+ std::vector<CodecConfiguration>{CodecConfiguration(
+ std::make_optional("LC3_16k_1"), std::nullopt, std::nullopt,
+ std::make_optional(16000), std::make_optional(7500),
+ std::make_optional(30), std::nullopt)}));
+
+ invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
+ std::vector<CodecConfiguration>{CodecConfiguration(
+ std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
+ std::nullopt, std::nullopt, std::make_optional(7500),
+ std::make_optional(30), std::nullopt)}));
+
+ invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
+ std::vector<CodecConfiguration>{CodecConfiguration(
+ std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
+ std::nullopt, std::make_optional(16000), std::nullopt,
+ std::make_optional(30), std::nullopt)}));
+
+ invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
+ std::vector<CodecConfiguration>{CodecConfiguration(
+ std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
+ std::nullopt, std::make_optional(16000), std::make_optional(7500),
+ std::nullopt, std::nullopt)}));
+
+ invalid_codec_configuration_test_cases.push_back(
+ CodecConfigurationList(std::vector<CodecConfiguration>{}));
+
+ return invalid_codec_configuration_test_cases;
+ }
+};
+
+TEST_P(UpdateCodecConfigurationsToMapTest, InvalidCodecConfigurations) {
+ Initialize();
+ auto le_audio_codec_capabilities = RunTestCase();
+ ASSERT_TRUE(le_audio_codec_capabilities.empty());
+}
+
+class UpdateStrategyConfigurationsToMapTest
+ : public BluetoothLeAudioCodecsProviderTest {
+ public:
+ static std::vector<StrategyConfigurationList>
+ CreateInvalidStrategyConfigurations() {
+ std::vector<StrategyConfigurationList>
+ invalid_strategy_configuration_test_cases;
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(
+ std::vector<StrategyConfiguration>{StrategyConfiguration(
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
+ std::make_optional(AudioLocation::STEREO),
+ std::make_optional(2), std::make_optional(2))}));
+
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(
+ std::vector<StrategyConfiguration>{StrategyConfiguration(
+ std::make_optional("MONO_ONE_CIS_PER_DEVICE"),
+ std::make_optional(AudioLocation::STEREO),
+ std::make_optional(2), std::make_optional(2))}));
+
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(
+ std::vector<StrategyConfiguration>{StrategyConfiguration(
+ std::nullopt, std::make_optional(AudioLocation::STEREO),
+ std::make_optional(2), std::make_optional(1))}));
+
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(
+ std::vector<StrategyConfiguration>{StrategyConfiguration(
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"), std::nullopt,
+ std::make_optional(2), std::make_optional(1))}));
+
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(
+ std::vector<StrategyConfiguration>{StrategyConfiguration(
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
+ std::make_optional(AudioLocation::STEREO), std::nullopt,
+ std::make_optional(1))}));
+
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(
+ std::vector<StrategyConfiguration>{StrategyConfiguration(
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
+ std::make_optional(AudioLocation::STEREO),
+ std::make_optional(2), std::nullopt)}));
+
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(std::vector<StrategyConfiguration>{}));
+
+ return invalid_strategy_configuration_test_cases;
+ }
+};
+
+TEST_P(UpdateStrategyConfigurationsToMapTest, InvalidStrategyConfigurations) {
+ Initialize();
+ auto le_audio_codec_capabilities = RunTestCase();
+ ASSERT_TRUE(le_audio_codec_capabilities.empty());
+}
+
+class ComposeLeAudioCodecCapabilitiesTest
+ : public BluetoothLeAudioCodecsProviderTest {
+ public:
+};
+
+TEST_P(ComposeLeAudioCodecCapabilitiesTest, CodecCapabilitiesNotEmpty) {
+ Initialize();
+ auto le_audio_codec_capabilities = RunTestCase();
+ ASSERT_TRUE(!le_audio_codec_capabilities.empty());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GetScenariosTest);
+INSTANTIATE_TEST_SUITE_P(
+ BluetoothLeAudioCodecsProviderTest, GetScenariosTest,
+ ::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
+ GetScenariosTest::CreateInvalidScenarios(), kValidConfigurationList,
+ kValidCodecConfigurationList, kValidStrategyConfigurationList)));
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UpdateConfigurationsToMapTest);
+INSTANTIATE_TEST_SUITE_P(
+ BluetoothLeAudioCodecsProviderTest, UpdateConfigurationsToMapTest,
+ ::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
+ kValidScenarioList,
+ UpdateConfigurationsToMapTest::CreateInvalidConfigurations(),
+ kValidCodecConfigurationList, kValidStrategyConfigurationList)));
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+ UpdateCodecConfigurationsToMapTest);
+INSTANTIATE_TEST_SUITE_P(
+ BluetoothLeAudioCodecsProviderTest, UpdateCodecConfigurationsToMapTest,
+ ::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
+ kValidScenarioList, kValidConfigurationList,
+ UpdateCodecConfigurationsToMapTest::CreateInvalidCodecConfigurations(),
+ kValidStrategyConfigurationList)));
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+ UpdateStrategyConfigurationsToMapTest);
+INSTANTIATE_TEST_SUITE_P(
+ BluetoothLeAudioCodecsProviderTest, UpdateStrategyConfigurationsToMapTest,
+ ::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
+ kValidScenarioList, kValidConfigurationList,
+ kValidCodecConfigurationList,
+ UpdateStrategyConfigurationsToMapTest::
+ CreateInvalidStrategyConfigurations())));
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+ ComposeLeAudioCodecCapabilitiesTest);
+INSTANTIATE_TEST_SUITE_P(
+ BluetoothLeAudioCodecsProviderTest, ComposeLeAudioCodecCapabilitiesTest,
+ ::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
+ kValidScenarioList, kValidConfigurationList,
+ kValidCodecConfigurationList, kValidStrategyConfigurationList)));
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp b/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp
index ab3c789..93c8376 100644
--- a/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp
+++ b/boot/aidl/vts/functional/VtsHalBootAidlTargetTest.cpp
@@ -33,7 +33,7 @@
using std::unordered_set;
// The main test class for the Boot HIDL HAL.
-class BootHidlTest : public ::testing::TestWithParam<std::string> {
+class BootAidlTest : public ::testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
const auto instance_name = GetParam();
@@ -48,14 +48,14 @@
};
// validity check Boot::getNumberSlots().
-TEST_P(BootHidlTest, GetNumberSlots) {
+TEST_P(BootAidlTest, GetNumberSlots) {
int32_t slots{};
boot->getNumberSlots(&slots);
ASSERT_LE(2, slots);
}
// validity check Boot::getCurrentSlot().
-TEST_P(BootHidlTest, GetCurrentSlot) {
+TEST_P(BootAidlTest, GetCurrentSlot) {
int curSlot = -1;
boot->getCurrentSlot(&curSlot);
int slots = 0;
@@ -64,7 +64,7 @@
}
// validity check Boot::markBootSuccessful().
-TEST_P(BootHidlTest, MarkBootSuccessful) {
+TEST_P(BootAidlTest, MarkBootSuccessful) {
const auto result = boot->markBootSuccessful();
ASSERT_TRUE(result.isOk());
int curSlot = 0;
@@ -74,7 +74,7 @@
ASSERT_TRUE(ret);
}
-TEST_P(BootHidlTest, SetActiveBootSlot) {
+TEST_P(BootAidlTest, SetActiveBootSlot) {
int curSlot = -1;
boot->getCurrentSlot(&curSlot);
ASSERT_GE(curSlot, 0);
@@ -107,7 +107,7 @@
}
}
-TEST_P(BootHidlTest, SetSlotAsUnbootable) {
+TEST_P(BootAidlTest, SetSlotAsUnbootable) {
int curSlot = -1;
boot->getCurrentSlot(&curSlot);
ASSERT_GE(curSlot, 0);
@@ -139,7 +139,7 @@
}
// validity check Boot::isSlotBootable() on good and bad inputs.
-TEST_P(BootHidlTest, IsSlotBootable) {
+TEST_P(BootAidlTest, IsSlotBootable) {
for (int s = 0; s < 2; s++) {
bool bootable = false;
const auto res = boot->isSlotBootable(s, &bootable);
@@ -153,7 +153,7 @@
}
// validity check Boot::isSlotMarkedSuccessful() on good and bad inputs.
-TEST_P(BootHidlTest, IsSlotMarkedSuccessful) {
+TEST_P(BootAidlTest, IsSlotMarkedSuccessful) {
for (int32_t s = 0; s < 2; s++) {
bool isSuccess = false;
const auto res = boot->isSlotMarkedSuccessful(s, &isSuccess);
@@ -166,7 +166,7 @@
}
// validity check Boot::getSuffix() on good and bad inputs.
-TEST_P(BootHidlTest, GetSuffix) {
+TEST_P(BootAidlTest, GetSuffix) {
string suffixStr;
unordered_set<string> suffixes;
int numSlots = 0;
@@ -190,5 +190,6 @@
}
INSTANTIATE_TEST_SUITE_P(
- PerInstance, BootHidlTest,
+ PerInstance, BootAidlTest,
testing::ValuesIn(android::getAidlHalInstanceNames(IBootControl::descriptor)));
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BootAidlTest);
diff --git a/compatibility_matrices/Android.bp b/compatibility_matrices/Android.bp
index 4dc3f6c..37c2820 100644
--- a/compatibility_matrices/Android.bp
+++ b/compatibility_matrices/Android.bp
@@ -28,7 +28,6 @@
"compatibility_matrix.3.xml",
],
kernel_configs: [
- "kernel_config_p_4.9",
"kernel_config_p_4.14",
],
}
@@ -40,7 +39,6 @@
"compatibility_matrix.4.xml",
],
kernel_configs: [
- "kernel_config_q_4.9",
"kernel_config_q_4.14",
"kernel_config_q_4.19",
],
diff --git a/identity/aidl/Android.bp b/identity/aidl/Android.bp
index 9f2e498..f568f7a 100644
--- a/identity/aidl/Android.bp
+++ b/identity/aidl/Android.bp
@@ -18,6 +18,7 @@
"android.hardware.security.keymint-V3",
],
stability: "vintf",
+ frozen: false,
backend: {
java: {
platform_apis: true,
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/B237048744.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/B237048744.aidl
deleted file mode 100644
index 7fee851..0000000
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/B237048744.aidl
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-// the interface (from the latest frozen version), the build system will
-// prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.hardware.identity;
-@VintfStability
-enum B237048744 {
- V5 = 0,
-}
diff --git a/identity/aidl/android/hardware/identity/B237048744.aidl b/identity/aidl/android/hardware/identity/B237048744.aidl
deleted file mode 100644
index 24b16c0..0000000
--- a/identity/aidl/android/hardware/identity/B237048744.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-package android.hardware.identity;
-
-@VintfStability
-enum B237048744 {
- V5 /* bump only includes import changes */,
-}
diff --git a/power/stats/aidl/OWNERS b/power/stats/aidl/OWNERS
index b290b49..e0d66d7 100644
--- a/power/stats/aidl/OWNERS
+++ b/power/stats/aidl/OWNERS
@@ -1,3 +1,4 @@
-bsschwar@google.com
+darrenhsu@google.com
+joaodias@google.com
krossmo@google.com
-tstrudel@google.com
+vincentwang@google.com
diff --git a/radio/aidl/vts/radio_aidl_hal_utils.cpp b/radio/aidl/vts/radio_aidl_hal_utils.cpp
index efc4f26..6ed8e7d 100644
--- a/radio/aidl/vts/radio_aidl_hal_utils.cpp
+++ b/radio/aidl/vts/radio_aidl_hal_utils.cpp
@@ -92,6 +92,10 @@
return testing::checkSubstringInCommandOutput("getprop persist.radio.multisim.config", "dsds");
}
+bool isDsDaEnabled() {
+ return testing::checkSubstringInCommandOutput("getprop persist.radio.multisim.config", "dsda");
+}
+
bool isTsTsEnabled() {
return testing::checkSubstringInCommandOutput("getprop persist.radio.multisim.config", "tsts");
}
diff --git a/radio/aidl/vts/radio_aidl_hal_utils.h b/radio/aidl/vts/radio_aidl_hal_utils.h
index 47976b9..8170a01 100644
--- a/radio/aidl/vts/radio_aidl_hal_utils.h
+++ b/radio/aidl/vts/radio_aidl_hal_utils.h
@@ -104,6 +104,11 @@
bool isDsDsEnabled();
/*
+ * Check if device is in DSDA (Dual SIM Dual Active).
+ */
+bool isDsDaEnabled();
+
+/*
* Check if device is in TSTS (Triple SIM Triple Standby).
*/
bool isTsTsEnabled();
diff --git a/radio/aidl/vts/radio_config_test.cpp b/radio/aidl/vts/radio_config_test.cpp
index 258b172..c979d28 100644
--- a/radio/aidl/vts/radio_config_test.cpp
+++ b/radio/aidl/vts/radio_config_test.cpp
@@ -176,7 +176,7 @@
slotPortMapping.physicalSlotId = -1;
slotPortMapping.portId = -1;
std::vector<SlotPortMapping> slotPortMappingList = {slotPortMapping};
- if (isDsDsEnabled()) {
+ if (isDsDsEnabled() || isDsDaEnabled()) {
slotPortMappingList.push_back(slotPortMapping);
} else if (isTsTsEnabled()) {
slotPortMappingList.push_back(slotPortMapping);
@@ -252,7 +252,7 @@
}
if (isSsSsEnabled()) {
EXPECT_EQ(1, simCount);
- } else if (isDsDsEnabled()) {
+ } else if (isDsDsEnabled() || isDsDaEnabled()) {
EXPECT_EQ(2, simCount);
} else if (isTsTsEnabled()) {
EXPECT_EQ(3, simCount);
diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
index 6efff2f..5a76a21 100644
--- a/security/keymint/aidl/Android.bp
+++ b/security/keymint/aidl/Android.bp
@@ -17,6 +17,7 @@
"android.hardware.security.secureclock-V1",
],
stability: "vintf",
+ frozen: false,
backend: {
java: {
platform_apis: true,
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
index ca517ac..f4c0095 100644
--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -798,7 +798,7 @@
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serial");
+ add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serialno");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER,
"ro.product.manufacturer");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
diff --git a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
index 1dc5df3..cd140c8 100644
--- a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
+++ b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -252,7 +252,7 @@
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serial");
+ add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serialno");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER,
"ro.product.manufacturer");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index 2194529..69fe434 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -1990,7 +1990,7 @@
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
- add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serial");
+ add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serialno");
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_MANUFACTURER, "ro.product.manufacturer");
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
diff --git a/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp b/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp
index fa50821..0924da7 100644
--- a/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp
+++ b/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp
@@ -170,7 +170,6 @@
if ((functions & GadgetFunction::RNDIS) != 0) {
ALOGI("setCurrentUsbFunctions rndis");
- if (linkFunction("gsi.rndis", (*functionCount)++)) return Status::ERROR;
std::string rndisFunction = GetProperty(kVendorRndisConfig, "");
if (rndisFunction != "") {
if (linkFunction(rndisFunction.c_str(), (*functionCount)++)) return Status::ERROR;
diff --git a/uwb/aidl/Android.bp b/uwb/aidl/Android.bp
index 52f0605..7dc2b7f 100755
--- a/uwb/aidl/Android.bp
+++ b/uwb/aidl/Android.bp
@@ -47,6 +47,7 @@
aidl_interface {
name: "android.hardware.uwb.fira_android",
+ owner: "google",
vendor_available: true,
srcs: ["android/hardware/uwb/fira_android/*.aidl"],
stability: "vintf",
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
index cbe2068..39bb5d9 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
@@ -44,4 +44,8 @@
CCC_SUPPORTED_PULSE_SHAPE_COMBOS = 166,
CCC_SUPPORTED_RAN_MULTIPLIER = 167,
SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 227,
+ SUPPORTED_MIN_RANGING_INTERVAL_MS = 228,
+ SUPPORTED_RANGE_DATA_NTF_CONFIG = 229,
+ SUPPORTED_RSSI_REPORTING = 230,
+ SUPPORTED_DIAGNOSTICS = 231,
}
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
index cd2e122..fbcfbff 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
@@ -36,4 +36,5 @@
enum UwbVendorGidAndroidOids {
ANDROID_GET_POWER_STATS = 0,
ANDROID_SET_COUNTRY_CODE = 1,
+ ANDROID_RANGE_DIAGNOSTICS = 2,
}
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
index d35728f..8413f06 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
@@ -43,4 +43,6 @@
NB_OF_RANGE_MEASUREMENTS = 227,
NB_OF_AZIMUTH_MEASUREMENTS = 228,
NB_OF_ELEVATION_MEASUREMENTS = 229,
+ ENABLE_DIAGNOSTICS = 232,
+ DIAGRAMS_FRAME_REPORTS_FIELDS = 233,
}
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
index 0140fdd..86479fb 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
@@ -150,4 +150,33 @@
* 0 - Feature not supported.
*/
SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 0xE3,
+
+ /**
+ * 4 byte value to indicate supported min ranging interval in ms.
+ */
+ SUPPORTED_MIN_RANGING_INTERVAL_MS = 0xE4,
+
+ /**
+ * 4 byte bitmask to indicate the supported RANGE_DATA_NTF_CONFIG values
+ *
+ * Bitmask where each bit corresponds to values used in
+ * RANGE_DATA_NTF_CONFIG in SET_APP_CFG_CMD
+ */
+ SUPPORTED_RANGE_DATA_NTF_CONFIG = 0xE5,
+
+ /**
+ * 1 byte bitmask to indicate the supported RSSI_REPORTING values
+ * Values:
+ * 1 - Feature supported.
+ * 0 - Feature not supported.
+ */
+ SUPPORTED_RSSI_REPORTING = 0xE6,
+
+ /**
+ * 1 byte value to indicate support for diagnostics feature.
+ * Values:
+ * 1 - Feature supported.
+ * 0 - Feature not supported.
+ */
+ SUPPORTED_DIAGNOSTICS = 0xE7,
}
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
index e389a2d..4768f55 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
@@ -30,5 +30,11 @@
ANDROID_GET_POWER_STATS = 0x0,
// Used to set the current regulatory country code (determined usinag
// SIM or hardcoded by OEM).
+ // Country code is sent as a 2 byte value corresponding to ISO-3166 country code.
+ // Note: "00" is used to indicate that the country code is unknown.
ANDROID_SET_COUNTRY_CODE = 0x1,
+ // Used by the notification to get UWB ranging diagnostics stats.
+ // Supported only if the UwbVendorCapabilityTlvTypes.SUPPORTED_DIAGNOSTICS set
+ // to 1.
+ ANDROID_RANGE_DIAGNOSTICS = 0x2,
}
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
index f43b249..f303ed9 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
@@ -66,4 +66,23 @@
NB_OF_AZIMUTH_MEASUREMENTS = 0xE4,
/** 1 byte data */
NB_OF_ELEVATION_MEASUREMENTS = 0xE5,
+
+ /**
+ * Supported only if the UwbVendorCapabilityTlvTypes
+ * .SUPPORTED_DIAGNOSTICS set to 1.
+ */
+ /**
+ * 1 byte data
+ * 1 - Enable,
+ * 0 - Disable
+ */
+ ENABLE_DIAGNOSTICS = 0xE8,
+ /**
+ * 1 byte bitmask
+ * b0: Activate RSSIs field,
+ * b1: Activate AoAs field,
+ * b2: Activate CIRs field,
+ * b3 - b7: RFU
+ */
+ DIAGRAMS_FRAME_REPORTS_FIELDS = 0xE9,
}