Merge "Revert "Add GESTURE_{X,Y}_OFFSET axes to input HAL""
diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp
index 92d7d54..691cf34 100644
--- a/audio/aidl/Android.bp
+++ b/audio/aidl/Android.bp
@@ -113,9 +113,12 @@
         "android/hardware/audio/core/AudioRoute.aidl",
         "android/hardware/audio/core/IConfig.aidl",
         "android/hardware/audio/core/IModule.aidl",
+        "android/hardware/audio/core/IStreamCallback.aidl",
         "android/hardware/audio/core/IStreamIn.aidl",
         "android/hardware/audio/core/IStreamOut.aidl",
         "android/hardware/audio/core/ITelephony.aidl",
+        "android/hardware/audio/core/MicrophoneDynamicInfo.aidl",
+        "android/hardware/audio/core/MicrophoneInfo.aidl",
         "android/hardware/audio/core/MmapBufferDescriptor.aidl",
         "android/hardware/audio/core/ModuleDebug.aidl",
         "android/hardware/audio/core/StreamDescriptor.aidl",
diff --git a/audio/aidl/TEST_MAPPING b/audio/aidl/TEST_MAPPING
new file mode 100644
index 0000000..6473d23
--- /dev/null
+++ b/audio/aidl/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "VtsHalAudioCoreTargetTest"
+    }
+  ]
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
index be382c5..0c7ca27 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
@@ -56,6 +56,7 @@
   void setMasterVolume(float volume);
   boolean getMicMute();
   void setMicMute(boolean mute);
+  android.hardware.audio.core.MicrophoneInfo[] getMicrophones();
   void updateAudioMode(android.hardware.audio.core.AudioMode mode);
   void updateScreenRotation(android.hardware.audio.core.IModule.ScreenRotation rotation);
   void updateScreenState(boolean isTurnedOn);
@@ -76,6 +77,7 @@
     android.hardware.audio.common.SourceMetadata sourceMetadata;
     @nullable android.media.audio.common.AudioOffloadInfo offloadInfo;
     long bufferSizeFrames;
+    @nullable android.hardware.audio.core.IStreamCallback callback;
   }
   @VintfStability
   parcelable OpenOutputStreamReturn {
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamCallback.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamCallback.aidl
new file mode 100644
index 0000000..5a2ab78
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamCallback.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.audio.core;
+@VintfStability
+interface IStreamCallback {
+  oneway void onTransferReady();
+  oneway void onError();
+  oneway void onDrainReady();
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamIn.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamIn.aidl
index d5ab3e8..e9c727f 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamIn.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamIn.aidl
@@ -35,5 +35,20 @@
 @VintfStability
 interface IStreamIn {
   void close();
+  android.hardware.audio.core.MicrophoneDynamicInfo[] getActiveMicrophones();
+  android.hardware.audio.core.IStreamIn.MicrophoneDirection getMicrophoneDirection();
+  void setMicrophoneDirection(android.hardware.audio.core.IStreamIn.MicrophoneDirection direction);
+  float getMicrophoneFieldDimension();
+  void setMicrophoneFieldDimension(float zoom);
   void updateMetadata(in android.hardware.audio.common.SinkMetadata sinkMetadata);
+  const int MIC_FIELD_DIMENSION_WIDE_ANGLE = -1;
+  const int MIC_FIELD_DIMENSION_NO_ZOOM = 0;
+  const int MIC_FIELD_DIMENSION_MAX_ZOOM = 1;
+  @Backing(type="int") @VintfStability
+  enum MicrophoneDirection {
+    UNSPECIFIED = 0,
+    FRONT = 1,
+    BACK = 2,
+    EXTERNAL = 3,
+  }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MicrophoneDynamicInfo.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MicrophoneDynamicInfo.aidl
new file mode 100644
index 0000000..50a5528
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MicrophoneDynamicInfo.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.audio.core;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable MicrophoneDynamicInfo {
+  @utf8InCpp String id;
+  android.hardware.audio.core.MicrophoneDynamicInfo.ChannelMapping[] channelMapping;
+  @Backing(type="int") @VintfStability
+  enum ChannelMapping {
+    UNUSED = 0,
+    DIRECT = 1,
+    PROCESSED = 2,
+  }
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MicrophoneInfo.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MicrophoneInfo.aidl
new file mode 100644
index 0000000..68c7f88
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MicrophoneInfo.aidl
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.audio.core;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable MicrophoneInfo {
+  @utf8InCpp String id;
+  android.media.audio.common.AudioDevice device;
+  android.hardware.audio.core.MicrophoneInfo.Location location = android.hardware.audio.core.MicrophoneInfo.Location.UNKNOWN;
+  int group = -1;
+  int indexInTheGroup = -1;
+  @nullable android.hardware.audio.core.MicrophoneInfo.Sensitivity sensitivity;
+  android.hardware.audio.core.MicrophoneInfo.Directionality directionality = android.hardware.audio.core.MicrophoneInfo.Directionality.UNKNOWN;
+  android.hardware.audio.core.MicrophoneInfo.FrequencyResponsePoint[] frequencyResponse;
+  @nullable android.hardware.audio.core.MicrophoneInfo.Coordinate position;
+  @nullable android.hardware.audio.core.MicrophoneInfo.Coordinate orientation;
+  const int GROUP_UNKNOWN = -1;
+  const int INDEX_IN_THE_GROUP_UNKNOWN = -1;
+  @Backing(type="int") @VintfStability
+  enum Location {
+    UNKNOWN = 0,
+    MAINBODY = 1,
+    MAINBODY_MOVABLE = 2,
+    PERIPHERAL = 3,
+  }
+  @VintfStability
+  parcelable Sensitivity {
+    float leveldBFS;
+    float maxSpldB;
+    float minSpldB;
+  }
+  @Backing(type="int") @VintfStability
+  enum Directionality {
+    UNKNOWN = 0,
+    OMNI = 1,
+    BI_DIRECTIONAL = 2,
+    CARDIOID = 3,
+    HYPER_CARDIOID = 4,
+    SUPER_CARDIOID = 5,
+  }
+  @VintfStability
+  parcelable FrequencyResponsePoint {
+    float frequencyHz;
+    float leveldB;
+  }
+  @VintfStability
+  parcelable Coordinate {
+    float x;
+    float y;
+    float z;
+  }
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl
index 80ee185..467d37b 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl
@@ -35,4 +35,5 @@
 @JavaDerive(equals=true, toString=true) @VintfStability
 parcelable ModuleDebug {
   boolean simulateDeviceConnections;
+  int streamTransientStateDelayMs;
 }
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 3a77ad1..3a4271b 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
@@ -42,8 +42,9 @@
   const int LATENCY_UNKNOWN = -1;
   @FixedSize @VintfStability
   parcelable Position {
-    long frames;
-    long timeNs;
+    long frames = -1;
+    long timeNs = -1;
+    const long UNKNOWN = -1;
   }
   @Backing(type="int") @VintfStability
   enum State {
@@ -53,14 +54,23 @@
     PAUSED = 4,
     DRAINING = 5,
     DRAIN_PAUSED = 6,
+    TRANSFERRING = 7,
+    TRANSFER_PAUSED = 8,
     ERROR = 100,
   }
+  @Backing(type="byte") @VintfStability
+  enum DrainMode {
+    DRAIN_UNSPECIFIED = 0,
+    DRAIN_ALL = 1,
+    DRAIN_EARLY_NOTIFY = 2,
+  }
   @FixedSize @VintfStability
   union Command {
-    int hal_reserved_exit;
+    int halReservedExit;
+    android.media.audio.common.Void getStatus;
     android.media.audio.common.Void start;
     int burst;
-    android.media.audio.common.Void drain;
+    android.hardware.audio.core.StreamDescriptor.DrainMode drain;
     android.media.audio.common.Void standby;
     android.media.audio.common.Void pause;
     android.media.audio.common.Void flush;
diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl
index be40051..786d5ee 100644
--- a/audio/aidl/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/android/hardware/audio/core/IModule.aidl
@@ -21,9 +21,11 @@
 import android.hardware.audio.core.AudioMode;
 import android.hardware.audio.core.AudioPatch;
 import android.hardware.audio.core.AudioRoute;
+import android.hardware.audio.core.IStreamCallback;
 import android.hardware.audio.core.IStreamIn;
 import android.hardware.audio.core.IStreamOut;
 import android.hardware.audio.core.ITelephony;
+import android.hardware.audio.core.MicrophoneInfo;
 import android.hardware.audio.core.ModuleDebug;
 import android.hardware.audio.core.StreamDescriptor;
 import android.media.audio.common.AudioOffloadInfo;
@@ -53,9 +55,13 @@
      * the HAL module behavior that would otherwise require human intervention.
      *
      * The HAL module must throw an error if there is an attempt to change
-     * the debug behavior for the aspect which is currently in use.
+     * the debug behavior for the aspect which is currently in use, or when
+     * the value of any of the debug flags is invalid. See 'ModuleDebug' for
+     * the full list of constraints.
      *
      * @param debug The debug options.
+     * @throws EX_ILLEGAL_ARGUMENT If some of the configuration parameters are
+     *                             invalid.
      * @throws EX_ILLEGAL_STATE If the flag(s) being changed affect functionality
      *                          which is currently in use.
      */
@@ -316,9 +322,13 @@
      * 'setAudioPortConfig' method. Existence of an audio patch involving this
      * port configuration is not required for successful opening of a stream.
      *
-     * If the port configuration has 'COMPRESS_OFFLOAD' output flag set,
-     * the framework must provide additional information about the encoded
-     * audio stream in 'offloadInfo' argument.
+     * If the port configuration has the 'COMPRESS_OFFLOAD' output flag set,
+     * the client must provide additional information about the encoded
+     * audio stream in the 'offloadInfo' argument.
+     *
+     * If the port configuration has the 'NON_BLOCKING' output flag set,
+     * the client must provide a callback for asynchronous notifications
+     * in the 'callback' argument.
      *
      * The requested buffer size is expressed in frames, thus the actual size
      * in bytes depends on the audio port configuration. Also, the HAL module
@@ -354,6 +364,8 @@
      *                             - If the offload info is not provided for an offload
      *                               port configuration.
      *                             - If a buffer of the requested size can not be provided.
+     *                             - If the callback is not provided for a non-blocking
+     *                               port configuration.
      * @throws EX_ILLEGAL_STATE In the following cases:
      *                          - If the port config already has a stream opened on it.
      *                          - If the limit on the open stream count for the port has
@@ -372,6 +384,8 @@
         @nullable AudioOffloadInfo offloadInfo;
         /** Requested audio I/O buffer minimum size, in frames. */
         long bufferSizeFrames;
+        /** Client callback interface for the non-blocking output mode. */
+        @nullable IStreamCallback callback;
     }
     @VintfStability
     parcelable OpenOutputStreamReturn {
@@ -592,6 +606,22 @@
     void setMicMute(boolean mute);
 
     /**
+     * Provide information describing built-in microphones of the HAL module.
+     *
+     * If there are no built-in microphones in the HAL module, it must return an
+     * empty vector. If there are microphones, but the HAL module does not
+     * possess the required information about them, EX_UNSUPPORTED_OPERATION
+     * must be thrown.
+     *
+     * If this method is supported by the HAL module, it must also support
+     * 'IStreamIn.getActiveMicrophones' method.
+     *
+     * @return The vector with information about each microphone.
+     * @throws EX_UNSUPPORTED_OPERATION If the information is unavailable.
+     */
+    MicrophoneInfo[] getMicrophones();
+
+    /**
      * Notify the HAL module on the change of the current audio mode.
      *
      * The current audio mode is always controlled by the client. This is an
diff --git a/audio/aidl/android/hardware/audio/core/IStreamCallback.aidl b/audio/aidl/android/hardware/audio/core/IStreamCallback.aidl
new file mode 100644
index 0000000..440ab25
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/IStreamCallback.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.core;
+
+/**
+ * This interface is used to indicate completion of asynchronous operations.
+ * See the state machines referenced by StreamDescriptor for details.
+ */
+@VintfStability
+oneway interface IStreamCallback {
+    /**
+     * Indicate that the stream is ready for next data exchange.
+     */
+    void onTransferReady();
+    /**
+     * Indicate that an irrecoverable error has occurred during the last I/O
+     * operation. After sending this callback, the stream enters the 'ERROR'
+     * state.
+     */
+    void onError();
+    /**
+     * Indicate that the stream has finished draining. This is only used
+     * for output streams because for input streams draining is performed
+     * by the client.
+     */
+    void onDrainReady();
+}
diff --git a/audio/aidl/android/hardware/audio/core/IStreamIn.aidl b/audio/aidl/android/hardware/audio/core/IStreamIn.aidl
index 0c3e3d1..0b6e02c 100644
--- a/audio/aidl/android/hardware/audio/core/IStreamIn.aidl
+++ b/audio/aidl/android/hardware/audio/core/IStreamIn.aidl
@@ -17,6 +17,7 @@
 package android.hardware.audio.core;
 
 import android.hardware.audio.common.SinkMetadata;
+import android.hardware.audio.core.MicrophoneDynamicInfo;
 
 /**
  * This interface provides means for receiving audio data from input devices.
@@ -39,6 +40,92 @@
     void close();
 
     /**
+     * Provides information on the microphones that are active for this stream.
+     *
+     * The returned array contains dynamic information on the microphones which
+     * are active for this stream. Each entry in the returned array must have a
+     * corresponding entry (matched by the 'MicrophoneInfo.id' field value) in
+     * the list of all available microphones which is provided by the
+     * 'IModule.getMicrophones' method.
+     *
+     * This method must be supported by the HAL module if
+     * 'IModule.getMicrophones' is supported.
+     *
+     * @return The vector with dynamic information on the microphones.
+     * @throws EX_ILLEGAL_STATE If the stream has already been closed.
+     * @throws EX_UNSUPPORTED_OPERATION If the information is unavailable.
+     */
+    MicrophoneDynamicInfo[] getActiveMicrophones();
+
+    @VintfStability
+    @Backing(type="int")
+    enum MicrophoneDirection {
+        /**
+         * Don't do any directionality processing of the activated microphone(s).
+         */
+        UNSPECIFIED = 0,
+        /**
+         * Optimize capture for audio coming from the screen-side of the device.
+         */
+        FRONT = 1,
+        /**
+         * Optimize capture for audio coming from the side of the device opposite the screen.
+         */
+        BACK = 2,
+        /**
+         * Optimize capture for audio coming from an off-device microphone.
+         */
+        EXTERNAL = 3,
+    }
+    /**
+     * Get the current logical microphone direction.
+     *
+     * @return The current logical microphone direction.
+     * @throws EX_ILLEGAL_STATE If the stream has already been closed.
+     * @throws EX_UNSUPPORTED_OPERATION If the information is unavailable.
+     */
+    MicrophoneDirection getMicrophoneDirection();
+    /**
+     * Set the current logical microphone direction.
+     *
+     * The client sets this parameter in order to specify its preference for
+     * optimizing the direction of capture when multiple microphones are in use.
+     *
+     * @param direction The preferred capture direction.
+     * @throws EX_ILLEGAL_STATE If the stream has already been closed.
+     * @throws EX_UNSUPPORTED_OPERATION If the operation is not supported.
+     */
+    void setMicrophoneDirection(MicrophoneDirection direction);
+
+    const int MIC_FIELD_DIMENSION_WIDE_ANGLE = -1;
+    const int MIC_FIELD_DIMENSION_NO_ZOOM = 0;
+    const int MIC_FIELD_DIMENSION_MAX_ZOOM = 1;
+    /**
+     * Get the "zoom factor" for the logical microphone.
+     *
+     * The returned value must be within the range of [-1.0, 1.0] (see
+     * MIC_FIELD_DIMENSION_* constants).
+     *
+     * @throws EX_ILLEGAL_STATE If the stream has already been closed.
+     * @throws EX_UNSUPPORTED_OPERATION If the information is unavailable.
+     */
+    float getMicrophoneFieldDimension();
+    /**
+     * Set the "zoom factor" for the logical microphone.
+     *
+     * If multiple microphones are in use, the provided zoom factor must be
+     * treated as a preference for their combined field dimension. The zoom
+     * factor must be within the range of [-1.0, 1.0] (see MIC_FIELD_DIMENSION_*
+     * constants).
+     *
+     * @param zoom The preferred field dimension of the microphone capture.
+     * @throws EX_ILLEGAL_ARGUMENT If the dimension value is outside of the range.
+     * @throws EX_ILLEGAL_STATE If the stream has already been closed.
+     * @throws EX_UNSUPPORTED_OPERATION If the operation is not supported.
+     */
+    void setMicrophoneFieldDimension(float zoom);
+
+    /**
      * Update stream metadata.
      *
      * Updates the metadata initially provided at the stream creation.
diff --git a/audio/aidl/android/hardware/audio/core/MicrophoneDynamicInfo.aidl b/audio/aidl/android/hardware/audio/core/MicrophoneDynamicInfo.aidl
new file mode 100644
index 0000000..36cc51f
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/MicrophoneDynamicInfo.aidl
@@ -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.
+ */
+
+package android.hardware.audio.core;
+
+/**
+ * Structure providing dynamic information on a microphone. This information
+ * changes between recording sessions.
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable MicrophoneDynamicInfo {
+    /**
+     * Unique alphanumeric id for the microphone. It must match the id of one of
+     * the 'MicrophoneInfo' entries returned by 'IModule.getMicrophones'.
+     */
+    @utf8InCpp String id;
+
+    @VintfStability
+    @Backing(type="int")
+    enum ChannelMapping {
+        /** Channel not used. */
+        UNUSED = 0,
+        /** Channel is used and the signal is not processed. */
+        DIRECT = 1,
+        /** Channel is used and the signal has some processing. */
+        PROCESSED = 2,
+    }
+    /**
+     * The vector is indexes by zero-based channels of the microphone, thus the
+     * element '0' corresponds to the first channel, '1' is the second, etc. The
+     * vector must contain at least 1 element.
+     */
+    ChannelMapping[] channelMapping;
+}
diff --git a/audio/aidl/android/hardware/audio/core/MicrophoneInfo.aidl b/audio/aidl/android/hardware/audio/core/MicrophoneInfo.aidl
new file mode 100644
index 0000000..3b8c7f3
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/MicrophoneInfo.aidl
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.core;
+
+import android.media.audio.common.AudioDevice;
+
+/**
+ * Structure providing static information on a microphone. This information
+ * never changes during the lifetime of the IModule which owns the microphone.
+ * The information presented in this structure indicates the location and
+ * orientation of the microphone on the device as well as useful information
+ * like frequency response and sensitivity.
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable MicrophoneInfo {
+    /**
+     * Unique alphanumeric id for the microphone. It must remain the same across
+     * device reboots. The client must never attempt to parse the value of this
+     * field.
+     */
+    @utf8InCpp String id;
+    /**
+     * Describes the location of the microphone in terms of managed audio devices.
+     */
+    AudioDevice device;
+
+    @VintfStability
+    @Backing(type="int")
+    enum Location {
+        /** Microphone location is unknown. */
+        UNKNOWN = 0,
+        /** The microphone is located on the main body of the device. */
+        MAINBODY = 1,
+        /** The microphone is located on a movable main body of the device. */
+        MAINBODY_MOVABLE = 2,
+        /** The microphone is located on a peripheral. */
+        PERIPHERAL = 3,
+    }
+    /** Location of the microphone in regard to the body of the device */
+    Location location = Location.UNKNOWN;
+
+    /**
+     * This value is used when the group of the microphone is unknown.
+     */
+    const int GROUP_UNKNOWN = -1;
+    /**
+     * An identifier to group related microphones together, for example,
+     * microphones of a microphone array should all belong to the same group.
+     * Note that microphones assigned to 'GROUP_UNKNOWN' do not form a group.
+     */
+    int group = GROUP_UNKNOWN;
+    /**
+     * This value is used when the index in the group of the microphone is
+     * unknown.
+     */
+    const int INDEX_IN_THE_GROUP_UNKNOWN = -1;
+    /**
+     * Index of this microphone within the group. The pair (group, index) must
+     * be unique within the same HAL module, except the pair
+     * (GROUP_UNKNOWN, INDEX_IN_THE_GROUP_UNKNOWN).
+     */
+    int indexInTheGroup = INDEX_IN_THE_GROUP_UNKNOWN;
+
+    @VintfStability
+    parcelable Sensitivity {
+        /** Level in dBFS produced by a 1000 Hz tone at 94 dB SPL. */
+        float leveldBFS;
+        /** Level in dB of the max SPL supported at 1000 Hz */
+        float maxSpldB;
+        /** Level in dB of the min SPL supported at 1000 Hz */
+        float minSpldB;
+    }
+    /**
+     * If provided, must describe acceptable sound pressure levels (SPL)
+     * for a 1 kHz sine wave, and the resulting level in dBFS.
+     */
+    @nullable Sensitivity sensitivity;
+
+    @VintfStability
+    @Backing(type="int")
+    enum Directionality {
+        UNKNOWN = 0,
+        OMNI = 1,
+        BI_DIRECTIONAL = 2,
+        CARDIOID = 3,
+        HYPER_CARDIOID = 4,
+        SUPER_CARDIOID = 5,
+    }
+    /**
+     * The standard polar pattern of the microphone.
+     */
+    Directionality directionality = Directionality.UNKNOWN;
+
+    /**
+     * A (frequency, level) pair. Used to represent frequency response.
+     */
+    @VintfStability
+    parcelable FrequencyResponsePoint {
+        float frequencyHz;
+        float leveldB;
+    }
+    /**
+     * Vector with ordered frequency responses (from low to high frequencies)
+     * with the frequency response of the microphone. Levels are in dB,
+     * relative to level at 1000 Hz.
+     */
+    FrequencyResponsePoint[] frequencyResponse;
+
+    /**
+     * A 3D point used to represent position or orientation of a microphone.
+     */
+    @VintfStability
+    parcelable Coordinate {
+        float x;
+        float y;
+        float z;
+    }
+    /**
+     * If provided, must specify distances of the microphone's capsule, in
+     * meters, from the bottom-left-back corner of the bounding box of device in
+     * its natural orientation (PORTRAIT for phones, LANDSCAPE for tablets, TVs,
+     * etc).
+     */
+    @nullable Coordinate position;
+    /**
+     * If provided, describes the normalized point which defines the main
+     * orientation of the microphone's capsule.
+     * Magnitude = sqrt(x^2 + y^2 + z^2) = 1.
+     */
+    @nullable Coordinate orientation;
+}
diff --git a/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl b/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl
index 858a9bd..871a5c9 100644
--- a/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl
+++ b/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl
@@ -35,4 +35,19 @@
      *    profiles.
      */
     boolean simulateDeviceConnections;
+    /**
+     * Must be non-negative. When set to non-zero, HAL module must delay
+     * transition from "transient" stream states (see StreamDescriptor.aidl)
+     * by the specified amount of milliseconds. The purpose of this delay
+     * is to allow VTS to test sending of stream commands while the stream is
+     * in a transient state. The delay must apply to newly created streams,
+     * it is not required to apply the delay to already opened streams.
+     *
+     * Note: the drawback of enabling this delay for asynchronous (non-blocking)
+     *       modes is that sending of callbacks will also be delayed, because
+     *       callbacks are sent once the stream state machine exits a transient
+     *       state. Thus, it's not recommended to use it with tests that require
+     *       waiting for an async callback.
+     */
+    int streamTransientStateDelayMs;
 }
diff --git a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
index 2b1fc99..65ea9ef 100644
--- a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
+++ b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
@@ -84,13 +84,13 @@
  *     are different.
  *
  * State machines of both input and output streams start from the 'STANDBY'
- * state.  Transitions between states happen naturally with changes in the
+ * 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.
+ * gets empty, thus it's a "transient" state.
  *
  * 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
@@ -100,6 +100,28 @@
  * 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.
+ *
+ * Non-blocking (asynchronous) modes introduce a new 'TRANSFERRING' state, which
+ * the state machine can enter after replying to the 'burst' command, instead of
+ * staying in the 'ACTIVE' state. In this case the client gets unblocked
+ * earlier, while the actual audio delivery to / from the observer is not
+ * complete yet. Once the HAL module is ready for the next transfer, it notifies
+ * the client via a oneway callback, and the machine switches to 'ACTIVE'
+ * state. The 'TRANSFERRING' state is thus "transient", similar to the
+ * 'DRAINING' state. For output streams, asynchronous transfer can be paused,
+ * and it's another new state: 'TRANSFER_PAUSED'. It differs from 'PAUSED' by
+ * the fact that no new writes are allowed. Please see 'stream-in-async-sm.gv'
+ * and 'stream-out-async-sm.gv' files for details. Below is the table summary
+ * for asynchronous only-states:
+ *
+ *  Producer | Buffer state | Consumer | Applies | State
+ *  active?  |              | active?  | to      |
+ * ==========|==============|==========|=========|==============================
+ *  Yes      | Not empty    | Yes      | Both    | TRANSFERRING, s/w x-runs counted
+ * ----------|--------------|----------|---------|-----------------------------
+ *  Yes      | Not empty    | No       | Output  | TRANSFER_PAUSED,
+ *           |              |          |         | h/w emits silence.
+ *
  */
 @JavaDerive(equals=true, toString=true)
 @VintfStability
@@ -116,10 +138,15 @@
     @VintfStability
     @FixedSize
     parcelable Position {
+        /**
+         * The value used when the position can not be reported by the HAL
+         * module.
+         */
+        const long UNKNOWN = -1;
         /** Frame count. */
-        long frames;
+        long frames = UNKNOWN;
         /** Timestamp in nanoseconds. */
-        long timeNs;
+        long timeNs = UNKNOWN;
     }
 
     @VintfStability
@@ -166,11 +193,24 @@
         /**
          * 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
+         * new data. If it emits a 'burst' command, this brings the stream
          * into the regular 'PAUSED' state.
          */
         DRAIN_PAUSED = 6,
         /**
+         * Used only for streams in asynchronous mode. The stream enters this
+         * state after receiving a 'burst' command and returning control back
+         * to the client, thus unblocking it.
+         */
+        TRANSFERRING = 7,
+        /**
+         * Used only for output streams in asynchronous mode only. The stream
+         * enters this state after receiving a 'pause' command while being in
+         * the 'TRANSFERRING' state. Unlike 'PAUSED' state, this state does not
+         * accept new writes.
+         */
+        TRANSFER_PAUSED = 8,
+        /**
          * 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.
@@ -178,6 +218,29 @@
         ERROR = 100,
     }
 
+    @VintfStability
+    @Backing(type="byte")
+    enum DrainMode {
+        /**
+         * Unspecified—used with input streams only, because the client controls
+         * draining.
+         */
+        DRAIN_UNSPECIFIED = 0,
+        /**
+         * Used with output streams only, the HAL module indicates drain
+         * completion when all remaining audio data has been consumed.
+         */
+        DRAIN_ALL = 1,
+        /**
+         * Used with output streams only, the HAL module indicates drain
+         * completion shortly before all audio data has been consumed in order
+         * to give the client an opportunity to provide data for the next track
+         * for gapless playback. The exact amount of provided time is specific
+         * to the HAL implementation.
+         */
+        DRAIN_EARLY_NOTIFY = 2,
+    }
+
     /**
      * Used for sending commands to the HAL module. The client writes into
      * the queue, the HAL module reads. The queue can only contain a single
@@ -198,7 +261,14 @@
          * implementation must pass a random cookie as the command argument,
          * which is only known to the implementation.
          */
-        int hal_reserved_exit;
+        int halReservedExit;
+        /**
+         * Retrieve the current state of the stream. This command must be
+         * processed by the stream in any state. The stream must provide current
+         * positions, counters, and its state in the reply. This command must be
+         * handled by the HAL module without any observable side effects.
+         */
+        Void getStatus;
         /**
          * See the state machines on the applicability of this command to
          * different states.
@@ -215,15 +285,14 @@
          *    read from the hardware into the 'audio.fmq' queue.
          *
          * In both cases it is allowed for this field to contain any
-         * non-negative number. The value 0 can be used if the client only needs
-         * to retrieve current positions and latency. Any sufficiently big value
-         * which exceeds the size of the queue's area which is currently
-         * available for reading or writing by the HAL module must be trimmed by
-         * the HAL module to the available size. Note that the HAL module is
-         * allowed to consume or provide less data than requested, and it must
-         * return the amount of actually read or written data via the
-         * 'Reply.fmqByteCount' field. Thus, only attempts to pass a negative
-         * number must be constituted as a client's error.
+         * non-negative number. Any sufficiently big value which exceeds the
+         * size of the queue's area which is currently available for reading or
+         * writing by the HAL module must be trimmed by the HAL module to the
+         * available size. Note that the HAL module is allowed to consume or
+         * provide less data than requested, and it must return the amount of
+         * actually read or written data via the 'Reply.fmqByteCount'
+         * field. Thus, only attempts to pass a negative number must be
+         * constituted as a client's error.
          *
          * Differences for the MMap No IRQ mode:
          *
@@ -233,13 +302,16 @@
          *    with sending of this command.
          *
          *  - the value must always be set to 0.
+         *
+         * See the state machines on the applicability of this command to
+         * different states.
          */
         int burst;
         /**
          * See the state machines on the applicability of this command to
          * different states.
          */
-        Void drain;
+        DrainMode drain;
         /**
          * See the state machines on the applicability of this command to
          * different states.
@@ -286,10 +358,6 @@
          *  - 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;
          */
@@ -307,9 +375,11 @@
          */
         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'.
+         * It is recommended to report the current position for any command. If
+         * the position can not be reported, for example 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), the 'Position::UNKNOWN' value must be used.
          *
          * For output streams: the moment when the specified stream position
          *   was presented to an external observer (i.e. presentation position).
@@ -401,6 +471,10 @@
          *     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.
+         *     Note: in non-blocking mode, when the HAL module goes to
+         *           the 'TRANSFERRING' state (as indicated by the 'reply.state'
+         *           field), the client must wait for the 'IStreamCallback.onTransferReady'
+         *           notification to arrive before starting the next burst.
          *
          * For input streams the following sequence of operations is used:
          *  1. The client writes the BURST command into the 'command' queue,
@@ -415,6 +489,10 @@
          *  5. The client wakes up due to 4.
          *  6. The client reads the reply and audio data. The client must
          *     always read from the FMQ all the data it contains.
+         *     Note: in non-blocking mode, when the HAL module goes to
+         *           the 'TRANSFERRING' state (as indicated by the 'reply.state'
+         *           field) the client must wait for the 'IStreamCallback.onTransferReady'
+         *           notification to arrive before starting the next burst.
          *
          */
         MQDescriptor<byte, SynchronizedReadWrite> fmq;
diff --git a/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv b/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv
new file mode 100644
index 0000000..818b18e
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv
@@ -0,0 +1,47 @@
+// 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-async-sm.gv -o stream-in-async-sm.png
+digraph stream_in_async_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];
+    // Note that when the producer (h/w) is passive, "burst" operations
+    // complete synchronously, bypassing the TRANSFERRING state.
+    I -> STANDBY;
+    STANDBY -> IDLE [label="start"];           // producer -> active
+    IDLE -> STANDBY [label="standby"];         // producer -> passive, buffer is cleared
+    IDLE -> TRANSFERRING [label="burst"];      // consumer -> active
+    ACTIVE -> PAUSED [label="pause"];          // consumer -> passive
+    ACTIVE -> DRAINING [label="drain"];        // producer -> passive
+    ACTIVE -> TRANSFERRING [label="burst"];
+    TRANSFERRING -> ACTIVE [label="←IStreamCallback.onTransferReady"];
+    TRANSFERRING -> PAUSED [label="pause"];    // consumer -> passive
+    TRANSFERRING -> DRAINING [label="drain"];  // producer -> passive
+    PAUSED -> TRANSFERRING [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="←IStreamCallback.onError"];
+    PAUSED -> ERROR [label="←IStreamCallback.onError"];
+    TRANSFERRING -> ERROR [label="←IStreamCallback.onError"];
+    ANY_STATE -> CLOSED [label="→IStream*.close"];
+    CLOSED -> F;
+}
diff --git a/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv b/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
new file mode 100644
index 0000000..e25b15a
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
@@ -0,0 +1,57 @@
+// 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-async-sm.gv -o stream-out-async-sm.png
+digraph stream_out_async_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];
+    // Note that when the consumer (h/w) is passive, "burst" operations
+    // complete synchronously, bypassing the TRANSFERRING state.
+    I -> STANDBY;
+    STANDBY -> IDLE [label="start"];                  // consumer -> active
+    STANDBY -> PAUSED [label="burst"];                // producer -> active
+    IDLE -> STANDBY [label="standby"];                // consumer -> passive
+    IDLE -> TRANSFERRING [label="burst"];             // producer -> active
+    ACTIVE -> PAUSED [label="pause"];                 // consumer -> passive (not consuming)
+    ACTIVE -> DRAINING [label="drain"];               // producer -> passive
+    ACTIVE -> TRANSFERRING [label="burst"];           // early unblocking
+    ACTIVE -> ACTIVE [label="burst"];                 // full write
+    TRANSFERRING -> ACTIVE [label="←IStreamCallback.onTransferReady"];
+    TRANSFERRING -> TRANSFER_PAUSED [label="pause"];  // consumer -> passive (not consuming)
+    TRANSFERRING -> DRAINING [label="drain"];         // producer -> passive
+    TRANSFER_PAUSED -> TRANSFERRING [label="start"];  // consumer -> active
+    TRANSFER_PAUSED -> DRAIN_PAUSED [label="drain"];  // producer -> passive
+    TRANSFER_PAUSED -> IDLE [label="flush"];          // buffer is cleared
+    PAUSED -> PAUSED [label="burst"];
+    PAUSED -> ACTIVE [label="start"];                 // consumer -> active
+    PAUSED -> IDLE [label="flush"];                   // producer -> passive, buffer is cleared
+    DRAINING -> IDLE [label="←IStreamCallback.onDrainReady"];
+    DRAINING -> TRANSFERRING [label="burst"];         // producer -> active
+    DRAINING -> DRAIN_PAUSED [label="pause"];         // consumer -> passive (not consuming)
+    DRAIN_PAUSED -> DRAINING [label="start"];         // consumer -> active
+    DRAIN_PAUSED -> TRANSFER_PAUSED [label="burst"];  // producer -> active
+    DRAIN_PAUSED -> IDLE [label="flush"];             // buffer is cleared
+    IDLE -> ERROR [label="←IStreamCallback.onError"];
+    DRAINING -> ERROR [label="←IStreamCallback.onError"];
+    TRANSFERRING -> ERROR [label="←IStreamCallback.onError"];
+    ANY_STATE -> CLOSED [label="→IStream*.close"];
+    CLOSED -> F;
+}
diff --git a/audio/aidl/common/StreamWorker.cpp b/audio/aidl/common/StreamWorker.cpp
index dda0e4a..0d2121c 100644
--- a/audio/aidl/common/StreamWorker.cpp
+++ b/audio/aidl/common/StreamWorker.cpp
@@ -25,15 +25,20 @@
 bool ThreadController::start(const std::string& name, int priority) {
     mThreadName = name;
     mThreadPriority = priority;
-    mWorker = std::thread(&ThreadController::workerThread, this);
+    if (kTestSingleThread != name) {
+        mWorker = std::thread(&ThreadController::workerThread, this);
+    } else {
+        // Simulate the case when the workerThread completes prior
+        // to the moment when we being waiting for its start.
+        workerThread();
+    }
     std::unique_lock<std::mutex> lock(mWorkerLock);
     android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
     mWorkerCv.wait(lock, [&]() {
         android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
-        return mWorkerState == WorkerState::RUNNING || !mError.empty();
+        return mWorkerState != WorkerState::INITIAL || !mError.empty();
     });
-    mWorkerStateChangeRequest = false;
-    return mWorkerState == WorkerState::RUNNING;
+    return mError.empty();
 }
 
 void ThreadController::stop() {
@@ -81,8 +86,8 @@
 void ThreadController::workerThread() {
     using Status = StreamLogic::Status;
 
-    std::string error = mLogic->init();
-    if (error.empty() && !mThreadName.empty()) {
+    std::string error;
+    if (!mThreadName.empty()) {
         std::string compliantName(mThreadName.substr(0, 15));
         if (int errCode = pthread_setname_np(pthread_self(), compliantName.c_str()); errCode != 0) {
             error.append("Failed to set thread name: ").append(strerror(errCode));
@@ -94,6 +99,9 @@
             error.append("Failed to set thread priority: ").append(strerror(errCode));
         }
     }
+    if (error.empty()) {
+        error.append(mLogic->init());
+    }
     {
         std::lock_guard<std::mutex> lock(mWorkerLock);
         mWorkerState = error.empty() ? WorkerState::RUNNING : WorkerState::STOPPED;
diff --git a/audio/aidl/common/include/StreamWorker.h b/audio/aidl/common/include/StreamWorker.h
index ab2ec26..e9c1070 100644
--- a/audio/aidl/common/include/StreamWorker.h
+++ b/audio/aidl/common/include/StreamWorker.h
@@ -32,7 +32,7 @@
 namespace internal {
 
 class ThreadController {
-    enum class WorkerState { STOPPED, RUNNING, PAUSE_REQUESTED, PAUSED, RESUME_REQUESTED };
+    enum class WorkerState { INITIAL, STOPPED, RUNNING, PAUSE_REQUESTED, PAUSED, RESUME_REQUESTED };
 
   public:
     explicit ThreadController(StreamLogic* logic) : mLogic(logic) {}
@@ -76,7 +76,7 @@
     std::thread mWorker;
     std::mutex mWorkerLock;
     std::condition_variable mWorkerCv;
-    WorkerState mWorkerState GUARDED_BY(mWorkerLock) = WorkerState::STOPPED;
+    WorkerState mWorkerState GUARDED_BY(mWorkerLock) = WorkerState::INITIAL;
     std::string mError GUARDED_BY(mWorkerLock);
     // The atomic lock-free variable is used to prevent priority inversions
     // that can occur when a high priority worker tries to acquire the lock
@@ -90,6 +90,9 @@
     std::atomic<bool> mWorkerStateChangeRequest GUARDED_BY(mWorkerLock) = false;
 };
 
+// A special thread name used in tests only.
+static const std::string kTestSingleThread = "__testST__";
+
 }  // namespace internal
 
 class StreamLogic {
diff --git a/audio/aidl/common/tests/streamworker_tests.cpp b/audio/aidl/common/tests/streamworker_tests.cpp
index 8ea8424..f7a30b9 100644
--- a/audio/aidl/common/tests/streamworker_tests.cpp
+++ b/audio/aidl/common/tests/streamworker_tests.cpp
@@ -283,4 +283,16 @@
     EXPECT_EQ(priority, worker.getPriority());
 }
 
+TEST_P(StreamWorkerTest, DeferredStartCheckNoError) {
+    stream.setStopStatus();
+    EXPECT_TRUE(worker.start(android::hardware::audio::common::internal::kTestSingleThread));
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, DeferredStartCheckWithError) {
+    stream.setErrorStatus();
+    EXPECT_FALSE(worker.start(android::hardware::audio::common::internal::kTestSingleThread));
+    EXPECT_TRUE(worker.hasError());
+}
+
 INSTANTIATE_TEST_SUITE_P(StreamWorker, StreamWorkerTest, testing::Bool());
diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp
index a3e5ff7..280814f 100644
--- a/audio/aidl/default/Configuration.cpp
+++ b/audio/aidl/default/Configuration.cpp
@@ -243,6 +243,13 @@
                                  AudioChannelLayout::LAYOUT_MONO, 48000, 0, true,
                                  createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0)));
 
+        MicrophoneInfo mic;
+        mic.id = "zero";
+        mic.device = zeroInDevice.ext.get<AudioPortExt::Tag::device>().device;
+        mic.group = 0;
+        mic.indexInTheGroup = 0;
+        c.microphones = std::vector<MicrophoneInfo>{mic};
+
         AudioPort primaryInMix =
                 createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(2, 2));
         primaryInMix.profiles.push_back(
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 6863fe3..1e561d4 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -31,6 +31,7 @@
 using aidl::android::hardware::audio::common::SinkMetadata;
 using aidl::android::hardware::audio::common::SourceMetadata;
 using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioDevice;
 using aidl::android::media::audio::common::AudioFormatDescription;
 using aidl::android::media::audio::common::AudioFormatType;
 using aidl::android::media::audio::common::AudioInputFlags;
@@ -97,6 +98,7 @@
 }
 
 ndk::ScopedAStatus Module::createStreamContext(int32_t in_portConfigId, int64_t in_bufferSizeFrames,
+                                               std::shared_ptr<IStreamCallback> asyncCallback,
                                                StreamContext* out_context) {
     if (in_bufferSizeFrames <= 0) {
         LOG(ERROR) << __func__ << ": non-positive buffer size " << in_bufferSizeFrames;
@@ -135,8 +137,9 @@
         StreamContext temp(
                 std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
                 std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
-                frameSize,
-                std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames));
+                portConfigIt->format.value(), portConfigIt->channelMask.value(),
+                std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
+                asyncCallback, mDebug.streamTransientStateDelayMs);
         if (temp.isValid()) {
             *out_context = std::move(temp);
         } else {
@@ -148,6 +151,39 @@
     return ndk::ScopedAStatus::ok();
 }
 
+std::vector<AudioDevice> Module::findConnectedDevices(int32_t portConfigId) {
+    std::vector<AudioDevice> result;
+    auto& ports = getConfig().ports;
+    auto portIds = portIdsFromPortConfigIds(findConnectedPortConfigIds(portConfigId));
+    for (auto it = portIds.begin(); it != portIds.end(); ++it) {
+        auto portIt = findById<AudioPort>(ports, *it);
+        if (portIt != ports.end() && portIt->ext.getTag() == AudioPortExt::Tag::device) {
+            result.push_back(portIt->ext.template get<AudioPortExt::Tag::device>().device);
+        }
+    }
+    return result;
+}
+
+std::set<int32_t> Module::findConnectedPortConfigIds(int32_t portConfigId) {
+    std::set<int32_t> result;
+    auto patchIdsRange = mPatches.equal_range(portConfigId);
+    auto& patches = getConfig().patches;
+    for (auto it = patchIdsRange.first; it != patchIdsRange.second; ++it) {
+        auto patchIt = findById<AudioPatch>(patches, it->second);
+        if (patchIt == patches.end()) {
+            LOG(FATAL) << __func__ << ": patch with id " << it->second << " taken from mPatches "
+                       << "not found in the configuration";
+        }
+        if (std::find(patchIt->sourcePortConfigIds.begin(), patchIt->sourcePortConfigIds.end(),
+                      portConfigId) != patchIt->sourcePortConfigIds.end()) {
+            result.insert(patchIt->sinkPortConfigIds.begin(), patchIt->sinkPortConfigIds.end());
+        } else {
+            result.insert(patchIt->sourcePortConfigIds.begin(), patchIt->sourcePortConfigIds.end());
+        }
+    }
+    return result;
+}
+
 ndk::ScopedAStatus Module::findPortIdForNewStream(int32_t in_portConfigId, AudioPort** port) {
     auto& configs = getConfig().portConfigs;
     auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
@@ -186,6 +222,19 @@
     return ndk::ScopedAStatus::ok();
 }
 
+template <typename C>
+std::set<int32_t> Module::portIdsFromPortConfigIds(C portConfigIds) {
+    std::set<int32_t> result;
+    auto& portConfigs = getConfig().portConfigs;
+    for (auto it = portConfigIds.begin(); it != portConfigIds.end(); ++it) {
+        auto portConfigIt = findById<AudioPortConfig>(portConfigs, *it);
+        if (portConfigIt != portConfigs.end()) {
+            result.insert(portConfigIt->portId);
+        }
+    }
+    return result;
+}
+
 internal::Configuration& Module::getConfig() {
     if (!mConfig) {
         mConfig.reset(new internal::Configuration(internal::getNullPrimaryConfiguration()));
@@ -222,12 +271,16 @@
     idsToConnect.insert(newPatch.sinkPortConfigIds.begin(), newPatch.sinkPortConfigIds.end());
     std::for_each(idsToDisconnect.begin(), idsToDisconnect.end(), [&](const auto& portConfigId) {
         if (idsToConnect.count(portConfigId) == 0) {
-            mStreams.setStreamIsConnected(portConfigId, false);
+            LOG(DEBUG) << "The stream on port config id " << portConfigId << " is not connected";
+            mStreams.setStreamIsConnected(portConfigId, {});
         }
     });
     std::for_each(idsToConnect.begin(), idsToConnect.end(), [&](const auto& portConfigId) {
         if (idsToDisconnect.count(portConfigId) == 0) {
-            mStreams.setStreamIsConnected(portConfigId, true);
+            const auto connectedDevices = findConnectedDevices(portConfigId);
+            LOG(DEBUG) << "The stream on port config id " << portConfigId
+                       << " is connected to: " << ::android::internal::ToString(connectedDevices);
+            mStreams.setStreamIsConnected(portConfigId, connectedDevices);
         }
     });
 }
@@ -242,6 +295,11 @@
                    << "while having external devices connected";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
+    if (in_debug.streamTransientStateDelayMs < 0) {
+        LOG(ERROR) << __func__ << ": streamTransientStateDelayMs is negative: "
+                   << in_debug.streamTransientStateDelayMs;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
     mDebug = in_debug;
     return ndk::ScopedAStatus::ok();
 }
@@ -456,19 +514,21 @@
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
     StreamContext context;
-    if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames, &context);
+    if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames, nullptr,
+                                          &context);
         !status.isOk()) {
         return status;
     }
     context.fillDescriptor(&_aidl_return->desc);
-    auto stream = ndk::SharedRefBase::make<StreamIn>(in_args.sinkMetadata, std::move(context));
+    auto stream = ndk::SharedRefBase::make<StreamIn>(in_args.sinkMetadata, std::move(context),
+                                                     mConfig->microphones);
     if (auto status = stream->init(); !status.isOk()) {
         return status;
     }
     StreamWrapper streamWrapper(stream);
     auto patchIt = mPatches.find(in_args.portConfigId);
     if (patchIt != mPatches.end()) {
-        streamWrapper.setStreamIsConnected(true);
+        streamWrapper.setStreamIsConnected(findConnectedDevices(in_args.portConfigId));
     }
     mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
     _aidl_return->stream = std::move(stream);
@@ -496,8 +556,16 @@
                    << " has COMPRESS_OFFLOAD flag set, requires offload info";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
+    const bool isNonBlocking = isBitPositionFlagSet(port->flags.get<AudioIoFlags::Tag::output>(),
+                                                    AudioOutputFlags::NON_BLOCKING);
+    if (isNonBlocking && in_args.callback == nullptr) {
+        LOG(ERROR) << __func__ << ": port id " << port->id
+                   << " has NON_BLOCKING flag set, requires async callback";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
     StreamContext context;
-    if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames, &context);
+    if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames,
+                                          isNonBlocking ? in_args.callback : nullptr, &context);
         !status.isOk()) {
         return status;
     }
@@ -510,7 +578,7 @@
     StreamWrapper streamWrapper(stream);
     auto patchIt = mPatches.find(in_args.portConfigId);
     if (patchIt != mPatches.end()) {
-        streamWrapper.setStreamIsConnected(true);
+        streamWrapper.setStreamIsConnected(findConnectedDevices(in_args.portConfigId));
     }
     mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
     _aidl_return->stream = std::move(stream);
@@ -829,6 +897,12 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus Module::getMicrophones(std::vector<MicrophoneInfo>* _aidl_return) {
+    *_aidl_return = mConfig->microphones;
+    LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
+    return ndk::ScopedAStatus::ok();
+}
+
 ndk::ScopedAStatus Module::updateAudioMode(AudioMode in_mode) {
     // No checks for supported audio modes here, it's an informative notification.
     LOG(DEBUG) << __func__ << ": " << toString(in_mode);
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 21dc4b6..be5887c 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -18,12 +18,17 @@
 #include <android-base/logging.h>
 #include <utils/SystemClock.h>
 
+#include <Utils.h>
+
 #include "core-impl/Module.h"
 #include "core-impl/Stream.h"
 
 using aidl::android::hardware::audio::common::SinkMetadata;
 using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::media::audio::common::AudioDevice;
 using aidl::android::media::audio::common::AudioOffloadInfo;
+using android::hardware::audio::common::getChannelCount;
+using android::hardware::audio::common::getFrameSizeInBytes;
 
 namespace aidl::android::hardware::audio::core {
 
@@ -35,13 +40,17 @@
         desc->reply = mReplyMQ->dupeDesc();
     }
     if (mDataMQ) {
-        desc->frameSizeBytes = mFrameSize;
-        desc->bufferSizeFrames =
-                mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / mFrameSize;
+        const size_t frameSize = getFrameSize();
+        desc->frameSizeBytes = frameSize;
+        desc->bufferSizeFrames = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / frameSize;
         desc->audio.set<StreamDescriptor::AudioBuffer::Tag::fmq>(mDataMQ->dupeDesc());
     }
 }
 
+size_t StreamContext::getFrameSize() const {
+    return getFrameSizeInBytes(mFormat, mChannelLayout);
+}
+
 bool StreamContext::isValid() const {
     if (mCommandMQ && !mCommandMQ->isValid()) {
         LOG(ERROR) << "command FMQ is invalid";
@@ -51,8 +60,8 @@
         LOG(ERROR) << "reply FMQ is invalid";
         return false;
     }
-    if (mFrameSize == 0) {
-        LOG(ERROR) << "frame size is not set";
+    if (getFrameSize() == 0) {
+        LOG(ERROR) << "frame size is invalid";
         return false;
     }
     if (mDataMQ && !mDataMQ->isValid()) {
@@ -87,32 +96,46 @@
 
 void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
                                             bool isConnected) const {
+    reply->status = STATUS_OK;
     if (isConnected) {
-        reply->status = STATUS_OK;
         reply->observable.frames = mFrameCount;
         reply->observable.timeNs = ::android::elapsedRealtimeNano();
     } else {
-        reply->status = STATUS_NO_INIT;
+        reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
+        reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
     }
 }
 
+void StreamWorkerCommonLogic::populateReplyWrongState(
+        StreamDescriptor::Reply* reply, const StreamDescriptor::Command& command) const {
+    LOG(WARNING) << "command '" << toString(command.getTag())
+                 << "' can not be handled in the state " << toString(mState);
+    reply->status = STATUS_INVALID_OPERATION;
+}
+
 const std::string StreamInWorkerLogic::kThreadName = "reader";
 
 StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
+    // Note: for input streams, draining is driven by the client, thus
+    // "empty buffer" condition can only happen while handling the 'burst'
+    // command. Thus, unlike for output streams, it does not make sense to
+    // delay the 'DRAINING' state here by 'mTransientStateDelayMs'.
+    // TODO: Add a delay for transitions of async operations when/if they added.
+
     StreamDescriptor::Command command{};
     if (!mCommandMQ->readBlocking(&command, 1)) {
         LOG(ERROR) << __func__ << ": reading of command from MQ failed";
         mState = StreamDescriptor::State::ERROR;
         return Status::ABORT;
     }
+    LOG(DEBUG) << __func__ << ": received command " << command.toString() << " in " << kThreadName;
     StreamDescriptor::Reply reply{};
     reply.status = STATUS_BAD_VALUE;
     using Tag = StreamDescriptor::Command::Tag;
     switch (command.getTag()) {
-        case Tag::hal_reserved_exit:
-            if (const int32_t cookie = command.get<Tag::hal_reserved_exit>();
+        case Tag::halReservedExit:
+            if (const int32_t cookie = command.get<Tag::halReservedExit>();
                 cookie == mInternalCommandCookie) {
-                LOG(DEBUG) << __func__ << ": received EXIT command";
                 setClosed();
                 // This is an internal command, no need to reply.
                 return Status::EXIT;
@@ -120,8 +143,10 @@
                 LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
             }
             break;
+        case Tag::getStatus:
+            populateReply(&reply, mIsConnected);
+            break;
         case Tag::start:
-            LOG(DEBUG) << __func__ << ": received START read command";
             if (mState == StreamDescriptor::State::STANDBY ||
                 mState == StreamDescriptor::State::DRAINING) {
                 populateReply(&reply, mIsConnected);
@@ -129,15 +154,13 @@
                                  ? 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;
+                populateReplyWrongState(&reply, command);
             }
             break;
         case Tag::burst:
             if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
-                LOG(DEBUG) << __func__ << ": received BURST read command for " << fmqByteCount
-                           << " bytes";
+                LOG(DEBUG) << __func__ << ": '" << toString(command.getTag()) << "' command for "
+                           << fmqByteCount << " bytes";
                 if (mState == StreamDescriptor::State::IDLE ||
                     mState == StreamDescriptor::State::ACTIVE ||
                     mState == StreamDescriptor::State::PAUSED ||
@@ -151,69 +174,61 @@
                     } 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.
+                        // In a real implementation, here we would either remain in
+                        // the 'DRAINING' state, or transfer to 'STANDBY' depending on the
+                        // buffer state.
                         mState = StreamDescriptor::State::STANDBY;
                     }
                 } else {
-                    LOG(WARNING) << __func__ << ": BURST command can not be handled in the state "
-                                 << toString(mState);
-                    reply.status = STATUS_INVALID_OPERATION;
+                    populateReplyWrongState(&reply, command);
                 }
             } else {
                 LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
             }
             break;
         case Tag::drain:
-            LOG(DEBUG) << __func__ << ": received DRAIN read command";
-            if (mState == StreamDescriptor::State::ACTIVE) {
-                usleep(1000);  // Simulate a blocking call into the driver.
-                populateReply(&reply, mIsConnected);
-                // Can switch the state to ERROR if a driver error occurs.
-                mState = StreamDescriptor::State::DRAINING;
+            if (command.get<Tag::drain>() == StreamDescriptor::DrainMode::DRAIN_UNSPECIFIED) {
+                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 {
+                    populateReplyWrongState(&reply, command);
+                }
             } else {
-                LOG(WARNING) << __func__ << ": DRAIN command can not be handled in the state "
-                             << toString(mState);
-                reply.status = STATUS_INVALID_OPERATION;
+                LOG(WARNING) << __func__
+                             << ": invalid drain mode: " << toString(command.get<Tag::drain>());
             }
             break;
         case Tag::standby:
-            LOG(DEBUG) << __func__ << ": received STANDBY read command";
             if (mState == StreamDescriptor::State::IDLE) {
                 usleep(1000);  // Simulate a blocking call into the driver.
                 populateReply(&reply, mIsConnected);
                 // Can switch the state to ERROR if a driver error occurs.
                 mState = StreamDescriptor::State::STANDBY;
             } else {
-                LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
-                             << toString(mState);
-                reply.status = STATUS_INVALID_OPERATION;
+                populateReplyWrongState(&reply, command);
             }
             break;
         case Tag::pause:
-            LOG(DEBUG) << __func__ << ": received PAUSE read command";
             if (mState == StreamDescriptor::State::ACTIVE) {
                 usleep(1000);  // Simulate a blocking call into the driver.
                 populateReply(&reply, mIsConnected);
                 // Can switch the state to ERROR if a driver error occurs.
                 mState = StreamDescriptor::State::PAUSED;
             } else {
-                LOG(WARNING) << __func__ << ": PAUSE command can not be handled in the state "
-                             << toString(mState);
-                reply.status = STATUS_INVALID_OPERATION;
+                populateReplyWrongState(&reply, command);
             }
             break;
         case Tag::flush:
-            LOG(DEBUG) << __func__ << ": received FLUSH read command";
             if (mState == StreamDescriptor::State::PAUSED) {
                 usleep(1000);  // Simulate a blocking call into the driver.
                 populateReply(&reply, mIsConnected);
                 // Can switch the state to ERROR if a driver error occurs.
                 mState = StreamDescriptor::State::STANDBY;
             } else {
-                LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
-                             << toString(mState);
-                reply.status = STATUS_INVALID_OPERATION;
+                populateReplyWrongState(&reply, command);
             }
             break;
     }
@@ -261,20 +276,52 @@
 const std::string StreamOutWorkerLogic::kThreadName = "writer";
 
 StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
+    if (mState == StreamDescriptor::State::DRAINING ||
+        mState == StreamDescriptor::State::TRANSFERRING) {
+        if (auto stateDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(
+                    std::chrono::steady_clock::now() - mTransientStateStart);
+            stateDurationMs >= mTransientStateDelayMs) {
+            if (mAsyncCallback == nullptr) {
+                // In blocking mode, mState can only be DRAINING.
+                mState = StreamDescriptor::State::IDLE;
+            } else {
+                // In a real implementation, the driver should notify the HAL about
+                // drain or transfer completion. In the stub, we switch unconditionally.
+                if (mState == StreamDescriptor::State::DRAINING) {
+                    mState = StreamDescriptor::State::IDLE;
+                    ndk::ScopedAStatus status = mAsyncCallback->onDrainReady();
+                    if (!status.isOk()) {
+                        LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
+                    }
+                } else {
+                    mState = StreamDescriptor::State::ACTIVE;
+                    ndk::ScopedAStatus status = mAsyncCallback->onTransferReady();
+                    if (!status.isOk()) {
+                        LOG(ERROR) << __func__ << ": error from onTransferReady: " << status;
+                    }
+                }
+            }
+            if (mTransientStateDelayMs.count() != 0) {
+                LOG(DEBUG) << __func__ << ": switched to state " << toString(mState)
+                           << " after a timeout";
+            }
+        }
+    }
+
     StreamDescriptor::Command command{};
     if (!mCommandMQ->readBlocking(&command, 1)) {
         LOG(ERROR) << __func__ << ": reading of command from MQ failed";
         mState = StreamDescriptor::State::ERROR;
         return Status::ABORT;
     }
+    LOG(DEBUG) << __func__ << ": received command " << command.toString() << " in " << kThreadName;
     StreamDescriptor::Reply reply{};
     reply.status = STATUS_BAD_VALUE;
     using Tag = StreamDescriptor::Command::Tag;
     switch (command.getTag()) {
-        case Tag::hal_reserved_exit:
-            if (const int32_t cookie = command.get<Tag::hal_reserved_exit>();
+        case Tag::halReservedExit:
+            if (const int32_t cookie = command.get<Tag::halReservedExit>();
                 cookie == mInternalCommandCookie) {
-                LOG(DEBUG) << __func__ << ": received EXIT command";
                 setClosed();
                 // This is an internal command, no need to reply.
                 return Status::EXIT;
@@ -282,8 +329,11 @@
                 LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
             }
             break;
-        case Tag::start:
-            LOG(DEBUG) << __func__ << ": received START write command";
+        case Tag::getStatus:
+            populateReply(&reply, mIsConnected);
+            break;
+        case Tag::start: {
+            bool commandAccepted = true;
             switch (mState) {
                 case StreamDescriptor::State::STANDBY:
                     mState = StreamDescriptor::State::IDLE;
@@ -292,97 +342,112 @@
                     mState = StreamDescriptor::State::ACTIVE;
                     break;
                 case StreamDescriptor::State::DRAIN_PAUSED:
-                    mState = StreamDescriptor::State::PAUSED;
+                    switchToTransientState(StreamDescriptor::State::DRAINING);
+                    break;
+                case StreamDescriptor::State::TRANSFER_PAUSED:
+                    switchToTransientState(StreamDescriptor::State::TRANSFERRING);
                     break;
                 default:
-                    LOG(WARNING) << __func__ << ": START command can not be handled in the state "
-                                 << toString(mState);
-                    reply.status = STATUS_INVALID_OPERATION;
+                    populateReplyWrongState(&reply, command);
+                    commandAccepted = false;
             }
-            if (reply.status != STATUS_INVALID_OPERATION) {
+            if (commandAccepted) {
                 populateReply(&reply, mIsConnected);
             }
-            break;
+        } break;
         case Tag::burst:
             if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
-                LOG(DEBUG) << __func__ << ": received BURST write command for " << fmqByteCount
-                           << " bytes";
-                if (mState !=
-                    StreamDescriptor::State::ERROR) {  // BURST can be handled in all valid states
+                LOG(DEBUG) << __func__ << ": '" << toString(command.getTag()) << "' command for "
+                           << fmqByteCount << " bytes";
+                if (mState != StreamDescriptor::State::ERROR &&
+                    mState != StreamDescriptor::State::TRANSFERRING &&
+                    mState != StreamDescriptor::State::TRANSFER_PAUSED) {
                     if (!write(fmqByteCount, &reply)) {
                         mState = StreamDescriptor::State::ERROR;
                     }
                     if (mState == StreamDescriptor::State::STANDBY ||
-                        mState == StreamDescriptor::State::DRAIN_PAUSED) {
-                        mState = StreamDescriptor::State::PAUSED;
+                        mState == StreamDescriptor::State::DRAIN_PAUSED ||
+                        mState == StreamDescriptor::State::PAUSED) {
+                        if (mAsyncCallback == nullptr ||
+                            mState != StreamDescriptor::State::DRAIN_PAUSED) {
+                            mState = StreamDescriptor::State::PAUSED;
+                        } else {
+                            mState = StreamDescriptor::State::TRANSFER_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.
+                               mState == StreamDescriptor::State::DRAINING ||
+                               mState == StreamDescriptor::State::ACTIVE) {
+                        if (mAsyncCallback == nullptr || reply.fmqByteCount == fmqByteCount) {
+                            mState = StreamDescriptor::State::ACTIVE;
+                        } else {
+                            switchToTransientState(StreamDescriptor::State::TRANSFERRING);
+                        }
+                    }
                 } else {
-                    LOG(WARNING) << __func__ << ": BURST command can not be handled in the state "
-                                 << toString(mState);
-                    reply.status = STATUS_INVALID_OPERATION;
+                    populateReplyWrongState(&reply, command);
                 }
             } else {
                 LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
             }
             break;
         case Tag::drain:
-            LOG(DEBUG) << __func__ << ": received DRAIN write command";
-            if (mState == StreamDescriptor::State::ACTIVE) {
-                usleep(1000);  // Simulate a blocking call into the driver.
-                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.
+            if (command.get<Tag::drain>() == StreamDescriptor::DrainMode::DRAIN_ALL ||
+                command.get<Tag::drain>() == StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY) {
+                if (mState == StreamDescriptor::State::ACTIVE ||
+                    mState == StreamDescriptor::State::TRANSFERRING) {
+                    usleep(1000);  // Simulate a blocking call into the driver.
+                    populateReply(&reply, mIsConnected);
+                    // Can switch the state to ERROR if a driver error occurs.
+                    switchToTransientState(StreamDescriptor::State::DRAINING);
+                } else if (mState == StreamDescriptor::State::TRANSFER_PAUSED) {
+                    mState = StreamDescriptor::State::DRAIN_PAUSED;
+                    populateReply(&reply, mIsConnected);
+                } else {
+                    populateReplyWrongState(&reply, command);
+                }
             } else {
-                LOG(WARNING) << __func__ << ": DRAIN command can not be handled in the state "
-                             << toString(mState);
-                reply.status = STATUS_INVALID_OPERATION;
+                LOG(WARNING) << __func__
+                             << ": invalid drain mode: " << toString(command.get<Tag::drain>());
             }
             break;
         case Tag::standby:
-            LOG(DEBUG) << __func__ << ": received STANDBY write command";
             if (mState == StreamDescriptor::State::IDLE) {
                 usleep(1000);  // Simulate a blocking call into the driver.
                 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;
+                populateReplyWrongState(&reply, command);
             }
             break;
-        case Tag::pause:
-            LOG(DEBUG) << __func__ << ": received PAUSE write command";
-            if (mState == StreamDescriptor::State::ACTIVE ||
-                mState == StreamDescriptor::State::DRAINING) {
+        case Tag::pause: {
+            bool commandAccepted = true;
+            switch (mState) {
+                case StreamDescriptor::State::ACTIVE:
+                    mState = StreamDescriptor::State::PAUSED;
+                    break;
+                case StreamDescriptor::State::DRAINING:
+                    mState = StreamDescriptor::State::DRAIN_PAUSED;
+                    break;
+                case StreamDescriptor::State::TRANSFERRING:
+                    mState = StreamDescriptor::State::TRANSFER_PAUSED;
+                    break;
+                default:
+                    populateReplyWrongState(&reply, command);
+                    commandAccepted = false;
+            }
+            if (commandAccepted) {
                 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;
             }
-            break;
+        } break;
         case Tag::flush:
-            LOG(DEBUG) << __func__ << ": received FLUSH write command";
             if (mState == StreamDescriptor::State::PAUSED ||
-                mState == StreamDescriptor::State::DRAIN_PAUSED) {
+                mState == StreamDescriptor::State::DRAIN_PAUSED ||
+                mState == StreamDescriptor::State::TRANSFER_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;
+                populateReplyWrongState(&reply, command);
             }
             break;
     }
@@ -450,9 +515,8 @@
 void StreamCommon<Metadata, StreamWorker>::stopWorker() {
     if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
         LOG(DEBUG) << __func__ << ": asking the worker to exit...";
-        auto cmd =
-                StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::hal_reserved_exit>(
-                        mContext.getInternalCommandCookie());
+        auto cmd = StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::halReservedExit>(
+                mContext.getInternalCommandCookie());
         // Note: never call 'pause' and 'resume' methods of StreamWorker
         // in the HAL implementation. These methods are to be used by
         // the client side only. Preventing the worker loop from running
@@ -475,11 +539,64 @@
     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 }
 
-StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext context)
-    : StreamCommon<SinkMetadata, StreamInWorker>(sinkMetadata, std::move(context)) {
+namespace {
+static std::map<AudioDevice, std::string> transformMicrophones(
+        const std::vector<MicrophoneInfo>& microphones) {
+    std::map<AudioDevice, std::string> result;
+    std::transform(microphones.begin(), microphones.end(), std::inserter(result, result.begin()),
+                   [](const auto& mic) { return std::make_pair(mic.device, mic.id); });
+    return result;
+}
+}  // namespace
+
+StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext context,
+                   const std::vector<MicrophoneInfo>& microphones)
+    : StreamCommon<SinkMetadata, StreamInWorker>(sinkMetadata, std::move(context)),
+      mMicrophones(transformMicrophones(microphones)) {
     LOG(DEBUG) << __func__;
 }
 
+ndk::ScopedAStatus StreamIn::getActiveMicrophones(
+        std::vector<MicrophoneDynamicInfo>* _aidl_return) {
+    std::vector<MicrophoneDynamicInfo> result;
+    std::vector<MicrophoneDynamicInfo::ChannelMapping> channelMapping{
+            getChannelCount(mContext.getChannelLayout()),
+            MicrophoneDynamicInfo::ChannelMapping::DIRECT};
+    for (auto it = mConnectedDevices.begin(); it != mConnectedDevices.end(); ++it) {
+        if (auto micIt = mMicrophones.find(*it); micIt != mMicrophones.end()) {
+            MicrophoneDynamicInfo dynMic;
+            dynMic.id = micIt->second;
+            dynMic.channelMapping = channelMapping;
+            result.push_back(std::move(dynMic));
+        }
+    }
+    *_aidl_return = std::move(result);
+    LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamIn::getMicrophoneDirection(MicrophoneDirection* _aidl_return) {
+    LOG(DEBUG) << __func__;
+    (void)_aidl_return;
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus StreamIn::setMicrophoneDirection(MicrophoneDirection in_direction) {
+    LOG(DEBUG) << __func__ << ": direction " << toString(in_direction);
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus StreamIn::getMicrophoneFieldDimension(float* _aidl_return) {
+    LOG(DEBUG) << __func__;
+    (void)_aidl_return;
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus StreamIn::setMicrophoneFieldDimension(float in_zoom) {
+    LOG(DEBUG) << __func__ << ": zoom " << in_zoom;
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
 StreamOut::StreamOut(const SourceMetadata& sourceMetadata, StreamContext context,
                      const std::optional<AudioOffloadInfo>& offloadInfo)
     : StreamCommon<SourceMetadata, StreamOutWorker>(sourceMetadata, std::move(context)),
diff --git a/audio/aidl/default/include/core-impl/Configuration.h b/audio/aidl/default/include/core-impl/Configuration.h
index d5cd30b..95adcd7 100644
--- a/audio/aidl/default/include/core-impl/Configuration.h
+++ b/audio/aidl/default/include/core-impl/Configuration.h
@@ -21,12 +21,14 @@
 
 #include <aidl/android/hardware/audio/core/AudioPatch.h>
 #include <aidl/android/hardware/audio/core/AudioRoute.h>
+#include <aidl/android/hardware/audio/core/MicrophoneInfo.h>
 #include <aidl/android/media/audio/common/AudioPort.h>
 #include <aidl/android/media/audio/common/AudioPortConfig.h>
 
 namespace aidl::android::hardware::audio::core::internal {
 
 struct Configuration {
+    std::vector<MicrophoneInfo> microphones;
     std::vector<::aidl::android::media::audio::common::AudioPort> ports;
     std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs;
     std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs;
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 0086743..3509327 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -77,6 +77,7 @@
     ndk::ScopedAStatus setMasterVolume(float in_volume) override;
     ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
     ndk::ScopedAStatus setMicMute(bool in_mute) override;
+    ndk::ScopedAStatus getMicrophones(std::vector<MicrophoneInfo>* _aidl_return) override;
     ndk::ScopedAStatus updateAudioMode(
             ::aidl::android::hardware::audio::core::AudioMode in_mode) override;
     ndk::ScopedAStatus updateScreenRotation(
@@ -86,10 +87,16 @@
     void cleanUpPatch(int32_t patchId);
     ndk::ScopedAStatus createStreamContext(
             int32_t in_portConfigId, int64_t in_bufferSizeFrames,
+            std::shared_ptr<IStreamCallback> asyncCallback,
             ::aidl::android::hardware::audio::core::StreamContext* out_context);
+    std::vector<::aidl::android::media::audio::common::AudioDevice> findConnectedDevices(
+            int32_t portConfigId);
+    std::set<int32_t> findConnectedPortConfigIds(int32_t portConfigId);
     ndk::ScopedAStatus findPortIdForNewStream(
             int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port);
     internal::Configuration& getConfig();
+    template <typename C>
+    std::set<int32_t> portIdsFromPortConfigIds(C portConfigIds);
     void registerPatch(const AudioPatch& patch);
     void updateStreamsConnectedState(const AudioPatch& oldPatch, const AudioPatch& newPatch);
 
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 5ee0f82..7a07eeb 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <atomic>
+#include <chrono>
 #include <cstdlib>
 #include <map>
 #include <memory>
@@ -28,7 +29,10 @@
 #include <aidl/android/hardware/audio/common/SourceMetadata.h>
 #include <aidl/android/hardware/audio/core/BnStreamIn.h>
 #include <aidl/android/hardware/audio/core/BnStreamOut.h>
+#include <aidl/android/hardware/audio/core/IStreamCallback.h>
+#include <aidl/android/hardware/audio/core/MicrophoneInfo.h>
 #include <aidl/android/hardware/audio/core/StreamDescriptor.h>
+#include <aidl/android/media/audio/common/AudioDevice.h>
 #include <aidl/android/media/audio/common/AudioOffloadInfo.h>
 #include <fmq/AidlMessageQueue.h>
 #include <system/thread_defs.h>
@@ -59,33 +63,53 @@
 
     StreamContext() = default;
     StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
-                  size_t frameSize, std::unique_ptr<DataMQ> dataMQ)
+                  const ::aidl::android::media::audio::common::AudioFormatDescription& format,
+                  const ::aidl::android::media::audio::common::AudioChannelLayout& channelLayout,
+                  std::unique_ptr<DataMQ> dataMQ, std::shared_ptr<IStreamCallback> asyncCallback,
+                  int transientStateDelayMs)
         : mCommandMQ(std::move(commandMQ)),
           mInternalCommandCookie(std::rand()),
           mReplyMQ(std::move(replyMQ)),
-          mFrameSize(frameSize),
-          mDataMQ(std::move(dataMQ)) {}
+          mFormat(format),
+          mChannelLayout(channelLayout),
+          mDataMQ(std::move(dataMQ)),
+          mAsyncCallback(asyncCallback),
+          mTransientStateDelayMs(transientStateDelayMs) {}
     StreamContext(StreamContext&& other)
         : mCommandMQ(std::move(other.mCommandMQ)),
           mInternalCommandCookie(other.mInternalCommandCookie),
           mReplyMQ(std::move(other.mReplyMQ)),
-          mFrameSize(other.mFrameSize),
-          mDataMQ(std::move(other.mDataMQ)) {}
+          mFormat(other.mFormat),
+          mChannelLayout(other.mChannelLayout),
+          mDataMQ(std::move(other.mDataMQ)),
+          mAsyncCallback(other.mAsyncCallback),
+          mTransientStateDelayMs(other.mTransientStateDelayMs) {}
     StreamContext& operator=(StreamContext&& other) {
         mCommandMQ = std::move(other.mCommandMQ);
         mInternalCommandCookie = other.mInternalCommandCookie;
         mReplyMQ = std::move(other.mReplyMQ);
-        mFrameSize = other.mFrameSize;
+        mFormat = std::move(other.mFormat);
+        mChannelLayout = std::move(other.mChannelLayout);
         mDataMQ = std::move(other.mDataMQ);
+        mAsyncCallback = other.mAsyncCallback;
+        mTransientStateDelayMs = other.mTransientStateDelayMs;
         return *this;
     }
 
     void fillDescriptor(StreamDescriptor* desc);
+    std::shared_ptr<IStreamCallback> getAsyncCallback() const { return mAsyncCallback; }
+    ::aidl::android::media::audio::common::AudioChannelLayout getChannelLayout() const {
+        return mChannelLayout;
+    }
     CommandMQ* getCommandMQ() const { return mCommandMQ.get(); }
     DataMQ* getDataMQ() const { return mDataMQ.get(); }
-    size_t getFrameSize() const { return mFrameSize; }
+    ::aidl::android::media::audio::common::AudioFormatDescription getFormat() const {
+        return mFormat;
+    }
+    size_t getFrameSize() const;
     int getInternalCommandCookie() const { return mInternalCommandCookie; }
     ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
+    int getTransientStateDelayMs() const { return mTransientStateDelayMs; }
     bool isValid() const;
     void reset();
 
@@ -93,8 +117,11 @@
     std::unique_ptr<CommandMQ> mCommandMQ;
     int mInternalCommandCookie;  // The value used to confirm that the command was posted internally
     std::unique_ptr<ReplyMQ> mReplyMQ;
-    size_t mFrameSize;
+    ::aidl::android::media::audio::common::AudioFormatDescription mFormat;
+    ::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout;
     std::unique_ptr<DataMQ> mDataMQ;
+    std::shared_ptr<IStreamCallback> mAsyncCallback;
+    int mTransientStateDelayMs;
 };
 
 class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic {
@@ -111,9 +138,17 @@
           mFrameSize(context.getFrameSize()),
           mCommandMQ(context.getCommandMQ()),
           mReplyMQ(context.getReplyMQ()),
-          mDataMQ(context.getDataMQ()) {}
+          mDataMQ(context.getDataMQ()),
+          mAsyncCallback(context.getAsyncCallback()),
+          mTransientStateDelayMs(context.getTransientStateDelayMs()) {}
     std::string init() override;
     void populateReply(StreamDescriptor::Reply* reply, bool isConnected) const;
+    void populateReplyWrongState(StreamDescriptor::Reply* reply,
+                                 const StreamDescriptor::Command& command) const;
+    void switchToTransientState(StreamDescriptor::State state) {
+        mState = state;
+        mTransientStateStart = std::chrono::steady_clock::now();
+    }
 
     // Atomic fields are used both by the main and worker threads.
     std::atomic<bool> mIsConnected = false;
@@ -125,6 +160,9 @@
     StreamContext::CommandMQ* mCommandMQ;
     StreamContext::ReplyMQ* mReplyMQ;
     StreamContext::DataMQ* mDataMQ;
+    std::shared_ptr<IStreamCallback> mAsyncCallback;
+    const std::chrono::duration<int, std::milli> mTransientStateDelayMs;
+    std::chrono::time_point<std::chrono::steady_clock> mTransientStateStart;
     // We use an array and the "size" field instead of a vector to be able to detect
     // memory allocation issues.
     std::unique_ptr<int8_t[]> mDataBuffer;
@@ -169,7 +207,11 @@
                        : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
     bool isClosed() const { return mWorker.isClosed(); }
-    void setIsConnected(bool connected) { mWorker.setIsConnected(connected); }
+    void setIsConnected(
+            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
+        mWorker.setIsConnected(!devices.empty());
+        mConnectedDevices = devices;
+    }
     ndk::ScopedAStatus updateMetadata(const Metadata& metadata);
 
   protected:
@@ -181,6 +223,7 @@
     Metadata mMetadata;
     StreamContext mContext;
     StreamWorker mWorker;
+    std::vector<::aidl::android::media::audio::common::AudioDevice> mConnectedDevices;
 };
 
 class StreamIn
@@ -190,6 +233,12 @@
         return StreamCommon<::aidl::android::hardware::audio::common::SinkMetadata,
                             StreamInWorker>::close();
     }
+    ndk::ScopedAStatus getActiveMicrophones(
+            std::vector<MicrophoneDynamicInfo>* _aidl_return) override;
+    ndk::ScopedAStatus getMicrophoneDirection(MicrophoneDirection* _aidl_return) override;
+    ndk::ScopedAStatus setMicrophoneDirection(MicrophoneDirection in_direction) override;
+    ndk::ScopedAStatus getMicrophoneFieldDimension(float* _aidl_return) override;
+    ndk::ScopedAStatus setMicrophoneFieldDimension(float in_zoom) override;
     ndk::ScopedAStatus updateMetadata(const ::aidl::android::hardware::audio::common::SinkMetadata&
                                               in_sinkMetadata) override {
         return StreamCommon<::aidl::android::hardware::audio::common::SinkMetadata,
@@ -198,7 +247,10 @@
 
   public:
     StreamIn(const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
-             StreamContext context);
+             StreamContext context, const std::vector<MicrophoneInfo>& microphones);
+
+  private:
+    const std::map<::aidl::android::media::audio::common::AudioDevice, std::string> mMicrophones;
 };
 
 class StreamOut : public StreamCommon<::aidl::android::hardware::audio::common::SourceMetadata,
@@ -237,11 +289,12 @@
                 },
                 mStream);
     }
-    void setStreamIsConnected(bool connected) {
+    void setStreamIsConnected(
+            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
         std::visit(
                 [&](auto&& ws) {
                     auto s = ws.lock();
-                    if (s) s->setIsConnected(connected);
+                    if (s) s->setIsConnected(devices);
                 },
                 mStream);
     }
@@ -264,9 +317,11 @@
         mStreams.insert(std::pair{portConfigId, sw});
         mStreams.insert(std::pair{portId, std::move(sw)});
     }
-    void setStreamIsConnected(int32_t portConfigId, bool connected) {
+    void setStreamIsConnected(
+            int32_t portConfigId,
+            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
         if (auto it = mStreams.find(portConfigId); it != mStreams.end()) {
-            it->second.setStreamIsConnected(connected);
+            it->second.setStreamIsConnected(devices);
         }
     }
 
diff --git a/audio/aidl/vts/AudioHalBinderServiceUtil.h b/audio/aidl/vts/AudioHalBinderServiceUtil.h
index c8d81b1..b4b4632 100644
--- a/audio/aidl/vts/AudioHalBinderServiceUtil.h
+++ b/audio/aidl/vts/AudioHalBinderServiceUtil.h
@@ -31,7 +31,7 @@
   public:
     ndk::SpAIBinder connectToService(const std::string& serviceName) {
         mServiceName = serviceName;
-        mBinder = ndk::SpAIBinder(AServiceManager_getService(serviceName.c_str()));
+        mBinder = ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str()));
         if (mBinder == nullptr) {
             LOG(ERROR) << "Failed to get service " << serviceName;
         } else {
diff --git a/audio/aidl/vts/ModuleConfig.cpp b/audio/aidl/vts/ModuleConfig.cpp
index 33c5b72..7e4b148 100644
--- a/audio/aidl/vts/ModuleConfig.cpp
+++ b/audio/aidl/vts/ModuleConfig.cpp
@@ -28,6 +28,7 @@
 
 using aidl::android::hardware::audio::core::IModule;
 using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioDeviceType;
 using aidl::android::media::audio::common::AudioEncapsulationMode;
 using aidl::android::media::audio::common::AudioFormatDescription;
 using aidl::android::media::audio::common::AudioFormatType;
@@ -62,6 +63,18 @@
     return {};
 }
 
+// static
+std::vector<aidl::android::media::audio::common::AudioPort> ModuleConfig::getBuiltInMicPorts(
+        const std::vector<aidl::android::media::audio::common::AudioPort>& ports) {
+    std::vector<AudioPort> result;
+    std::copy_if(ports.begin(), ports.end(), std::back_inserter(result), [](const auto& port) {
+        const auto type = port.ext.template get<AudioPortExt::Tag::device>().device.type;
+        return type.connection.empty() && (type.type == AudioDeviceType::IN_MICROPHONE ||
+                                           type.type == AudioDeviceType::IN_MICROPHONE_BACK);
+    });
+    return result;
+}
+
 template <typename T>
 auto findById(const std::vector<T>& v, int32_t id) {
     return std::find_if(v.begin(), v.end(), [&](const auto& p) { return p.id == id; });
@@ -107,39 +120,46 @@
     return result;
 }
 
-std::vector<AudioPort> ModuleConfig::getInputMixPorts() const {
+std::vector<AudioPort> ModuleConfig::getInputMixPorts(bool attachedOnly) const {
     std::vector<AudioPort> result;
-    std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [](const auto& port) {
+    std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
         return port.ext.getTag() == AudioPortExt::Tag::mix &&
-               port.flags.getTag() == AudioIoFlags::Tag::input;
+               port.flags.getTag() == AudioIoFlags::Tag::input &&
+               (!attachedOnly || !getAttachedSourceDevicesPortsForMixPort(port).empty());
     });
     return result;
 }
 
-std::vector<AudioPort> ModuleConfig::getOutputMixPorts() const {
+std::vector<AudioPort> ModuleConfig::getOutputMixPorts(bool attachedOnly) const {
     std::vector<AudioPort> result;
-    std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [](const auto& port) {
+    std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
         return port.ext.getTag() == AudioPortExt::Tag::mix &&
-               port.flags.getTag() == AudioIoFlags::Tag::output;
+               port.flags.getTag() == AudioIoFlags::Tag::output &&
+               (!attachedOnly || !getAttachedSinkDevicesPortsForMixPort(port).empty());
     });
     return result;
 }
 
+std::vector<AudioPort> ModuleConfig::getNonBlockingMixPorts(bool attachedOnly,
+                                                            bool singlePort) const {
+    return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) {
+        return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
+                                    AudioOutputFlags::NON_BLOCKING);
+    });
+}
+
 std::vector<AudioPort> ModuleConfig::getOffloadMixPorts(bool attachedOnly, bool singlePort) const {
-    std::vector<AudioPort> result;
-    const auto mixPorts = getMixPorts(false /*isInput*/);
-    auto offloadPortIt = mixPorts.begin();
-    while (offloadPortIt != mixPorts.end()) {
-        offloadPortIt = std::find_if(offloadPortIt, mixPorts.end(), [&](const AudioPort& port) {
-            return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
-                                        AudioOutputFlags::COMPRESS_OFFLOAD) &&
-                   (!attachedOnly || !getAttachedSinkDevicesPortsForMixPort(port).empty());
-        });
-        if (offloadPortIt == mixPorts.end()) break;
-        result.push_back(*offloadPortIt++);
-        if (singlePort) break;
-    }
-    return result;
+    return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) {
+        return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
+                                    AudioOutputFlags::COMPRESS_OFFLOAD);
+    });
+}
+
+std::vector<AudioPort> ModuleConfig::getPrimaryMixPorts(bool attachedOnly, bool singlePort) const {
+    return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) {
+        return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
+                                    AudioOutputFlags::PRIMARY);
+    });
 }
 
 std::vector<AudioPort> ModuleConfig::getAttachedDevicesPortsForMixPort(
@@ -193,7 +213,7 @@
 
 std::optional<ModuleConfig::SrcSinkPair> ModuleConfig::getNonRoutableSrcSinkPair(
         bool isInput) const {
-    const auto mixPorts = getMixPorts(isInput);
+    const auto mixPorts = getMixPorts(isInput, false /*attachedOnly*/);
     std::set<std::pair<int32_t, int32_t>> allowedRoutes;
     for (const auto& route : mRoutes) {
         for (const auto srcPortId : route.sourcePortIds) {
@@ -343,6 +363,20 @@
            profile.sampleRates.empty() || profile.channelMasks.empty();
 }
 
+std::vector<AudioPort> ModuleConfig::findMixPorts(
+        bool isInput, bool attachedOnly, bool singlePort,
+        const std::function<bool(const AudioPort&)>& pred) const {
+    std::vector<AudioPort> result;
+    const auto mixPorts = getMixPorts(isInput, attachedOnly);
+    for (auto mixPortIt = mixPorts.begin(); mixPortIt != mixPorts.end();) {
+        mixPortIt = std::find_if(mixPortIt, mixPorts.end(), pred);
+        if (mixPortIt == mixPorts.end()) break;
+        result.push_back(*mixPortIt++);
+        if (singlePort) break;
+    }
+    return result;
+}
+
 std::vector<AudioPortConfig> ModuleConfig::generateAudioMixPortConfigs(
         const std::vector<AudioPort>& ports, bool isInput, bool singleProfile) const {
     std::vector<AudioPortConfig> result;
diff --git a/audio/aidl/vts/ModuleConfig.h b/audio/aidl/vts/ModuleConfig.h
index dc109a7..7247f3b 100644
--- a/audio/aidl/vts/ModuleConfig.h
+++ b/audio/aidl/vts/ModuleConfig.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <functional>
 #include <optional>
 #include <set>
 #include <utility>
@@ -36,20 +37,32 @@
     static std::optional<aidl::android::media::audio::common::AudioOffloadInfo>
     generateOffloadInfoIfNeeded(
             const aidl::android::media::audio::common::AudioPortConfig& portConfig);
+    static std::vector<aidl::android::media::audio::common::AudioPort> getBuiltInMicPorts(
+            const std::vector<aidl::android::media::audio::common::AudioPort>& ports);
 
     explicit ModuleConfig(aidl::android::hardware::audio::core::IModule* module);
     const ndk::ScopedAStatus& getStatus() const { return mStatus; }
     std::string getError() const { return mStatus.getMessage(); }
 
     std::vector<aidl::android::media::audio::common::AudioPort> getAttachedDevicePorts() const;
-    std::vector<aidl::android::media::audio::common::AudioPort> getExternalDevicePorts() const;
-    std::vector<aidl::android::media::audio::common::AudioPort> getInputMixPorts() const;
-    std::vector<aidl::android::media::audio::common::AudioPort> getOutputMixPorts() const;
-    std::vector<aidl::android::media::audio::common::AudioPort> getMixPorts(bool isInput) const {
-        return isInput ? getInputMixPorts() : getOutputMixPorts();
+    std::vector<aidl::android::media::audio::common::AudioPort> getAttachedMicrophonePorts() const {
+        return getBuiltInMicPorts(getAttachedDevicePorts());
     }
+    std::vector<aidl::android::media::audio::common::AudioPort> getExternalDevicePorts() const;
+    std::vector<aidl::android::media::audio::common::AudioPort> getInputMixPorts(
+            bool attachedOnly) const;
+    std::vector<aidl::android::media::audio::common::AudioPort> getOutputMixPorts(
+            bool attachedOnly) const;
+    std::vector<aidl::android::media::audio::common::AudioPort> getMixPorts(
+            bool isInput, bool attachedOnly) const {
+        return isInput ? getInputMixPorts(attachedOnly) : getOutputMixPorts(attachedOnly);
+    }
+    std::vector<aidl::android::media::audio::common::AudioPort> getNonBlockingMixPorts(
+            bool attachedOnly, bool singlePort) const;
     std::vector<aidl::android::media::audio::common::AudioPort> getOffloadMixPorts(
             bool attachedOnly, bool singlePort) const;
+    std::vector<aidl::android::media::audio::common::AudioPort> getPrimaryMixPorts(
+            bool attachedOnly, bool singlePort) const;
 
     std::vector<aidl::android::media::audio::common::AudioPort> getAttachedDevicesPortsForMixPort(
             bool isInput, const aidl::android::media::audio::common::AudioPort& mixPort) const {
@@ -78,14 +91,17 @@
     }
     std::vector<aidl::android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts()
             const {
-        auto inputs = generateAudioMixPortConfigs(getInputMixPorts(), true, false);
-        auto outputs = generateAudioMixPortConfigs(getOutputMixPorts(), false, false);
+        auto inputs =
+                generateAudioMixPortConfigs(getInputMixPorts(false /*attachedOnly*/), true, false);
+        auto outputs = generateAudioMixPortConfigs(getOutputMixPorts(false /*attachedOnly*/), false,
+                                                   false);
         inputs.insert(inputs.end(), outputs.begin(), outputs.end());
         return inputs;
     }
     std::vector<aidl::android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts(
             bool isInput) const {
-        return generateAudioMixPortConfigs(getMixPorts(isInput), isInput, false);
+        return generateAudioMixPortConfigs(getMixPorts(isInput, false /*attachedOnly*/), isInput,
+                                           false);
     }
     std::vector<aidl::android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts(
             bool isInput, const aidl::android::media::audio::common::AudioPort& port) const {
@@ -93,7 +109,8 @@
     }
     std::optional<aidl::android::media::audio::common::AudioPortConfig> getSingleConfigForMixPort(
             bool isInput) const {
-        const auto config = generateAudioMixPortConfigs(getMixPorts(isInput), isInput, true);
+        const auto config = generateAudioMixPortConfigs(
+                getMixPorts(isInput, false /*attachedOnly*/), isInput, true);
         if (!config.empty()) {
             return *config.begin();
         }
@@ -121,6 +138,10 @@
     std::string toString() const;
 
   private:
+    std::vector<aidl::android::media::audio::common::AudioPort> findMixPorts(
+            bool isInput, bool attachedOnly, bool singlePort,
+            const std::function<bool(const aidl::android::media::audio::common::AudioPort&)>& pred)
+            const;
     std::vector<aidl::android::media::audio::common::AudioPortConfig> generateAudioMixPortConfigs(
             const std::vector<aidl::android::media::audio::common::AudioPort>& ports, bool isInput,
             bool singleProfile) const;
diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
index 5e9aa7f..c0c04f4 100644
--- a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
@@ -15,12 +15,16 @@
  */
 
 #include <algorithm>
+#include <chrono>
 #include <cmath>
+#include <condition_variable>
 #include <limits>
 #include <memory>
+#include <mutex>
 #include <optional>
 #include <set>
 #include <string>
+#include <variant>
 #include <vector>
 
 #define LOG_TAG "VtsHalAudioCore"
@@ -30,6 +34,7 @@
 #include <Utils.h>
 #include <aidl/Gtest.h>
 #include <aidl/Vintf.h>
+#include <aidl/android/hardware/audio/core/BnStreamCallback.h>
 #include <aidl/android/hardware/audio/core/IModule.h>
 #include <aidl/android/hardware/audio/core/ITelephony.h>
 #include <aidl/android/media/audio/common/AudioIoFlags.h>
@@ -54,6 +59,8 @@
 using aidl::android::hardware::audio::core::IStreamIn;
 using aidl::android::hardware::audio::core::IStreamOut;
 using aidl::android::hardware::audio::core::ITelephony;
+using aidl::android::hardware::audio::core::MicrophoneDynamicInfo;
+using aidl::android::hardware::audio::core::MicrophoneInfo;
 using aidl::android::hardware::audio::core::ModuleDebug;
 using aidl::android::hardware::audio::core::StreamDescriptor;
 using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
@@ -187,6 +194,7 @@
         *isSupported = false;
         return;
     }
+    ASSERT_TRUE(status.isOk()) << "Unexpected status from a getter: " << status;
     *isSupported = true;
     for (const auto v : validValues) {
         EXPECT_IS_OK((inst->*setter)(v)) << "for valid value: " << v;
@@ -389,15 +397,132 @@
     std::unique_ptr<DataMQ> mDataMQ;
 };
 
-class StreamLogicDriver {
+struct StreamEventReceiver {
+    virtual ~StreamEventReceiver() = default;
+    enum class Event { None, DrainReady, Error, TransferReady };
+    virtual std::tuple<int, Event> getLastEvent() const = 0;
+    virtual std::tuple<int, Event> waitForEvent(int clientEventSeq) = 0;
+    static constexpr int kEventSeqInit = -1;
+};
+std::string toString(StreamEventReceiver::Event event) {
+    switch (event) {
+        case StreamEventReceiver::Event::None:
+            return "None";
+        case StreamEventReceiver::Event::DrainReady:
+            return "DrainReady";
+        case StreamEventReceiver::Event::Error:
+            return "Error";
+        case StreamEventReceiver::Event::TransferReady:
+            return "TransferReady";
+    }
+    return std::to_string(static_cast<int32_t>(event));
+}
+
+// Transition to the next state happens either due to a command from the client,
+// or after an event received from the server.
+using TransitionTrigger = std::variant<StreamDescriptor::Command, StreamEventReceiver::Event>;
+using StateTransition = std::pair<TransitionTrigger, StreamDescriptor::State>;
+struct StateSequence {
+    virtual ~StateSequence() = default;
+    virtual void rewind() = 0;
+    virtual bool done() const = 0;
+    virtual TransitionTrigger getTrigger() = 0;
+    virtual std::set<StreamDescriptor::State> getExpectedStates() = 0;
+    virtual void advance(StreamDescriptor::State state) = 0;
+};
+
+static const StreamDescriptor::Command kGetStatusCommand =
+        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::getStatus>(Void{});
+static const StreamDescriptor::Command kStartCommand =
+        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::start>(Void{});
+static const StreamDescriptor::Command kBurstCommand =
+        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::burst>(0);
+static const StreamDescriptor::Command kDrainInCommand =
+        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::drain>(
+                StreamDescriptor::DrainMode::DRAIN_UNSPECIFIED);
+static const StreamDescriptor::Command kDrainOutAllCommand =
+        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::drain>(
+                StreamDescriptor::DrainMode::DRAIN_ALL);
+static const StreamDescriptor::Command kDrainOutEarlyCommand =
+        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::drain>(
+                StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY);
+static const StreamDescriptor::Command kStandbyCommand =
+        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::standby>(Void{});
+static const StreamDescriptor::Command kPauseCommand =
+        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::pause>(Void{});
+static const StreamDescriptor::Command kFlushCommand =
+        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::flush>(Void{});
+static const StreamEventReceiver::Event kTransferReadyEvent =
+        StreamEventReceiver::Event::TransferReady;
+static const StreamEventReceiver::Event kDrainReadyEvent = StreamEventReceiver::Event::DrainReady;
+
+// Handle possible bifurcations:
+//   - on burst and on start: 'TRANSFERRING' -> {'ACTIVE', 'TRANSFERRING'}
+//   - on pause: 'TRANSFER_PAUSED' -> {'PAUSED', 'TRANSFER_PAUSED'}
+// It is assumed that the 'steps' provided on the construction contain the sequence
+// for the async case, which gets corrected in the case when the HAL decided to do
+// a synchronous transfer.
+class SmartStateSequence : public StateSequence {
   public:
+    explicit SmartStateSequence(const std::vector<StateTransition>& steps) : mSteps(steps) {}
+    explicit SmartStateSequence(std::vector<StateTransition>&& steps) : mSteps(std::move(steps)) {}
+    void rewind() override { mCurrentStep = 0; }
+    bool done() const override { return mCurrentStep >= mSteps.size(); }
+    TransitionTrigger getTrigger() override { return mSteps[mCurrentStep].first; }
+    std::set<StreamDescriptor::State> getExpectedStates() override {
+        std::set<StreamDescriptor::State> result = {getState()};
+        if (isBurstBifurcation() || isStartBifurcation()) {
+            result.insert(StreamDescriptor::State::ACTIVE);
+        } else if (isPauseBifurcation()) {
+            result.insert(StreamDescriptor::State::PAUSED);
+        }
+        return result;
+    }
+    void advance(StreamDescriptor::State state) override {
+        if (isBurstBifurcation() && state == StreamDescriptor::State::ACTIVE &&
+            mCurrentStep + 1 < mSteps.size() &&
+            mSteps[mCurrentStep + 1].first == TransitionTrigger{kTransferReadyEvent}) {
+            mCurrentStep++;
+        }
+        mCurrentStep++;
+    }
+
+  private:
+    StreamDescriptor::State getState() const { return mSteps[mCurrentStep].second; }
+    bool isBurstBifurcation() {
+        return getTrigger() == TransitionTrigger{kBurstCommand}&& getState() ==
+               StreamDescriptor::State::TRANSFERRING;
+    }
+    bool isPauseBifurcation() {
+        return getTrigger() == TransitionTrigger{kPauseCommand}&& getState() ==
+               StreamDescriptor::State::TRANSFER_PAUSED;
+    }
+    bool isStartBifurcation() {
+        return getTrigger() == TransitionTrigger{kStartCommand}&& getState() ==
+               StreamDescriptor::State::TRANSFERRING;
+    }
+    const std::vector<StateTransition> mSteps;
+    size_t mCurrentStep = 0;
+};
+
+std::string toString(const TransitionTrigger& trigger) {
+    if (std::holds_alternative<StreamDescriptor::Command>(trigger)) {
+        return std::string("'")
+                .append(toString(std::get<StreamDescriptor::Command>(trigger).getTag()))
+                .append("' command");
+    }
+    return std::string("'")
+            .append(toString(std::get<StreamEventReceiver::Event>(trigger)))
+            .append("' event");
+}
+
+struct StreamLogicDriver {
     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;
+    virtual TransitionTrigger getNextTrigger(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,
@@ -410,46 +535,102 @@
 
 class StreamCommonLogic : public StreamLogic {
   protected:
-    StreamCommonLogic(const StreamContext& context, StreamLogicDriver* driver)
+    StreamCommonLogic(const StreamContext& context, StreamLogicDriver* driver,
+                      StreamEventReceiver* eventReceiver)
         : mCommandMQ(context.getCommandMQ()),
           mReplyMQ(context.getReplyMQ()),
           mDataMQ(context.getDataMQ()),
           mData(context.getBufferSizeBytes()),
-          mDriver(driver) {}
+          mDriver(driver),
+          mEventReceiver(eventReceiver) {}
     StreamContext::CommandMQ* getCommandMQ() const { return mCommandMQ; }
     StreamContext::ReplyMQ* getReplyMQ() const { return mReplyMQ; }
+    StreamContext::DataMQ* getDataMQ() const { return mDataMQ; }
     StreamLogicDriver* getDriver() const { return mDriver; }
+    StreamEventReceiver* getEventReceiver() const { return mEventReceiver; }
 
-    std::string init() override { return ""; }
+    std::string init() override {
+        LOG(DEBUG) << __func__;
+        return "";
+    }
+    std::optional<StreamDescriptor::Command> maybeGetNextCommand(int* actualSize = nullptr) {
+        TransitionTrigger trigger = mDriver->getNextTrigger(mData.size(), actualSize);
+        if (StreamEventReceiver::Event* expEvent =
+                    std::get_if<StreamEventReceiver::Event>(&trigger);
+            expEvent != nullptr) {
+            auto [eventSeq, event] = mEventReceiver->waitForEvent(mLastEventSeq);
+            mLastEventSeq = eventSeq;
+            if (event != *expEvent) {
+                LOG(ERROR) << __func__ << ": expected event " << toString(*expEvent) << ", got "
+                           << toString(event);
+                return {};
+            }
+            // If we were waiting for an event, the new stream state must be retrieved
+            // via 'getStatus'.
+            return StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::getStatus>(
+                    Void{});
+        }
+        return std::get<StreamDescriptor::Command>(trigger);
+    }
+    bool readDataFromMQ(size_t readCount) {
+        std::vector<int8_t> data(readCount);
+        if (mDataMQ->read(data.data(), readCount)) {
+            memcpy(mData.data(), data.data(), std::min(mData.size(), data.size()));
+            return true;
+        }
+        LOG(ERROR) << __func__ << ": reading of " << readCount << " bytes from MQ failed";
+        return false;
+    }
+    bool writeDataToMQ() {
+        if (mDataMQ->write(mData.data(), mData.size())) {
+            return true;
+        }
+        LOG(ERROR) << __func__ << ": writing of " << mData.size() << " bytes to MQ failed";
+        return false;
+    }
 
+  private:
     StreamContext::CommandMQ* mCommandMQ;
     StreamContext::ReplyMQ* mReplyMQ;
     StreamContext::DataMQ* mDataMQ;
     std::vector<int8_t> mData;
     StreamLogicDriver* const mDriver;
+    StreamEventReceiver* const mEventReceiver;
+    int mLastEventSeq = StreamEventReceiver::kEventSeqInit;
 };
 
 class StreamReaderLogic : public StreamCommonLogic {
   public:
-    StreamReaderLogic(const StreamContext& context, StreamLogicDriver* driver)
-        : StreamCommonLogic(context, driver) {}
+    StreamReaderLogic(const StreamContext& context, StreamLogicDriver* driver,
+                      StreamEventReceiver* eventReceiver)
+        : StreamCommonLogic(context, driver, eventReceiver) {}
 
   protected:
     Status cycle() override {
         if (getDriver()->done()) {
+            LOG(DEBUG) << __func__ << ": clean exit";
             return Status::EXIT;
         }
-        StreamDescriptor::Command command = getDriver()->getNextCommand(mData.size());
-        if (!mCommandMQ->writeBlocking(&command, 1)) {
+        StreamDescriptor::Command command;
+        if (auto maybeCommand = maybeGetNextCommand(); maybeCommand.has_value()) {
+            command = std::move(maybeCommand.value());
+        } else {
+            LOG(ERROR) << __func__ << ": no next command";
+            return Status::ABORT;
+        }
+        LOG(DEBUG) << "Writing command: " << command.toString();
+        if (!getCommandMQ()->writeBlocking(&command, 1)) {
             LOG(ERROR) << __func__ << ": writing of command into MQ failed";
             return Status::ABORT;
         }
         StreamDescriptor::Reply reply{};
-        if (!mReplyMQ->readBlocking(&reply, 1)) {
-            LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
+        LOG(DEBUG) << "Reading reply...";
+        if (!getReplyMQ()->readBlocking(&reply, 1)) {
             return Status::ABORT;
         }
+        LOG(DEBUG) << "Reply received: " << reply.toString();
         if (getDriver()->interceptRawReply(reply)) {
+            LOG(DEBUG) << __func__ << ": reply has been intercepted by the driver";
             return Status::CONTINUE;
         }
         if (reply.status != STATUS_OK) {
@@ -463,11 +644,11 @@
                        << ": received invalid byte count in the reply: " << reply.fmqByteCount;
             return Status::ABORT;
         }
-        if (static_cast<size_t>(reply.fmqByteCount) != mDataMQ->availableToRead()) {
+        if (static_cast<size_t>(reply.fmqByteCount) != getDataMQ()->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();
+                       << " != " << getDataMQ()->availableToRead();
         }
         if (reply.latencyMs < 0 && reply.latencyMs != StreamDescriptor::LATENCY_UNKNOWN) {
             LOG(ERROR) << __func__ << ": received invalid latency value: " << reply.latencyMs;
@@ -484,10 +665,8 @@
             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()));
+        if (const size_t readCount = getDataMQ()->availableToRead(); readCount > 0) {
+            if (readDataFromMQ(readCount)) {
                 goto checkAcceptedReply;
             }
             LOG(ERROR) << __func__ << ": reading of " << readCount << " data bytes from MQ failed";
@@ -505,29 +684,39 @@
 
 class StreamWriterLogic : public StreamCommonLogic {
   public:
-    StreamWriterLogic(const StreamContext& context, StreamLogicDriver* driver)
-        : StreamCommonLogic(context, driver) {}
+    StreamWriterLogic(const StreamContext& context, StreamLogicDriver* driver,
+                      StreamEventReceiver* eventReceiver)
+        : StreamCommonLogic(context, driver, eventReceiver) {}
 
   protected:
     Status cycle() override {
         if (getDriver()->done()) {
+            LOG(DEBUG) << __func__ << ": clean exit";
             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";
+        StreamDescriptor::Command command;
+        if (auto maybeCommand = maybeGetNextCommand(&actualSize); maybeCommand.has_value()) {
+            command = std::move(maybeCommand.value());
+        } else {
+            LOG(ERROR) << __func__ << ": no next command";
             return Status::ABORT;
         }
-        if (!mCommandMQ->writeBlocking(&command, 1)) {
+        if (actualSize != 0 && !writeDataToMQ()) {
+            return Status::ABORT;
+        }
+        LOG(DEBUG) << "Writing command: " << command.toString();
+        if (!getCommandMQ()->writeBlocking(&command, 1)) {
             LOG(ERROR) << __func__ << ": writing of command into MQ failed";
             return Status::ABORT;
         }
         StreamDescriptor::Reply reply{};
-        if (!mReplyMQ->readBlocking(&reply, 1)) {
+        LOG(DEBUG) << "Reading reply...";
+        if (!getReplyMQ()->readBlocking(&reply, 1)) {
             LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
             return Status::ABORT;
         }
+        LOG(DEBUG) << "Reply received: " << reply.toString();
         if (getDriver()->interceptRawReply(reply)) {
             return Status::CONTINUE;
         }
@@ -542,10 +731,10 @@
                        << ": received invalid byte count in the reply: " << reply.fmqByteCount;
             return Status::ABORT;
         }
-        if (mDataMQ->availableToWrite() != mDataMQ->getQuantumCount()) {
+        if (getDataMQ()->availableToWrite() != getDataMQ()->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();
+                       << "available to write " << getDataMQ()->availableToWrite()
+                       << ", total size: " << getDataMQ()->getQuantumCount();
             return Status::ABORT;
         }
         if (reply.latencyMs < 0 && reply.latencyMs != StreamDescriptor::LATENCY_UNKNOWN) {
@@ -571,6 +760,71 @@
 };
 using StreamWriter = StreamWorker<StreamWriterLogic>;
 
+class DefaultStreamCallback : public ::aidl::android::hardware::audio::core::BnStreamCallback,
+                              public StreamEventReceiver {
+    ndk::ScopedAStatus onTransferReady() override {
+        LOG(DEBUG) << __func__;
+        putLastEvent(Event::TransferReady);
+        return ndk::ScopedAStatus::ok();
+    }
+    ndk::ScopedAStatus onError() override {
+        LOG(DEBUG) << __func__;
+        putLastEvent(Event::Error);
+        return ndk::ScopedAStatus::ok();
+    }
+    ndk::ScopedAStatus onDrainReady() override {
+        LOG(DEBUG) << __func__;
+        putLastEvent(Event::DrainReady);
+        return ndk::ScopedAStatus::ok();
+    }
+
+  public:
+    // To avoid timing out the whole test suite in case no event is received
+    // from the HAL, use a local timeout for event waiting.
+    static constexpr auto kEventTimeoutMs = std::chrono::milliseconds(1000);
+
+    StreamEventReceiver* getEventReceiver() { return this; }
+    std::tuple<int, Event> getLastEvent() const override {
+        std::lock_guard l(mLock);
+        return getLastEvent_l();
+    }
+    std::tuple<int, Event> waitForEvent(int clientEventSeq) override {
+        std::unique_lock l(mLock);
+        android::base::ScopedLockAssertion lock_assertion(mLock);
+        LOG(DEBUG) << __func__ << ": client " << clientEventSeq << ", last " << mLastEventSeq;
+        if (mCv.wait_for(l, kEventTimeoutMs, [&]() {
+                android::base::ScopedLockAssertion lock_assertion(mLock);
+                return clientEventSeq < mLastEventSeq;
+            })) {
+        } else {
+            LOG(WARNING) << __func__ << ": timed out waiting for an event";
+            putLastEvent_l(Event::None);
+        }
+        return getLastEvent_l();
+    }
+
+  private:
+    std::tuple<int, Event> getLastEvent_l() const REQUIRES(mLock) {
+        return std::make_tuple(mLastEventSeq, mLastEvent);
+    }
+    void putLastEvent(Event event) {
+        {
+            std::lock_guard l(mLock);
+            putLastEvent_l(event);
+        }
+        mCv.notify_one();
+    }
+    void putLastEvent_l(Event event) REQUIRES(mLock) {
+        mLastEventSeq++;
+        mLastEvent = event;
+    }
+
+    mutable std::mutex mLock;
+    std::condition_variable mCv;
+    int mLastEventSeq GUARDED_BY(mLock) = kEventSeqInit;
+    Event mLastEvent GUARDED_BY(mLock) = Event::None;
+};
+
 template <typename T>
 struct IOTraits {
     static constexpr bool is_input = std::is_same_v<T, IStreamIn>;
@@ -607,6 +861,7 @@
     }
     Stream* get() const { return mStream.get(); }
     const StreamContext* getContext() const { return mContext ? &(mContext.value()) : nullptr; }
+    StreamEventReceiver* getEventReceiver() { return mStreamCallback->getEventReceiver(); }
     std::shared_ptr<Stream> getSharedPointer() const { return mStream; }
     const AudioPortConfig& getPortConfig() const { return mPortConfig.get(); }
     int32_t getPortId() const { return mPortConfig.getId(); }
@@ -616,6 +871,7 @@
     std::shared_ptr<Stream> mStream;
     StreamDescriptor mDescriptor;
     std::optional<StreamContext> mContext;
+    std::shared_ptr<DefaultStreamCallback> mStreamCallback;
 };
 
 SinkMetadata GenerateSinkMetadata(const AudioPortConfig& portConfig) {
@@ -636,11 +892,15 @@
     args.portConfigId = portConfig.id;
     args.sinkMetadata = GenerateSinkMetadata(portConfig);
     args.bufferSizeFrames = bufferSizeFrames;
+    auto callback = ndk::SharedRefBase::make<DefaultStreamCallback>();
+    // TODO: Uncomment when support for asynchronous input is implemented.
+    // args.callback = callback;
     aidl::android::hardware::audio::core::IModule::OpenInputStreamReturn ret;
     ScopedAStatus status = module->openInputStream(args, &ret);
     if (status.isOk()) {
         mStream = std::move(ret.stream);
         mDescriptor = std::move(ret.desc);
+        mStreamCallback = std::move(callback);
     }
     return status;
 }
@@ -665,11 +925,14 @@
     args.sourceMetadata = GenerateSourceMetadata(portConfig);
     args.offloadInfo = ModuleConfig::generateOffloadInfoIfNeeded(portConfig);
     args.bufferSizeFrames = bufferSizeFrames;
+    auto callback = ndk::SharedRefBase::make<DefaultStreamCallback>();
+    args.callback = callback;
     aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
     ScopedAStatus status = module->openOutputStream(args, &ret);
     if (status.isOk()) {
         mStream = std::move(ret.stream);
         mDescriptor = std::move(ret.desc);
+        mStreamCallback = std::move(callback);
     }
     return status;
 }
@@ -1307,6 +1570,42 @@
     // TODO: Test that mic mute actually mutes input.
 }
 
+TEST_P(AudioCoreModule, GetMicrophones) {
+    ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+    const std::vector<AudioPort> builtInMicPorts = moduleConfig->getAttachedMicrophonePorts();
+    std::vector<MicrophoneInfo> micInfos;
+    ScopedAStatus status = module->getMicrophones(&micInfos);
+    if (!status.isOk()) {
+        EXPECT_EQ(EX_UNSUPPORTED_OPERATION, status.getExceptionCode());
+        ASSERT_FALSE(builtInMicPorts.empty())
+                << "When the HAL module does not have built-in microphones, IModule.getMicrophones"
+                << " must complete with no error and return an empty list";
+        GTEST_SKIP() << "Microphone info is not supported";
+    }
+    std::set<int32_t> micPortIdsWithInfo;
+    for (const auto& micInfo : micInfos) {
+        const auto& micDevice = micInfo.device;
+        const auto it =
+                std::find_if(builtInMicPorts.begin(), builtInMicPorts.end(), [&](const auto& port) {
+                    return port.ext.template get<AudioPortExt::Tag::device>().device == micDevice;
+                });
+        if (it != builtInMicPorts.end()) {
+            micPortIdsWithInfo.insert(it->id);
+        } else {
+            ADD_FAILURE() << "No device port found with a device specified for the microphone \""
+                          << micInfo.id << "\": " << micDevice.toString();
+        }
+    }
+    if (micPortIdsWithInfo.size() != builtInMicPorts.size()) {
+        std::vector<AudioPort> micPortsNoInfo;
+        std::copy_if(builtInMicPorts.begin(), builtInMicPorts.end(),
+                     std::back_inserter(micPortsNoInfo),
+                     [&](const auto& port) { return micPortIdsWithInfo.count(port.id) == 0; });
+        ADD_FAILURE() << "No MicrophoneInfo is provided for the following microphone device ports: "
+                      << ::android::internal::ToString(micPortsNoInfo);
+    }
+}
+
 TEST_P(AudioCoreModule, UpdateAudioMode) {
     for (const auto mode : ::ndk::enum_range<AudioMode>()) {
         EXPECT_IS_OK(module->updateAudioMode(mode)) << toString(mode);
@@ -1379,10 +1678,10 @@
     }
 }
 
+using CommandSequence = std::vector<StreamDescriptor::Command>;
 class StreamLogicDriverInvalidCommand : public StreamLogicDriver {
   public:
-    StreamLogicDriverInvalidCommand(const std::vector<StreamDescriptor::Command>& commands)
-        : mCommands(commands) {}
+    StreamLogicDriverInvalidCommand(const CommandSequence& commands) : mCommands(commands) {}
 
     std::string getUnexpectedStatuses() {
         // This method is intended to be called after the worker thread has joined,
@@ -1396,25 +1695,29 @@
     }
 
     bool done() override { return mNextCommand >= mCommands.size(); }
-    StreamDescriptor::Command getNextCommand(int, int* actualSize) override {
+    TransitionTrigger getNextTrigger(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();
+        const size_t currentCommand = mNextCommand - 1;  // increased by getNextTrigger
+        const bool isLastCommand = currentCommand == mCommands.size() - 1;
+        // All but the last command should run correctly. The last command must return 'BAD_VALUE'
+        // status.
+        if ((!isLastCommand && reply.status != STATUS_OK) ||
+            (isLastCommand && reply.status != STATUS_BAD_VALUE)) {
+            std::string s = mCommands[currentCommand].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;
+            // Process the reply, since the worker exits in case of an error.
+            return false;
         }
-        return true;
+        return isLastCommand;
     }
     bool processValidReply(const StreamDescriptor::Reply&) override { return true; }
 
   private:
-    const std::vector<StreamDescriptor::Command> mCommands;
+    const CommandSequence mCommands;
     size_t mNextCommand = 0;
     std::vector<std::string> mStatuses;
 };
@@ -1483,13 +1786,11 @@
 
     void OpenOverMaxCount() {
         constexpr bool isInput = IOTraits<Stream>::is_input;
-        auto ports = moduleConfig->getMixPorts(isInput);
+        auto ports = moduleConfig->getMixPorts(isInput, true /*attachedOnly*/);
         bool hasSingleRun = false;
         for (const auto& port : ports) {
             const size_t maxStreamCount = port.ext.get<AudioPortExt::Tag::mix>().maxOpenStreamCount;
-            if (maxStreamCount == 0 ||
-                moduleConfig->getAttachedDevicesPortsForMixPort(isInput, port).empty()) {
-                // No restrictions or no permanently attached devices.
+            if (maxStreamCount == 0) {
                 continue;
             }
             auto portConfigs = moduleConfig->getPortConfigsForMixPorts(isInput, port);
@@ -1556,22 +1857,46 @@
     }
 
     void SendInvalidCommandImpl(const AudioPortConfig& portConfig) {
-        std::vector<StreamDescriptor::Command> commands = {
-                StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::hal_reserved_exit>(
-                        0),
-                // TODO: For proper testing of input streams, need to put the stream into
-                // a state which accepts BURST commands.
-                StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::burst>(-1),
-                StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::burst>(
-                        std::numeric_limits<int32_t>::min()),
-        };
-        WithStream<Stream> stream(portConfig);
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
-        StreamLogicDriverInvalidCommand driver(commands);
-        typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver);
-        ASSERT_TRUE(worker.start());
-        worker.join();
-        EXPECT_EQ("", driver.getUnexpectedStatuses());
+        using TestSequence = std::pair<std::string, CommandSequence>;
+        // The last command in 'CommandSequence' is the one that must trigger
+        // an error status. All preceding commands are to put the state machine
+        // into a state which accepts the last command.
+        std::vector<TestSequence> sequences{
+                std::make_pair(std::string("HalReservedExit"),
+                               std::vector{StreamDescriptor::Command::make<
+                                       StreamDescriptor::Command::Tag::halReservedExit>(0)}),
+                std::make_pair(std::string("BurstNeg"),
+                               std::vector{kStartCommand,
+                                           StreamDescriptor::Command::make<
+                                                   StreamDescriptor::Command::Tag::burst>(-1)}),
+                std::make_pair(
+                        std::string("BurstMinInt"),
+                        std::vector{kStartCommand, StreamDescriptor::Command::make<
+                                                           StreamDescriptor::Command::Tag::burst>(
+                                                           std::numeric_limits<int32_t>::min())})};
+        if (IOTraits<Stream>::is_input) {
+            sequences.emplace_back("DrainAll",
+                                   std::vector{kStartCommand, kBurstCommand, kDrainOutAllCommand});
+            sequences.emplace_back(
+                    "DrainEarly", std::vector{kStartCommand, kBurstCommand, kDrainOutEarlyCommand});
+        } else {
+            sequences.emplace_back("DrainUnspecified",
+                                   std::vector{kStartCommand, kBurstCommand, kDrainInCommand});
+        }
+        for (const auto& seq : sequences) {
+            SCOPED_TRACE(std::string("Sequence ").append(seq.first));
+            LOG(DEBUG) << __func__ << ": Sequence " << seq.first;
+            WithStream<Stream> stream(portConfig);
+            ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+            StreamLogicDriverInvalidCommand driver(seq.second);
+            typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver,
+                                                     stream.getEventReceiver());
+            LOG(DEBUG) << __func__ << ": starting worker...";
+            ASSERT_TRUE(worker.start());
+            LOG(DEBUG) << __func__ << ": joining worker...";
+            worker.join();
+            EXPECT_EQ("", driver.getUnexpectedStatuses());
+        }
     }
 };
 using AudioStreamIn = AudioStream<IStreamIn>;
@@ -1590,20 +1915,127 @@
 TEST_IN_AND_OUT_STREAM(ResetPortConfigWithOpenStream);
 TEST_IN_AND_OUT_STREAM(SendInvalidCommand);
 
+namespace aidl::android::hardware::audio::core {
+std::ostream& operator<<(std::ostream& os, const IStreamIn::MicrophoneDirection& md) {
+    os << toString(md);
+    return os;
+}
+}  // namespace aidl::android::hardware::audio::core
+
+TEST_P(AudioStreamIn, ActiveMicrophones) {
+    std::vector<MicrophoneInfo> micInfos;
+    ScopedAStatus status = module->getMicrophones(&micInfos);
+    if (!status.isOk()) {
+        GTEST_SKIP() << "Microphone info is not supported";
+    }
+    const auto ports = moduleConfig->getInputMixPorts(true /*attachedOnly*/);
+    if (ports.empty()) {
+        GTEST_SKIP() << "No input mix ports for attached devices";
+    }
+    for (const auto& port : ports) {
+        const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port);
+        ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for input mix port";
+        WithStream<IStreamIn> stream(portConfig.value());
+        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+        {
+            // The port of the stream is not connected, thus the list of active mics must be empty.
+            std::vector<MicrophoneDynamicInfo> activeMics;
+            EXPECT_IS_OK(stream.get()->getActiveMicrophones(&activeMics));
+            EXPECT_TRUE(activeMics.empty()) << "a stream on an unconnected port returns a "
+                                               "non-empty list of active microphones";
+        }
+        if (auto micDevicePorts = ModuleConfig::getBuiltInMicPorts(
+                    moduleConfig->getAttachedSourceDevicesPortsForMixPort(port));
+            !micDevicePorts.empty()) {
+            auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(micDevicePorts[0]);
+            WithAudioPatch patch(true /*isInput*/, stream.getPortConfig(), devicePortConfig);
+            ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
+            std::vector<MicrophoneDynamicInfo> activeMics;
+            EXPECT_IS_OK(stream.get()->getActiveMicrophones(&activeMics));
+            for (const auto& mic : activeMics) {
+                EXPECT_NE(micInfos.end(),
+                          std::find_if(micInfos.begin(), micInfos.end(),
+                                       [&](const auto& micInfo) { return micInfo.id == mic.id; }))
+                        << "active microphone \"" << mic.id << "\" is not listed in "
+                        << "microphone infos returned by the module: "
+                        << ::android::internal::ToString(micInfos);
+                EXPECT_NE(0UL, mic.channelMapping.size())
+                        << "No channels specified for the microphone \"" << mic.id << "\"";
+            }
+        }
+        {
+            // Now the port of the stream is not connected again, re-check that there are no
+            // active microphones.
+            std::vector<MicrophoneDynamicInfo> activeMics;
+            EXPECT_IS_OK(stream.get()->getActiveMicrophones(&activeMics));
+            EXPECT_TRUE(activeMics.empty()) << "a stream on an unconnected port returns a "
+                                               "non-empty list of active microphones";
+        }
+    }
+}
+
+TEST_P(AudioStreamIn, MicrophoneDirection) {
+    using MD = IStreamIn::MicrophoneDirection;
+    const auto ports = moduleConfig->getInputMixPorts(true /*attachedOnly*/);
+    if (ports.empty()) {
+        GTEST_SKIP() << "No input mix ports for attached devices";
+    }
+    bool isSupported = false;
+    for (const auto& port : ports) {
+        const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port);
+        ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for input mix port";
+        WithStream<IStreamIn> stream(portConfig.value());
+        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+        EXPECT_NO_FATAL_FAILURE(
+                TestAccessors<MD>(stream.get(), &IStreamIn::getMicrophoneDirection,
+                                  &IStreamIn::setMicrophoneDirection,
+                                  std::vector<MD>(enum_range<MD>().begin(), enum_range<MD>().end()),
+                                  {}, &isSupported));
+        if (!isSupported) break;
+    }
+    if (!isSupported) {
+        GTEST_SKIP() << "Microphone direction is not supported";
+    }
+}
+
+TEST_P(AudioStreamIn, MicrophoneFieldDimension) {
+    const auto ports = moduleConfig->getInputMixPorts(true /*attachedOnly*/);
+    if (ports.empty()) {
+        GTEST_SKIP() << "No input mix ports for attached devices";
+    }
+    bool isSupported = false;
+    for (const auto& port : ports) {
+        const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port);
+        ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for input mix port";
+        WithStream<IStreamIn> stream(portConfig.value());
+        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+        EXPECT_NO_FATAL_FAILURE(TestAccessors<float>(
+                stream.get(), &IStreamIn::getMicrophoneFieldDimension,
+                &IStreamIn::setMicrophoneFieldDimension,
+                {IStreamIn::MIC_FIELD_DIMENSION_WIDE_ANGLE,
+                 IStreamIn::MIC_FIELD_DIMENSION_WIDE_ANGLE / 2.0f,
+                 IStreamIn::MIC_FIELD_DIMENSION_NO_ZOOM,
+                 IStreamIn::MIC_FIELD_DIMENSION_MAX_ZOOM / 2.0f,
+                 IStreamIn::MIC_FIELD_DIMENSION_MAX_ZOOM},
+                {IStreamIn::MIC_FIELD_DIMENSION_WIDE_ANGLE * 2,
+                 IStreamIn::MIC_FIELD_DIMENSION_MAX_ZOOM * 2,
+                 IStreamIn::MIC_FIELD_DIMENSION_WIDE_ANGLE * 1.1f,
+                 IStreamIn::MIC_FIELD_DIMENSION_MAX_ZOOM * 1.1f, -INFINITY, INFINITY, -NAN, NAN},
+                &isSupported));
+        if (!isSupported) break;
+    }
+    if (!isSupported) {
+        GTEST_SKIP() << "Microphone direction is not supported";
+    }
+}
+
 TEST_P(AudioStreamOut, OpenTwicePrimary) {
-    const auto mixPorts = moduleConfig->getMixPorts(false);
-    auto primaryPortIt = std::find_if(mixPorts.begin(), mixPorts.end(), [](const AudioPort& port) {
-        return port.flags.getTag() == AudioIoFlags::Tag::output &&
-               isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
-                                    AudioOutputFlags::PRIMARY);
-    });
-    if (primaryPortIt == mixPorts.end()) {
-        GTEST_SKIP() << "No primary mix port";
+    const auto mixPorts =
+            moduleConfig->getPrimaryMixPorts(true /*attachedOnly*/, true /*singlePort*/);
+    if (mixPorts.empty()) {
+        GTEST_SKIP() << "No primary mix port which could be routed to attached devices";
     }
-    if (moduleConfig->getAttachedSinkDevicesPortsForMixPort(*primaryPortIt).empty()) {
-        GTEST_SKIP() << "Primary mix port can not be routed to any of attached devices";
-    }
-    const auto portConfig = moduleConfig->getSingleConfigForMixPort(false, *primaryPortIt);
+    const auto portConfig = moduleConfig->getSingleConfigForMixPort(false, *mixPorts.begin());
     ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for the primary mix port";
     EXPECT_NO_FATAL_FAILURE(OpenTwiceSamePortConfigImpl(portConfig.value()));
 }
@@ -1615,27 +2047,51 @@
         GTEST_SKIP()
                 << "No mix port for compressed offload that could be routed to attached devices";
     }
-    const auto portConfig =
-            moduleConfig->getSingleConfigForMixPort(false, *offloadMixPorts.begin());
-    ASSERT_TRUE(portConfig.has_value())
-            << "No profiles specified for the compressed offload mix port";
+    const auto config = moduleConfig->getSingleConfigForMixPort(false, *offloadMixPorts.begin());
+    ASSERT_TRUE(config.has_value()) << "No profiles specified for the compressed offload mix port";
+    WithAudioPortConfig portConfig(config.value());
+    ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get()));
     StreamDescriptor descriptor;
     std::shared_ptr<IStreamOut> ignored;
     aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
-    args.portConfigId = portConfig.value().id;
-    args.sourceMetadata = GenerateSourceMetadata(portConfig.value());
+    args.portConfigId = portConfig.getId();
+    args.sourceMetadata = GenerateSourceMetadata(portConfig.get());
     args.bufferSizeFrames = kDefaultBufferSizeFrames;
     aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
     EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openOutputStream(args, &ret))
             << "when no offload info is provided for a compressed offload mix port";
 }
 
-using CommandAndState = std::pair<StreamDescriptor::Command, StreamDescriptor::State>;
+TEST_P(AudioStreamOut, RequireAsyncCallback) {
+    const auto nonBlockingMixPorts =
+            moduleConfig->getNonBlockingMixPorts(true /*attachedOnly*/, true /*singlePort*/);
+    if (nonBlockingMixPorts.empty()) {
+        GTEST_SKIP()
+                << "No mix port for non-blocking output that could be routed to attached devices";
+    }
+    const auto config =
+            moduleConfig->getSingleConfigForMixPort(false, *nonBlockingMixPorts.begin());
+    ASSERT_TRUE(config.has_value()) << "No profiles specified for the non-blocking mix port";
+    WithAudioPortConfig portConfig(config.value());
+    ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get()));
+    StreamDescriptor descriptor;
+    std::shared_ptr<IStreamOut> ignored;
+    aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
+    args.portConfigId = portConfig.getId();
+    args.sourceMetadata = GenerateSourceMetadata(portConfig.get());
+    args.offloadInfo = ModuleConfig::generateOffloadInfoIfNeeded(portConfig.get());
+    args.bufferSizeFrames = kDefaultBufferSizeFrames;
+    aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
+    EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openOutputStream(args, &ret))
+            << "when no async callback is provided for a non-blocking mix port";
+}
 
 class StreamLogicDefaultDriver : public StreamLogicDriver {
   public:
-    explicit StreamLogicDefaultDriver(const std::vector<CommandAndState>& commands)
-        : mCommands(commands) {}
+    explicit StreamLogicDefaultDriver(std::shared_ptr<StateSequence> commands)
+        : mCommands(commands) {
+        mCommands->rewind();
+    }
 
     // The three methods below is intended to be called after the worker
     // thread has joined, thus no extra synchronization is needed.
@@ -1643,59 +2099,72 @@
     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 {
-        auto command = mCommands[mNextCommand++].first;
-        if (command.getTag() == StreamDescriptor::Command::Tag::burst) {
-            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 (maxDataSize > 1) maxDataSize--;
-                *actualSize = maxDataSize;
+    bool done() override { return mCommands->done(); }
+    TransitionTrigger getNextTrigger(int maxDataSize, int* actualSize) override {
+        auto trigger = mCommands->getTrigger();
+        if (StreamDescriptor::Command* command = std::get_if<StreamDescriptor::Command>(&trigger);
+            command != nullptr) {
+            if (command->getTag() == StreamDescriptor::Command::Tag::burst) {
+                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 (maxDataSize > 1) maxDataSize--;
+                    *actualSize = maxDataSize;
+                }
+                command->set<StreamDescriptor::Command::Tag::burst>(maxDataSize);
+            } else {
+                if (actualSize != nullptr) *actualSize = 0;
             }
-            command.set<StreamDescriptor::Command::Tag::burst>(maxDataSize);
-        } else {
-            if (actualSize != nullptr) *actualSize = 0;
         }
-        return command;
+        return trigger;
     }
     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;
+        if (reply.observable.frames != StreamDescriptor::Position::UNKNOWN) {
+            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;
         }
-        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(lastCommandState.first.toString());
+        auto expected = mCommands->getExpectedStates();
+        if (expected.count(reply.state) == 0) {
+            std::string s =
+                    std::string("Unexpected transition from the state ")
+                            .append(mPreviousState.has_value() ? toString(mPreviousState.value())
+                                                               : "<initial state>")
+                            .append(" to ")
+                            .append(toString(reply.state))
+                            .append(" (expected one of ")
+                            .append(::android::internal::ToString(expected))
+                            .append(") caused by the ")
+                            .append(toString(mCommands->getTrigger()));
             LOG(ERROR) << __func__ << ": " << s;
             mUnexpectedTransition = std::move(s);
             return false;
         }
+        mCommands->advance(reply.state);
+        mPreviousState = reply.state;
         return true;
     }
 
   protected:
-    const std::vector<CommandAndState>& mCommands;
-    size_t mNextCommand = 0;
+    std::shared_ptr<StateSequence> mCommands;
+    std::optional<StreamDescriptor::State> mPreviousState;
     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 { NAMED_CMD_NAME, NAMED_CMD_DELAY_MS, NAMED_CMD_STREAM_TYPE, NAMED_CMD_CMDS };
+enum class StreamTypeFilter { ANY, SYNC, ASYNC };
+using NamedCommandSequence =
+        std::tuple<std::string, int, StreamTypeFilter, std::shared_ptr<StateSequence>>;
 enum { PARAM_MODULE_NAME, PARAM_CMD_SEQ, PARAM_SETUP_SEQ };
 using StreamIoTestParameters =
         std::tuple<std::string /*moduleName*/, NamedCommandSequence, bool /*useSetupSequence2*/>;
@@ -1716,7 +2185,29 @@
         }
         for (const auto& portConfig : allPortConfigs) {
             SCOPED_TRACE(portConfig.toString());
-            const auto& commandsAndStates = std::get<PARAM_CMD_SEQ>(GetParam()).second;
+            const bool isNonBlocking =
+                    IOTraits<Stream>::is_input
+                            ? false
+                            :
+                            // TODO: Uncomment when support for asynchronous input is implemented.
+                            /*isBitPositionFlagSet(
+                              portConfig.flags.value().template get<AudioIoFlags::Tag::input>(),
+                              AudioInputFlags::NON_BLOCKING) :*/
+                            isBitPositionFlagSet(portConfig.flags.value()
+                                                         .template get<AudioIoFlags::Tag::output>(),
+                                                 AudioOutputFlags::NON_BLOCKING);
+            if (auto streamType =
+                        std::get<NAMED_CMD_STREAM_TYPE>(std::get<PARAM_CMD_SEQ>(GetParam()));
+                (isNonBlocking && streamType == StreamTypeFilter::SYNC) ||
+                (!isNonBlocking && streamType == StreamTypeFilter::ASYNC)) {
+                continue;
+            }
+            WithDebugFlags delayTransientStates = WithDebugFlags::createNested(debug);
+            delayTransientStates.flags().streamTransientStateDelayMs =
+                    std::get<NAMED_CMD_DELAY_MS>(std::get<PARAM_CMD_SEQ>(GetParam()));
+            ASSERT_NO_FATAL_FAILURE(delayTransientStates.SetUp(module.get()));
+            const auto& commandsAndStates =
+                    std::get<NAMED_CMD_CMDS>(std::get<PARAM_CMD_SEQ>(GetParam()));
             if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
                 ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates));
             } else {
@@ -1732,7 +2223,7 @@
 
     // Set up a patch first, then open a stream.
     void RunStreamIoCommandsImplSeq1(const AudioPortConfig& portConfig,
-                                     const std::vector<CommandAndState>& commandsAndStates) {
+                                     std::shared_ptr<StateSequence> commandsAndStates) {
         auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
                 IOTraits<Stream>::is_input, portConfig);
         ASSERT_FALSE(devicePorts.empty());
@@ -1743,9 +2234,12 @@
         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);
+        typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver,
+                                                 stream.getEventReceiver());
 
+        LOG(DEBUG) << __func__ << ": starting worker...";
         ASSERT_TRUE(worker.start());
+        LOG(DEBUG) << __func__ << ": joining worker...";
         worker.join();
         EXPECT_FALSE(worker.hasError()) << worker.getError();
         EXPECT_EQ("", driver.getUnexpectedStateTransition());
@@ -1757,11 +2251,12 @@
 
     // Open a stream, then set up a patch for it.
     void RunStreamIoCommandsImplSeq2(const AudioPortConfig& portConfig,
-                                     const std::vector<CommandAndState>& commandsAndStates) {
+                                     std::shared_ptr<StateSequence> 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);
+        typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver,
+                                                 stream.getEventReceiver());
 
         auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
                 IOTraits<Stream>::is_input, portConfig);
@@ -1770,7 +2265,9 @@
         WithAudioPatch patch(IOTraits<Stream>::is_input, stream.getPortConfig(), devicePortConfig);
         ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
 
+        LOG(DEBUG) << __func__ << ": starting worker...";
         ASSERT_TRUE(worker.start());
+        LOG(DEBUG) << __func__ << ": joining worker...";
         worker.join();
         EXPECT_FALSE(worker.hasError()) << worker.getError();
         EXPECT_EQ("", driver.getUnexpectedStateTransition());
@@ -1975,103 +2472,210 @@
                          android::PrintInstanceNameToString);
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioStreamOut);
 
-static const StreamDescriptor::Command kStartCommand =
-        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::start>(Void{});
-static const StreamDescriptor::Command kBurstCommand =
-        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::burst>(0);
-static const StreamDescriptor::Command kDrainCommand =
-        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::drain>(Void{});
-static const StreamDescriptor::Command kStandbyCommand =
-        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::standby>(Void{});
-static const StreamDescriptor::Command kPauseCommand =
-        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::pause>(Void{});
-static const StreamDescriptor::Command kFlushCommand =
-        StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::flush>(Void{});
-static const NamedCommandSequence kReadOrWriteSeq =
-        std::make_pair(std::string("ReadOrWrite"),
-                       std::vector<CommandAndState>{
-                               std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
-                               std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
-                               std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
-                               std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE)});
-static const NamedCommandSequence kDrainInSeq =
-        std::make_pair(std::string("Drain"),
-                       std::vector<CommandAndState>{
-                               std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
-                               std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
-                               std::make_pair(kDrainCommand, StreamDescriptor::State::DRAINING),
-                               std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE),
-                               std::make_pair(kDrainCommand, StreamDescriptor::State::DRAINING),
-                               // TODO: This will need to be changed once DRAIN starts taking time.
-                               std::make_pair(kBurstCommand, StreamDescriptor::State::STANDBY)});
-static const NamedCommandSequence kDrainOutSeq =
-        std::make_pair(std::string("Drain"),
-                       std::vector<CommandAndState>{
-                               std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
-                               std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
-                               // TODO: This will need to be changed once DRAIN starts taking time.
-                               std::make_pair(kDrainCommand, 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(kStartCommand, StreamDescriptor::State::IDLE),
-                                     std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
-                                     std::make_pair(kDrainCommand, StreamDescriptor::State::IDLE)});
-static const NamedCommandSequence kStandbySeq =
-        std::make_pair(std::string("Standby"),
-                       std::vector<CommandAndState>{
-                               std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
-                               std::make_pair(kStandbyCommand, StreamDescriptor::State::STANDBY),
-                               // Perform a read or write in order to advance observable position
-                               // (this is verified by tests).
-                               std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
-                               std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE)});
+// This is the value used in test sequences for which the test needs to ensure
+// that the HAL stays in a transient state long enough to receive the next command.
+static const int kStreamTransientStateTransitionDelayMs = 3000;
+
+// TODO: Add async test cases for input once it is implemented.
+
+std::shared_ptr<StateSequence> makeBurstCommands(bool isSync, size_t burstCount) {
+    const auto burst =
+            isSync ? std::vector<StateTransition>{std::make_pair(kBurstCommand,
+                                                                 StreamDescriptor::State::ACTIVE)}
+                   : std::vector<StateTransition>{
+                             std::make_pair(kBurstCommand, StreamDescriptor::State::TRANSFERRING),
+                             std::make_pair(kTransferReadyEvent, StreamDescriptor::State::ACTIVE)};
+    std::vector<StateTransition> result{
+            std::make_pair(kStartCommand, StreamDescriptor::State::IDLE)};
+    for (size_t i = 0; i < burstCount; ++i) {
+        result.insert(result.end(), burst.begin(), burst.end());
+    }
+    return std::make_shared<SmartStateSequence>(result);
+}
+static const NamedCommandSequence kReadSeq =
+        std::make_tuple(std::string("Read"), 0, StreamTypeFilter::ANY, makeBurstCommands(true, 3));
+static const NamedCommandSequence kWriteSyncSeq = std::make_tuple(
+        std::string("Write"), 0, StreamTypeFilter::SYNC, makeBurstCommands(true, 3));
+static const NamedCommandSequence kWriteAsyncSeq = std::make_tuple(
+        std::string("Write"), 0, StreamTypeFilter::ASYNC, makeBurstCommands(false, 3));
+
+std::shared_ptr<StateSequence> makeAsyncDrainCommands(bool isInput) {
+    return std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+            std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+            std::make_pair(kBurstCommand, isInput ? StreamDescriptor::State::ACTIVE
+                                                  : StreamDescriptor::State::TRANSFERRING),
+            std::make_pair(isInput ? kDrainInCommand : kDrainOutAllCommand,
+                           StreamDescriptor::State::DRAINING),
+            isInput ? std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE)
+                    : std::make_pair(kBurstCommand, StreamDescriptor::State::TRANSFERRING),
+            std::make_pair(isInput ? kDrainInCommand : kDrainOutAllCommand,
+                           StreamDescriptor::State::DRAINING)});
+}
+static const NamedCommandSequence kWriteDrainAsyncSeq =
+        std::make_tuple(std::string("WriteDrain"), kStreamTransientStateTransitionDelayMs,
+                        StreamTypeFilter::ASYNC, makeAsyncDrainCommands(false));
+static const NamedCommandSequence kDrainInSeq = std::make_tuple(
+        std::string("Drain"), 0, StreamTypeFilter::ANY, makeAsyncDrainCommands(true));
+
+std::shared_ptr<StateSequence> makeDrainOutCommands(bool isSync) {
+    return std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+            std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+            std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+            std::make_pair(kDrainOutAllCommand, StreamDescriptor::State::DRAINING),
+            std::make_pair(isSync ? TransitionTrigger(kGetStatusCommand)
+                                  : TransitionTrigger(kDrainReadyEvent),
+                           StreamDescriptor::State::IDLE)});
+}
+static const NamedCommandSequence kDrainOutSyncSeq = std::make_tuple(
+        std::string("Drain"), 0, StreamTypeFilter::SYNC, makeDrainOutCommands(true));
+static const NamedCommandSequence kDrainOutAsyncSeq = std::make_tuple(
+        std::string("Drain"), 0, StreamTypeFilter::ASYNC, makeDrainOutCommands(false));
+
+std::shared_ptr<StateSequence> makeDrainOutPauseCommands(bool isSync) {
+    return std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+            std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+            std::make_pair(kBurstCommand, isSync ? StreamDescriptor::State::ACTIVE
+                                                 : StreamDescriptor::State::TRANSFERRING),
+            std::make_pair(kDrainOutAllCommand, StreamDescriptor::State::DRAINING),
+            std::make_pair(kPauseCommand, StreamDescriptor::State::DRAIN_PAUSED),
+            std::make_pair(kStartCommand, StreamDescriptor::State::DRAINING),
+            std::make_pair(kPauseCommand, StreamDescriptor::State::DRAIN_PAUSED),
+            std::make_pair(kBurstCommand, isSync ? StreamDescriptor::State::PAUSED
+                                                 : StreamDescriptor::State::TRANSFER_PAUSED)});
+}
+static const NamedCommandSequence kDrainPauseOutSyncSeq =
+        std::make_tuple(std::string("DrainPause"), kStreamTransientStateTransitionDelayMs,
+                        StreamTypeFilter::SYNC, makeDrainOutPauseCommands(true));
+static const NamedCommandSequence kDrainPauseOutAsyncSeq =
+        std::make_tuple(std::string("DrainPause"), kStreamTransientStateTransitionDelayMs,
+                        StreamTypeFilter::ASYNC, makeDrainOutPauseCommands(false));
+
+// This sequence also verifies that the capture / presentation position is not reset on standby.
+std::shared_ptr<StateSequence> makeStandbyCommands(bool isInput, bool isSync) {
+    return std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+            std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+            std::make_pair(kStandbyCommand, StreamDescriptor::State::STANDBY),
+            std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+            std::make_pair(kBurstCommand, isInput || isSync
+                                                  ? StreamDescriptor::State::ACTIVE
+                                                  : StreamDescriptor::State::TRANSFERRING),
+            std::make_pair(kPauseCommand, isInput || isSync
+                                                  ? StreamDescriptor::State::PAUSED
+                                                  : StreamDescriptor::State::TRANSFER_PAUSED),
+            std::make_pair(kFlushCommand, isInput ? StreamDescriptor::State::STANDBY
+                                                  : StreamDescriptor::State::IDLE),
+            std::make_pair(isInput ? kGetStatusCommand : kStandbyCommand,  // no-op for input
+                           StreamDescriptor::State::STANDBY),
+            std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+            std::make_pair(kBurstCommand, isInput || isSync
+                                                  ? StreamDescriptor::State::ACTIVE
+                                                  : StreamDescriptor::State::TRANSFERRING)});
+}
+static const NamedCommandSequence kStandbyInSeq = std::make_tuple(
+        std::string("Standby"), 0, StreamTypeFilter::ANY, makeStandbyCommands(true, false));
+static const NamedCommandSequence kStandbyOutSyncSeq = std::make_tuple(
+        std::string("Standby"), 0, StreamTypeFilter::SYNC, makeStandbyCommands(false, true));
+static const NamedCommandSequence kStandbyOutAsyncSeq =
+        std::make_tuple(std::string("Standby"), kStreamTransientStateTransitionDelayMs,
+                        StreamTypeFilter::ASYNC, makeStandbyCommands(false, false));
+
 static const NamedCommandSequence kPauseInSeq =
-        std::make_pair(std::string("Pause"),
-                       std::vector<CommandAndState>{
-                               std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
-                               std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
-                               std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
-                               std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
-                               std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
-                               std::make_pair(kFlushCommand, StreamDescriptor::State::STANDBY)});
-static const NamedCommandSequence kPauseOutSeq =
-        std::make_pair(std::string("Pause"),
-                       std::vector<CommandAndState>{
-                               std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
-                               std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
-                               std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
-                               std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE),
-                               std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
-                               std::make_pair(kBurstCommand, StreamDescriptor::State::PAUSED),
-                               std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE),
-                               std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED)});
-static const NamedCommandSequence kFlushInSeq =
-        std::make_pair(std::string("Flush"),
-                       std::vector<CommandAndState>{
-                               std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
-                               std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
-                               std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
-                               std::make_pair(kFlushCommand, StreamDescriptor::State::STANDBY)});
-static const NamedCommandSequence kFlushOutSeq = std::make_pair(
-        std::string("Flush"),
-        std::vector<CommandAndState>{std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
-                                     std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
-                                     std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
-                                     std::make_pair(kFlushCommand, StreamDescriptor::State::IDLE)});
+        std::make_tuple(std::string("Pause"), 0, StreamTypeFilter::ANY,
+                        std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+                                std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+                                std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+                                std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+                                std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+                                std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+                                std::make_pair(kFlushCommand, StreamDescriptor::State::STANDBY)}));
+static const NamedCommandSequence kPauseOutSyncSeq =
+        std::make_tuple(std::string("Pause"), 0, StreamTypeFilter::SYNC,
+                        std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+                                std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+                                std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+                                std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+                                std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE),
+                                std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+                                std::make_pair(kBurstCommand, StreamDescriptor::State::PAUSED),
+                                std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE),
+                                std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED)}));
+/* TODO: Figure out a better way for testing sync/async bursts
+static const NamedCommandSequence kPauseOutAsyncSeq = std::make_tuple(
+        std::string("Pause"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
+        std::make_shared<StaticStateSequence>(std::vector<StateTransition>{
+                std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+                std::make_pair(kBurstCommand, StreamDescriptor::State::TRANSFERRING),
+                std::make_pair(kPauseCommand, StreamDescriptor::State::TRANSFER_PAUSED),
+                std::make_pair(kStartCommand, StreamDescriptor::State::TRANSFERRING),
+                std::make_pair(kPauseCommand, StreamDescriptor::State::TRANSFER_PAUSED),
+                std::make_pair(kDrainOutAllCommand, StreamDescriptor::State::DRAIN_PAUSED),
+                std::make_pair(kBurstCommand, StreamDescriptor::State::TRANSFER_PAUSED)}));
+*/
+
+std::shared_ptr<StateSequence> makeFlushCommands(bool isInput, bool isSync) {
+    return std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+            std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+            std::make_pair(kBurstCommand, isInput || isSync
+                                                  ? StreamDescriptor::State::ACTIVE
+                                                  : StreamDescriptor::State::TRANSFERRING),
+            std::make_pair(kPauseCommand, isInput || isSync
+                                                  ? StreamDescriptor::State::PAUSED
+                                                  : StreamDescriptor::State::TRANSFER_PAUSED),
+            std::make_pair(kFlushCommand, isInput ? StreamDescriptor::State::STANDBY
+                                                  : StreamDescriptor::State::IDLE)});
+}
+static const NamedCommandSequence kFlushInSeq = std::make_tuple(
+        std::string("Flush"), 0, StreamTypeFilter::ANY, makeFlushCommands(true, false));
+static const NamedCommandSequence kFlushOutSyncSeq = std::make_tuple(
+        std::string("Flush"), 0, StreamTypeFilter::SYNC, makeFlushCommands(false, true));
+static const NamedCommandSequence kFlushOutAsyncSeq =
+        std::make_tuple(std::string("Flush"), kStreamTransientStateTransitionDelayMs,
+                        StreamTypeFilter::ASYNC, makeFlushCommands(false, false));
+
+std::shared_ptr<StateSequence> makeDrainPauseFlushOutCommands(bool isSync) {
+    return std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+            std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+            std::make_pair(kBurstCommand, isSync ? StreamDescriptor::State::ACTIVE
+                                                 : StreamDescriptor::State::TRANSFERRING),
+            std::make_pair(kDrainOutAllCommand, StreamDescriptor::State::DRAINING),
+            std::make_pair(kPauseCommand, StreamDescriptor::State::DRAIN_PAUSED),
+            std::make_pair(kFlushCommand, StreamDescriptor::State::IDLE)});
+}
+static const NamedCommandSequence kDrainPauseFlushOutSyncSeq =
+        std::make_tuple(std::string("DrainPauseFlush"), kStreamTransientStateTransitionDelayMs,
+                        StreamTypeFilter::SYNC, makeDrainPauseFlushOutCommands(true));
+static const NamedCommandSequence kDrainPauseFlushOutAsyncSeq =
+        std::make_tuple(std::string("DrainPauseFlush"), kStreamTransientStateTransitionDelayMs,
+                        StreamTypeFilter::ASYNC, makeDrainPauseFlushOutCommands(false));
+
+// Note, this isn't the "official" enum printer, it is only used to make the test name suffix.
+std::string PrintStreamFilterToString(StreamTypeFilter filter) {
+    switch (filter) {
+        case StreamTypeFilter::ANY:
+            return "";
+        case StreamTypeFilter::SYNC:
+            return "Sync";
+        case StreamTypeFilter::ASYNC:
+            return "Async";
+    }
+    return std::string("Unknown").append(std::to_string(static_cast<int32_t>(filter)));
+}
 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(std::get<NAMED_CMD_NAME>(std::get<PARAM_CMD_SEQ>(info.param)))
+            .append(PrintStreamFilterToString(
+                    std::get<NAMED_CMD_STREAM_TYPE>(std::get<PARAM_CMD_SEQ>(info.param))))
             .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,
+                         testing::Values(kReadSeq, kDrainInSeq, kStandbyInSeq, kPauseInSeq,
                                          kFlushInSeq),
                          testing::Values(false, true)),
         GetStreamIoTestName);
@@ -2079,8 +2683,13 @@
 INSTANTIATE_TEST_SUITE_P(
         AudioStreamIoOutTest, AudioStreamIoOut,
         testing::Combine(testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
-                         testing::Values(kReadOrWriteSeq, kDrainOutSeq, kDrainPauseOutSeq,
-                                         kStandbySeq, kPauseOutSeq, kFlushOutSeq),
+                         testing::Values(kWriteSyncSeq, kWriteAsyncSeq, kWriteDrainAsyncSeq,
+                                         kDrainOutSyncSeq, kDrainPauseOutSyncSeq,
+                                         kDrainPauseOutAsyncSeq, kStandbyOutSyncSeq,
+                                         kStandbyOutAsyncSeq,
+                                         kPauseOutSyncSeq,  // kPauseOutAsyncSeq,
+                                         kFlushOutSyncSeq, kFlushOutAsyncSeq,
+                                         kDrainPauseFlushOutSyncSeq, kDrainPauseFlushOutAsyncSeq),
                          testing::Values(false, true)),
         GetStreamIoTestName);
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioStreamIoOut);
@@ -2095,7 +2704,6 @@
     void OnTestStart(const ::testing::TestInfo& test_info) override {
         TraceTestState("Started", test_info);
     }
-
     void OnTestEnd(const ::testing::TestInfo& test_info) override {
         TraceTestState("Completed", test_info);
     }
@@ -2109,6 +2717,7 @@
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
+    android::base::SetMinimumLogSeverity(::android::base::DEBUG);
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/common/all-versions/default/service/android.hardware.audio.service.rc b/audio/common/all-versions/default/service/android.hardware.audio.service.rc
index 45fef9a..f859f21 100644
--- a/audio/common/all-versions/default/service/android.hardware.audio.service.rc
+++ b/audio/common/all-versions/default/service/android.hardware.audio.service.rc
@@ -4,6 +4,8 @@
     # media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
     group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub
     capabilities BLOCK_SUSPEND
+    # setting RLIMIT_RTPRIO allows binder RT priority inheritance
+    rlimit rtprio 10 10
     ioprio rt 4
     task_profiles ProcessCapacityHigh HighPerformance
     onrestart restart audioserver
diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp
index b954fcd..d03118a 100644
--- a/audio/core/all-versions/default/Device.cpp
+++ b/audio/core/all-versions/default/Device.cpp
@@ -30,6 +30,7 @@
 #include <algorithm>
 
 #include <android/log.h>
+#include <hidl/HidlTransportSupport.h>
 #include <mediautils/MemoryLeakTrackUtil.h>
 #include <memunreachable/memunreachable.h>
 
@@ -183,6 +184,7 @@
     if (status == OK) {
         streamOut = new StreamOut(this, halStream);
         ++mOpenedStreamsCount;
+        android::hardware::setMinSchedulerPolicy(streamOut, SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
     }
     status_t convertStatus =
             HidlUtils::audioConfigFromHal(halConfig, false /*isInput*/, suggestedConfig);
@@ -220,6 +222,7 @@
     if (status == OK) {
         streamIn = new StreamIn(this, halStream);
         ++mOpenedStreamsCount;
+        android::hardware::setMinSchedulerPolicy(streamIn, SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
     }
     status_t convertStatus =
             HidlUtils::audioConfigFromHal(halConfig, true /*isInput*/, suggestedConfig);
diff --git a/audio/core/all-versions/default/DevicesFactory.cpp b/audio/core/all-versions/default/DevicesFactory.cpp
index f44daf0..011f9ac 100644
--- a/audio/core/all-versions/default/DevicesFactory.cpp
+++ b/audio/core/all-versions/default/DevicesFactory.cpp
@@ -23,6 +23,8 @@
 #include <string.h>
 
 #include <android/log.h>
+#include <hidl/HidlTransportSupport.h>
+#include <system/thread_defs.h>
 
 namespace android {
 namespace hardware {
@@ -103,6 +105,7 @@
     int halStatus = loadAudioInterface(moduleName, &halDevice);
     if (halStatus == OK) {
         result = new DeviceShim(halDevice);
+        android::hardware::setMinSchedulerPolicy(result, SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
         retval = Result::OK;
     } else if (halStatus == -EINVAL) {
         retval = Result::NOT_INITIALIZED;
diff --git a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
index 4b76a0b..719f752 100644
--- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -955,10 +955,16 @@
     ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
     const int presentationeEndPrecisionMs = 1000;
     const int sampleRate = 44100;
+    // The duration of sine882hz3s.mp3 is: 3 seconds + (576 + 756) samples.
+    // This is a mono file, thus 1 frame = 1 sample for it.
+    const int fullTrackDurationMs = 3000 + (576 + 756) * 1000 / sampleRate;
     const int significantSampleNumber = (presentationeEndPrecisionMs * sampleRate) / 1000;
+    // 'delay' is the amount of frames ignored at the beginning, 'padding' is the amount of frames
+    // ignored at the end of the track. Extra 1000 samples are requested for trimming to reduce the
+    // test running time.
     const int delay = 576 + 1000;
     const int padding = 756 + 1000;
-    const int durationMs = 3000 - 44;
+    const int durationMs = fullTrackDurationMs - (delay + padding) * 1000 / sampleRate;
     auto start = std::chrono::steady_clock::now();
     auto callbacks = sp<OffloadCallbacks>::make();
     std::mutex presentationEndLock;
diff --git a/audio/effect/all-versions/default/EffectsFactory.cpp b/audio/effect/all-versions/default/EffectsFactory.cpp
index e93ad89..9bf309c 100644
--- a/audio/effect/all-versions/default/EffectsFactory.cpp
+++ b/audio/effect/all-versions/default/EffectsFactory.cpp
@@ -32,6 +32,7 @@
 
 #include <UuidUtils.h>
 #include <android/log.h>
+#include <hidl/HidlTransportSupport.h>
 #include <media/EffectsFactoryApi.h>
 #include <system/audio_effects/effect_aec.h>
 #include <system/audio_effects/effect_agc.h>
@@ -44,6 +45,7 @@
 #include <system/audio_effects/effect_presetreverb.h>
 #include <system/audio_effects/effect_virtualizer.h>
 #include <system/audio_effects/effect_visualizer.h>
+#include <system/thread_defs.h>
 #include <util/EffectUtils.h>
 
 namespace android {
@@ -189,6 +191,7 @@
         status = (*handle)->get_descriptor(handle, &halDescriptor);
         if (status == OK) {
             effect = dispatchEffectInstanceCreation(halDescriptor, handle);
+            android::hardware::setMinSchedulerPolicy(effect, SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
             effectId = EffectMap::getInstance().add(handle);
         } else {
             ALOGE("Error querying effect descriptor for %s: %s",
diff --git a/automotive/remoteaccess/hal/default/Android.bp b/automotive/remoteaccess/hal/default/Android.bp
index a2bf86c..f27b8f8 100644
--- a/automotive/remoteaccess/hal/default/Android.bp
+++ b/automotive/remoteaccess/hal/default/Android.bp
@@ -18,11 +18,9 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-cc_binary {
-    name: "android.hardware.automotive.remoteaccess@V1-default-service",
+cc_defaults {
+    name: "remote-access-hal-defaults",
     vendor: true,
-    vintf_fragments: ["remoteaccess-default-service.xml"],
-    init_rc: ["remoteaccess-default-service.rc"],
     relative_install_path: "hw",
     srcs: ["src/RemoteAccessImpl.cpp"],
     whole_static_libs: [
@@ -41,10 +39,29 @@
     ],
     cflags: [
         "-Wno-unused-parameter",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.automotive.remoteaccess@V1-default-service",
+    defaults: ["remote-access-hal-defaults"],
+    vintf_fragments: ["remoteaccess-default-service.xml"],
+    init_rc: ["remoteaccess-default-service.rc"],
+    cflags: [
         "-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
     ],
 }
 
+cc_binary {
+    name: "android.hardware.automotive.remoteaccess@V1-tcu-test-service",
+    defaults: ["remote-access-hal-defaults"],
+    vintf_fragments: ["remoteaccess-default-service.xml"],
+    init_rc: ["remoteaccess-tcu-test-service.rc"],
+    cflags: [
+        "-DGRPC_SERVICE_ADDRESS=\"10.10.10.1:50051\"",
+    ],
+}
+
 cc_library {
     name: "RemoteAccessService",
     vendor_available: true,
diff --git a/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service.rc b/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service.rc
new file mode 100644
index 0000000..6437d70
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service.rc
@@ -0,0 +1,4 @@
+service vendor.remoteaccess-default /vendor/bin/hw/android.hardware.automotive.remoteaccess@V1-tcu-test-service
+    class hal
+    user vehicle_network
+    group system inet
diff --git a/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
index 5cd58d3..4be30a2 100644
--- a/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
+++ b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
@@ -111,7 +111,9 @@
         // Try to stop the reading stream.
         if (mGetRemoteTasksContext) {
             mGetRemoteTasksContext->TryCancel();
-            mGetRemoteTasksContext.reset();
+            // Don't reset mGetRemoteTaskContext here since the read stream might still be affective
+            // and might still be using it. This will cause reader->Read to return false and
+            // mGetRemoteTasksContext will be cleared after reader->Finish() is called.
         }
         mTaskWaitStopped = true;
         mCv.notify_all();
@@ -155,6 +157,7 @@
             }
         }
         Status status = reader->Finish();
+        mGetRemoteTasksContext.reset();
 
         ALOGE("GetRemoteTasks stream breaks, code: %d, message: %s, sleeping for 10s and retry",
               status.error_code(), status.error_message().c_str());
diff --git a/automotive/remoteaccess/test_grpc_server/README.md b/automotive/remoteaccess/test_grpc_server/README.md
index 6bc1829..af3d54a 100644
--- a/automotive/remoteaccess/test_grpc_server/README.md
+++ b/automotive/remoteaccess/test_grpc_server/README.md
@@ -75,11 +75,18 @@
 
 * Under android root: `source build/envsetup.sh`
 
+* Add
+  ```
+  PRODUCT_SOONG_NAMESPACES += hardware/interfaces/automotive/remoteaccess/test_grpc_server/lib`
+  ```
+
+  to `device/generic/car/common/car.mk`.
+
 * `lunch sdk_car_x86_64-userdebug`
 
 * `make -j TestWakeupClientServer`
 
-* `make -j ApPowerControlLib`
+* `make -j ApPOwerControlLib`
 
 ## How to push the test wakeup client to a TCU which runs Android.
 
@@ -99,7 +106,7 @@
 
 * `adb push vendor/bin/TestWakeupClientServer /vendor/bin`
 
-* `adb push vendor/lib/ApPowerControlLib.so /vendor/lib`
+* `adb push vendor/lib64/ApPowerControlLib.so /vendor/lib64`
 
 * `adb shell`
 
@@ -116,6 +123,13 @@
 
 * Under android root, `source build/envsetup.sh`
 
+* Add
+  ```
+  PRODUCT_SOONG_NAMESPACES += hardware/interfaces/automotive/remoteaccess/test_grpc_server/lib`
+  ```
+
+  to `device/generic/car/common/car.mk`.
+
 * `lunch sdk_car_x86_64-userdebug`
 
 * `m -j`
@@ -150,8 +164,12 @@
 
 * `make -j TestWakeupClientServer`
 
+* `make -j ApPOwerControlLib`
+
 * `adb push $ANDROID_PRODUCT_OUT/vendor/bin/TestWakeupClientServer /vendor/bin`
 
+* `adb push $ANDROID_PRODUCT_OUT/vendor/lib64/ApPowerControlLib.so /vendor/lib64`
+
 * `adb shell`
 
 * `emulator_car_x86_64:/ # su`
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleDisplay.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleDisplay.aidl
index 5aeafa4..be335ec 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleDisplay.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleDisplay.aidl
@@ -36,4 +36,7 @@
 enum VehicleDisplay {
   MAIN = 0,
   INSTRUMENT_CLUSTER = 1,
+  HUD = 2,
+  INPUT = 3,
+  AUXILIARY = 4,
 }
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleHwMotionButtonStateFlag.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleHwMotionButtonStateFlag.aidl
new file mode 100644
index 0000000..a7fee08
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleHwMotionButtonStateFlag.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.automotive.vehicle;
+@Backing(type="int") @VintfStability
+enum VehicleHwMotionButtonStateFlag {
+  BUTTON_PRIMARY = 1,
+  BUTTON_SECONDARY = 2,
+  BUTTON_TERTIARY = 4,
+  BUTTON_BACK = 8,
+  BUTTON_FORWARD = 16,
+  BUTTON_STYLUS_PRIMARY = 32,
+  BUTTON_STYLUS_SECONDARY = 64,
+}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleHwMotionInputAction.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleHwMotionInputAction.aidl
new file mode 100644
index 0000000..db4b41e
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleHwMotionInputAction.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.automotive.vehicle;
+@Backing(type="int") @VintfStability
+enum VehicleHwMotionInputAction {
+  ACTION_DOWN = 0,
+  ACTION_UP = 1,
+  ACTION_MOVE = 2,
+  ACTION_CANCEL = 3,
+  ACTION_OUTSIDE = 4,
+  ACTION_POINTER_DOWN = 5,
+  ACTION_POINTER_UP = 6,
+  ACTION_HOVER_MOVE = 7,
+  ACTION_SCROLL = 8,
+  ACTION_HOVER_ENTER = 9,
+  ACTION_HOVER_EXIT = 10,
+  ACTION_BUTTON_PRESS = 11,
+  ACTION_BUTTON_RELEASE = 12,
+}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleHwMotionInputSource.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleHwMotionInputSource.aidl
new file mode 100644
index 0000000..88c7873
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleHwMotionInputSource.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.automotive.vehicle;
+@Backing(type="int") @VintfStability
+enum VehicleHwMotionInputSource {
+  SOURCE_UNKNOWN = 0,
+  SOURCE_KEYBOARD = 1,
+  SOURCE_DPAD = 2,
+  SOURCE_GAMEPAD = 3,
+  SOURCE_TOUCHSCREEN = 4,
+  SOURCE_MOUSE = 5,
+  SOURCE_STYLUS = 6,
+  SOURCE_BLUETOOTH_STYLUS = 7,
+  SOURCE_TRACKBALL = 8,
+  SOURCE_MOUSE_RELATIVE = 9,
+  SOURCE_TOUCHPAD = 10,
+  SOURCE_TOUCH_NAVIGATION = 11,
+  SOURCE_ROTARY_ENCODER = 12,
+  SOURCE_JOYSTICK = 13,
+  SOURCE_HDMI = 14,
+  SOURCE_SENSOR = 15,
+}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleHwMotionToolType.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleHwMotionToolType.aidl
new file mode 100644
index 0000000..2b3bc7f
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleHwMotionToolType.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 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.automotive.vehicle;
+@Backing(type="int") @VintfStability
+enum VehicleHwMotionToolType {
+  TOOL_TYPE_UNKNOWN = 0,
+  TOOL_TYPE_FINGER = 1,
+  TOOL_TYPE_STYLUS = 2,
+  TOOL_TYPE_MOUSE = 3,
+  TOOL_TYPE_ERASER = 4,
+}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
index ab64e07..054e59d 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -71,6 +71,7 @@
   CURRENT_GEAR = 289408001,
   PARKING_BRAKE_ON = 287310850,
   PARKING_BRAKE_AUTO_APPLY = 287310851,
+  EV_BRAKE_REGENERATION_LEVEL = 289408012,
   FUEL_LEVEL_LOW = 287310853,
   NIGHT_MODE = 287310855,
   TURN_SIGNAL_STATE = 289408008,
@@ -114,6 +115,8 @@
   AP_POWER_BOOTUP_REASON = 289409538,
   DISPLAY_BRIGHTNESS = 289409539,
   HW_KEY_INPUT = 289475088,
+  HW_KEY_INPUT_V2 = 367004177,
+  HW_MOTION_INPUT = 367004178,
   HW_ROTARY_INPUT = 289475104,
   HW_CUSTOM_INPUT = 289475120,
   DOOR_POS = 373295872,
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleDisplay.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleDisplay.aidl
index 1759a90..6642eee 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleDisplay.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleDisplay.aidl
@@ -23,5 +23,21 @@
      * The primary Android display (for example, center console)
      */
     MAIN = 0,
+    /**
+     * Instrument cluster display. This may exist only for driver
+     */
     INSTRUMENT_CLUSTER = 1,
+
+    /**
+     * Head Up Display. This may exist only for driver
+     */
+    HUD = 2,
+    /**
+     * Dedicated display for showing IME for {@code MAIN}
+     */
+    INPUT = 3,
+    /**
+     * Auxiliary display which can provide additional screen for {@code MAIN} display
+     */
+    AUXILIARY = 4,
 }
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleHwMotionButtonStateFlag.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleHwMotionButtonStateFlag.aidl
new file mode 100644
index 0000000..5313a55
--- /dev/null
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleHwMotionButtonStateFlag.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package android.hardware.automotive.vehicle;
+
+/**
+ * See {@code android.view.MotionEvent#getButtonState()} for more details.
+ */
+@VintfStability
+@Backing(type="int")
+enum VehicleHwMotionButtonStateFlag {
+    /**
+     * Button state primary
+     */
+    BUTTON_PRIMARY = 0x0001,
+    /**
+     * Button state secondary
+     */
+    BUTTON_SECONDARY = 0x0002,
+    /**
+     * Button state tertiary
+     */
+    BUTTON_TERTIARY = 0x0004,
+    /**
+     * Button state back
+     */
+    BUTTON_BACK = 0x0008,
+    /**
+     * Button state forward
+     */
+    BUTTON_FORWARD = 0x0010,
+    /**
+     * Button state stylus primary
+     */
+    BUTTON_STYLUS_PRIMARY = 0x0020,
+    /**
+     * Button state stylus secondary
+     */
+    BUTTON_STYLUS_SECONDARY = 0x0040,
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleHwMotionInputAction.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleHwMotionInputAction.aidl
new file mode 100644
index 0000000..d80119a
--- /dev/null
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleHwMotionInputAction.aidl
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+package android.hardware.automotive.vehicle;
+
+/**
+ * See {@code android.view.MotionEvent#ACTION_*} fields for more details.
+ */
+@VintfStability
+@Backing(type="int")
+enum VehicleHwMotionInputAction {
+    /**
+     * Motion down
+     */
+    ACTION_DOWN = 0,
+    /**
+     * Motion up
+     */
+    ACTION_UP = 1,
+    /**
+     * Motion move
+     */
+    ACTION_MOVE = 2,
+    /**
+     * Motion cancel
+     */
+    ACTION_CANCEL = 3,
+    /**
+     * Motion outside
+     */
+    ACTION_OUTSIDE = 4,
+    /**
+     * Motion pointer down
+     */
+    ACTION_POINTER_DOWN = 5,
+    /**
+     * Motion pointer up
+     */
+    ACTION_POINTER_UP = 6,
+    /**
+     * Motion hover move
+     */
+    ACTION_HOVER_MOVE = 7,
+    /**
+     * Motion scroll
+     */
+    ACTION_SCROLL = 8,
+    /**
+     * Motion hover enter
+     */
+    ACTION_HOVER_ENTER = 9,
+    /**
+     * Motion hover exit
+     */
+    ACTION_HOVER_EXIT = 10,
+    /**
+     * Motion button press
+     */
+    ACTION_BUTTON_PRESS = 11,
+    /**
+     * Motion button release
+     */
+    ACTION_BUTTON_RELEASE = 12,
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleHwMotionInputSource.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleHwMotionInputSource.aidl
new file mode 100644
index 0000000..fb79983
--- /dev/null
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleHwMotionInputSource.aidl
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+package android.hardware.automotive.vehicle;
+
+/**
+ * See {@code android.view.InputDevice#SOURCE_*} for more details.
+ */
+@VintfStability
+@Backing(type="int")
+enum VehicleHwMotionInputSource {
+    /**
+     * Unknown source
+     */
+    SOURCE_UNKNOWN = 0,
+    /**
+     * Source keyboard
+     */
+    SOURCE_KEYBOARD = 1,
+    /**
+     * Source dpad
+     */
+    SOURCE_DPAD = 2,
+    /**
+     * Source game pad
+     */
+    SOURCE_GAMEPAD = 3,
+    /**
+     * Source touch screen
+     */
+    SOURCE_TOUCHSCREEN = 4,
+    /**
+     * Source mouse
+     */
+    SOURCE_MOUSE = 5,
+    /**
+     * Source stylus
+     */
+    SOURCE_STYLUS = 6,
+    /**
+     * Source bluetooth stylus
+     */
+    SOURCE_BLUETOOTH_STYLUS = 7,
+    /**
+     * Source trackball
+     */
+    SOURCE_TRACKBALL = 8,
+    /**
+     * Source mouse relative
+     */
+    SOURCE_MOUSE_RELATIVE = 9,
+    /**
+     * Source touchpad
+     */
+    SOURCE_TOUCHPAD = 10,
+    /**
+     * Source touch navigation
+     */
+    SOURCE_TOUCH_NAVIGATION = 11,
+    /**
+     * Source rotary encoder
+     */
+    SOURCE_ROTARY_ENCODER = 12,
+    /**
+     * Source joystick
+     */
+    SOURCE_JOYSTICK = 13,
+    /**
+     * Source hdmi
+     */
+    SOURCE_HDMI = 14,
+    /**
+     * Source sensor
+     */
+    SOURCE_SENSOR = 15,
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleHwMotionToolType.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleHwMotionToolType.aidl
new file mode 100644
index 0000000..08c9aed
--- /dev/null
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleHwMotionToolType.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 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.automotive.vehicle;
+
+/**
+ * See {@code android.view.MotionEvent#TOOL_TYPE_*} for more details.
+ */
+@VintfStability
+@Backing(type="int")
+enum VehicleHwMotionToolType {
+    /**
+     * Unknown type such as for a trackballl or other non-pointing device
+     */
+    TOOL_TYPE_UNKNOWN = 0,
+    /**
+     * Finger type
+     */
+    TOOL_TYPE_FINGER = 1,
+    /**
+     * Stylus type
+     */
+    TOOL_TYPE_STYLUS = 2,
+    /**
+     * Mouse type
+     */
+    TOOL_TYPE_MOUSE = 3,
+    /**
+     * Eraser type
+     */
+    TOOL_TYPE_ERASER = 4,
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
index baca8ab..b3116bd 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -331,9 +331,11 @@
     FUEL_DOOR_OPEN = 0x0308 + 0x10000000 + 0x01000000
             + 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
     /**
-     * EV battery level in WH, if EV or hybrid
+     * Battery level for EV or hybrid vehicle
      *
-     * Value may not exceed INFO_EV_BATTERY_CAPACITY
+     * Returns the current battery level, if EV or hybrid. This value will not exceed
+     * EV_CURRENT_BATTERY_CAPACITY. To calculate the battery percentage, use:
+     * (EV_BATTERY_LEVEL/EV_CURRENT_BATTERY_CAPACITY)*100.
      *
      * @change_mode VehiclePropertyChangeMode.CONTINUOUS
      * @access VehiclePropertyAccess.READ
@@ -489,6 +491,21 @@
     PARKING_BRAKE_AUTO_APPLY = 0x0403 + 0x10000000 + 0x01000000
             + 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
     /**
+     * Regenerative braking level of a electronic vehicle
+     *
+     * The maxInt32Value and minInt32Value in VehicleAreaConfig must be defined. All values between
+     * minInt32Value and maxInt32Value must be supported. The minInt32Value must be 0.
+     *
+     * The maxInt32Value in default area's VehicleAreaConfig indicates the maximum amount of energy
+     * regenerated from braking. The minInt32Value in default area's VehicleAreaConfig indicates no
+     * regenerative braking.
+     *
+     * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+     * @access VehiclePropertyAccess.READ_WRITE
+     */
+    EV_BRAKE_REGENERATION_LEVEL =
+            0x040C + VehiclePropertyGroup.SYSTEM + VehicleArea.GLOBAL + VehiclePropertyType.INT32,
+    /**
      * Warning for fuel low level.
      *
      * This property corresponds to the low fuel warning on the dashboard.
@@ -1228,6 +1245,62 @@
     HW_KEY_INPUT = 0x0A10 + 0x10000000 + 0x01000000
             + 0x00410000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
     /**
+     * Property to feed H/W input events to android
+     *
+     * int32array[0]: target display defined by VehicleDisplay like VehicleDisplay::MAIN,
+     *                VehicleDisplay::INSTRUMENT_CLUSTER, VehicleDisplay::AUX
+     * int32array[1]: key code, must use standard android key code like KEYCODE_HOME, KEYCODE_BACK
+     * int32array[2]: action defined in VehicleHwKeyInputAction like
+     *                VehicleHwKeyInputAction::ACTION_UP, VehicleHwKeyInputAction::ACTION_UP
+     * int32array[3]: repeat count of the event. For key down events, this is the repeat count
+     *                with the first down starting at 0 and counting up from there. For key up
+     *                events, this is always equal to 0
+     *
+     * int64array[0]: down time, elapsed nanoseconds since boot. Denotes the time of the most
+     *                recent key down event. For the down event, it will be the event time of the
+     *                down event itself
+     *
+     * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+     * @access VehiclePropertyAccess.READ
+     * @config_flags
+     */
+    HW_KEY_INPUT_V2 =
+            0x0A11 + VehiclePropertyGroup.SYSTEM + VehicleArea.SEAT + VehiclePropertyType.MIXED,
+    /**
+     * Property to feed H/W input events to android
+     *
+     * int32array[0]: target display defined by VehicleDisplay like VehicleDisplay::MAIN,
+     *                VehicleDisplay::INSTRUMENT_CLUSTER, VehicleDisplay::AUX
+     * int32array[1]: input type defined in VehicleHwMotionInputSource like
+     *                VehicleHwMotionInputSource::SOURCE_KEYBOARD,
+     *                VehicleHwMotionInputSource::SOURCE_DPAD
+     * int32array[2]: action code defined in VehicleHwMotionInputAction like
+     *                VehicleHwMotionInputAction::ACTION_UP, VehicleHwMotionInputAction::ACTION_DOWN
+     * int32array[3]: button state flag defined in VehicleHwMotionButtonStateFlag like
+     *                VehicleHwMotionButtonStateFlag::BUTTON_PRIMARY,
+     *                VehicleHwMotionButtonStateFlag::BUTTON_SECONDARY
+     * int32array[4]: pointer events count, N. N must be a positive integer
+     * int32array[5:5+N-1]: pointer id, length N
+     * int32array[5+N:5+2*N-1] : tool type, length N. As defined in VehicleHwMotionToolType like
+     *                           VehicleHwMotionToolType::TOOL_TYPE_FINGER,
+     *                           VehicleHwMotionToolType::TOOL_TYPE_STYLUS
+     *
+     * floatArray[0:N-1] : x data, length N
+     * floatArray[N:2*N-1] : y data, length N
+     * floatArray[2*N:3*N-1] : pressure data, length N
+     * floatArray[3*N:4*N-1] : size data, length N
+     *
+     * int64array[0]: down time, elapsed nanoseconds since boot. Denotes the time when the user
+     *                originally pressed down to start a stream of position events. For the down
+     *                event, it will be the event time of the down event itself
+     *
+     * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+     * @access VehiclePropertyAccess.READ
+     * @config_flags
+     */
+    HW_MOTION_INPUT =
+            0x0A12 + VehiclePropertyGroup.SYSTEM + VehicleArea.SEAT + VehiclePropertyType.MIXED,
+    /**
      * Property to feed H/W rotary events to android
      *
      * int32Values[0] : RotaryInputType identifying which rotary knob rotated
diff --git a/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
index 85ca474..82daa61 100644
--- a/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
+++ b/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
@@ -71,6 +71,7 @@
         {VehicleProperty::CURRENT_GEAR, VehiclePropertyAccess::READ},
         {VehicleProperty::PARKING_BRAKE_ON, VehiclePropertyAccess::READ},
         {VehicleProperty::PARKING_BRAKE_AUTO_APPLY, VehiclePropertyAccess::READ},
+        {VehicleProperty::EV_BRAKE_REGENERATION_LEVEL, VehiclePropertyAccess::READ_WRITE},
         {VehicleProperty::FUEL_LEVEL_LOW, VehiclePropertyAccess::READ},
         {VehicleProperty::NIGHT_MODE, VehiclePropertyAccess::READ},
         {VehicleProperty::TURN_SIGNAL_STATE, VehiclePropertyAccess::READ},
@@ -114,6 +115,8 @@
         {VehicleProperty::AP_POWER_BOOTUP_REASON, VehiclePropertyAccess::READ},
         {VehicleProperty::DISPLAY_BRIGHTNESS, VehiclePropertyAccess::READ_WRITE},
         {VehicleProperty::HW_KEY_INPUT, VehiclePropertyAccess::READ},
+        {VehicleProperty::HW_KEY_INPUT_V2, VehiclePropertyAccess::READ},
+        {VehicleProperty::HW_MOTION_INPUT, VehiclePropertyAccess::READ},
         {VehicleProperty::HW_ROTARY_INPUT, VehiclePropertyAccess::READ},
         {VehicleProperty::HW_CUSTOM_INPUT, VehiclePropertyAccess::READ},
         {VehicleProperty::DOOR_POS, VehiclePropertyAccess::READ_WRITE},
diff --git a/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
index f95580a..419c6de 100644
--- a/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
+++ b/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
@@ -71,6 +71,7 @@
         {VehicleProperty::CURRENT_GEAR, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::PARKING_BRAKE_ON, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::PARKING_BRAKE_AUTO_APPLY, VehiclePropertyChangeMode::ON_CHANGE},
+        {VehicleProperty::EV_BRAKE_REGENERATION_LEVEL, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::FUEL_LEVEL_LOW, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::NIGHT_MODE, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::TURN_SIGNAL_STATE, VehiclePropertyChangeMode::ON_CHANGE},
@@ -114,6 +115,8 @@
         {VehicleProperty::AP_POWER_BOOTUP_REASON, VehiclePropertyChangeMode::STATIC},
         {VehicleProperty::DISPLAY_BRIGHTNESS, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::HW_KEY_INPUT, VehiclePropertyChangeMode::ON_CHANGE},
+        {VehicleProperty::HW_KEY_INPUT_V2, VehiclePropertyChangeMode::ON_CHANGE},
+        {VehicleProperty::HW_MOTION_INPUT, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::HW_ROTARY_INPUT, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::HW_CUSTOM_INPUT, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::DOOR_POS, VehiclePropertyChangeMode::ON_CHANGE},
diff --git a/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
index d0caed2..7c6a92e 100644
--- a/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
+++ b/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
@@ -63,6 +63,7 @@
         Map.entry(VehicleProperty.CURRENT_GEAR, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.PARKING_BRAKE_ON, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.PARKING_BRAKE_AUTO_APPLY, VehiclePropertyAccess.READ),
+        Map.entry(VehicleProperty.EV_BRAKE_REGENERATION_LEVEL, VehiclePropertyAccess.READ_WRITE),
         Map.entry(VehicleProperty.FUEL_LEVEL_LOW, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.NIGHT_MODE, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.TURN_SIGNAL_STATE, VehiclePropertyAccess.READ),
@@ -106,6 +107,8 @@
         Map.entry(VehicleProperty.AP_POWER_BOOTUP_REASON, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.DISPLAY_BRIGHTNESS, VehiclePropertyAccess.READ_WRITE),
         Map.entry(VehicleProperty.HW_KEY_INPUT, VehiclePropertyAccess.READ),
+        Map.entry(VehicleProperty.HW_KEY_INPUT_V2, VehiclePropertyAccess.READ),
+        Map.entry(VehicleProperty.HW_MOTION_INPUT, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.HW_ROTARY_INPUT, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.HW_CUSTOM_INPUT, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.DOOR_POS, VehiclePropertyAccess.READ_WRITE),
diff --git a/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
index 96fe0a5..c54702b 100644
--- a/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
+++ b/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
@@ -63,6 +63,7 @@
         Map.entry(VehicleProperty.CURRENT_GEAR, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.PARKING_BRAKE_ON, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.PARKING_BRAKE_AUTO_APPLY, VehiclePropertyChangeMode.ON_CHANGE),
+        Map.entry(VehicleProperty.EV_BRAKE_REGENERATION_LEVEL, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.FUEL_LEVEL_LOW, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.NIGHT_MODE, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.TURN_SIGNAL_STATE, VehiclePropertyChangeMode.ON_CHANGE),
@@ -106,6 +107,8 @@
         Map.entry(VehicleProperty.AP_POWER_BOOTUP_REASON, VehiclePropertyChangeMode.STATIC),
         Map.entry(VehicleProperty.DISPLAY_BRIGHTNESS, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.HW_KEY_INPUT, VehiclePropertyChangeMode.ON_CHANGE),
+        Map.entry(VehicleProperty.HW_KEY_INPUT_V2, VehiclePropertyChangeMode.ON_CHANGE),
+        Map.entry(VehicleProperty.HW_MOTION_INPUT, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.HW_ROTARY_INPUT, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.HW_CUSTOM_INPUT, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.DOOR_POS, VehiclePropertyChangeMode.ON_CHANGE),
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
index bd80ad5..5623b87 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -123,7 +123,7 @@
             "property": "VehicleProperty::VEHICLE_SPEED_DISPLAY_UNITS",
             "defaultValue": {
                 "int32Values": [
-                    "VehicleUnit::KILOMETERS_PER_HOUR"
+                    "VehicleUnit::MILES_PER_HOUR"
                 ]
             },
             "configArray": [
@@ -1545,6 +1545,21 @@
             }
         },
         {
+            "property": "VehicleProperty::EV_BRAKE_REGENERATION_LEVEL",
+            "defaultValue": {
+                "int32Values": [
+                    0
+                ]
+            },
+            "areas": [
+                {
+                    "areaId": 0,
+                    "minInt32Value": 0,
+                    "maxInt32Value": 3
+                }
+            ]
+        },
+        {
             "property": "VehicleProperty::FUEL_LEVEL_LOW",
             "defaultValue": {
                 "int32Values": [
@@ -1556,7 +1571,7 @@
             "property": "VehicleProperty::FUEL_VOLUME_DISPLAY_UNITS",
             "defaultValue": {
                 "int32Values": [
-                    "VehicleUnit::LITER"
+                    "VehicleUnit::US_GALLON"
                 ]
             },
             "configArray": [
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
index 0a502c3..736ecaa 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -627,11 +627,7 @@
     } else if (EqualsIgnoreCase(option, "--inject-event")) {
         result.buffer = dumpInjectEvent(options);
     } else if (EqualsIgnoreCase(option, kUserHalDumpOption)) {
-        if (options.size() == 1) {
-            result.buffer = mFakeUserHal->showDumpHelp();
-        } else {
-            result.buffer = mFakeUserHal->dump(options[1]);
-        }
+        result.buffer = mFakeUserHal->dump();
     } else if (EqualsIgnoreCase(option, "--genfakedata")) {
         result.buffer = genFakeDataCommand(options);
     } else {
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
index 3f97a4d..0184462 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -1520,26 +1520,16 @@
     ASSERT_THAT(result.buffer, ContainsRegex("Invalid option: --invalid"));
 }
 
-TEST_F(FakeVehicleHardwareTest, testDumpFakeUserHalHelp) {
-    std::vector<std::string> options;
-    options.push_back("--user-hal");
-
-    DumpResult result = getHardware()->dump(options);
-    ASSERT_FALSE(result.callerShouldDumpState);
-    ASSERT_NE(result.buffer, "");
-    ASSERT_THAT(result.buffer, ContainsRegex("dumps state used for user management"));
-}
-
 TEST_F(FakeVehicleHardwareTest, testDumpFakeUserHal) {
     std::vector<std::string> options;
     options.push_back("--user-hal");
-    // Indent: " ".
-    options.push_back(" ");
 
     DumpResult result = getHardware()->dump(options);
     ASSERT_FALSE(result.callerShouldDumpState);
     ASSERT_NE(result.buffer, "");
-    ASSERT_THAT(result.buffer, ContainsRegex(" No InitialUserInfo response\n"));
+    ASSERT_THAT(result.buffer,
+                ContainsRegex("No InitialUserInfo response\nNo SwitchUser response\nNo CreateUser "
+                              "response\nNo SetUserIdentificationAssociation response\n"));
 }
 
 struct SetPropTestCase {
diff --git a/automotive/vehicle/aidl/impl/fake_impl/userhal/include/FakeUserHal.h b/automotive/vehicle/aidl/impl/fake_impl/userhal/include/FakeUserHal.h
index 4ae9c8c..fcbe8fd 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/userhal/include/FakeUserHal.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/userhal/include/FakeUserHal.h
@@ -64,7 +64,7 @@
     std::string showDumpHelp() const;
 
     // Dump its contents.
-    std::string dump(std::string indent) const;
+    std::string dump() const;
 
   private:
     const std::shared_ptr<VehiclePropValuePool> mValuePool;
diff --git a/automotive/vehicle/aidl/impl/fake_impl/userhal/src/FakeUserHal.cpp b/automotive/vehicle/aidl/impl/fake_impl/userhal/src/FakeUserHal.cpp
index 91318be..878c2e7 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/userhal/src/FakeUserHal.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/userhal/src/FakeUserHal.cpp
@@ -339,33 +339,31 @@
     return fmt::format("{}: dumps state used for user management\n", kUserHalDumpOption);
 }
 
-std::string FakeUserHal::dump(std::string indent) const {
+std::string FakeUserHal::dump() const {
     std::scoped_lock<std::mutex> lockGuard(mLock);
 
     std::string info;
     if (mInitialUserResponseFromCmd != nullptr) {
-        info += fmt::format("{}InitialUserInfo response: {}\n", indent,
+        info += fmt::format("InitialUserInfo response: {}\n",
                             mInitialUserResponseFromCmd->toString());
     } else {
-        info += fmt::format("{}No InitialUserInfo response\n", indent);
+        info += "No InitialUserInfo response\n";
     }
     if (mSwitchUserResponseFromCmd != nullptr) {
-        info += fmt::format("{}SwitchUser response: {}\n", indent,
-                            mSwitchUserResponseFromCmd->toString());
+        info += fmt::format("SwitchUser response: {}\n", mSwitchUserResponseFromCmd->toString());
     } else {
-        info += fmt::format("{}No SwitchUser response\n", indent);
+        info += "No SwitchUser response\n";
     }
     if (mCreateUserResponseFromCmd != nullptr) {
-        info += fmt::format("{}CreateUser response: {}\n", indent,
-                            mCreateUserResponseFromCmd->toString());
+        info += fmt::format("CreateUser response: {}\n", mCreateUserResponseFromCmd->toString());
     } else {
-        info += fmt::format("{}No CreateUser response\n", indent);
+        info += "No CreateUser response\n";
     }
     if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
-        info += fmt::format("{}SetUserIdentificationAssociation response: {}\n", indent,
+        info += fmt::format("SetUserIdentificationAssociation response: {}\n",
                             mSetUserIdentificationAssociationResponseFromCmd->toString());
     } else {
-        info += fmt::format("{}No SetUserIdentificationAssociation response\n", indent);
+        info += "No SetUserIdentificationAssociation response\n";
     }
     return info;
 }
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index d447bf8..4a4e023 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -789,10 +789,13 @@
     for (uint32_t i = 0; i < numArgs; i++) {
         options.push_back(args[i]);
     }
+    if (options.size() == 1 && options[0] == "-a") {
+        // Ignore "-a" option. Bugreport will call with this option.
+        options.clear();
+    }
     DumpResult result = mVehicleHardware->dump(options);
     dprintf(fd, "%s", (result.buffer + "\n").c_str());
     if (!result.callerShouldDumpState) {
-        dprintf(fd, "Skip dumping Vehicle HAL State.\n");
         return STATUS_OK;
     }
     dprintf(fd, "Vehicle HAL State: \n");
diff --git a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
index 7eefe2a..33a697c 100644
--- a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
+++ b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
@@ -481,6 +481,12 @@
                             actualPropertyType);
 }
 
+TEST_P(VtsHalAutomotiveVehicleTargetTest, verifyEvBrakeRegenerationLevelConfig) {
+    verifyProperty(VehicleProperty::EV_BRAKE_REGENERATION_LEVEL,
+                   VehiclePropertyAccess::READ_WRITE, VehiclePropertyChangeMode::ON_CHANGE,
+                   VehiclePropertyGroup::SYSTEM, VehicleArea::GLOBAL, VehiclePropertyType::INT32);
+}
+
 TEST_P(VtsHalAutomotiveVehicleTargetTest, verifyDoorChildLockEnabledConfig) {
     verifyProperty(VehicleProperty::DOOR_CHILD_LOCK_ENABLED, VehiclePropertyAccess::READ_WRITE,
                    VehiclePropertyChangeMode::ON_CHANGE, VehiclePropertyGroup::SYSTEM,
diff --git a/biometrics/fingerprint/aidl/default/fingerprint-example.rc b/biometrics/fingerprint/aidl/default/fingerprint-example.rc
index 574438e..10db00d 100644
--- a/biometrics/fingerprint/aidl/default/fingerprint-example.rc
+++ b/biometrics/fingerprint/aidl/default/fingerprint-example.rc
@@ -2,3 +2,8 @@
     class hal
     user nobody
     group nobody
+    disabled
+on property:ro.vendor.fingerprint_virtual_hal_start=true
+    enable vendor.fingerprint-example
+on property:persist.vendor.fingerprint.virtual.type=*
+    enable vendor.fingerprint-example
diff --git a/camera/common/aidl/Android.bp b/camera/common/aidl/Android.bp
index d21ae58..e1c9cc5 100644
--- a/camera/common/aidl/Android.bp
+++ b/camera/common/aidl/Android.bp
@@ -19,6 +19,9 @@
         java: {
             enabled: false,
         },
+        rust: {
+            enabled: true,
+        }
     },
     versions_with_info: [
         {
diff --git a/camera/device/aidl/Android.bp b/camera/device/aidl/Android.bp
index c184677..2e5b667 100644
--- a/camera/device/aidl/Android.bp
+++ b/camera/device/aidl/Android.bp
@@ -27,6 +27,9 @@
             sdk_version: "module_current",
             enabled: false,
         },
+        rust: {
+            enabled: true,
+        }
     },
     versions_with_info: [
         {
diff --git a/camera/device/default/ExternalCameraDeviceSession.cpp b/camera/device/default/ExternalCameraDeviceSession.cpp
index 68c6d2e..736bc3c 100644
--- a/camera/device/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/default/ExternalCameraDeviceSession.cpp
@@ -714,8 +714,8 @@
         return Status::ILLEGAL_ARGUMENT;
     }
 
-    if (info == nullptr || results == nullptr || info == nullptr || session == nullptr) {
-        ALOGE("%s, output arguments (%p, %p, %p, %p) much not be null", __FUNCTION__, msgs, results,
+    if (msgs == nullptr || results == nullptr || info == nullptr || session == nullptr) {
+        ALOGE("%s, output arguments (%p, %p, %p, %p) must not be null", __FUNCTION__, msgs, results,
               info, session);
     }
 
diff --git a/camera/metadata/aidl/Android.bp b/camera/metadata/aidl/Android.bp
index 301a943..0113a4b 100644
--- a/camera/metadata/aidl/Android.bp
+++ b/camera/metadata/aidl/Android.bp
@@ -19,6 +19,9 @@
         java: {
             enabled: false,
         },
+        rust: {
+            enabled: true,
+        }
     },
     versions_with_info: [
         {
diff --git a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSection.aidl b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSection.aidl
index 8bed156..e28ecad 100644
--- a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSection.aidl
+++ b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSection.aidl
@@ -70,5 +70,6 @@
   ANDROID_HEIC_INFO = 29,
   ANDROID_AUTOMOTIVE = 30,
   ANDROID_AUTOMOTIVE_LENS = 31,
+  ANDROID_EXTENSION = 32,
   VENDOR_SECTION = 32768,
 }
diff --git a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl
index 1725347..a223309 100644
--- a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl
+++ b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl
@@ -70,5 +70,6 @@
   ANDROID_HEIC_INFO_START = 1900544,
   ANDROID_AUTOMOTIVE_START = 1966080,
   ANDROID_AUTOMOTIVE_LENS_START = 2031616,
+  ANDROID_EXTENSION_START = 2097152,
   VENDOR_SECTION_START = -2147483648,
 }
diff --git a/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSection.aidl b/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSection.aidl
index 99e28b9..0d79f0d 100644
--- a/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSection.aidl
+++ b/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSection.aidl
@@ -61,5 +61,6 @@
     ANDROID_HEIC_INFO,
     ANDROID_AUTOMOTIVE,
     ANDROID_AUTOMOTIVE_LENS,
+    ANDROID_EXTENSION,
     VENDOR_SECTION = 0x8000,
 }
diff --git a/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl b/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl
index 62c71e9..8f57128 100644
--- a/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl
+++ b/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataSectionStart.aidl
@@ -63,5 +63,6 @@
     ANDROID_HEIC_INFO_START = CameraMetadataSection.ANDROID_HEIC_INFO << 16,
     ANDROID_AUTOMOTIVE_START = CameraMetadataSection.ANDROID_AUTOMOTIVE << 16,
     ANDROID_AUTOMOTIVE_LENS_START = CameraMetadataSection.ANDROID_AUTOMOTIVE_LENS << 16,
+    ANDROID_EXTENSION_START = CameraMetadataSection.ANDROID_EXTENSION << 16,
     VENDOR_SECTION_START = CameraMetadataSection.VENDOR_SECTION << 16,
 }
diff --git a/camera/provider/aidl/Android.bp b/camera/provider/aidl/Android.bp
index d7e613e..19dede0 100644
--- a/camera/provider/aidl/Android.bp
+++ b/camera/provider/aidl/Android.bp
@@ -26,6 +26,9 @@
         cpp: {
             enabled: false,
         },
+        rust: {
+            enabled: true,
+        },
     },
     versions_with_info: [
         {
diff --git a/common/aidl/Android.bp b/common/aidl/Android.bp
index 7f6890c..f3ea8e8 100644
--- a/common/aidl/Android.bp
+++ b/common/aidl/Android.bp
@@ -36,6 +36,9 @@
             ],
             min_sdk_version: "29",
         },
+        rust: {
+            enabled: true,
+        }
     },
     frozen: true,
     versions: [
diff --git a/common/fmq/aidl/Android.bp b/common/fmq/aidl/Android.bp
index 95fcc41..0715575 100644
--- a/common/fmq/aidl/Android.bp
+++ b/common/fmq/aidl/Android.bp
@@ -36,6 +36,9 @@
             ],
             min_sdk_version: "29",
         },
+        rust: {
+            enabled: true,
+        }
     },
     frozen: true,
     versions: ["1"],
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 61438b4..03411d0 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -277,7 +277,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.graphics.composer3</name>
-        <version>1</version>
+        <version>2</version>
         <interface>
             <name>IComposer</name>
             <instance>default</instance>
@@ -424,14 +424,6 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.oemlock</name>
-        <version>1.0</version>
-        <interface>
-            <name>IOemLock</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="false">
         <name>android.hardware.power</name>
         <version>2-4</version>
@@ -557,15 +549,6 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.secure_element</name>
-        <version>1.0-2</version>
-        <interface>
-            <name>ISecureElement</name>
-            <regex-instance>eSE[1-9][0-9]*</regex-instance>
-            <regex-instance>SIM[1-9][0-9]*</regex-instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.secure_element</name>
         <version>1</version>
diff --git a/current.txt b/current.txt
index 146ded6..0fb8b49 100644
--- a/current.txt
+++ b/current.txt
@@ -931,5 +931,6 @@
 
 # ABI preserving changes to HALs during Android U
 2aa559cda86c358c6429114ef6bc72c1b43281e98f9eb6b4df5e7073c8d05767 android.hardware.automotive.vehicle@2.0::types
+42abd285a4293dadb8c89bc63b90cae2872fbffe90c4517aa3ea4965e8aecff7 android.hardware.graphics.common@1.2::types
 
 # There will be no more HIDL HALs. Use AIDL instead.
diff --git a/graphics/common/1.2/types.hal b/graphics/common/1.2/types.hal
index ebea1dc..07e9882 100644
--- a/graphics/common/1.2/types.hal
+++ b/graphics/common/1.2/types.hal
@@ -77,6 +77,8 @@
     HEIF = 0x1004,
 };
 
+@export(name="android_color_mode_v1_2_t", value_prefix="HAL_COLOR_MODE_",
+        export_parent="false")
 enum ColorMode : @1.1::ColorMode {
     /**
      * DISPLAY_BT2020 corresponds with display settings that implement the ITU-R
diff --git a/graphics/common/aidl/Android.bp b/graphics/common/aidl/Android.bp
index 84bc1af..6c8ff4b 100644
--- a/graphics/common/aidl/Android.bp
+++ b/graphics/common/aidl/Android.bp
@@ -39,6 +39,9 @@
             ],
             min_sdk_version: "29",
         },
+        rust: {
+            enabled: true,
+        }
     },
     frozen: false,
     versions_with_info: [
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
index fa66812..4d7bf13 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
@@ -811,8 +811,7 @@
     EXPECT_TRUE(status.isOk());
 }
 
-// TODO(b/250036572): disable this due to no implementation and revup on cuttlefish
-TEST_P(GraphicsComposerAidlTest, DISABLED_GetOverlaySupport) {
+TEST_P(GraphicsComposerAidlTest, GetOverlaySupport) {
     const auto& [status, _] = mComposerClient->getOverlaySupport();
     if (!status.isOk() && status.getExceptionCode() == EX_SERVICE_SPECIFIC &&
         status.getServiceSpecificError() == IComposerClient::EX_UNSUPPORTED) {
diff --git a/media/bufferpool/aidl/Android.bp b/media/bufferpool/aidl/Android.bp
index 68ac489..5ea2948 100644
--- a/media/bufferpool/aidl/Android.bp
+++ b/media/bufferpool/aidl/Android.bp
@@ -24,6 +24,7 @@
 aidl_interface {
     name: "android.hardware.media.bufferpool2",
     vendor_available: true,
+    double_loadable: true,
     srcs: ["android/hardware/media/bufferpool2/*.aidl"],
     imports: [
         "android.hardware.common-V2",
diff --git a/media/c2/aidl/Android.bp b/media/c2/aidl/Android.bp
index 10867c0..56531db 100644
--- a/media/c2/aidl/Android.bp
+++ b/media/c2/aidl/Android.bp
@@ -1,8 +1,18 @@
 // 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.media.c2",
     vendor_available: true,
+    double_loadable: true,
     srcs: ["android/hardware/media/c2/*.aidl"],
     include_dirs: [
         "frameworks/native/aidl/gui",
diff --git a/neuralnetworks/1.2/utils/src/BurstUtils.cpp b/neuralnetworks/1.2/utils/src/BurstUtils.cpp
index b589c46..c4c096d 100644
--- a/neuralnetworks/1.2/utils/src/BurstUtils.cpp
+++ b/neuralnetworks/1.2/utils/src/BurstUtils.cpp
@@ -190,12 +190,13 @@
     size_t index = 0;
 
     // validate packet information
-    if (data.size() == 0 || data[index].getDiscriminator() != discriminator::packetInformation) {
+    if (index >= data.size() ||
+        data.at(index).getDiscriminator() != discriminator::packetInformation) {
         return NN_ERROR() << "FMQ Request packet ill-formed";
     }
 
     // unpackage packet information
-    const FmqRequestDatum::PacketInformation& packetInfo = data[index].packetInformation();
+    const FmqRequestDatum::PacketInformation& packetInfo = data.at(index).packetInformation();
     index++;
     const uint32_t packetSize = packetInfo.packetSize;
     const uint32_t numberOfInputOperands = packetInfo.numberOfInputOperands;
@@ -212,13 +213,14 @@
     inputs.reserve(numberOfInputOperands);
     for (size_t operand = 0; operand < numberOfInputOperands; ++operand) {
         // validate input operand information
-        if (data[index].getDiscriminator() != discriminator::inputOperandInformation) {
+        if (index >= data.size() ||
+            data.at(index).getDiscriminator() != discriminator::inputOperandInformation) {
             return NN_ERROR() << "FMQ Request packet ill-formed";
         }
 
         // unpackage operand information
         const FmqRequestDatum::OperandInformation& operandInfo =
-                data[index].inputOperandInformation();
+                data.at(index).inputOperandInformation();
         index++;
         const bool hasNoValue = operandInfo.hasNoValue;
         const V1_0::DataLocation location = operandInfo.location;
@@ -229,12 +231,13 @@
         dimensions.reserve(numberOfDimensions);
         for (size_t i = 0; i < numberOfDimensions; ++i) {
             // validate dimension
-            if (data[index].getDiscriminator() != discriminator::inputOperandDimensionValue) {
+            if (index >= data.size() ||
+                data.at(index).getDiscriminator() != discriminator::inputOperandDimensionValue) {
                 return NN_ERROR() << "FMQ Request packet ill-formed";
             }
 
             // unpackage dimension
-            const uint32_t dimension = data[index].inputOperandDimensionValue();
+            const uint32_t dimension = data.at(index).inputOperandDimensionValue();
             index++;
 
             // store result
@@ -251,13 +254,14 @@
     outputs.reserve(numberOfOutputOperands);
     for (size_t operand = 0; operand < numberOfOutputOperands; ++operand) {
         // validate output operand information
-        if (data[index].getDiscriminator() != discriminator::outputOperandInformation) {
+        if (index >= data.size() ||
+            data.at(index).getDiscriminator() != discriminator::outputOperandInformation) {
             return NN_ERROR() << "FMQ Request packet ill-formed";
         }
 
         // unpackage operand information
         const FmqRequestDatum::OperandInformation& operandInfo =
-                data[index].outputOperandInformation();
+                data.at(index).outputOperandInformation();
         index++;
         const bool hasNoValue = operandInfo.hasNoValue;
         const V1_0::DataLocation location = operandInfo.location;
@@ -268,12 +272,13 @@
         dimensions.reserve(numberOfDimensions);
         for (size_t i = 0; i < numberOfDimensions; ++i) {
             // validate dimension
-            if (data[index].getDiscriminator() != discriminator::outputOperandDimensionValue) {
+            if (index >= data.size() ||
+                data.at(index).getDiscriminator() != discriminator::outputOperandDimensionValue) {
                 return NN_ERROR() << "FMQ Request packet ill-formed";
             }
 
             // unpackage dimension
-            const uint32_t dimension = data[index].outputOperandDimensionValue();
+            const uint32_t dimension = data.at(index).outputOperandDimensionValue();
             index++;
 
             // store result
@@ -290,12 +295,13 @@
     slots.reserve(numberOfPools);
     for (size_t pool = 0; pool < numberOfPools; ++pool) {
         // validate input operand information
-        if (data[index].getDiscriminator() != discriminator::poolIdentifier) {
+        if (index >= data.size() ||
+            data.at(index).getDiscriminator() != discriminator::poolIdentifier) {
             return NN_ERROR() << "FMQ Request packet ill-formed";
         }
 
         // unpackage operand information
-        const int32_t poolId = data[index].poolIdentifier();
+        const int32_t poolId = data.at(index).poolIdentifier();
         index++;
 
         // store result
@@ -303,17 +309,17 @@
     }
 
     // validate measureTiming
-    if (data[index].getDiscriminator() != discriminator::measureTiming) {
+    if (index >= data.size() || data.at(index).getDiscriminator() != discriminator::measureTiming) {
         return NN_ERROR() << "FMQ Request packet ill-formed";
     }
 
     // unpackage measureTiming
-    const V1_2::MeasureTiming measure = data[index].measureTiming();
+    const V1_2::MeasureTiming measure = data.at(index).measureTiming();
     index++;
 
     // validate packet information
     if (index != packetSize) {
-        return NN_ERROR() << "FMQ Result packet ill-formed";
+        return NN_ERROR() << "FMQ Request packet ill-formed";
     }
 
     // return request
@@ -328,12 +334,13 @@
     size_t index = 0;
 
     // validate packet information
-    if (data.size() == 0 || data[index].getDiscriminator() != discriminator::packetInformation) {
+    if (index >= data.size() ||
+        data.at(index).getDiscriminator() != discriminator::packetInformation) {
         return NN_ERROR() << "FMQ Result packet ill-formed";
     }
 
     // unpackage packet information
-    const FmqResultDatum::PacketInformation& packetInfo = data[index].packetInformation();
+    const FmqResultDatum::PacketInformation& packetInfo = data.at(index).packetInformation();
     index++;
     const uint32_t packetSize = packetInfo.packetSize;
     const V1_0::ErrorStatus errorStatus = packetInfo.errorStatus;
@@ -349,12 +356,13 @@
     outputShapes.reserve(numberOfOperands);
     for (size_t operand = 0; operand < numberOfOperands; ++operand) {
         // validate operand information
-        if (data[index].getDiscriminator() != discriminator::operandInformation) {
+        if (index >= data.size() ||
+            data.at(index).getDiscriminator() != discriminator::operandInformation) {
             return NN_ERROR() << "FMQ Result packet ill-formed";
         }
 
         // unpackage operand information
-        const FmqResultDatum::OperandInformation& operandInfo = data[index].operandInformation();
+        const FmqResultDatum::OperandInformation& operandInfo = data.at(index).operandInformation();
         index++;
         const bool isSufficient = operandInfo.isSufficient;
         const uint32_t numberOfDimensions = operandInfo.numberOfDimensions;
@@ -364,12 +372,13 @@
         dimensions.reserve(numberOfDimensions);
         for (size_t i = 0; i < numberOfDimensions; ++i) {
             // validate dimension
-            if (data[index].getDiscriminator() != discriminator::operandDimensionValue) {
+            if (index >= data.size() ||
+                data.at(index).getDiscriminator() != discriminator::operandDimensionValue) {
                 return NN_ERROR() << "FMQ Result packet ill-formed";
             }
 
             // unpackage dimension
-            const uint32_t dimension = data[index].operandDimensionValue();
+            const uint32_t dimension = data.at(index).operandDimensionValue();
             index++;
 
             // store result
@@ -381,12 +390,13 @@
     }
 
     // validate execution timing
-    if (data[index].getDiscriminator() != discriminator::executionTiming) {
+    if (index >= data.size() ||
+        data.at(index).getDiscriminator() != discriminator::executionTiming) {
         return NN_ERROR() << "FMQ Result packet ill-formed";
     }
 
     // unpackage execution timing
-    const V1_2::Timing timing = data[index].executionTiming();
+    const V1_2::Timing timing = data.at(index).executionTiming();
     index++;
 
     // validate packet information
diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
index 5a76a21..4753afb 100644
--- a/security/keymint/aidl/Android.bp
+++ b/security/keymint/aidl/Android.bp
@@ -71,6 +71,13 @@
     ],
 }
 
+cc_defaults {
+    name: "keymint_use_latest_hal_aidl_cpp_shared",
+    shared_libs: [
+        "android.hardware.security.keymint-V3-cpp",
+    ],
+}
+
 // A rust_defaults that includes the latest KeyMint AIDL library.
 // Modules that depend on KeyMint directly can include this cc_defaults to avoid
 // managing dependency versions explicitly.
diff --git a/sensors/1.0/default/Android.bp b/sensors/1.0/default/Android.bp
index 2e4e1b0..bb31050 100644
--- a/sensors/1.0/default/Android.bp
+++ b/sensors/1.0/default/Android.bp
@@ -44,6 +44,12 @@
         "libhidlbase",
         "android.hardware.sensors@1.0",
     ],
+    whole_static_libs: [
+        "sensors_common_convert",
+    ],
+    export_static_lib_headers: [
+        "sensors_common_convert",
+    ],
     local_include_dirs: ["include/sensors"],
     export_shared_lib_headers: [
         "libhardware",
diff --git a/sensors/1.0/default/convert.cpp b/sensors/1.0/default/convert.cpp
index 43ee327..ae71a97 100644
--- a/sensors/1.0/default/convert.cpp
+++ b/sensors/1.0/default/convert.cpp
@@ -196,6 +196,11 @@
     }
 }
 
+void convertFromASensorEvent(const ASensorEvent& src, Event* dst) {
+    convertFromSensorEvent(
+            android::hardware::sensors::implementation::common::convertASensorEvent(src), dst);
+}
+
 void convertToSensorEvent(const Event &src, sensors_event_t *dst) {
     *dst = {.version = sizeof(sensors_event_t),
             .sensor = src.sensorHandle,
diff --git a/sensors/1.0/default/include/sensors/convert.h b/sensors/1.0/default/include/sensors/convert.h
index c3a0125..ae773df 100644
--- a/sensors/1.0/default/include/sensors/convert.h
+++ b/sensors/1.0/default/include/sensors/convert.h
@@ -20,6 +20,7 @@
 
 #include <android/hardware/sensors/1.0/ISensors.h>
 #include <hardware/sensors.h>
+#include <sensors/common_convert.h>
 
 namespace android {
 namespace hardware {
@@ -31,6 +32,7 @@
 void convertToSensor(const SensorInfo &src, sensor_t *dst);
 
 void convertFromSensorEvent(const sensors_event_t &src, Event *dst);
+void convertFromASensorEvent(const ASensorEvent& src, Event* dst);
 void convertToSensorEvent(const Event &src, sensors_event_t *dst);
 
 bool convertFromSharedMemInfo(const SharedMemInfo& memIn, sensors_direct_mem_t *memOut);
diff --git a/sensors/aidl/convert/Android.bp b/sensors/aidl/convert/Android.bp
index d47de8e..0b31597 100644
--- a/sensors/aidl/convert/Android.bp
+++ b/sensors/aidl/convert/Android.bp
@@ -37,6 +37,12 @@
         "libutils",
         "android.hardware.sensors-V1-ndk",
     ],
+    whole_static_libs: [
+        "sensors_common_convert",
+    ],
+    export_static_lib_headers: [
+        "sensors_common_convert",
+    ],
     local_include_dirs: ["include/aidl/sensors"],
     export_shared_lib_headers: [
         "libhardware",
diff --git a/sensors/aidl/convert/convert.cpp b/sensors/aidl/convert/convert.cpp
index 415f435..abd4d55 100644
--- a/sensors/aidl/convert/convert.cpp
+++ b/sensors/aidl/convert/convert.cpp
@@ -490,6 +490,10 @@
     }
 }
 
+void convertFromASensorEvent(const ASensorEvent& src, Event* dst) {
+    convertFromSensorEvent(common::convertASensorEvent(src), dst);
+}
+
 }  // namespace implementation
 }  // namespace sensors
 }  // namespace hardware
diff --git a/sensors/aidl/convert/include/aidl/sensors/convert.h b/sensors/aidl/convert/include/aidl/sensors/convert.h
index 702b226..44504fe 100644
--- a/sensors/aidl/convert/include/aidl/sensors/convert.h
+++ b/sensors/aidl/convert/include/aidl/sensors/convert.h
@@ -18,6 +18,7 @@
 
 #include <aidl/android/hardware/sensors/ISensors.h>
 #include <hardware/sensors.h>
+#include <sensors/common_convert.h>
 
 namespace android {
 namespace hardware {
@@ -29,6 +30,7 @@
 void convertToSensorEvent(const aidl::android::hardware::sensors::Event& src, sensors_event_t* dst);
 void convertFromSensorEvent(const sensors_event_t& src,
                             aidl::android::hardware::sensors::Event* dst);
+void convertFromASensorEvent(const ASensorEvent& src, aidl::android::hardware::sensors::Event* dst);
 
 }  // namespace implementation
 }  // namespace sensors
diff --git a/sensors/common/convert/Android.bp b/sensors/common/convert/Android.bp
new file mode 100644
index 0000000..230665e
--- /dev/null
+++ b/sensors/common/convert/Android.bp
@@ -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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+    name: "sensors_common_convert",
+    srcs: [
+        "convert.cpp",
+    ],
+    vendor_available: true,
+    host_supported: true,
+    local_include_dirs: ["include"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libhardware",
+    ],
+    header_libs: [
+        "libandroid_sensor_headers",
+    ],
+
+    export_include_dirs: ["include"],
+    export_header_lib_headers: [
+        "libandroid_sensor_headers",
+    ],
+}
diff --git a/sensors/common/convert/convert.cpp b/sensors/common/convert/convert.cpp
new file mode 100644
index 0000000..27de32c
--- /dev/null
+++ b/sensors/common/convert/convert.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 <sensors/common_convert.h>
+#include <cstring>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace implementation {
+namespace common {
+
+sensors_event_t convertASensorEvent(const ASensorEvent& src) {
+    // Attempt to ensure these types are compatible.
+    static_assert(sizeof(sensors_event_t) == sizeof(ASensorEvent));
+    static_assert(offsetof(sensors_event_t, timestamp) == offsetof(ASensorEvent, timestamp));
+    static_assert(offsetof(sensors_event_t, flags) == offsetof(ASensorEvent, flags));
+
+    // TODO(b/259711109) Follow up work to handle this in a safer way.
+    return *reinterpret_cast<const sensors_event_t*>(&src);
+}
+
+}  // namespace common
+}  // namespace implementation
+}  // namespace sensors
+}  // namespace hardware
+}  // namespace android
diff --git a/sensors/common/convert/include/sensors/common_convert.h b/sensors/common/convert/include/sensors/common_convert.h
new file mode 100644
index 0000000..a281369
--- /dev/null
+++ b/sensors/common/convert/include/sensors/common_convert.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/sensor.h>
+#include <hardware/sensors.h>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace implementation {
+namespace common {
+
+sensors_event_t convertASensorEvent(const ASensorEvent& aEvent);
+
+}  // namespace common
+}  // namespace implementation
+}  // namespace sensors
+}  // namespace hardware
+}  // namespace android
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingDevice.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingDevice.aidl
index 50be508..dfd8686 100644
--- a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingDevice.aidl
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingDevice.aidl
@@ -32,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.thermal;
+/* @hide */
 @VintfStability
 parcelable CoolingDevice {
   android.hardware.thermal.CoolingType type;
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingType.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingType.aidl
index 57c8939..d2eb389 100644
--- a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingType.aidl
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingType.aidl
@@ -32,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.thermal;
+/* @hide */
 @Backing(type="int") @VintfStability
 enum CoolingType {
   FAN = 0,
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermal.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermal.aidl
index 0aed5ec..c9b6cab 100644
--- a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermal.aidl
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermal.aidl
@@ -32,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.thermal;
+/* @hide */
 @VintfStability
 interface IThermal {
   android.hardware.thermal.CoolingDevice[] getCoolingDevices();
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermalChangedCallback.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermalChangedCallback.aidl
index 6b3f922..5e1d753 100644
--- a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermalChangedCallback.aidl
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermalChangedCallback.aidl
@@ -32,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.thermal;
+/* @hide */
 @VintfStability
 interface IThermalChangedCallback {
   oneway void notifyThrottling(in android.hardware.thermal.Temperature temperature);
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/Temperature.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/Temperature.aidl
index 7156415..3bf08bf 100644
--- a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/Temperature.aidl
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/Temperature.aidl
@@ -32,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.thermal;
+/* @hide */
 @VintfStability
 parcelable Temperature {
   android.hardware.thermal.TemperatureType type;
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureThreshold.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureThreshold.aidl
index 6da561f..c5ca4b9 100644
--- a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureThreshold.aidl
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureThreshold.aidl
@@ -32,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.thermal;
+/* @hide */
 @VintfStability
 parcelable TemperatureThreshold {
   android.hardware.thermal.TemperatureType type;
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureType.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureType.aidl
index c6a08c1..0a9efdd 100644
--- a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureType.aidl
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureType.aidl
@@ -32,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.thermal;
+/* @hide */
 @Backing(type="int") @VintfStability
 enum TemperatureType {
   UNKNOWN = -1,
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/ThrottlingSeverity.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/ThrottlingSeverity.aidl
index e86b581..8fe3df6 100644
--- a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/ThrottlingSeverity.aidl
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/ThrottlingSeverity.aidl
@@ -32,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.hardware.thermal;
+/* @hide */
 @Backing(type="int") @VintfStability
 enum ThrottlingSeverity {
   NONE = 0,
diff --git a/thermal/aidl/android/hardware/thermal/CoolingDevice.aidl b/thermal/aidl/android/hardware/thermal/CoolingDevice.aidl
index 6d974a5..1f2360d 100644
--- a/thermal/aidl/android/hardware/thermal/CoolingDevice.aidl
+++ b/thermal/aidl/android/hardware/thermal/CoolingDevice.aidl
@@ -18,6 +18,7 @@
 
 import android.hardware.thermal.CoolingType;
 
+/* @hide */
 @VintfStability
 parcelable CoolingDevice {
     /**
diff --git a/thermal/aidl/android/hardware/thermal/CoolingType.aidl b/thermal/aidl/android/hardware/thermal/CoolingType.aidl
index 1b430d2..08beb55 100644
--- a/thermal/aidl/android/hardware/thermal/CoolingType.aidl
+++ b/thermal/aidl/android/hardware/thermal/CoolingType.aidl
@@ -18,6 +18,7 @@
 
 /**
  * Device cooling device types
+ * @hide
  */
 @VintfStability
 @Backing(type="int")
diff --git a/thermal/aidl/android/hardware/thermal/IThermal.aidl b/thermal/aidl/android/hardware/thermal/IThermal.aidl
index 8b79cb4..dd87b3a 100644
--- a/thermal/aidl/android/hardware/thermal/IThermal.aidl
+++ b/thermal/aidl/android/hardware/thermal/IThermal.aidl
@@ -23,6 +23,7 @@
 import android.hardware.thermal.TemperatureThreshold;
 import android.hardware.thermal.TemperatureType;
 
+/* @hide */
 @VintfStability
 interface IThermal {
     /**
diff --git a/thermal/aidl/android/hardware/thermal/IThermalChangedCallback.aidl b/thermal/aidl/android/hardware/thermal/IThermalChangedCallback.aidl
index 6fe2dac..105f085 100644
--- a/thermal/aidl/android/hardware/thermal/IThermalChangedCallback.aidl
+++ b/thermal/aidl/android/hardware/thermal/IThermalChangedCallback.aidl
@@ -20,6 +20,7 @@
 
 /**
  * IThermalChangedCallback send throttling notification to clients.
+ * @hide
  */
 @VintfStability
 interface IThermalChangedCallback {
diff --git a/thermal/aidl/android/hardware/thermal/Temperature.aidl b/thermal/aidl/android/hardware/thermal/Temperature.aidl
index f0041ed..281d68d 100644
--- a/thermal/aidl/android/hardware/thermal/Temperature.aidl
+++ b/thermal/aidl/android/hardware/thermal/Temperature.aidl
@@ -19,6 +19,7 @@
 import android.hardware.thermal.TemperatureType;
 import android.hardware.thermal.ThrottlingSeverity;
 
+/* @hide */
 @VintfStability
 parcelable Temperature {
     /**
diff --git a/thermal/aidl/android/hardware/thermal/TemperatureThreshold.aidl b/thermal/aidl/android/hardware/thermal/TemperatureThreshold.aidl
index 9ecdab3..8065f76 100644
--- a/thermal/aidl/android/hardware/thermal/TemperatureThreshold.aidl
+++ b/thermal/aidl/android/hardware/thermal/TemperatureThreshold.aidl
@@ -18,6 +18,7 @@
 
 import android.hardware.thermal.TemperatureType;
 
+/* @hide */
 @VintfStability
 parcelable TemperatureThreshold {
     /**
diff --git a/thermal/aidl/android/hardware/thermal/TemperatureType.aidl b/thermal/aidl/android/hardware/thermal/TemperatureType.aidl
index aebe7ce..467d096 100644
--- a/thermal/aidl/android/hardware/thermal/TemperatureType.aidl
+++ b/thermal/aidl/android/hardware/thermal/TemperatureType.aidl
@@ -18,6 +18,7 @@
 
 /**
  * Device temperature types
+ * @hide
  */
 @VintfStability
 @Backing(type="int")
diff --git a/thermal/aidl/android/hardware/thermal/ThrottlingSeverity.aidl b/thermal/aidl/android/hardware/thermal/ThrottlingSeverity.aidl
index 29f0724..c66e6c2 100644
--- a/thermal/aidl/android/hardware/thermal/ThrottlingSeverity.aidl
+++ b/thermal/aidl/android/hardware/thermal/ThrottlingSeverity.aidl
@@ -18,6 +18,7 @@
 
 /**
  * Device throttling severity
+ * @hide
  */
 @VintfStability
 @Backing(type="int")
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 6ec8d57..b9ac7b9 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
@@ -43,10 +43,12 @@
   CCC_SUPPORTED_UWB_CONFIGS = 165,
   CCC_SUPPORTED_PULSE_SHAPE_COMBOS = 166,
   CCC_SUPPORTED_RAN_MULTIPLIER = 167,
+  CCC_SUPPORTED_MAX_RANGING_SESSION_NUMBER = 168,
   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,
   SUPPORTED_MIN_SLOT_DURATION = 232,
+  SUPPORTED_MAX_RANGING_SESSION_NUMBER = 233,
 }
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
index b182f9d..a3bb7a6 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
@@ -139,6 +139,11 @@
     /** Int value for indicating supported ran multiplier */
     CCC_SUPPORTED_RAN_MULTIPLIER = 0xA7,
 
+    /**
+     * Int value to indicate supported max number of ccc ranging sessions
+     */
+    CCC_SUPPORTED_MAX_RANGING_SESSION_NUMBER = 0xA8,
+
     /*********************************************
      * FIRA specific
      ********************************************/
@@ -184,4 +189,9 @@
      * 4 byte value to indicate supported min slot duration in ms.
      */
     SUPPORTED_MIN_SLOT_DURATION = 0xE8,
+
+    /**
+     * Int value to indicate supported max number of fira ranging sessions
+     */
+    SUPPORTED_MAX_RANGING_SESSION_NUMBER = 0xE9,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppConfigurationData.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppConfigurationData.aidl
new file mode 100644
index 0000000..2225330
--- /dev/null
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppConfigurationData.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.wifi.supplicant;
+@VintfStability
+parcelable DppConfigurationData {
+  byte[] ssid;
+  String password;
+  byte[] psk;
+  android.hardware.wifi.supplicant.DppAkm securityAkm;
+  android.hardware.wifi.supplicant.DppConnectionKeys dppConnectionKeys;
+  boolean connStatusRequested;
+}
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppStatusErrorCode.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppStatusErrorCode.aidl
new file mode 100644
index 0000000..d72633b
--- /dev/null
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppStatusErrorCode.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.wifi.supplicant;
+@Backing(type="int") @VintfStability
+enum DppStatusErrorCode {
+  UNKNOWN = -1,
+  SUCCESS = 0,
+  NOT_COMPATIBLE = 1,
+  AUTH_FAILURE = 2,
+  UNWRAP_FAILURE = 3,
+  BAD_GROUP = 4,
+  CONFIGURE_FAILURE = 5,
+  RESPONSE_PENDING = 6,
+  INVALID_CONNECTOR = 7,
+  NO_MATCH = 8,
+  CONFIG_REJECTED = 9,
+  NO_AP = 10,
+  CONFIGURE_PENDING = 11,
+  CSR_NEEDED = 12,
+  CSR_BAD = 13,
+  NEW_KEY_NEEDED = 14,
+}
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
index c7dd584..45563b3 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
@@ -44,6 +44,9 @@
   oneway void onDppFailure(in android.hardware.wifi.supplicant.DppFailureCode code, in String ssid, in String channelList, in char[] bandList);
   oneway void onDppProgress(in android.hardware.wifi.supplicant.DppProgressCode code);
   oneway void onDppSuccess(in android.hardware.wifi.supplicant.DppEventType event);
+  /**
+   * @deprecated This callback is deprecated from AIDL v2, newer HAL should call onDppConfigReceived.
+   */
   oneway void onDppSuccessConfigReceived(in byte[] ssid, in String password, in byte[] psk, in android.hardware.wifi.supplicant.DppAkm securityAkm, in android.hardware.wifi.supplicant.DppConnectionKeys dppConnectionKeys);
   oneway void onDppSuccessConfigSent();
   oneway void onEapFailure(in byte[] bssid, in int errorCode);
@@ -64,4 +67,12 @@
   oneway void onQosPolicyReset();
   oneway void onQosPolicyRequest(in int qosPolicyRequestId, in android.hardware.wifi.supplicant.QosPolicyData[] qosPolicyData);
   oneway void onStateChangedWithAkm(in android.hardware.wifi.supplicant.StaIfaceCallbackState newState, in byte[] bssid, in int id, in byte[] ssid, in boolean filsHlpSent, in android.hardware.wifi.supplicant.KeyMgmtMask keyMgmtMask);
+  oneway void onMloLinksInfoChanged(in android.hardware.wifi.supplicant.ISupplicantStaIfaceCallback.MloLinkInfoChangeReason reason);
+  oneway void onDppConfigReceived(in android.hardware.wifi.supplicant.DppConfigurationData configData);
+  oneway void onDppConnectionStatusResultSent(in android.hardware.wifi.supplicant.DppStatusErrorCode code);
+  @Backing(type="int") @VintfStability
+  enum MloLinkInfoChangeReason {
+    TID_TO_LINK_MAP = 0,
+    MULTI_LINK_RECONFIG_AP_REMOVAL = 1,
+  }
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl
index 0bdec34..bfc05a4 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl
@@ -128,6 +128,7 @@
   void setWepTxKeyIdx(in int keyIdx);
   void setRoamingConsortiumSelection(in byte[] selectedRcoi);
   void setMinimumTlsVersionEapPhase1Param(android.hardware.wifi.supplicant.TlsVersion tlsVersion);
+  void setStrictConservativePeerMode(in boolean enable);
   const int SSID_MAX_LEN_IN_BYTES = 32;
   const int PSK_PASSPHRASE_MIN_LEN_IN_BYTES = 8;
   const int PSK_PASSPHRASE_MAX_LEN_IN_BYTES = 63;
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/MloLink.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/MloLink.aidl
index 5e2c47b..f30ca94 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/MloLink.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/MloLink.aidl
@@ -36,4 +36,6 @@
 parcelable MloLink {
   byte linkId;
   byte[] staLinkMacAddress;
+  byte tidsUplinkMap;
+  byte tidsDownlinkMap;
 }
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/DppConfigurationData.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/DppConfigurationData.aidl
new file mode 100644
index 0000000..b25bc40
--- /dev/null
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/DppConfigurationData.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+package android.hardware.wifi.supplicant;
+
+import android.hardware.wifi.supplicant.DppAkm;
+import android.hardware.wifi.supplicant.DppConnectionKeys;
+
+/**
+ * DPP configuration related information.
+ */
+@VintfStability
+parcelable DppConfigurationData {
+    /*
+     * SSID of the network.
+     */
+    byte[] ssid;
+    /*
+     * WPA2 or SAE passphrase.
+     */
+    String password;
+    /*
+     * Pre-shared key encoded in hex.
+     */
+    byte[] psk;
+    /*
+     * AKM that can be provisioned using DPP.
+     */
+    DppAkm securityAkm;
+    /*
+     * Connection keys that are used for DPP network connection.
+     */
+    DppConnectionKeys dppConnectionKeys;
+    /*
+     * Optional flag to indicate that the configurator requested connection status
+     * result.
+     * This flag is set to true if the enrollee receives the configuration response
+     * frame with sendConnStatus attribute.
+     */
+    boolean connStatusRequested;
+}
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/DppStatusErrorCode.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/DppStatusErrorCode.aidl
new file mode 100644
index 0000000..30965d6
--- /dev/null
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/DppStatusErrorCode.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package android.hardware.wifi.supplicant;
+
+/**
+ * DppStatusErrorCode: The possible values for the DPP status and error codes
+ * in the DPP protocol.
+ * See Easy Connect specification V2.0 section 8.3.4 for details.
+ */
+@VintfStability
+@Backing(type="int")
+enum DppStatusErrorCode {
+    UNKNOWN = -1,
+    SUCCESS = 0,
+    NOT_COMPATIBLE = 1,
+    AUTH_FAILURE = 2,
+    UNWRAP_FAILURE = 3,
+    BAD_GROUP = 4,
+    CONFIGURE_FAILURE = 5,
+    RESPONSE_PENDING = 6,
+    INVALID_CONNECTOR = 7,
+    NO_MATCH = 8,
+    CONFIG_REJECTED = 9,
+    NO_AP = 10,
+    CONFIGURE_PENDING = 11,
+    CSR_NEEDED = 12,
+    CSR_BAD = 13,
+    NEW_KEY_NEEDED = 14,
+}
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
index c7961fa..29bb0f9 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
@@ -22,10 +22,12 @@
 import android.hardware.wifi.supplicant.BssTmData;
 import android.hardware.wifi.supplicant.BssidChangeReason;
 import android.hardware.wifi.supplicant.DppAkm;
+import android.hardware.wifi.supplicant.DppConfigurationData;
 import android.hardware.wifi.supplicant.DppConnectionKeys;
 import android.hardware.wifi.supplicant.DppEventType;
 import android.hardware.wifi.supplicant.DppFailureCode;
 import android.hardware.wifi.supplicant.DppProgressCode;
+import android.hardware.wifi.supplicant.DppStatusErrorCode;
 import android.hardware.wifi.supplicant.Hs20AnqpData;
 import android.hardware.wifi.supplicant.KeyMgmtMask;
 import android.hardware.wifi.supplicant.OsuMethod;
@@ -144,6 +146,9 @@
      * Indicates DPP configuration received success event in Enrolee mode.
      * This is also triggered when Configurator generates credentials for itself
      * using generateSelfDppConfiguration() API
+     * <p>
+     * @deprecated This callback is deprecated from AIDL v2, newer HAL should call
+     * onDppConfigReceived.
      */
     void onDppSuccessConfigReceived(in byte[] ssid, in String password, in byte[] psk,
             in DppAkm securityAkm, in DppConnectionKeys dppConnectionKeys);
@@ -329,4 +334,54 @@
      */
     void onStateChangedWithAkm(in StaIfaceCallbackState newState, in byte[] bssid, in int id,
             in byte[] ssid, in boolean filsHlpSent, in KeyMgmtMask keyMgmtMask);
+
+    /**
+     * Reason codes to be used with the callback |ISupplicantStaIfaceCallback.onMloLinksInfoChanged|
+     */
+    @VintfStability
+    @Backing(type="int")
+    enum MloLinkInfoChangeReason {
+        /**
+         * TID-to-link mapping has changed. Updated mappings will be set in
+         * |MloLinksInfo.MloLink[].tids_downlink_map| and
+         * |MloLinksInfo.MloLink[].tids_uplink_map| for each of the links.
+         *
+         * STA MLD will operate in default mode if a TID-to-link mapping is not
+         * indicated by the callback. In default mode, all TIDs are mapped to
+         * all setup links in downlink and uplink directions.
+         */
+        TID_TO_LINK_MAP = 0,
+        /**
+         * Multi-link reconfiguration - AP removal as described in
+         * IEEE 802.11be spec, section 35.3.6. This is a mandatory feature for
+         * station.
+         *
+         * Removed link will not be present in |ISupplicantStaIface.getConnectionMloLinksInfo|.
+         */
+        MULTI_LINK_RECONFIG_AP_REMOVAL = 1,
+    }
+
+    /**
+     * Used to indicate that Multi Link status has changed due to the provided
+     * reason. Upadted MLO link status can be fetched using
+     * |ISupplicantStaIface.getConnectionMloLinksInfo|
+     *
+     * |MloLink.linkId| and |MloLink.staLinkMacAddress| are not expected
+     * to change.
+     *
+     * @param reason Reason as given in MloLinkInfoChangeReason.
+     */
+    void onMloLinksInfoChanged(in MloLinkInfoChangeReason reason);
+
+    /**
+     * Indicates DPP configuration received success event in Enrollee mode.
+     * This is also triggered when Configurator generates credentials for itself
+     * using generateSelfDppConfiguration() API
+     */
+    void onDppConfigReceived(in DppConfigurationData configData);
+
+    /**
+     * Indicates that DPP connection status result frame is sent.
+     */
+    void onDppConnectionStatusResultSent(in DppStatusErrorCode code);
 }
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl
index 44512a9..750cf72 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaNetwork.aidl
@@ -1131,4 +1131,14 @@
      *         |SupplicantStatusCode.FAILURE_NETWORK_INVALID|
      */
     void setMinimumTlsVersionEapPhase1Param(TlsVersion tlsVersion);
+
+    /**
+     * Enable the strict conservative peer mode for EAP-SIM/AKA/AKA'
+     *
+     * @param enable true to enable, false to disable.
+     * @throws ServiceSpecificException with one of the following values:
+     *         |SupplicantStatusCode.FAILURE_UNKNOWN|,
+     *         |SupplicantStatusCode.FAILURE_NETWORK_INVALID|
+     */
+    void setStrictConservativePeerMode(in boolean enable);
 }
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/MloLink.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/MloLink.aidl
index 0e23728..7608e0a 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/MloLink.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/MloLink.aidl
@@ -30,4 +30,32 @@
      * STA Link MAC Address
      */
     byte[/* 6 */] staLinkMacAddress;
+    /**
+     * Bitset where each bit indicates TID mapped to this link in uplink and
+     * downlink direction.
+     *
+     * Traffic Identifier (TID) is an identifier used to classify a packet. It
+     * is represented as a four bit number identifying the QoS traffic within
+     * MAC data service. There are 16 possible values for TID, out of only 8 are
+     * practically used to identify differentiated services.
+     *
+     * A TID-to-link mapping indicates links on which frames belonging to each
+     * TID can be exchanged. IEEE 802.11be draft 2.0 defines the mapping for TID
+     * values between 0 to 7 only. Once associated, an MLO link state is
+     * considered as active if at least one TID is mapped to the link. Link
+     * state is considered as idle if no TID is mapped to the link.
+     *
+     * TIDs can be mapped to uplink, downlink or both directions.
+     * e.g.
+     *  - TID 4 is mapped to this link in uplink direction, if bit 4 in
+     *    MloLink#tids_uplink_map is set.
+     *  - TID 2 is mapped to both directions for this link, if bit 2 of both
+     *    MloLink#tids_uplink_map and MloLink#tids_downlink_map are set.
+     *
+     * In case of default link mapping, tids_uplink_map and tids_downlink_map
+     * is set to 0xFF for all the links.
+     *
+     */
+    byte tidsUplinkMap;
+    byte tidsDownlinkMap;
 }
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
index d57f539..cb2881b 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
@@ -213,6 +213,20 @@
             ::aidl::android::hardware::wifi::supplicant::KeyMgmtMask /* keyMgmtMask*/) override {
         return ndk::ScopedAStatus::ok();
     }
+    ::ndk::ScopedAStatus onMloLinksInfoChanged(
+            ::aidl::android::hardware::wifi::supplicant::ISupplicantStaIfaceCallback::
+                    MloLinkInfoChangeReason /* reason */) override {
+        return ndk::ScopedAStatus::ok();
+    }
+    ::ndk::ScopedAStatus onDppConfigReceived(
+            const ::aidl::android::hardware::wifi::supplicant::
+                    DppConfigurationData& /* configData */) override {
+        return ndk::ScopedAStatus::ok();
+    }
+    ::ndk::ScopedAStatus onDppConnectionStatusResultSent(
+            ::aidl::android::hardware::wifi::supplicant::DppStatusErrorCode /* code */) override {
+        return ndk::ScopedAStatus::ok();
+    }
 };
 
 class SupplicantStaIfaceAidlTest : public testing::TestWithParam<std::string> {
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
index e5d976c..6ff64a5 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
@@ -650,6 +650,14 @@
 }
 
 /*
+ * SetStrictConservativePeerMode
+ */
+TEST_P(SupplicantStaNetworkAidlTest, SetStrictConversativePeerMode) {
+    EXPECT_TRUE(sta_network_->setStrictConservativePeerMode(true).isOk());
+    EXPECT_TRUE(sta_network_->setStrictConservativePeerMode(false).isOk());
+}
+
+/*
  * SendNetworkEapIdentityResponse
  */
 TEST_P(SupplicantStaNetworkAidlTest, SendNetworkEapIdentityResponse) {